diff --git a/src/wayland/meta-wayland-data-device.c b/src/wayland/meta-wayland-data-device.c index 6c2f6628e..9e94e1d24 100644 --- a/src/wayland/meta-wayland-data-device.c +++ b/src/wayland/meta-wayland-data-device.c @@ -745,6 +745,7 @@ meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data_devic drag_grab->handler = meta_wayland_input_attach_event_handler (input, event_iface, + FALSE, drag_grab); meta_wayland_data_source_set_seat (source, seat); } diff --git a/src/wayland/meta-wayland-input.c b/src/wayland/meta-wayland-input.c index 8115c489d..a0caa7253 100644 --- a/src/wayland/meta-wayland-input.c +++ b/src/wayland/meta-wayland-input.c @@ -27,12 +27,14 @@ #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-tablet-seat.h" +#include "wayland/meta-wayland.h" struct _MetaWaylandEventHandler { const MetaWaylandEventInterface *iface; MetaWaylandInput *input; gpointer user_data; + gboolean grabbing; struct wl_list link; }; @@ -42,10 +44,20 @@ struct _MetaWaylandInput MetaWaylandSeat *seat; struct wl_list event_handler_list; + ClutterStage *stage; + ClutterGrab *grab; }; +static void meta_wayland_input_sync_focus (MetaWaylandInput *input); + G_DEFINE_FINAL_TYPE (MetaWaylandInput, meta_wayland_input, G_TYPE_OBJECT) +static void +on_stage_is_grabbed_change (MetaWaylandInput *input) +{ + meta_wayland_input_sync_focus (input); +} + static void meta_wayland_input_init (MetaWaylandInput *input) { @@ -58,6 +70,10 @@ meta_wayland_input_finalize (GObject *object) MetaWaylandInput *input = META_WAYLAND_INPUT (object); MetaWaylandEventHandler *handler, *next; + g_signal_handlers_disconnect_by_func (input->stage, + on_stage_is_grabbed_change, + input); + wl_list_for_each_safe (handler, next, &input->event_handler_list, link) meta_wayland_input_detach_event_handler (input, handler); @@ -76,9 +92,18 @@ MetaWaylandInput * meta_wayland_input_new (MetaWaylandSeat *seat) { MetaWaylandInput *input; + MetaWaylandCompositor *compositor = seat->compositor; + MetaContext *context = + meta_wayland_compositor_get_context (compositor); + MetaBackend *backend = meta_context_get_backend (context); input = g_object_new (META_TYPE_WAYLAND_INPUT, NULL); input->seat = seat; + input->stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); + + g_signal_connect_swapped (input->stage, "notify::is-grabbed", + G_CALLBACK (on_stage_is_grabbed_change), + input); return input; } @@ -88,14 +113,18 @@ meta_wayland_event_handler_invalidate_focus (MetaWaylandEventHandler *handler, ClutterInputDevice *device, ClutterEventSequence *sequence) { + MetaWaylandInput *input = handler->input; MetaWaylandSurface *surface = NULL; if (!handler->iface->focus) return; - /* Only the first handler can focus other than a NULL surface */ - if (meta_wayland_input_is_current_handler (handler->input, handler) && - handler->iface->get_focus_surface) + if (handler->iface->get_focus_surface && + /* Only the first handler can focus other than a NULL surface */ + meta_wayland_input_is_current_handler (input, handler) && + /* Stage should either be ungrabbed, or grabbed to self */ + (!clutter_stage_get_grab_actor (input->stage) || + (input->grab && !clutter_grab_is_revoked (input->grab)))) { surface = handler->iface->get_focus_surface (handler, device, sequence, @@ -212,9 +241,35 @@ meta_wayland_event_handler_handle_event (MetaWaylandEventHandler *handler, g_assert_not_reached (); } +static void +meta_wayland_input_sync_focus (MetaWaylandInput *input) +{ + MetaWaylandEventHandler *handler; + + g_assert (!wl_list_empty (&input->event_handler_list)); + handler = wl_container_of (input->event_handler_list.next, handler, link); + meta_wayland_event_handler_invalidate_all_focus (handler); +} + +static void +on_grab_revocation_change (MetaWaylandInput *input) +{ + meta_wayland_input_sync_focus (input); +} + +static gboolean +grab_handle_event (const ClutterEvent *event, + gpointer user_data) +{ + MetaWaylandInput *input = user_data; + + return meta_wayland_input_handle_event (input, event); +} + MetaWaylandEventHandler * meta_wayland_input_attach_event_handler (MetaWaylandInput *input, const MetaWaylandEventInterface *iface, + gboolean grab, gpointer user_data) { MetaWaylandEventHandler *handler; @@ -222,15 +277,45 @@ meta_wayland_input_attach_event_handler (MetaWaylandInput *input, handler = g_new0 (MetaWaylandEventHandler, 1); handler->iface = iface; handler->input = input; + handler->grabbing = grab; handler->user_data = user_data; wl_list_init (&handler->link); wl_list_insert (&input->event_handler_list, &handler->link); + if (grab && !input->grab) + { + MetaWaylandCompositor *compositor = input->seat->compositor; + MetaContext *context = + meta_wayland_compositor_get_context (compositor); + MetaBackend *backend = meta_context_get_backend (context); + ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); + + input->grab = clutter_stage_grab_input_only (stage, + grab_handle_event, + input, + NULL); + g_signal_connect_swapped (input->grab, "notify::revoked", + G_CALLBACK (on_grab_revocation_change), + input); + } + meta_wayland_event_handler_invalidate_all_focus (handler); return handler; } +static gboolean +should_be_grabbed (MetaWaylandInput *input) +{ + MetaWaylandEventHandler *handler; + gboolean grabbing = FALSE; + + wl_list_for_each (handler, &input->event_handler_list, link) + grabbing |= handler->grabbing; + + return grabbing; +} + void meta_wayland_input_detach_event_handler (MetaWaylandInput *input, MetaWaylandEventHandler *handler) @@ -249,6 +334,16 @@ meta_wayland_input_detach_event_handler (MetaWaylandInput *input, meta_wayland_event_handler_invalidate_all_focus (head); } + if (input->grab && !should_be_grabbed (input)) + { + g_signal_handlers_disconnect_by_func (input->grab, + on_grab_revocation_change, + input); + + clutter_grab_dismiss (input->grab); + g_clear_object (&input->grab); + } + g_free (handler); } diff --git a/src/wayland/meta-wayland-input.h b/src/wayland/meta-wayland-input.h index 5eff00435..8342457ba 100644 --- a/src/wayland/meta-wayland-input.h +++ b/src/wayland/meta-wayland-input.h @@ -77,6 +77,7 @@ MetaWaylandInput * meta_wayland_input_new (MetaWaylandSeat *seat); MetaWaylandEventHandler * meta_wayland_input_attach_event_handler (MetaWaylandInput *input, const MetaWaylandEventInterface *iface, + gboolean grab, gpointer user_data); gboolean meta_wayland_input_is_current_handler (MetaWaylandInput *input, diff --git a/src/wayland/meta-wayland-pointer-constraints.c b/src/wayland/meta-wayland-pointer-constraints.c index d067a327e..178197d7e 100644 --- a/src/wayland/meta-wayland-pointer-constraints.c +++ b/src/wayland/meta-wayland-pointer-constraints.c @@ -397,6 +397,7 @@ meta_wayland_pointer_constraint_enable (MetaWaylandPointerConstraint *constraint constraint->handler = meta_wayland_input_attach_event_handler (input, &pointer_constraints_event_interface, + FALSE, constraint); constraint->confinement = diff --git a/src/wayland/meta-wayland-popup.c b/src/wayland/meta-wayland-popup.c index 2af5cb853..d157029fd 100644 --- a/src/wayland/meta-wayland-popup.c +++ b/src/wayland/meta-wayland-popup.c @@ -207,7 +207,7 @@ meta_wayland_popup_grab_create (MetaWaylandSeat *seat, grab->handler = meta_wayland_input_attach_event_handler (input, &popup_event_interface, - grab); + FALSE, grab); return grab; } diff --git a/src/wayland/meta-wayland-seat.c b/src/wayland/meta-wayland-seat.c index a99eae99b..28efa70ee 100644 --- a/src/wayland/meta-wayland-seat.c +++ b/src/wayland/meta-wayland-seat.c @@ -308,7 +308,8 @@ meta_wayland_seat_new (MetaWaylandCompositor *compositor, seat->input_handler = meta_wayland_input_new (seat); seat->default_handler = meta_wayland_input_attach_event_handler (seat->input_handler, - &default_event_interface, seat); + &default_event_interface, + FALSE, seat); return seat; } diff --git a/src/wayland/meta-xwayland-grab-keyboard.c b/src/wayland/meta-xwayland-grab-keyboard.c index 88dd8c78e..dbf3c5b07 100644 --- a/src/wayland/meta-xwayland-grab-keyboard.c +++ b/src/wayland/meta-xwayland-grab-keyboard.c @@ -251,7 +251,7 @@ meta_xwayland_keyboard_grab_activate (MetaXwaylandKeyboardActiveGrab *active_gra active_grab->handler = meta_wayland_input_attach_event_handler (input, &grab_event_interface, - active_grab); + FALSE, active_grab); } }