clutter: Reuse GPtrArray for event emission

_clutter_actor_handle_event() currently allocates a new GPtrArray on the
heap for every single event emission, let's avoid this by keeping an
array around in ClutterStage and reusing that.

This is moving the last few bits of event emission into ClutterStage,
which will be useful when we introduce implicit grabbing in subsequent
commits.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2342>
This commit is contained in:
Jonas Dreßler 2022-08-03 17:47:01 +02:00 committed by Marge Bot
parent a62ae73478
commit 4358f8da7c
3 changed files with 120 additions and 71 deletions

View File

@ -246,10 +246,6 @@ void _clutter_actor_pop_clone_paint
ClutterActorAlign _clutter_actor_get_effective_x_align (ClutterActor *self); ClutterActorAlign _clutter_actor_get_effective_x_align (ClutterActor *self);
void _clutter_actor_handle_event (ClutterActor *actor,
ClutterActor *root,
const ClutterEvent *event);
void _clutter_actor_attach_clone (ClutterActor *actor, void _clutter_actor_attach_clone (ClutterActor *actor,
ClutterActor *clone); ClutterActor *clone);
void _clutter_actor_detach_clone (ClutterActor *actor, void _clutter_actor_detach_clone (ClutterActor *actor,
@ -279,6 +275,10 @@ void clutter_actor_attach_grab (ClutterActor *actor,
void clutter_actor_detach_grab (ClutterActor *actor, void clutter_actor_detach_grab (ClutterActor *actor,
ClutterGrab *grab); ClutterGrab *grab);
void clutter_actor_collect_event_actors (ClutterActor *self,
ClutterActor *deepmost,
GPtrArray *actors);
G_END_DECLS G_END_DECLS
#endif /* __CLUTTER_ACTOR_PRIVATE_H__ */ #endif /* __CLUTTER_ACTOR_PRIVATE_H__ */

View File

@ -18527,68 +18527,6 @@ clutter_actor_get_color_state (ClutterActor *self)
return self->priv->color_state; return self->priv->color_state;
} }
void
_clutter_actor_handle_event (ClutterActor *self,
ClutterActor *root,
const ClutterEvent *event)
{
GPtrArray *event_tree;
ClutterActor *iter;
gboolean in_root = FALSE;
gint i = 0;
event_tree = g_ptr_array_sized_new (64);
g_ptr_array_set_free_func (event_tree, (GDestroyNotify) g_object_unref);
/* build the list of of emitters for the event */
iter = self;
while (iter != NULL)
{
ClutterActor *parent = iter->priv->parent;
if (CLUTTER_ACTOR_IS_REACTIVE (iter) || /* an actor must be reactive */
parent == NULL) /* unless it's the stage */
{
/* keep a reference on the actor, so that it remains valid
* for the duration of the signal emission
*/
g_ptr_array_add (event_tree, g_object_ref (iter));
}
if (iter == root)
{
in_root = TRUE;
break;
}
iter = parent;
}
/* The grab root conceptually extends infinitely in all
* directions, so it handles the events that fall outside of
* the actor.
*/
if (root && !in_root)
{
if (!clutter_actor_event (root, event, TRUE))
clutter_actor_event (root, event, FALSE);
goto done;
}
/* Capture: from top-level downwards */
for (i = event_tree->len - 1; i >= 0; i--)
if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, TRUE))
goto done;
/* Bubble: from source upwards */
for (i = 0; i < event_tree->len; i++)
if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, FALSE))
goto done;
done:
g_ptr_array_free (event_tree, TRUE);
}
static void static void
clutter_actor_set_child_transform_internal (ClutterActor *self, clutter_actor_set_child_transform_internal (ClutterActor *self,
const graphene_matrix_t *transform) const graphene_matrix_t *transform)
@ -19218,3 +19156,42 @@ clutter_actor_detach_grab (ClutterActor *self,
priv->grabs = g_list_remove (priv->grabs, grab); priv->grabs = g_list_remove (priv->grabs, grab);
} }
void
clutter_actor_collect_event_actors (ClutterActor *self,
ClutterActor *deepmost,
GPtrArray *actors)
{
ClutterActor *iter;
gboolean in_root = FALSE;
g_assert (actors->len == 0);
iter = deepmost;
while (iter != NULL)
{
ClutterActor *parent = iter->priv->parent;
if (CLUTTER_ACTOR_IS_REACTIVE (iter) || /* an actor must be reactive */
parent == NULL) /* unless it's the stage */
g_ptr_array_add (actors, g_object_ref (iter));
if (iter == self)
{
in_root = TRUE;
break;
}
iter = parent;
}
/* The grab root conceptually extends infinitely in all
* directions, so it handles the events that fall outside of
* the actor.
*/
if (!in_root)
{
g_ptr_array_remove_range (actors, 0, actors->len);
g_ptr_array_add (actors, g_object_ref (self));
}
}

View File

