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: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3420>
This commit is contained in:
Carlos Garnacho 2023-11-15 18:52:51 +01:00 committed by Robert Mader
parent 125ba92169
commit 5ade2060a7
5 changed files with 177 additions and 145 deletions

View File

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

View File

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

View File

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

View File

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

View File

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