From 5ade2060a7eaf01157b46c52501343fddd77672f Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Wed, 15 Nov 2023 18:52:51 +0100 Subject: [PATCH] wayland: Port popup grabs to MetaWaylandEventInterface This is again a grab interface that mostly wants to meddle with focus, logically setting a NULL surface if the surface client does not match the popup client. Since popups are meant to naturally work with any input device, the code has been refactored to not involve the MetaWaylandPointer directly in MetaWaylandPopup creation or getting the top popup surface (memory management was shuffled), or compressing multiple grabbing xdg_popups together (the existing grab maintains a single MetaWaylandEventHandler for all). Part-of: --- src/wayland/meta-wayland-pointer.c | 38 ----- src/wayland/meta-wayland-pointer.h | 7 - src/wayland/meta-wayland-popup.c | 230 ++++++++++++++++----------- src/wayland/meta-wayland-popup.h | 7 +- src/wayland/meta-wayland-xdg-shell.c | 40 ++++- 5 files changed, 177 insertions(+), 145 deletions(-) diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c index 06e3cf713..c15e39fcc 100644 --- a/src/wayland/meta-wayland-pointer.c +++ b/src/wayland/meta-wayland-pointer.c @@ -1139,32 +1139,6 @@ meta_wayland_pointer_cancel_grab (MetaWaylandPointer *pointer) pointer->grab->interface->cancel (pointer->grab); } -void -meta_wayland_pointer_end_popup_grab (MetaWaylandPointer *pointer) -{ - MetaWaylandPopupGrab *popup_grab = (MetaWaylandPopupGrab*)pointer->grab; - - meta_wayland_popup_grab_destroy (popup_grab); -} - -MetaWaylandPopup * -meta_wayland_pointer_start_popup_grab (MetaWaylandPointer *pointer, - MetaWaylandPopupSurface *popup_surface) -{ - MetaWaylandPopupGrab *grab; - - if (pointer->grab != &pointer->default_grab && - !meta_wayland_pointer_grab_is_popup_grab (pointer->grab)) - return NULL; - - if (pointer->grab == &pointer->default_grab) - grab = meta_wayland_popup_grab_create (pointer, popup_surface); - else - grab = (MetaWaylandPopupGrab*)pointer->grab; - - return meta_wayland_popup_create (popup_surface, grab); -} - void meta_wayland_pointer_get_relative_coordinates (MetaWaylandPointer *pointer, MetaWaylandSurface *surface, @@ -1384,18 +1358,6 @@ meta_wayland_pointer_can_popup (MetaWaylandPointer *pointer, uint32_t serial) return pointer->grab_serial == serial; } -MetaWaylandSurface * -meta_wayland_pointer_get_top_popup (MetaWaylandPointer *pointer) -{ - MetaWaylandPopupGrab *grab; - - if (!meta_wayland_pointer_grab_is_popup_grab (pointer->grab)) - return NULL; - - grab = (MetaWaylandPopupGrab*)pointer->grab; - return meta_wayland_popup_grab_get_top_popup(grab); -} - static void relative_pointer_destroy (struct wl_client *client, struct wl_resource *resource) diff --git a/src/wayland/meta-wayland-pointer.h b/src/wayland/meta-wayland-pointer.h index 5e64fe623..b25fccbca 100644 --- a/src/wayland/meta-wayland-pointer.h +++ b/src/wayland/meta-wayland-pointer.h @@ -122,11 +122,6 @@ void meta_wayland_pointer_start_grab (MetaWaylandPointer *pointer, void meta_wayland_pointer_end_grab (MetaWaylandPointer *pointer); -MetaWaylandPopup *meta_wayland_pointer_start_popup_grab (MetaWaylandPointer *pointer, - MetaWaylandPopupSurface *popup_surface); - -void meta_wayland_pointer_end_popup_grab (MetaWaylandPointer *pointer); - void meta_wayland_pointer_get_relative_coordinates (MetaWaylandPointer *pointer, MetaWaylandSurface *surface, wl_fixed_t *x, @@ -144,8 +139,6 @@ gboolean meta_wayland_pointer_can_grab_surface (MetaWaylandPointer *pointer, gboolean meta_wayland_pointer_can_popup (MetaWaylandPointer *pointer, uint32_t serial); -MetaWaylandSurface *meta_wayland_pointer_get_top_popup (MetaWaylandPointer *pointer); - MetaWaylandPointerClient * meta_wayland_pointer_get_pointer_client (MetaWaylandPointer *pointer, struct wl_client *client); void meta_wayland_pointer_unbind_pointer_client_resource (struct wl_resource *resource); diff --git a/src/wayland/meta-wayland-popup.c b/src/wayland/meta-wayland-popup.c index 3777e9c4d..2af5cb853 100644 --- a/src/wayland/meta-wayland-popup.c +++ b/src/wayland/meta-wayland-popup.c @@ -53,7 +53,10 @@ G_DEFINE_INTERFACE (MetaWaylandPopupSurface, meta_wayland_popup_surface, struct _MetaWaylandPopupGrab { - MetaWaylandPointerGrab generic; + MetaWaylandSeat *seat; + MetaWaylandEventHandler *handler; + + int press_count; struct wl_client *grab_client; struct wl_list all_popups; @@ -66,12 +69,7 @@ struct _MetaWaylandPopup struct wl_list link; }; -static void -meta_wayland_popup_grab_begin (MetaWaylandPopupGrab *grab, - MetaWaylandSurface *surface); - -static void -meta_wayland_popup_grab_end (MetaWaylandPopupGrab *grab); +static void meta_wayland_popup_grab_finish (MetaWaylandPopupGrab *grab); static void meta_wayland_popup_surface_default_init (MetaWaylandPopupSurfaceInterface *iface) @@ -90,121 +88,165 @@ meta_wayland_popup_surface_dismiss (MetaWaylandPopupSurface *popup_surface) META_WAYLAND_POPUP_SURFACE_GET_IFACE (popup_surface)->dismiss (popup_surface); } +static void +meta_wayland_popup_surface_finish (MetaWaylandPopupSurface *popup_surface) +{ + META_WAYLAND_POPUP_SURFACE_GET_IFACE (popup_surface)->finish (popup_surface); +} + static MetaWaylandSurface * meta_wayland_popup_surface_get_surface (MetaWaylandPopupSurface *popup_surface) { return META_WAYLAND_POPUP_SURFACE_GET_IFACE (popup_surface)->get_surface (popup_surface); } -static void -popup_grab_focus (MetaWaylandPointerGrab *grab, - MetaWaylandSurface *surface) +static MetaWaylandSurface * +popup_grab_get_focus_surface (MetaWaylandEventHandler *handler, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + gpointer user_data) { - MetaWaylandPopupGrab *popup_grab = (MetaWaylandPopupGrab*)grab; - MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (grab->pointer); - MetaWaylandPointer *pointer = grab->pointer; + MetaWaylandPopupGrab *popup_grab = user_data; + ClutterSeat *clutter_seat = clutter_input_device_get_seat (device); + MetaWaylandSurface *surface; - /* - * We rely on having a pointer grab even when the seat doesn't have - * the pointer capability. In this case, we shouldn't update any pointer focus - * since there is no such thing when the seat doesn't have the pointer - * capability. - */ - if (!meta_wayland_seat_has_pointer (seat)) - return; + if (device == clutter_seat_get_keyboard (clutter_seat) && + !wl_list_empty (&popup_grab->all_popups)) + { + /* Keyboard focus must always go to the topmost surface */ + return meta_wayland_popup_grab_get_top_popup (popup_grab); + } + else + { + surface = meta_wayland_event_handler_chain_up_get_focus_surface (handler, + device, + sequence); - /* Popup grabs are in owner-events mode (ie, events for the same client - are reported as normal) */ - if (surface && - wl_resource_get_client (surface->resource) == popup_grab->grab_client) - meta_wayland_pointer_set_focus (grab->pointer, surface); - else if (pointer->button_count == 0) - meta_wayland_pointer_set_focus (grab->pointer, NULL); + if (surface && + wl_resource_get_client (surface->resource) == popup_grab->grab_client) + return surface; + } + + return NULL; } static void -popup_grab_motion (MetaWaylandPointerGrab *grab, - const ClutterEvent *event) +popup_grab_focus (MetaWaylandEventHandler *handler, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + MetaWaylandSurface *surface, + gpointer user_data) { - meta_wayland_pointer_send_motion (grab->pointer, event); + meta_wayland_event_handler_chain_up_focus (handler, device, sequence, surface); } -static void -popup_grab_button (MetaWaylandPointerGrab *grab, - const ClutterEvent *event) +static gboolean +popup_grab_press (MetaWaylandEventHandler *handler, + const ClutterEvent *event, + gpointer user_data) { - MetaWaylandPointer *pointer = grab->pointer; + MetaWaylandPopupGrab *popup_grab = user_data; - if (pointer->focus_surface) - meta_wayland_pointer_send_button (grab->pointer, event); - else if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE && - pointer->button_count == 0) - meta_wayland_pointer_end_popup_grab (grab->pointer); + popup_grab->press_count++; + + return CLUTTER_EVENT_PROPAGATE; } -static void -popup_grab_cancel (MetaWaylandPointerGrab *grab) +static gboolean +popup_grab_release (MetaWaylandEventHandler *handler, + const ClutterEvent *event, + gpointer user_data) { - meta_wayland_pointer_end_popup_grab (grab->pointer); + MetaWaylandPopupGrab *popup_grab = user_data; + ClutterInputDevice *device = clutter_event_get_source_device (event); + ClutterEventSequence *sequence = clutter_event_get_event_sequence (event); + + popup_grab->press_count = MAX (0, popup_grab->press_count - 1); + + if (popup_grab->press_count == 0) + { + MetaWaylandSurface *surface; + + surface = meta_wayland_event_handler_chain_up_get_focus_surface (popup_grab->handler, + device, + sequence); + if (!surface || + wl_resource_get_client (surface->resource) != popup_grab->grab_client) + { + meta_wayland_popup_grab_finish (popup_grab); + return CLUTTER_EVENT_STOP; + } + } + + return CLUTTER_EVENT_PROPAGATE; } -static MetaWaylandPointerGrabInterface popup_grab_interface = { +static MetaWaylandEventInterface popup_event_interface = { + popup_grab_get_focus_surface, popup_grab_focus, - popup_grab_motion, - popup_grab_button, - popup_grab_cancel + NULL, /* motion */ + popup_grab_press, + popup_grab_release, }; MetaWaylandPopupGrab * -meta_wayland_popup_grab_create (MetaWaylandPointer *pointer, +meta_wayland_popup_grab_create (MetaWaylandSeat *seat, MetaWaylandPopupSurface *popup_surface) { MetaWaylandSurface *surface = meta_wayland_popup_surface_get_surface (popup_surface); struct wl_client *client = wl_resource_get_client (surface->resource); + MetaWaylandInput *input = meta_wayland_seat_get_input (seat); MetaWaylandPopupGrab *grab; grab = g_new0 (MetaWaylandPopupGrab, 1); - grab->generic.interface = &popup_grab_interface; - grab->generic.pointer = pointer; + grab->seat = seat; grab->grab_client = client; wl_list_init (&grab->all_popups); - meta_wayland_popup_grab_begin (grab, surface); + grab->handler = + meta_wayland_input_attach_event_handler (input, + &popup_event_interface, + grab); return grab; } void -meta_wayland_popup_grab_destroy (MetaWaylandPopupGrab *grab) -{ - meta_wayland_popup_grab_end (grab); - g_free (grab); -} - -static void -meta_wayland_popup_grab_begin (MetaWaylandPopupGrab *grab, - MetaWaylandSurface *surface) -{ - MetaWaylandPointer *pointer = grab->generic.pointer; - - meta_wayland_pointer_start_grab (pointer, (MetaWaylandPointerGrab*)grab); -} - -void -meta_wayland_popup_grab_end (MetaWaylandPopupGrab *grab) +meta_wayland_popup_grab_finish (MetaWaylandPopupGrab *grab) { MetaWaylandPopup *popup, *tmp; - g_assert (grab->generic.interface == &popup_grab_interface); - wl_list_for_each_safe (popup, tmp, &grab->all_popups, link) { - meta_wayland_popup_surface_done (popup->popup_surface); + MetaWaylandPopupSurface *popup_surface = popup->popup_surface; + + meta_wayland_popup_surface_done (popup_surface); meta_wayland_popup_destroy (popup); + meta_wayland_popup_surface_finish (popup_surface); + } +} + +void +meta_wayland_popup_grab_destroy (MetaWaylandPopupGrab *grab) +{ + g_assert (wl_list_empty (&grab->all_popups)); + + if (grab->handler) + { + MetaWaylandInput *input = meta_wayland_seat_get_input (grab->seat); + + meta_wayland_input_detach_event_handler (input, grab->handler); + grab->handler = NULL; } - meta_wayland_pointer_end_grab (grab->generic.pointer); + g_free (grab); +} + +gboolean +meta_wayland_popup_grab_has_popups (MetaWaylandPopupGrab *grab) +{ + return !wl_list_empty (&grab->all_popups); } MetaWaylandSurface * @@ -218,41 +260,45 @@ meta_wayland_popup_grab_get_top_popup (MetaWaylandPopupGrab *grab) return meta_wayland_popup_surface_get_surface (popup->popup_surface); } -gboolean -meta_wayland_pointer_grab_is_popup_grab (MetaWaylandPointerGrab *grab) -{ - return grab->interface == &popup_grab_interface; -} - void meta_wayland_popup_destroy (MetaWaylandPopup *popup) { meta_wayland_popup_surface_dismiss (popup->popup_surface); - wl_list_remove (&popup->link); g_free (popup); } +static void +meta_wayland_popup_grab_repick_keyboard_focus (MetaWaylandPopupGrab *popup_grab) +{ + MetaWaylandSeat *seat = popup_grab->seat; + MetaContext *context = + meta_wayland_compositor_get_context (seat->compositor); + MetaBackend *backend = meta_context_get_backend (context); + ClutterBackend *clutter_backend = + meta_backend_get_clutter_backend (backend); + ClutterSeat *clutter_seat = + clutter_backend_get_default_seat (clutter_backend); + MetaWaylandInput *input; + + input = meta_wayland_seat_get_input (seat); + meta_wayland_input_invalidate_focus (input, + clutter_seat_get_keyboard (clutter_seat), + NULL); +} + void meta_wayland_popup_dismiss (MetaWaylandPopup *popup) { + MetaWaylandPopupSurface *popup_surface = popup->popup_surface; MetaWaylandPopupGrab *popup_grab = popup->grab; meta_wayland_popup_destroy (popup); if (wl_list_empty (&popup_grab->all_popups)) - { - meta_wayland_pointer_end_popup_grab (popup_grab->generic.pointer); - } + meta_wayland_popup_surface_finish (popup_surface); else - { - MetaWaylandSurface *top_popup_surface; - MetaWaylandSeat *seat; - - top_popup_surface = meta_wayland_popup_grab_get_top_popup (popup_grab); - seat = meta_wayland_pointer_get_seat (popup_grab->generic.pointer); - meta_wayland_seat_set_input_focus (seat, top_popup_surface); - } + meta_wayland_popup_grab_repick_keyboard_focus (popup_grab); } MetaWaylandSurface * @@ -268,7 +314,6 @@ meta_wayland_popup_create (MetaWaylandPopupSurface *popup_surface, MetaWaylandSurface *surface = meta_wayland_popup_surface_get_surface (popup_surface); MetaWaylandPopup *popup; - MetaWaylandSeat *seat; /* Don't allow creating popups if the grab has a different client. */ if (grab->grab_client != wl_resource_get_client (surface->resource)) @@ -280,8 +325,7 @@ meta_wayland_popup_create (MetaWaylandPopupSurface *popup_surface, wl_list_insert (&grab->all_popups, &popup->link); - seat = meta_wayland_pointer_get_seat (grab->generic.pointer); - meta_wayland_seat_set_input_focus (seat, surface); + meta_wayland_popup_grab_repick_keyboard_focus (grab); return popup; } diff --git a/src/wayland/meta-wayland-popup.h b/src/wayland/meta-wayland-popup.h index 19a3c0094..cf15767b2 100644 --- a/src/wayland/meta-wayland-popup.h +++ b/src/wayland/meta-wayland-popup.h @@ -37,17 +37,18 @@ struct _MetaWaylandPopupSurfaceInterface void (*done) (MetaWaylandPopupSurface *popup_surface); void (*dismiss) (MetaWaylandPopupSurface *popup_surface); + void (*finish) (MetaWaylandPopupSurface *popup_surface); MetaWaylandSurface *(*get_surface) (MetaWaylandPopupSurface *popup_surface); }; -MetaWaylandPopupGrab *meta_wayland_popup_grab_create (MetaWaylandPointer *pointer, +MetaWaylandPopupGrab *meta_wayland_popup_grab_create (MetaWaylandSeat *seat, MetaWaylandPopupSurface *popup_surface); void meta_wayland_popup_grab_destroy (MetaWaylandPopupGrab *grab); -MetaWaylandSurface *meta_wayland_popup_grab_get_top_popup (MetaWaylandPopupGrab *grab); +gboolean meta_wayland_popup_grab_has_popups (MetaWaylandPopupGrab *grab); -gboolean meta_wayland_pointer_grab_is_popup_grab (MetaWaylandPointerGrab *grab); +MetaWaylandSurface *meta_wayland_popup_grab_get_top_popup (MetaWaylandPopupGrab *grab); MetaWaylandPopup *meta_wayland_popup_create (MetaWaylandPopupSurface *surface, MetaWaylandPopupGrab *grab); diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c index 91df6ec0f..28b39577b 100644 --- a/src/wayland/meta-wayland-xdg-shell.c +++ b/src/wayland/meta-wayland-xdg-shell.c @@ -55,6 +55,7 @@ typedef struct _MetaWaylandXdgShellClient struct wl_resource *resource; GList *surfaces; GList *surface_constructors; + MetaWaylandPopupGrab *popup_grab; } MetaWaylandXdgShellClient; struct _MetaWaylandXdgPositioner @@ -1125,6 +1126,9 @@ finish_popup_setup (MetaWaylandXdgPopup *xdg_popup) MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_surface); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_popup); + MetaWaylandXdgSurfacePrivate *xdg_surface_priv = + meta_wayland_xdg_surface_get_instance_private (xdg_surface); + MetaWaylandXdgShellClient *xdg_shell_client = xdg_surface_priv->shell_client; struct wl_resource *xdg_wm_base_resource = meta_wayland_xdg_surface_get_wm_base_resource (xdg_surface); MetaWaylandSurface *surface = @@ -1153,7 +1157,7 @@ finish_popup_setup (MetaWaylandXdgPopup *xdg_popup) if (seat) { - MetaWaylandSurface *top_popup; + MetaWaylandSurface *top_popup = NULL; if (!meta_wayland_seat_can_popup (seat, serial)) { @@ -1161,7 +1165,9 @@ finish_popup_setup (MetaWaylandXdgPopup *xdg_popup) return; } - top_popup = meta_wayland_pointer_get_top_popup (seat->pointer); + if (xdg_shell_client->popup_grab) + top_popup = meta_wayland_popup_grab_get_top_popup (xdg_shell_client->popup_grab); + if (top_popup && parent_surface != top_popup) { wl_resource_post_error (xdg_wm_base_resource, @@ -1193,8 +1199,16 @@ finish_popup_setup (MetaWaylandXdgPopup *xdg_popup) meta_window_focus (window, meta_display_get_current_time (display)); popup_surface = META_WAYLAND_POPUP_SURFACE (surface->role); - popup = meta_wayland_pointer_start_popup_grab (seat->pointer, - popup_surface); + + if (!xdg_shell_client->popup_grab) + { + xdg_shell_client->popup_grab = + meta_wayland_popup_grab_create (seat, popup_surface); + } + + popup = meta_wayland_popup_create (popup_surface, + xdg_shell_client->popup_grab); + if (popup == NULL) { xdg_popup_send_popup_done (xdg_popup->resource); @@ -1506,6 +1520,23 @@ meta_wayland_xdg_popup_dismiss (MetaWaylandPopupSurface *popup_surface) meta_wayland_xdg_popup_unmap (xdg_popup); } +static void +meta_wayland_xdg_popup_finish (MetaWaylandPopupSurface *popup_surface) +{ + MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (popup_surface); + MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup); + MetaWaylandXdgSurfacePrivate *xdg_surface_priv = + meta_wayland_xdg_surface_get_instance_private (xdg_surface); + MetaWaylandXdgShellClient *xdg_shell_client = xdg_surface_priv->shell_client; + + if (xdg_shell_client->popup_grab && + !meta_wayland_popup_grab_has_popups (xdg_shell_client->popup_grab)) + { + meta_wayland_popup_grab_destroy (xdg_shell_client->popup_grab); + xdg_shell_client->popup_grab = NULL; + } +} + static MetaWaylandSurface * meta_wayland_xdg_popup_get_surface (MetaWaylandPopupSurface *popup_surface) { @@ -1520,6 +1551,7 @@ popup_surface_iface_init (MetaWaylandPopupSurfaceInterface *iface) { iface->done = meta_wayland_xdg_popup_done; iface->dismiss = meta_wayland_xdg_popup_dismiss; + iface->finish = meta_wayland_xdg_popup_finish; iface->get_surface = meta_wayland_xdg_popup_get_surface; }