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); 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 struct _MetaWaylandSurface
{ {
GObject parent; GObject parent;
@ -221,6 +231,23 @@ struct _MetaWaylandSurface
struct wl_listener destroy_listener; struct wl_listener destroy_listener;
} popup; } 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. */ /* wl_subsurface stuff. */
struct { struct {
MetaWaylandSurface *parent; MetaWaylandSurface *parent;

View File

@ -34,14 +34,6 @@
#include "wayland/meta-wayland-versions.h" #include "wayland/meta-wayland-versions.h"
#include "wayland/meta-window-wayland.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 struct _MetaWaylandSurfaceRoleWlShellSurface
{ {
MetaWaylandSurfaceRoleShellSurface parent; MetaWaylandSurfaceRoleShellSurface parent;
@ -51,14 +43,36 @@ G_DEFINE_TYPE (MetaWaylandSurfaceRoleWlShellSurface,
meta_wayland_surface_role_wl_shell_surface, meta_wayland_surface_role_wl_shell_surface,
META_TYPE_WAYLAND_SURFACE_ROLE_SHELL_SURFACE); META_TYPE_WAYLAND_SURFACE_ROLE_SHELL_SURFACE);
static void
sync_wl_shell_parent_relationship (MetaWaylandSurface *surface,
MetaWaylandSurface *parent);
static void static void
wl_shell_surface_destructor (struct wl_resource *resource) wl_shell_surface_destructor (struct wl_resource *resource)
{ {
MetaWaylandSurface *surface = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
GList *l;
meta_wayland_compositor_destroy_frame_callbacks (surface->compositor, meta_wayland_compositor_destroy_frame_callbacks (surface->compositor,
surface); 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) if (surface->popup.popup)
{ {
@ -68,11 +82,7 @@ wl_shell_surface_destructor (struct wl_resource *resource)
meta_wayland_popup_dismiss (surface->popup.popup); meta_wayland_popup_dismiss (surface->popup.popup);
} }
if (surface->popup.parent) surface->wl_shell_surface = NULL;
{
wl_list_remove (&surface->popup.parent_destroy_listener.link);
surface->popup.parent = NULL;
}
} }
static void static void
@ -146,6 +156,12 @@ wl_shell_surface_resize (struct wl_client *client,
static void static void
wl_shell_surface_set_state (MetaWaylandSurface *surface, wl_shell_surface_set_state (MetaWaylandSurface *surface,
MetaWlShellSurfaceState state) MetaWlShellSurfaceState state)
{
MetaWlShellSurfaceState old_state = surface->wl_shell.state;
surface->wl_shell.state = state;
if (surface->window && old_state != state)
{ {
if (state == META_WL_SHELL_SURFACE_STATE_FULLSCREEN) if (state == META_WL_SHELL_SURFACE_STATE_FULLSCREEN)
meta_window_make_fullscreen (surface->window); meta_window_make_fullscreen (surface->window);
@ -157,6 +173,7 @@ wl_shell_surface_set_state (MetaWaylandSurface *surface,
else else
meta_window_unmaximize (surface->window, META_MAXIMIZE_BOTH); meta_window_unmaximize (surface->window, META_MAXIMIZE_BOTH);
} }
}
static void static void
wl_shell_surface_set_toplevel (struct wl_client *client, wl_shell_surface_set_toplevel (struct wl_client *client,
@ -168,6 +185,23 @@ wl_shell_surface_set_toplevel (struct wl_client *client,
META_WL_SHELL_SURFACE_STATE_TOPLEVEL); 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 static void
wl_shell_surface_set_transient (struct wl_client *client, wl_shell_surface_set_transient (struct wl_client *client,
struct wl_resource *resource, 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); MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
wl_shell_surface_set_state (surface, 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); set_wl_shell_surface_parent (surface, parent_surf);
meta_window_wayland_place_relative_to (surface->window, surface->wl_shell.x = x;
parent_surf->window, surface->wl_shell.y = y;
x, y);
if (surface->window && parent_surf->window)
sync_wl_shell_parent_relationship (surface, parent_surf);
} }
static void static void
@ -222,6 +258,25 @@ handle_wl_shell_popup_destroyed (struct wl_listener *listener,
surface->popup.popup = NULL; 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 static void
wl_shell_surface_set_popup (struct wl_client *client, wl_shell_surface_set_popup (struct wl_client *client,
struct wl_resource *resource, 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 *surface = wl_resource_get_user_data (resource);
MetaWaylandSurface *parent_surf = wl_resource_get_user_data (parent_resource); MetaWaylandSurface *parent_surf = wl_resource_get_user_data (parent_resource);
MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
MetaWaylandPopup *popup;
if (surface->popup.popup) if (surface->popup.popup)
{ {
@ -246,7 +300,7 @@ wl_shell_surface_set_popup (struct wl_client *client,
} }
wl_shell_surface_set_state (surface, 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)) if (!meta_wayland_seat_can_popup (seat, serial))
{ {
@ -254,28 +308,20 @@ wl_shell_surface_set_popup (struct wl_client *client,
return; 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 = parent_surf;
surface->popup.parent_destroy_listener.notify = surface->popup.parent_destroy_listener.notify =
handle_wl_shell_popup_parent_destroyed; handle_wl_shell_popup_parent_destroyed;
wl_resource_add_destroy_listener (parent_surf->resource, wl_resource_add_destroy_listener (parent_surf->resource,
&surface->popup.parent_destroy_listener); &surface->popup.parent_destroy_listener);
popup = meta_wayland_pointer_start_popup_grab (&seat->pointer, surface); set_wl_shell_surface_parent (surface, parent_surf);
if (!popup) surface->wl_shell.popup_seat = seat;
{ surface->wl_shell.x = x;
wl_shell_surface_send_popup_done (resource); surface->wl_shell.y = y;
return; surface->wl_shell.pending_popup = TRUE;
}
surface->popup.popup = popup; if (surface->window && parent_surf->window)
surface->popup.destroy_listener.notify = handle_wl_shell_popup_destroyed; sync_wl_shell_parent_relationship (surface, parent_surf);
wl_signal_add (meta_wayland_popup_get_destroy_signal (popup),
&surface->popup.destroy_listener);
} }
static void static void
@ -296,6 +342,10 @@ wl_shell_surface_set_title (struct wl_client *client,
{ {
MetaWaylandSurface *surface = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
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); meta_window_set_title (surface->window, title);
} }
@ -306,6 +356,10 @@ wl_shell_surface_set_class (struct wl_client *client,
{ {
MetaWaylandSurface *surface = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
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_); meta_window_set_wm_class (surface->window, class_, class_);
} }
@ -322,6 +376,56 @@ static const struct wl_shell_surface_interface meta_wayland_wl_shell_surface_int
wl_shell_surface_set_class, 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 static void
wl_shell_get_shell_surface (struct wl_client *client, wl_shell_get_shell_surface (struct wl_client *client,
struct wl_resource *resource, struct wl_resource *resource,
@ -329,7 +433,6 @@ wl_shell_get_shell_surface (struct wl_client *client,
struct wl_resource *surface_resource) struct wl_resource *surface_resource)
{ {
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
MetaWindow *window;
if (surface->wl_shell_surface != NULL) if (surface->wl_shell_surface != NULL)
{ {
@ -357,8 +460,7 @@ wl_shell_get_shell_surface (struct wl_client *client,
surface, surface,
wl_shell_surface_destructor); wl_shell_surface_destructor);
window = meta_window_wayland_new (meta_get_display (), surface); create_wl_shell_surface_window (surface);
meta_wayland_surface_set_window (surface, window);
} }
static const struct wl_shell_interface meta_wayland_wl_shell_interface = { 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. */ * convenient for us. */
if (surface->buffer_ref.buffer && !window) if (surface->buffer_ref.buffer && !window)
{ {
window = meta_window_wayland_new (meta_get_display (), surface); create_wl_shell_surface_window (surface);
meta_wayland_surface_set_window (surface, window);
} }
else if (!surface->buffer_ref.buffer && window) else if (!surface->buffer_ref.buffer && window)
{ {
@ -457,7 +558,7 @@ wl_shell_surface_role_popup_done (MetaWaylandSurfaceRoleShellSurface *shell_surf
} }
static void static void
meta_wayland_surface_role_wl_shell_surface_init (MetaWaylandSurfaceRoleWlShellSurface *role) meta_wayland_surface_role_wl_shell_surface_init (MetaWaylandSurfaceRoleWlShellSurface *wl_shell_surface)
{ {
} }