wayland: Handle pointer focus inhibition at the Clutter level

The MetaWaylandPointer used to put this together through
MetaCursorTracker cursor visibility, and ClutterSeat-level
inhibition API, applying the pointer focus changes due to
visibility logically to Wayland clients.

In order to make this work over all Clutter widgetry
instead of just Wayland clients, make the ClutterSeat-level
inhibition API control this feature at the ClutterStage picking
level, and leave/enter the seat pointer as appropriate.

By default, the seat pointer has (un)focus inhibited. The
MetaCursorTracker has been made another player in unfocus
inhibition, simply asking for the pointer to get its focus
while the cursor is visible.

This in practice means that picking code may return a NULL
actor, some asserts and preconditions had to be changed to
handle this, plus some test code slightly.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3420>
This commit is contained in:
Carlos Garnacho 2023-11-10 13:14:49 +01:00
parent 7a2411ce50
commit 09101e36f8
4 changed files with 52 additions and 51 deletions

View File

@ -1626,6 +1626,13 @@ clutter_stage_class_init (ClutterStageClass *klass)
klass->deactivate = clutter_stage_real_deactivate;
}
static void
on_seat_unfocus_inhibited_changed (ClutterStage *stage,
ClutterSeat *seat)
{
clutter_stage_repick_device (stage, clutter_seat_get_pointer (seat));
}
static void
clutter_stage_init (ClutterStage *self)
{
@ -1633,6 +1640,7 @@ clutter_stage_init (ClutterStage *self)
ClutterStagePrivate *priv;
ClutterStageWindow *impl;
ClutterBackend *backend;
ClutterSeat *seat;
GError *error;
/* a stage is a top-level object */
@ -1686,6 +1694,12 @@ clutter_stage_init (ClutterStage *self)
clutter_stage_set_title (self, g_get_prgname ());
clutter_stage_set_key_focus (self, NULL);
clutter_stage_set_viewport (self, geom.width, geom.height);
seat = clutter_backend_get_default_seat (backend);
g_signal_connect_object (seat, "is-unfocus-inhibited-changed",
G_CALLBACK (on_seat_unfocus_inhibited_changed),
self,
G_CONNECT_SWAPPED);
}
static void
@ -3568,29 +3582,37 @@ clutter_stage_pick_and_update_device (ClutterStage *stage,
graphene_point_t point,
uint32_t time_ms)
{
ClutterActor *new_actor;
ClutterActor *new_actor = NULL;
MtkRegion *clear_area = NULL;
ClutterSeat *seat;
if ((flags & CLUTTER_DEVICE_UPDATE_IGNORE_CACHE) == 0)
seat = clutter_input_device_get_seat (device);
if (sequence ||
device != clutter_seat_get_pointer (seat) ||
clutter_seat_is_unfocus_inhibited (seat))
{
if (clutter_stage_check_in_clear_area (stage, device,
sequence, point))
if ((flags & CLUTTER_DEVICE_UPDATE_IGNORE_CACHE) == 0)
{
clutter_stage_set_device_coords (stage, device,
sequence, point);
return clutter_stage_get_device_actor (stage, device, sequence);
if (clutter_stage_check_in_clear_area (stage, device,
sequence, point))
{
clutter_stage_set_device_coords (stage, device,
sequence, point);
return clutter_stage_get_device_actor (stage, device, sequence);
}
}
new_actor = _clutter_stage_do_pick (stage,
point.x,
point.y,
CLUTTER_PICK_REACTIVE,
&clear_area);
/* Picking should never fail, but if it does, we bail out here */
g_return_val_if_fail (new_actor != NULL, NULL);
}
new_actor = _clutter_stage_do_pick (stage,
point.x,
point.y,
CLUTTER_PICK_REACTIVE,
&clear_area);
/* Picking should never fail, but if it does, we bail out here */
g_return_val_if_fail (new_actor != NULL, NULL);
clutter_stage_update_device (stage,
device, sequence,
source_device,
@ -4393,7 +4415,9 @@ clutter_stage_emit_event (ClutterStage *self,
}
}
g_assert (target_actor != NULL);
if (!target_actor)
return;
seat_grab_actor = priv->topmost_grab ? priv->topmost_grab->actor : CLUTTER_ACTOR (self);
is_sequence_begin =

View File

@ -55,6 +55,7 @@ typedef struct _MetaCursorTrackerPrivate
MetaBackend *backend;
gboolean is_showing;
gboolean pointer_focus;
int track_position_count;
@ -531,6 +532,7 @@ meta_cursor_tracker_set_pointer_visible (MetaCursorTracker *tracker,
{
MetaCursorTrackerPrivate *priv =
meta_cursor_tracker_get_instance_private (tracker);
ClutterSeat *seat;
if (visible == priv->is_showing)
return;
@ -539,6 +541,13 @@ meta_cursor_tracker_set_pointer_visible (MetaCursorTracker *tracker,
sync_cursor (tracker);
g_signal_emit (tracker, signals[VISIBILITY_CHANGED], 0);
seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
if (priv->is_showing)
clutter_seat_inhibit_unfocus (seat);
else
clutter_seat_uninhibit_unfocus (seat);
}
MetaBackend *

View File

@ -662,6 +662,7 @@ grab_input_only (void)
}
CLUTTER_TEST_SUITE (
CLUTTER_TEST_UNIT ("/grab/input-only", grab_input_only);
CLUTTER_TEST_UNIT ("/grab/grab-under-pointer", grab_under_pointer)
CLUTTER_TEST_UNIT ("/grab/grab-under-pointers-parent", grab_under_pointers_parent)
CLUTTER_TEST_UNIT ("/grab/grab-outside-pointer", grab_outside_pointer)
@ -672,5 +673,4 @@ CLUTTER_TEST_SUITE (
CLUTTER_TEST_UNIT ("/grab/grab-unordered-ungrab-2", grab_unordered_ungrab_2)
CLUTTER_TEST_UNIT ("/grab/key-focus-in-grab", grab_key_focus_in_grab);
CLUTTER_TEST_UNIT ("/grab/key-focus-outside-grab", grab_key_focus_outside_grab);
CLUTTER_TEST_UNIT ("/grab/input-only", grab_input_only);
)

View File

@ -302,14 +302,9 @@ static void
sync_focus_surface (MetaWaylandPointer *pointer)
{
MetaBackend *backend = backend_from_pointer (pointer);
MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
ClutterBackend *clutter_backend = clutter_get_default_backend ();
ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_backend);
ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
if (clutter_stage_get_grab_actor (stage) != NULL ||
(!meta_cursor_tracker_get_pointer_visible (cursor_tracker) &&
!clutter_seat_is_unfocus_inhibited (clutter_seat)))
if (clutter_stage_get_grab_actor (stage) != NULL)
{
meta_wayland_pointer_set_focus (pointer, NULL);
return;
@ -457,9 +452,6 @@ default_grab_focus (MetaWaylandPointerGrab *grab,
MetaWaylandPointer *pointer = grab->pointer;
MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer);
MetaBackend *backend = backend_from_pointer (pointer);
MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
ClutterBackend *clutter_backend = clutter_get_default_backend ();
ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_backend);
ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
if (!meta_wayland_seat_has_pointer (seat))
@ -468,10 +460,6 @@ default_grab_focus (MetaWaylandPointerGrab *grab,
if (clutter_stage_get_grab_actor (stage) != NULL)
return;
if (!meta_cursor_tracker_get_pointer_visible (cursor_tracker) &&
!clutter_seat_is_unfocus_inhibited (clutter_seat))
return;
if (pointer->button_count > 0)
return;
@ -537,16 +525,6 @@ meta_wayland_pointer_enable (MetaWaylandPointer *pointer)
"cursor-changed",
G_CALLBACK (meta_wayland_pointer_on_cursor_changed),
pointer);
g_signal_connect_swapped (cursor_tracker,
"visibility-changed",
G_CALLBACK (sync_focus_surface),
pointer);
g_signal_connect_swapped (clutter_seat,
"is-unfocus-inhibited-changed",
G_CALLBACK (sync_focus_surface),
pointer);
}
void
@ -554,8 +532,6 @@ meta_wayland_pointer_disable (MetaWaylandPointer *pointer)
{
MetaBackend *backend = backend_from_pointer (pointer);
MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
ClutterBackend *clutter_backend = clutter_get_default_backend ();
ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_backend);
g_hash_table_foreach (pointer->pointer_clients,
make_resources_inert_foreach,
@ -565,14 +541,6 @@ meta_wayland_pointer_disable (MetaWaylandPointer *pointer)
(gpointer) meta_wayland_pointer_on_cursor_changed,
pointer);
g_signal_handlers_disconnect_by_func (cursor_tracker,
sync_focus_surface,
pointer);
g_signal_handlers_disconnect_by_func (clutter_seat,
sync_focus_surface,
pointer);
if (pointer->cursor_surface)
{
g_clear_signal_handler (&pointer->cursor_surface_destroy_id,