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
This commit is contained in:
Jonas Ådahl 2015-12-15 18:14:25 +08:00
parent b3ba8e897e
commit c2643ba5ac
2 changed files with 180 additions and 52 deletions

View File

@ -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;

View File

@ -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)
{
}