wayland: implement support for popup surfaces

Popup surfaces are mapped into override_redirect surfaces
of a DROPDOWN_MENU type, with the addition of a special pointer
grab.

https://bugzilla.gnome.org/show_bug.cgi?id=707863
This commit is contained in:
Giovanni Campagna 2013-09-10 15:53:31 +02:00
parent 81d9797544
commit 76e2455d1b
6 changed files with 211 additions and 37 deletions

View File

@ -678,7 +678,9 @@ void meta_window_update_layer (MetaWindow *window);
void meta_window_recalc_features (MetaWindow *window); void meta_window_recalc_features (MetaWindow *window);
/* recalc_window_type is x11 only, wayland does its thing and then calls type_changed */
void meta_window_recalc_window_type (MetaWindow *window); void meta_window_recalc_window_type (MetaWindow *window);
void meta_window_type_changed (MetaWindow *window);
void meta_window_stack_just_below (MetaWindow *window, void meta_window_stack_just_below (MetaWindow *window,
MetaWindow *below_this_one); MetaWindow *below_this_one);

View File

@ -1049,16 +1049,6 @@ meta_window_new_shared (MetaDisplay *display,
if (client_type == META_WINDOW_CLIENT_TYPE_X11) if (client_type == META_WINDOW_CLIENT_TYPE_X11)
{ {
if (window->override_redirect)
{
window->decorated = FALSE;
window->always_sticky = TRUE;
window->has_close_func = FALSE;
window->has_shade_func = FALSE;
window->has_move_func = FALSE;
window->has_resize_func = FALSE;
}
meta_display_register_x_window (display, &window->xwindow, window); meta_display_register_x_window (display, &window->xwindow, window);
meta_window_update_shape_region_x11 (window); meta_window_update_shape_region_x11 (window);
meta_window_update_input_region_x11 (window); meta_window_update_input_region_x11 (window);
@ -1077,6 +1067,16 @@ meta_window_new_shared (MetaDisplay *display,
else else
meta_wayland_surface_set_initial_state (window->surface, window); meta_wayland_surface_set_initial_state (window->surface, window);
if (window->override_redirect)
{
window->decorated = FALSE;
window->always_sticky = TRUE;
window->has_close_func = FALSE;
window->has_shade_func = FALSE;
window->has_move_func = FALSE;
window->has_resize_func = FALSE;
}
if (!window->override_redirect && if (!window->override_redirect &&
client_type == META_WINDOW_CLIENT_TYPE_X11) client_type == META_WINDOW_CLIENT_TYPE_X11)
{ {
@ -1085,7 +1085,8 @@ meta_window_new_shared (MetaDisplay *display,
meta_window_update_role (window); meta_window_update_role (window);
} }
meta_window_update_net_wm_type (window); if (client_type == META_WINDOW_CLIENT_TYPE_X11)
meta_window_update_net_wm_type (window);
if (!window->override_redirect) if (!window->override_redirect)
meta_window_update_icon_now (window); meta_window_update_icon_now (window);
@ -8549,36 +8550,40 @@ recalc_window_type (MetaWindow *window)
window->type, window->desc, old_type); window->type, window->desc, old_type);
if (old_type != window->type) if (old_type != window->type)
{ meta_window_type_changed (window);
gboolean old_decorated = window->decorated; }
GObject *object = G_OBJECT (window);
window->attached = meta_window_should_attach_to_parent (window); void
recalc_window_features (window); meta_window_type_changed (MetaWindow *window)
{
gboolean old_decorated = window->decorated;
GObject *object = G_OBJECT (window);
if (!window->override_redirect) window->attached = meta_window_should_attach_to_parent (window);
set_net_wm_state (window); recalc_window_features (window);
/* Update frame */ if (!window->override_redirect)
if (window->decorated) set_net_wm_state (window);
meta_window_ensure_frame (window);
else
meta_window_destroy_frame (window);
/* update stacking constraints */ /* Update frame */
meta_window_update_layer (window); if (window->decorated)
meta_window_ensure_frame (window);
else
meta_window_destroy_frame (window);
meta_window_grab_keys (window); /* update stacking constraints */
meta_window_update_layer (window);
g_object_freeze_notify (object); meta_window_grab_keys (window);
if (old_decorated != window->decorated) g_object_freeze_notify (object);
g_object_notify (object, "decorated");
g_object_notify (object, "window-type"); if (old_decorated != window->decorated)
g_object_notify (object, "decorated");
g_object_thaw_notify (object); g_object_notify (object, "window-type");
}
g_object_thaw_notify (object);
} }
static void static void
@ -8676,7 +8681,10 @@ recalc_window_features (MetaWindow *window)
old_always_sticky = window->always_sticky; old_always_sticky = window->always_sticky;
/* Use MWM hints initially */ /* Use MWM hints initially */
window->decorated = window->mwm_decorated; if (window->client_type == META_WINDOW_CLIENT_TYPE_X11)
window->decorated = window->mwm_decorated;
else
window->decorated = FALSE;
window->border_only = window->mwm_border_only; window->border_only = window->mwm_border_only;
window->has_close_func = window->mwm_has_close_func; window->has_close_func = window->mwm_has_close_func;
window->has_minimize_func = window->mwm_has_minimize_func; window->has_minimize_func = window->mwm_has_minimize_func;

View File

@ -51,6 +51,8 @@
#include <string.h> #include <string.h>
static void meta_wayland_pointer_end_popup_grab (MetaWaylandPointer *pointer);
static MetaWaylandSeat * static MetaWaylandSeat *
meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer) meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer)
{ {
@ -109,6 +111,7 @@ default_grab_button (MetaWaylandPointerGrab *grab,
struct wl_client *client = wl_resource_get_client (resource); struct wl_client *client = wl_resource_get_client (resource);
struct wl_display *display = wl_client_get_display (client); struct wl_display *display = wl_client_get_display (client);
serial = wl_display_next_serial (display); serial = wl_display_next_serial (display);
pointer->click_serial = serial;
wl_pointer_send_button (resource, serial, time, button, state_w); wl_pointer_send_button (resource, serial, time, button, state_w);
} }
@ -459,3 +462,105 @@ meta_wayland_pointer_destroy_focus (MetaWaylandPointer *pointer)
meta_wayland_pointer_set_focus (pointer, NULL, 0, 0); meta_wayland_pointer_set_focus (pointer, NULL, 0, 0);
} }
} }
static void
popup_grab_focus (MetaWaylandPointerGrab *grab,
MetaWaylandSurface *surface,
wl_fixed_t x,
wl_fixed_t y)
{
/* Popup grabs are in owner-events mode (ie, events for the same client
are reported as normal) */
if (wl_resource_get_client (surface->resource) ==
wl_resource_get_client (grab->focus->resource))
default_grab_focus (grab, surface, x, y);
else
meta_wayland_pointer_set_focus (grab->pointer, NULL, 0, 0);
}
static void
popup_grab_motion (MetaWaylandPointerGrab *grab,
uint32_t time,
wl_fixed_t x,
wl_fixed_t y)
{
default_grab_motion (grab, time, x, y);
}
static void
popup_grab_button (MetaWaylandPointerGrab *grab,
uint32_t time,
uint32_t button,
uint32_t state)
{
MetaWaylandPointer *pointer = grab->pointer;
if (pointer->focus_resource)
{
/* This is ensured by popup_grab_focus */
g_assert (wl_resource_get_client (pointer->focus_resource) ==
wl_resource_get_client (grab->focus->resource));
default_grab_button (grab, time, button, state);
}
else if (state == WL_POINTER_BUTTON_STATE_RELEASED &&
pointer->button_count == 0)
meta_wayland_pointer_end_popup_grab (grab->pointer);
}
static MetaWaylandPointerGrabInterface popup_grab = {
popup_grab_focus,
popup_grab_motion,
popup_grab_button
};
static void
meta_wayland_pointer_end_popup_grab (MetaWaylandPointer *pointer)
{
MetaWaylandPointerGrab *grab;
grab = pointer->grab;
g_assert (grab->interface == &popup_grab);
if (grab->focus)
{
wl_shell_surface_send_popup_done (grab->focus->shell_surface->resource);
wl_list_remove (&grab->focus_destroy_listener.link);
}
meta_wayland_pointer_end_grab (pointer);
g_slice_free (MetaWaylandPointerGrab, grab);
}
static void
on_popup_surface_destroy (struct wl_listener *listener,
void *data)
{
MetaWaylandPointerGrab *grab =
wl_container_of (listener, grab, focus_destroy_listener);
grab->focus = NULL;
meta_wayland_pointer_end_popup_grab (grab->pointer);
}
gboolean
meta_wayland_pointer_start_popup_grab (MetaWaylandPointer *pointer,
MetaWaylandSurface *surface)
{
MetaWaylandPointerGrab *grab;
if (pointer->grab != &pointer->default_grab)
return FALSE;
grab = g_slice_new0 (MetaWaylandPointerGrab);
grab->interface = &popup_grab;
grab->pointer = pointer;
grab->focus = surface;
grab->focus_destroy_listener.notify = on_popup_surface_destroy;
wl_resource_add_destroy_listener (surface->resource, &grab->focus_destroy_listener);
meta_wayland_pointer_start_grab (pointer, grab);
return TRUE;
}

