From c2643ba5aceb1f6cb5da62882c98a2b1eed6e6f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 15 Dec 2015 18:14:25 +0800 Subject: [PATCH] wayland: Keep wl_shell_surface state during loss of window It has been common practice (in QT5 for example) to set wl_shell_surface state at situations where mutter will have destroyed the MetaWindow. This commit keeps track of the relevant state separately from MetaWindow, and synchronizes when needed. https://bugzilla.gnome.org/show_bug.cgi?id=757623 https://bugzilla.gnome.org/show_bug.cgi?id=763431 --- src/wayland/meta-wayland-surface.h | 27 ++++ src/wayland/meta-wayland-wl-shell.c | 205 +++++++++++++++++++++------- 2 files changed, 180 insertions(+), 52 deletions(-) diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h index cf6b79b35..190ee1f14 100644 --- a/src/wayland/meta-wayland-surface.h +++ b/src/wayland/meta-wayland-surface.h @@ -156,6 +156,16 @@ struct _MetaWaylandDragDestFuncs MetaWaylandSurface *surface); }; +typedef enum +{ + META_WL_SHELL_SURFACE_STATE_NONE, + META_WL_SHELL_SURFACE_STATE_TOPLEVEL, + META_WL_SHELL_SURFACE_STATE_POPUP, + META_WL_SHELL_SURFACE_STATE_TRANSIENT, + META_WL_SHELL_SURFACE_STATE_FULLSCREEN, + META_WL_SHELL_SURFACE_STATE_MAXIMIZED, +} MetaWlShellSurfaceState; + struct _MetaWaylandSurface { GObject parent; @@ -221,6 +231,23 @@ struct _MetaWaylandSurface struct wl_listener destroy_listener; } popup; + /* wl_shell_surface */ + struct { + MetaWlShellSurfaceState state; + + char *title; + char *wm_class; + + gboolean pending_popup; + MetaWaylandSurface *parent_surface; + GList *children; + + MetaWaylandSeat *popup_seat; + + int x; + int y; + } wl_shell; + /* wl_subsurface stuff. */ struct { MetaWaylandSurface *parent; diff --git a/src/wayland/meta-wayland-wl-shell.c b/src/wayland/meta-wayland-wl-shell.c index aecbb1823..6933ebba3 100644 --- a/src/wayland/meta-wayland-wl-shell.c +++ b/src/wayland/meta-wayland-wl-shell.c @@ -34,14 +34,6 @@ #include "wayland/meta-wayland-versions.h" #include "wayland/meta-window-wayland.h" -typedef enum -{ - META_WL_SHELL_SURFACE_STATE_NONE, - META_WL_SHELL_SURFACE_STATE_TOPLEVEL, - META_WL_SHELL_SURFACE_STATE_FULLSCREEN, - META_WL_SHELL_SURFACE_STATE_MAXIMIZED, -} MetaWlShellSurfaceState; - struct _MetaWaylandSurfaceRoleWlShellSurface { MetaWaylandSurfaceRoleShellSurface parent; @@ -51,14 +43,36 @@ G_DEFINE_TYPE (MetaWaylandSurfaceRoleWlShellSurface, meta_wayland_surface_role_wl_shell_surface, META_TYPE_WAYLAND_SURFACE_ROLE_SHELL_SURFACE); +static void +sync_wl_shell_parent_relationship (MetaWaylandSurface *surface, + MetaWaylandSurface *parent); + static void wl_shell_surface_destructor (struct wl_resource *resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); + GList *l; meta_wayland_compositor_destroy_frame_callbacks (surface->compositor, surface); - surface->wl_shell_surface = NULL; + + for (l = surface->wl_shell.children; l; l = l->next) + { + MetaWaylandSurface *child_surface = l->data; + + child_surface->wl_shell.parent_surface = NULL; + } + + if (surface->wl_shell.parent_surface) + { + MetaWaylandSurface *parent_surface = surface->wl_shell.parent_surface; + + parent_surface->wl_shell.children = + g_list_remove (parent_surface->wl_shell.children, surface); + } + + g_free (surface->wl_shell.title); + g_free (surface->wl_shell.wm_class); if (surface->popup.popup) { @@ -68,11 +82,7 @@ wl_shell_surface_destructor (struct wl_resource *resource) meta_wayland_popup_dismiss (surface->popup.popup); } - if (surface->popup.parent) - { - wl_list_remove (&surface->popup.parent_destroy_listener.link); - surface->popup.parent = NULL; - } + surface->wl_shell_surface = NULL; } static void @@ -147,15 +157,22 @@ static void wl_shell_surface_set_state (MetaWaylandSurface *surface, MetaWlShellSurfaceState state) { - if (state == META_WL_SHELL_SURFACE_STATE_FULLSCREEN) - meta_window_make_fullscreen (surface->window); - else - meta_window_unmake_fullscreen (surface->window); + MetaWlShellSurfaceState old_state = surface->wl_shell.state; - if (state == META_WL_SHELL_SURFACE_STATE_MAXIMIZED) - meta_window_maximize (surface->window, META_MAXIMIZE_BOTH); - else - meta_window_unmaximize (surface->window, META_MAXIMIZE_BOTH); + surface->wl_shell.state = state; + + if (surface->window && old_state != state) + { + if (state == META_WL_SHELL_SURFACE_STATE_FULLSCREEN) + meta_window_make_fullscreen (surface->window); + else + meta_window_unmake_fullscreen (surface->window); + + if (state == META_WL_SHELL_SURFACE_STATE_MAXIMIZED) + meta_window_maximize (surface->window, META_MAXIMIZE_BOTH); + else + meta_window_unmaximize (surface->window, META_MAXIMIZE_BOTH); + } } static void @@ -168,6 +185,23 @@ wl_shell_surface_set_toplevel (struct wl_client *client, META_WL_SHELL_SURFACE_STATE_TOPLEVEL); } +static void +set_wl_shell_surface_parent (MetaWaylandSurface *surface, + MetaWaylandSurface *parent) +{ + MetaWaylandSurface *old_parent = surface->wl_shell.parent_surface; + + if (old_parent) + { + old_parent->wl_shell.children = + g_list_remove (old_parent->wl_shell.children, surface); + } + + parent->wl_shell.children = g_list_append (parent->wl_shell.children, + surface); + surface->wl_shell.parent_surface = parent; +} + static void wl_shell_surface_set_transient (struct wl_client *client, struct wl_resource *resource, @@ -180,12 +214,14 @@ wl_shell_surface_set_transient (struct wl_client *client, MetaWaylandSurface *surface = wl_resource_get_user_data (resource); wl_shell_surface_set_state (surface, - META_WL_SHELL_SURFACE_STATE_TOPLEVEL); + META_WL_SHELL_SURFACE_STATE_TRANSIENT); - meta_window_set_transient_for (surface->window, parent_surf->window); - meta_window_wayland_place_relative_to (surface->window, - parent_surf->window, - x, y); + set_wl_shell_surface_parent (surface, parent_surf); + surface->wl_shell.x = x; + surface->wl_shell.y = y; + + if (surface->window && parent_surf->window) + sync_wl_shell_parent_relationship (surface, parent_surf); } static void @@ -222,6 +258,25 @@ handle_wl_shell_popup_destroyed (struct wl_listener *listener, surface->popup.popup = NULL; } +static void +create_popup (MetaWaylandSurface *surface) +{ + MetaWaylandSeat *seat = surface->wl_shell.popup_seat; + MetaWaylandPopup *popup; + + popup = meta_wayland_pointer_start_popup_grab (&seat->pointer, surface); + if (!popup) + { + wl_shell_surface_send_popup_done (surface->wl_shell_surface); + return; + } + + surface->popup.popup = popup; + surface->popup.destroy_listener.notify = handle_wl_shell_popup_destroyed; + wl_signal_add (meta_wayland_popup_get_destroy_signal (popup), + &surface->popup.destroy_listener); +} + static void wl_shell_surface_set_popup (struct wl_client *client, struct wl_resource *resource, @@ -235,7 +290,6 @@ wl_shell_surface_set_popup (struct wl_client *client, MetaWaylandSurface *surface = wl_resource_get_user_data (resource); MetaWaylandSurface *parent_surf = wl_resource_get_user_data (parent_resource); MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); - MetaWaylandPopup *popup; if (surface->popup.popup) { @@ -246,7 +300,7 @@ wl_shell_surface_set_popup (struct wl_client *client, } wl_shell_surface_set_state (surface, - META_WL_SHELL_SURFACE_STATE_TOPLEVEL); + META_WL_SHELL_SURFACE_STATE_POPUP); if (!meta_wayland_seat_can_popup (seat, serial)) { @@ -254,28 +308,20 @@ wl_shell_surface_set_popup (struct wl_client *client, return; } - meta_window_set_transient_for (surface->window, parent_surf->window); - meta_window_wayland_place_relative_to (surface->window, - parent_surf->window, - x, y); - surface->popup.parent = parent_surf; surface->popup.parent_destroy_listener.notify = handle_wl_shell_popup_parent_destroyed; wl_resource_add_destroy_listener (parent_surf->resource, &surface->popup.parent_destroy_listener); - popup = meta_wayland_pointer_start_popup_grab (&seat->pointer, surface); - if (!popup) - { - wl_shell_surface_send_popup_done (resource); - return; - } + set_wl_shell_surface_parent (surface, parent_surf); + surface->wl_shell.popup_seat = seat; + surface->wl_shell.x = x; + surface->wl_shell.y = y; + surface->wl_shell.pending_popup = TRUE; - surface->popup.popup = popup; - surface->popup.destroy_listener.notify = handle_wl_shell_popup_destroyed; - wl_signal_add (meta_wayland_popup_get_destroy_signal (popup), - &surface->popup.destroy_listener); + if (surface->window && parent_surf->window) + sync_wl_shell_parent_relationship (surface, parent_surf); } static void @@ -296,7 +342,11 @@ wl_shell_surface_set_title (struct wl_client *client, { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); - meta_window_set_title (surface->window, title); + g_clear_pointer (&surface->wl_shell.title, g_free); + surface->wl_shell.title = g_strdup (title); + + if (surface->window) + meta_window_set_title (surface->window, title); } static void @@ -306,7 +356,11 @@ wl_shell_surface_set_class (struct wl_client *client, { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); - meta_window_set_wm_class (surface->window, class_, class_); + g_clear_pointer (&surface->wl_shell.wm_class, g_free); + surface->wl_shell.wm_class = g_strdup (class_); + + if (surface->window) + meta_window_set_wm_class (surface->window, class_, class_); } static const struct wl_shell_surface_interface meta_wayland_wl_shell_surface_interface = { @@ -322,6 +376,56 @@ static const struct wl_shell_surface_interface meta_wayland_wl_shell_surface_int wl_shell_surface_set_class, }; +static void +sync_wl_shell_parent_relationship (MetaWaylandSurface *surface, + MetaWaylandSurface *parent) +{ + meta_window_set_transient_for (surface->window, parent->window); + + if (surface->wl_shell.state == META_WL_SHELL_SURFACE_STATE_POPUP || + surface->wl_shell.state == META_WL_SHELL_SURFACE_STATE_TRANSIENT) + meta_window_wayland_place_relative_to (surface->window, + parent->window, + surface->wl_shell.x, + surface->wl_shell.y); + + if (surface->wl_shell.state == META_WL_SHELL_SURFACE_STATE_POPUP && + surface->wl_shell.pending_popup) + { + create_popup (surface); + surface->wl_shell.pending_popup = FALSE; + } +} + +static void +create_wl_shell_surface_window (MetaWaylandSurface *surface) +{ + MetaWaylandSurface *parent; + GList *l; + + surface->window = meta_window_wayland_new (meta_get_display (), surface); + meta_wayland_surface_set_window (surface, surface->window); + + if (surface->wl_shell.title) + meta_window_set_title (surface->window, surface->wl_shell.title); + if (surface->wl_shell.wm_class) + meta_window_set_wm_class (surface->window, + surface->wl_shell.wm_class, + surface->wl_shell.wm_class); + + parent = surface->wl_shell.parent_surface; + if (parent && parent->window) + sync_wl_shell_parent_relationship (surface, parent); + + for (l = surface->wl_shell.children; l; l = l->next) + { + MetaWaylandSurface *child = l->data; + + if (child->window) + sync_wl_shell_parent_relationship (child, surface); + } +} + static void wl_shell_get_shell_surface (struct wl_client *client, struct wl_resource *resource, @@ -329,7 +433,6 @@ wl_shell_get_shell_surface (struct wl_client *client, struct wl_resource *surface_resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); - MetaWindow *window; if (surface->wl_shell_surface != NULL) { @@ -357,8 +460,7 @@ wl_shell_get_shell_surface (struct wl_client *client, surface, wl_shell_surface_destructor); - window = meta_window_wayland_new (meta_get_display (), surface); - meta_wayland_surface_set_window (surface, window); + create_wl_shell_surface_window (surface); } static const struct wl_shell_interface meta_wayland_wl_shell_interface = { @@ -395,8 +497,7 @@ wl_shell_surface_role_commit (MetaWaylandSurfaceRole *surface_role, * convenient for us. */ if (surface->buffer_ref.buffer && !window) { - window = meta_window_wayland_new (meta_get_display (), surface); - meta_wayland_surface_set_window (surface, window); + create_wl_shell_surface_window (surface); } else if (!surface->buffer_ref.buffer && window) { @@ -457,7 +558,7 @@ wl_shell_surface_role_popup_done (MetaWaylandSurfaceRoleShellSurface *shell_surf } static void -meta_wayland_surface_role_wl_shell_surface_init (MetaWaylandSurfaceRoleWlShellSurface *role) +meta_wayland_surface_role_wl_shell_surface_init (MetaWaylandSurfaceRoleWlShellSurface *wl_shell_surface) { }