From 4358f8da7c20137e8aac7c7a9a76207536f05ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Wed, 3 Aug 2022 17:47:01 +0200 Subject: [PATCH] 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: --- clutter/clutter/clutter-actor-private.h | 8 +- clutter/clutter/clutter-actor.c | 101 +++++++++--------------- clutter/clutter/clutter-stage.c | 82 +++++++++++++++++-- 3 files changed, 120 insertions(+), 71 deletions(-) diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h index 0dd038564..b3d72748f 100644 --- a/clutter/clutter/clutter-actor-private.h +++ b/clutter/clutter/clutter-actor-private.h @@ -246,10 +246,6 @@ void _clutter_actor_pop_clone_paint 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, ClutterActor *clone); 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, ClutterGrab *grab); +void clutter_actor_collect_event_actors (ClutterActor *self, + ClutterActor *deepmost, + GPtrArray *actors); + G_END_DECLS #endif /* __CLUTTER_ACTOR_PRIVATE_H__ */ diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 8af90122d..4c5590d49 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -18527,68 +18527,6 @@ clutter_actor_get_color_state (ClutterActor *self) 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 clutter_actor_set_child_transform_internal (ClutterActor *self, const graphene_matrix_t *transform) @@ -19218,3 +19156,42 @@ clutter_actor_detach_grab (ClutterActor *self, 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)); + } +} diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 33e744df6..bd89ebe37 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -121,6 +121,7 @@ struct _ClutterStagePrivate ClutterGrabState grab_state; GQueue *event_queue; + GPtrArray *cur_event_actors; 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_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->touch_sequences); @@ -1574,6 +1578,9 @@ clutter_stage_init (ClutterStage *self) } 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 = g_hash_table_new_full (NULL, NULL, @@ -3413,13 +3420,74 @@ create_crossing_event (ClutterStage *stage, 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 clutter_stage_emit_crossing_event (ClutterStage *self, const ClutterEvent *event, ClutterActor *deepmost, 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 @@ -4028,7 +4096,7 @@ clutter_stage_emit_event (ClutterStage *self, ClutterInputDevice *device = clutter_event_get_device (event); ClutterEventSequence *sequence = clutter_event_get_event_sequence (event); PointerDeviceEntry *entry; - ClutterActor *target_actor = NULL; + ClutterActor *target_actor = NULL, *seat_grab_actor = NULL; if (sequence != NULL) entry = g_hash_table_lookup (priv->touch_sequences, sequence); @@ -4094,8 +4162,12 @@ clutter_stage_emit_event (ClutterStage *self, } 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_stage_get_grab_actor (self), - event); + clutter_actor_collect_event_actors (seat_grab_actor, target_actor, priv->cur_event_actors); + + emit_event (event, priv->cur_event_actors); + + g_ptr_array_remove_range (priv->cur_event_actors, 0, + priv->cur_event_actors->len); }