View File

@ -42,6 +42,8 @@ struct _MetaWaylandPointerGrab
MetaWaylandPointer *pointer; MetaWaylandPointer *pointer;
MetaWaylandSurface *focus; MetaWaylandSurface *focus;
wl_fixed_t x, y; wl_fixed_t x, y;
struct wl_listener focus_destroy_listener;
}; };
struct _MetaWaylandPointer struct _MetaWaylandPointer
@ -51,6 +53,7 @@ struct _MetaWaylandPointer
struct wl_resource *focus_resource; struct wl_resource *focus_resource;
struct wl_listener focus_listener; struct wl_listener focus_listener;
guint32 focus_serial; guint32 focus_serial;
guint32 click_serial;
struct wl_signal focus_signal; struct wl_signal focus_signal;
MetaWaylandPointerGrab *grab; MetaWaylandPointerGrab *grab;
@ -96,6 +99,10 @@ meta_wayland_pointer_begin_modal (MetaWaylandPointer *pointer);
void void
meta_wayland_pointer_end_modal (MetaWaylandPointer *pointer); meta_wayland_pointer_end_modal (MetaWaylandPointer *pointer);
gboolean
meta_wayland_pointer_start_popup_grab (MetaWaylandPointer *pointer,
MetaWaylandSurface *popup);
void void
meta_wayland_pointer_set_current (MetaWaylandPointer *pointer, meta_wayland_pointer_set_current (MetaWaylandPointer *pointer,
MetaWaylandSurface *surface); MetaWaylandSurface *surface);