@ -121,6 +121,7 @@ struct _ClutterStagePrivate
ClutterGrabState grab_state; ClutterGrabState grab_state;
GQueue *event_queue; GQueue *event_queue;
GPtrArray *cur_event_actors;
GArray *paint_volume_stack; GArray *paint_volume_stack;
@ -1222,6 +1223,9 @@ clutter_stage_finalize (GObject *object)
g_queue_foreach (priv->event_queue, (GFunc) clutter_event_free, NULL); g_queue_foreach (priv->event_queue, (GFunc) clutter_event_free, NULL);
g_queue_free (priv->event_queue); g_queue_free (priv->event_queue);
g_assert (priv->cur_event_actors->len == 0);
g_ptr_array_free (priv->cur_event_actors, TRUE);
g_hash_table_destroy (priv->pointer_devices); g_hash_table_destroy (priv->pointer_devices);
g_hash_table_destroy (priv->touch_sequences); g_hash_table_destroy (priv->touch_sequences);
@ -1574,6 +1578,9 @@ clutter_stage_init (ClutterStage *self)
} }
priv->event_queue = g_queue_new (); priv->event_queue = g_queue_new ();
priv->cur_event_actors = g_ptr_array_sized_new (32);
g_ptr_array_set_free_func (priv->cur_event_actors,
(GDestroyNotify) g_object_unref);
priv->pointer_devices = priv->pointer_devices =
g_hash_table_new_full (NULL, NULL, g_hash_table_new_full (NULL, NULL,
@ -3413,13 +3420,74 @@ create_crossing_event (ClutterStage *stage,
return event; return event;
} }
typedef enum
{
EVENT_NOT_HANDLED,
EVENT_HANDLED_BY_ACTOR
} EventHandledState;
static EventHandledState
emit_event (const ClutterEvent *event,
GPtrArray *actors)
{
int i;
/* Capture: from top-level downwards */
for (i = actors->len - 1; i >= 0; i--)
{
ClutterActor *actor = g_ptr_array_index (actors, i);
if (clutter_actor_event (actor, event, TRUE))
return EVENT_HANDLED_BY_ACTOR;
}
/* Bubble: from source upwards */
for (i = 0; i < actors->len; i++)
{
ClutterActor *actor = g_ptr_array_index (actors, i);
if (clutter_actor_event (actor, event, FALSE))
return EVENT_HANDLED_BY_ACTOR;
}
return EVENT_NOT_HANDLED;
}
static void static void
clutter_stage_emit_crossing_event (ClutterStage *self, clutter_stage_emit_crossing_event (ClutterStage *self,
const ClutterEvent *event, const ClutterEvent *event,
ClutterActor *deepmost, ClutterActor *deepmost,
ClutterActor *topmost) ClutterActor *topmost)
{ {
_clutter_actor_handle_event (deepmost, topmost, event); ClutterStagePrivate *priv = self->priv;
gboolean in_event_emission;
GPtrArray *event_actors;
if (topmost == NULL)
topmost = CLUTTER_ACTOR (self);
/* Crossings can happen while we're in the middle of event emission
* (for example when an actor goes unmapped or gets grabbed), so we
* can't reuse our priv->cur_event_actors here, it might already be in use.
*/
in_event_emission = priv->cur_event_actors->len != 0;
if (in_event_emission)
{
event_actors = g_ptr_array_sized_new (32);
g_ptr_array_set_free_func (event_actors, g_object_unref);
}
else
{
event_actors = g_ptr_array_ref (priv->cur_event_actors);
}
clutter_actor_collect_event_actors (topmost, deepmost, event_actors);
emit_event (event, event_actors);
g_ptr_array_remove_range (event_actors, 0, event_actors->len);
g_ptr_array_unref (event_actors);
} }
void void
@ -4028,7 +4096,7 @@ clutter_stage_emit_event (ClutterStage *self,
ClutterInputDevice *device = clutter_event_get_device (event); ClutterInputDevice *device = clutter_event_get_device (event);
ClutterEventSequence *sequence = clutter_event_get_event_sequence (event); ClutterEventSequence *sequence = clutter_event_get_event_sequence (event);
PointerDeviceEntry *entry; PointerDeviceEntry *entry;
ClutterActor *target_actor = NULL; ClutterActor *target_actor = NULL, *seat_grab_actor = NULL;
if (sequence != NULL) if (sequence != NULL)
entry = g_hash_table_lookup (priv->touch_sequences, sequence); entry = g_hash_table_lookup (priv->touch_sequences, sequence);
@ -4094,8 +4162,12 @@ clutter_stage_emit_event (ClutterStage *self,
} }
g_assert (target_actor != NULL); g_assert (target_actor != NULL);
seat_grab_actor = priv->topmost_grab ? priv->topmost_grab->actor : CLUTTER_ACTOR (self);
_clutter_actor_handle_event (target_actor, clutter_actor_collect_event_actors (seat_grab_actor, target_actor, priv->cur_event_actors);
clutter_stage_get_grab_actor (self),
event); emit_event (event, priv->cur_event_actors);
g_ptr_array_remove_range (priv->cur_event_actors, 0,
priv->cur_event_actors->len);
} }