clutter/stage: Repick when pointer actor goes unmapped
I've overseen quite an important case in commit
98a5cb37d9
: Repicking only when actors get
destroyed is not enough, we actually need to repick when actors go
hidden/unmapped.
While we could also listen to notify::mapped just like we listen to
notify::reactive, it seems better to avoid using property notifications
here due to the usage of g_object_freeze/thaw_notify() in ClutterActor.
It can lead to the stage receiving a notify::mapped with mapped = true
for a pointer actor, which really shouldn't happen (just like
notify::reactive with reactive = true shouldn't happen).
Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5124
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2333>
This commit is contained in:
parent
358df1c569
commit
cfdca246f2
3 changed files with 69 additions and 57 deletions
|
@ -1701,6 +1701,13 @@ clutter_actor_real_unmap (ClutterActor *self)
|
||||||
*/
|
*/
|
||||||
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]);
|
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]);
|
||||||
|
|
||||||
|
if (priv->has_pointer)
|
||||||
|
{
|
||||||
|
ClutterActor *stage = _clutter_actor_get_stage_internal (self);
|
||||||
|
|
||||||
|
clutter_stage_invalidate_focus (CLUTTER_STAGE (stage), self);
|
||||||
|
}
|
||||||
|
|
||||||
/* relinquish keyboard focus if we were unmapped while owning it */
|
/* relinquish keyboard focus if we were unmapped while owning it */
|
||||||
if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
|
||||||
maybe_unset_key_focus (self);
|
maybe_unset_key_focus (self);
|
||||||
|
@ -12440,8 +12447,12 @@ void
|
||||||
clutter_actor_set_reactive (ClutterActor *actor,
|
clutter_actor_set_reactive (ClutterActor *actor,
|
||||||
gboolean reactive)
|
gboolean reactive)
|
||||||
{
|
{
|
||||||
|
ClutterActorPrivate *priv;
|
||||||
|
|
||||||
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
||||||
|
|
||||||
|
priv = actor->priv;
|
||||||
|
|
||||||
if (reactive == CLUTTER_ACTOR_IS_REACTIVE (actor))
|
if (reactive == CLUTTER_ACTOR_IS_REACTIVE (actor))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -12451,6 +12462,13 @@ clutter_actor_set_reactive (ClutterActor *actor,
|
||||||
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE);
|
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE);
|
||||||
|
|
||||||
g_object_notify_by_pspec (G_OBJECT (actor), obj_props[PROP_REACTIVE]);
|
g_object_notify_by_pspec (G_OBJECT (actor), obj_props[PROP_REACTIVE]);
|
||||||
|
|
||||||
|
if (!CLUTTER_ACTOR_IS_REACTIVE (actor) && priv->has_pointer)
|
||||||
|
{
|
||||||
|
ClutterActor *stage = _clutter_actor_get_stage_internal (actor);
|
||||||
|
|
||||||
|
clutter_stage_invalidate_focus (CLUTTER_STAGE (stage), actor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -154,6 +154,9 @@ ClutterActor * clutter_stage_pick_and_update_device (ClutterStage *s
|
||||||
void clutter_stage_unlink_grab (ClutterStage *self,
|
void clutter_stage_unlink_grab (ClutterStage *self,
|
||||||
ClutterGrab *grab);
|
ClutterGrab *grab);
|
||||||
|
|
||||||
|
void clutter_stage_invalidate_focus (ClutterStage *self,
|
||||||
|
ClutterActor *actor);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __CLUTTER_STAGE_PRIVATE_H__ */
|
#endif /* __CLUTTER_STAGE_PRIVATE_H__ */
|
||||||
|
|
|
@ -3150,53 +3150,62 @@ clutter_stage_set_actor_needs_immediate_relayout (ClutterStage *stage)
|
||||||
priv->actor_needs_immediate_relayout = TRUE;
|
priv->actor_needs_immediate_relayout = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
on_device_actor_reactive_changed (ClutterActor *actor,
|
clutter_stage_invalidate_focus (ClutterStage *self,
|
||||||
GParamSpec *pspec,
|
ClutterActor *actor)
|
||||||
PointerDeviceEntry *entry)
|
|
||||||
{
|
{
|
||||||
ClutterStage *self = entry->stage;
|
ClutterStagePrivate *priv = self->priv;
|
||||||
|
GHashTableIter iter;
|
||||||
|
gpointer value;
|
||||||
|
|
||||||
g_assert (!clutter_actor_get_reactive (actor));
|
if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
|
||||||
|
return;
|
||||||
|
|
||||||
clutter_stage_pick_and_update_device (self,
|
g_assert (!clutter_actor_is_mapped (actor) || !clutter_actor_get_reactive (actor));
|
||||||
entry->device,
|
|
||||||
entry->sequence,
|
|
||||||
CLUTTER_DEVICE_UPDATE_IGNORE_CACHE |
|
|
||||||
CLUTTER_DEVICE_UPDATE_EMIT_CROSSING,
|
|
||||||
entry->coords,
|
|
||||||
CLUTTER_CURRENT_TIME);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
g_hash_table_iter_init (&iter, priv->pointer_devices);
|
||||||
on_device_actor_destroyed (ClutterActor *actor,
|
while (g_hash_table_iter_next (&iter, NULL, &value))
|
||||||
PointerDeviceEntry *entry)
|
{
|
||||||
{
|
PointerDeviceEntry *entry = value;
|
||||||
/* Simply unset the current_actor pointer here, there's no need to
|
|
||||||
* unset has_pointer or to disconnect any signals because the actor
|
if (entry->current_actor != actor)
|
||||||
* is gone anyway.
|
continue;
|
||||||
*/
|
|
||||||
entry->current_actor = NULL;
|
clutter_stage_pick_and_update_device (self,
|
||||||
g_clear_pointer (&entry->clear_area, cairo_region_destroy);
|
entry->device,
|
||||||
clutter_stage_repick_device (entry->stage, entry->device);
|
NULL,
|
||||||
|
CLUTTER_DEVICE_UPDATE_IGNORE_CACHE |
|
||||||
|
CLUTTER_DEVICE_UPDATE_EMIT_CROSSING,
|
||||||
|
entry->coords,
|
||||||
|
CLUTTER_CURRENT_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_hash_table_iter_init (&iter, priv->touch_sequences);
|
||||||
|
while (g_hash_table_iter_next (&iter, NULL, &value))
|
||||||
|
{
|
||||||
|
PointerDeviceEntry *entry = value;
|
||||||
|
|
||||||
|
if (entry->current_actor != actor)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
clutter_stage_pick_and_update_device (self,
|
||||||
|
entry->device,
|
||||||
|
entry->sequence,
|
||||||
|
CLUTTER_DEVICE_UPDATE_IGNORE_CACHE |
|
||||||
|
CLUTTER_DEVICE_UPDATE_EMIT_CROSSING,
|
||||||
|
entry->coords,
|
||||||
|
CLUTTER_CURRENT_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actor != CLUTTER_ACTOR (self))
|
||||||
|
g_assert (!clutter_actor_has_pointer (actor));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
free_pointer_device_entry (PointerDeviceEntry *entry)
|
free_pointer_device_entry (PointerDeviceEntry *entry)
|
||||||
{
|
{
|
||||||
if (entry->current_actor)
|
if (entry->current_actor)
|
||||||
{
|
_clutter_actor_set_has_pointer (entry->current_actor, FALSE);
|
||||||
ClutterActor *actor = entry->current_actor;
|
|
||||||
|
|
||||||
g_signal_handlers_disconnect_by_func (actor,
|
|
||||||
G_CALLBACK (on_device_actor_reactive_changed),
|
|
||||||
entry);
|
|
||||||
g_signal_handlers_disconnect_by_func (actor,
|
|
||||||
G_CALLBACK (on_device_actor_destroyed),
|
|
||||||
entry);
|
|
||||||
|
|
||||||
_clutter_actor_set_has_pointer (actor, FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_clear_pointer (&entry->clear_area, cairo_region_destroy);
|
g_clear_pointer (&entry->clear_area, cairo_region_destroy);
|
||||||
|
|
||||||
|
@ -3240,30 +3249,12 @@ clutter_stage_update_device_entry (ClutterStage *self,
|
||||||
if (entry->current_actor != actor)
|
if (entry->current_actor != actor)
|
||||||
{
|
{
|
||||||
if (entry->current_actor)
|
if (entry->current_actor)
|
||||||
{
|
_clutter_actor_set_has_pointer (entry->current_actor, FALSE);
|
||||||
ClutterActor *old_actor = entry->current_actor;
|
|
||||||
|
|
||||||
g_signal_handlers_disconnect_by_func (old_actor,
|
|
||||||
G_CALLBACK (on_device_actor_reactive_changed),
|
|
||||||
entry);
|
|
||||||
g_signal_handlers_disconnect_by_func (old_actor,
|
|
||||||
G_CALLBACK (on_device_actor_destroyed),
|
|
||||||
entry);
|
|
||||||
|
|
||||||
_clutter_actor_set_has_pointer (old_actor, FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
entry->current_actor = actor;
|
entry->current_actor = actor;
|
||||||
|
|
||||||
if (actor)
|
if (actor)
|
||||||
{
|
_clutter_actor_set_has_pointer (actor, TRUE);
|
||||||
g_signal_connect (actor, "notify::reactive",
|
|
||||||
G_CALLBACK (on_device_actor_reactive_changed), entry);
|
|
||||||
g_signal_connect (actor, "destroy",
|
|
||||||
G_CALLBACK (on_device_actor_destroyed), entry);
|
|
||||||
|
|
||||||
_clutter_actor_set_has_pointer (actor, TRUE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_clear_pointer (&entry->clear_area, cairo_region_destroy);
|
g_clear_pointer (&entry->clear_area, cairo_region_destroy);
|
||||||
|
|
Loading…
Reference in a new issue