View File

@ -758,13 +758,38 @@ shell_surface_set_fullscreen (struct wl_client *client,
static void static void
shell_surface_set_popup (struct wl_client *client, shell_surface_set_popup (struct wl_client *client,
struct wl_resource *resource, struct wl_resource *resource,
struct wl_resource *seat, struct wl_resource *seat_resource,
guint32 serial, guint32 serial,
struct wl_resource *parent, struct wl_resource *parent,
gint32 x, gint32 x,
gint32 y, gint32 y,
guint32 flags) guint32 flags)
{ {
MetaWaylandSurfaceExtension *shell_surface = wl_resource_get_user_data (resource);
MetaWaylandSurface *surface = shell_surface->surface;
MetaWaylandCompositor *compositor = surface->compositor;
MetaWaylandSeat *seat = compositor->seat;
if (serial < seat->pointer.click_serial)
{
/* stale request */
return;
}
if (surface->window)
{
meta_warning ("Client set_popup() on an already visible window, this is not supported\n");
}
else
{
ensure_initial_state (surface);
surface->initial_state->initial_type = META_WAYLAND_SURFACE_POPUP;
surface->initial_state->transient_for = parent;
surface->initial_state->x = x;
surface->initial_state->y = y;
}
} }
static void static void
@ -1060,10 +1085,14 @@ meta_wayland_surface_set_initial_state (MetaWaylandSurface *surface,
MetaWindow *window) MetaWindow *window)
{ {
MetaWaylandSurfaceInitialState *initial = surface->initial_state; MetaWaylandSurfaceInitialState *initial = surface->initial_state;
MetaWaylandCompositor *compositor = surface->compositor;
MetaWaylandSeat *seat = compositor->seat;
if (initial == NULL) if (initial == NULL)
return; return;
window->type = META_WINDOW_NORMAL;
/* Note that we poke at the bits directly here, because we're /* Note that we poke at the bits directly here, because we're
in the middle of meta_window_new_shared() */ in the middle of meta_window_new_shared() */
switch (initial->initial_type) switch (initial->initial_type)
@ -1076,6 +1105,15 @@ meta_wayland_surface_set_initial_state (MetaWaylandSurface *surface,
case META_WAYLAND_SURFACE_MAXIMIZED: case META_WAYLAND_SURFACE_MAXIMIZED:
window->maximized_horizontally = window->maximized_vertically = TRUE; window->maximized_horizontally = window->maximized_vertically = TRUE;
break; break;
case META_WAYLAND_SURFACE_POPUP:
window->override_redirect = TRUE;
window->type = META_WINDOW_DROPDOWN_MENU;
window->mapped = TRUE;
window->showing_for_first_time = FALSE;
window->placed = TRUE;
if (!meta_wayland_pointer_start_popup_grab (&seat->pointer, surface))
wl_shell_surface_send_popup_done (surface->shell_surface->resource);
break;
default: default:
g_assert_not_reached (); g_assert_not_reached ();
} }
@ -1083,8 +1121,18 @@ meta_wayland_surface_set_initial_state (MetaWaylandSurface *surface,
if (initial->transient_for) if (initial->transient_for)
{ {
MetaWaylandSurface *parent = wl_resource_get_user_data (initial->transient_for); MetaWaylandSurface *parent = wl_resource_get_user_data (initial->transient_for);
if (parent) if (parent && parent->window)
window->transient_for = g_object_ref (parent->window); {
window->transient_for = g_object_ref (parent->window);
if (initial->initial_type == META_WAYLAND_SURFACE_POPUP)
{
window->rect.x = parent->window->rect.x + initial->x;
window->rect.y = parent->window->rect.y + initial->y;
}
}
window->type = META_WINDOW_DIALOG;
} }
if (initial->title) if (initial->title)
@ -1101,6 +1149,8 @@ meta_wayland_surface_set_initial_state (MetaWaylandSurface *surface,
initial->gtk_application_object_path, initial->gtk_application_object_path,
initial->gtk_window_object_path); initial->gtk_window_object_path);
meta_window_type_changed (window);
free_initial_state (initial); free_initial_state (initial);
surface->initial_state = NULL; surface->initial_state = NULL;
} }

View File

@ -69,13 +69,15 @@ typedef struct
typedef enum { typedef enum {
META_WAYLAND_SURFACE_TOPLEVEL = 0, META_WAYLAND_SURFACE_TOPLEVEL = 0,
META_WAYLAND_SURFACE_MAXIMIZED, META_WAYLAND_SURFACE_MAXIMIZED,
META_WAYLAND_SURFACE_FULLSCREEN META_WAYLAND_SURFACE_FULLSCREEN,
META_WAYLAND_SURFACE_POPUP,
} MetaWaylandSurfaceType; } MetaWaylandSurfaceType;
typedef struct typedef struct
{ {
MetaWaylandSurfaceType initial_type; MetaWaylandSurfaceType initial_type;
struct wl_resource *transient_for; struct wl_resource *transient_for;
int x, y;
char *title; char *title;
char *wm_class; char *wm_class;