clutter/stage: Cancel parts of implicit grabs when ClutterGrabs happen

A ClutterGrab takes precedence over implicit grabs, so when one happens,
let's check which part of the implicit grab tree is inside the new
ClutterGrab. Cancel and remove the parts which aren't, and if nothing
is in there anymore, cancel the whole implicit grab.

Emitting crossing events correctly here is getting quite tricky:

- When the implicit grab didn't get cancelled by the ClutterGrab, we
simply want to emit all GRAB_NOTIFY crossings to the implicit grab, as
we do with all other crossings.

- When the implicit grab did get cancelled and the new ClutterGrab wants
to emit ENTER crossings, we want those to be emitted to the actual
targets, so cancel the implicit grab before emission.

- In the last case where the implicit grab did get cancelled and the new
ClutterGrab wants to emit LEAVE crossings, those should be emitted to
the implicit grab again, so we cancel the grab only after the emission
of those.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2342>
This commit is contained in:
Jonas Dreßler 2022-11-05 23:54:01 +01:00 committed by Marge Bot
parent 2dd851d9f4
commit debbd88f8c

View File

@ -3845,13 +3845,27 @@ clutter_stage_pick_and_update_device (ClutterStage *stage,
return new_actor; return new_actor;
} }
static void
cleanup_implicit_grab (PointerDeviceEntry *entry)
{
clutter_actor_set_implicitly_grabbed (entry->implicit_grab_actor, FALSE);
entry->implicit_grab_actor = NULL;
g_array_remove_range (entry->event_emission_chain, 0,
entry->event_emission_chain->len);
entry->press_count = 0;
}
static void static void
clutter_stage_notify_grab_on_pointer_entry (ClutterStage *stage, clutter_stage_notify_grab_on_pointer_entry (ClutterStage *stage,
PointerDeviceEntry *entry, PointerDeviceEntry *entry,
ClutterActor *grab_actor, ClutterActor *grab_actor,
ClutterActor *old_grab_actor) ClutterActor *old_grab_actor)
{ {
ClutterStagePrivate *priv = stage->priv;
gboolean pointer_in_grab, pointer_in_old_grab; gboolean pointer_in_grab, pointer_in_old_grab;
gboolean implicit_grab_cancelled = FALSE;
ClutterEventType event_type = CLUTTER_NOTHING; ClutterEventType event_type = CLUTTER_NOTHING;
ClutterActor *topmost, *deepmost; ClutterActor *topmost, *deepmost;
@ -3867,6 +3881,62 @@ clutter_stage_notify_grab_on_pointer_entry (ClutterStage *stage,
old_grab_actor == entry->current_actor || old_grab_actor == entry->current_actor ||
clutter_actor_contains (old_grab_actor, entry->current_actor); clutter_actor_contains (old_grab_actor, entry->current_actor);
if (grab_actor && entry->press_count > 0)
{
ClutterInputDevice *device = entry->device;
ClutterEventSequence *sequence = entry->sequence;
unsigned int i;
unsigned int n_removed = 0;
implicit_grab_cancelled = TRUE;
for (i = 0; i < entry->event_emission_chain->len; i++)
{
EventReceiver *receiver =
&g_array_index (entry->event_emission_chain, EventReceiver, i);
if (receiver->actor)
{
if (!clutter_actor_contains (grab_actor, receiver->actor))
{
g_clear_object (&receiver->actor);
n_removed++;
}
else
{
implicit_grab_cancelled = FALSE;
}
}
else if (receiver->action)
{
ClutterActor *action_actor =
clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (receiver->action));
if (!action_actor || !clutter_actor_contains (grab_actor, action_actor))
{
clutter_action_sequence_cancelled (receiver->action,
device,
sequence);
g_clear_object (&receiver->action);
n_removed++;
}
else
{
implicit_grab_cancelled = FALSE;
}
}
}
if (n_removed > 0)
{
CLUTTER_NOTE (GRABS,
"[grab=%p device=%p sequence=%p implicit_grab_cancelled=%d] "
"Cancelled %u actors and actions on implicit grab due to new seat grab",
priv->topmost_grab, device, sequence, implicit_grab_cancelled,
n_removed);
}
}
/* Equate NULL actors to the stage here, to ease calculations further down. */ /* Equate NULL actors to the stage here, to ease calculations further down. */
if (!grab_actor) if (!grab_actor)
grab_actor = CLUTTER_ACTOR (stage); grab_actor = CLUTTER_ACTOR (stage);
@ -3874,7 +3944,10 @@ clutter_stage_notify_grab_on_pointer_entry (ClutterStage *stage,
old_grab_actor = CLUTTER_ACTOR (stage); old_grab_actor = CLUTTER_ACTOR (stage);
if (grab_actor == old_grab_actor) if (grab_actor == old_grab_actor)
{
g_assert (!implicit_grab_cancelled);
return; return;
}
if (pointer_in_grab && pointer_in_old_grab) if (pointer_in_grab && pointer_in_old_grab)
{ {
@ -3921,10 +3994,16 @@ clutter_stage_notify_grab_on_pointer_entry (ClutterStage *stage,
topmost = find_common_root_actor (stage, grab_actor, old_grab_actor); topmost = find_common_root_actor (stage, grab_actor, old_grab_actor);
} }
if (event_type == CLUTTER_ENTER && implicit_grab_cancelled)
cleanup_implicit_grab (entry);
if (event_type != CLUTTER_NOTHING) if (event_type != CLUTTER_NOTHING)
{ {
ClutterEvent *event; ClutterEvent *event;
if (entry->implicit_grab_actor)
deepmost = find_common_root_actor (stage, entry->implicit_grab_actor, deepmost);
event = create_crossing_event (stage, event = create_crossing_event (stage,
entry->device, entry->device,
entry->sequence, entry->sequence,
@ -3945,6 +4024,10 @@ clutter_stage_notify_grab_on_pointer_entry (ClutterStage *stage,
clutter_event_free (event); clutter_event_free (event);
} }
if ((event_type == CLUTTER_NOTHING || event_type == CLUTTER_LEAVE) &&
implicit_grab_cancelled)
cleanup_implicit_grab (entry);
} }
static void static void
@ -4406,12 +4489,7 @@ clutter_stage_maybe_lost_implicit_grab (ClutterStage *self,
sync_crossings_on_implicit_grab_end (self, entry); sync_crossings_on_implicit_grab_end (self, entry);
clutter_actor_set_implicitly_grabbed (entry->implicit_grab_actor, FALSE); cleanup_implicit_grab (entry);
entry->implicit_grab_actor = NULL;
g_array_remove_range (entry->event_emission_chain, 0,
entry->event_emission_chain->len);
entry->press_count = 0;
} }
void void
@ -4530,9 +4608,7 @@ clutter_stage_emit_event (ClutterStage *self,
if (event->type == CLUTTER_BUTTON_RELEASE) if (event->type == CLUTTER_BUTTON_RELEASE)
sync_crossings_on_implicit_grab_end (self, entry); sync_crossings_on_implicit_grab_end (self, entry);
clutter_actor_set_implicitly_grabbed (entry->implicit_grab_actor, FALSE); cleanup_implicit_grab (entry);
entry->implicit_grab_actor = NULL;
g_array_remove_range (entry->event_emission_chain, 0, entry->event_emission_chain->len);
} }
} }