From e27d2702a8aa446eb63ca29518564ebefad49f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Fri, 10 Apr 2020 00:39:07 +0200 Subject: [PATCH 1/7] clutter/stage: Add private method to get the stage-views of a rectangle We'll need this method for implementing a new stage-views-on API for ClutterActor which lists the stage views an actor is on. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196 --- clutter/clutter/clutter-stage-private.h | 3 +++ clutter/clutter/clutter-stage.c | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h index 9f018ab84d8..ba4a77b733f 100644 --- a/clutter/clutter/clutter-stage-private.h +++ b/clutter/clutter/clutter-stage-private.h @@ -136,6 +136,9 @@ void _clutter_stage_presented (ClutterStage *stag void clutter_stage_queue_actor_relayout (ClutterStage *stage, ClutterActor *actor); +GList * clutter_stage_get_views_for_rect (ClutterStage *stage, + const graphene_rect_t *rect); + G_END_DECLS #endif /* __CLUTTER_STAGE_PRIVATE_H__ */ diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 61737abd4b6..fed84e9b001 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -4404,3 +4404,27 @@ _clutter_stage_get_max_view_scale_factor_for_rect (ClutterStage *stage, *view_scale = scale; return TRUE; } + +GList * +clutter_stage_get_views_for_rect (ClutterStage *stage, + const graphene_rect_t *rect) +{ + ClutterStagePrivate *priv = stage->priv; + GList *views_for_rect = NULL; + GList *l; + + for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next) + { + ClutterStageView *view = l->data; + cairo_rectangle_int_t view_layout; + graphene_rect_t view_rect; + + clutter_stage_view_get_layout (view, &view_layout); + _clutter_util_rect_from_rectangle (&view_layout, &view_rect); + + if (graphene_rect_intersection (&view_rect, rect, NULL)) + views_for_rect = g_list_prepend (views_for_rect, view); + } + + return views_for_rect; +} -- GitLab From 670f4f22fea2de5650bc67ad6f17b99b78185be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Fri, 10 Apr 2020 00:53:33 +0200 Subject: [PATCH 2/7] clutter/stage: Remove _clutter_stage_get_max_view_scale_factor_for_rect Since we now have _clutter_stage_get_views_for_rect(), we can easily replace _clutter_stage_get_max_view_scale_factor_for_rect() with it and remove that private method. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196 --- clutter/clutter/clutter-actor.c | 16 +++++++-- clutter/clutter/clutter-stage-private.h | 3 -- clutter/clutter/clutter-stage.c | 46 +++++++------------------ 3 files changed, 26 insertions(+), 39 deletions(-) diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index df6b14338f1..4d79e88bac7 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -17421,17 +17421,27 @@ _clutter_actor_get_resource_scale_for_rect (ClutterActor *self, float *resource_scale) { ClutterActor *stage; + g_autoptr (GList) views = NULL; + GList *l; float max_scale = 0; stage = _clutter_actor_get_stage_internal (self); if (!stage) return FALSE; - if (!_clutter_stage_get_max_view_scale_factor_for_rect (CLUTTER_STAGE (stage), - bounding_rect, - &max_scale)) + views = clutter_stage_get_views_for_rect (CLUTTER_STAGE (stage), + bounding_rect); + + if (!views) return FALSE; + for (l = views; l; l = l->next) + { + ClutterStageView *view = l->data; + + max_scale = MAX (clutter_stage_view_get_scale (view), max_scale); + } + *resource_scale = max_scale; return TRUE; diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h index ba4a77b733f..5d785b644aa 100644 --- a/clutter/clutter/clutter-stage-private.h +++ b/clutter/clutter/clutter-stage-private.h @@ -125,9 +125,6 @@ gboolean _clutter_stage_update_state (ClutterStage *stag void _clutter_stage_set_scale_factor (ClutterStage *stage, int factor); -gboolean _clutter_stage_get_max_view_scale_factor_for_rect (ClutterStage *stage, - graphene_rect_t *rect, - float *view_scale); void _clutter_stage_presented (ClutterStage *stage, CoglFrameEvent frame_event, diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index fed84e9b001..1c5f5685afa 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -4077,20 +4077,29 @@ clutter_stage_get_capture_final_size (ClutterStage *stage, int *out_height, float *out_scale) { - float max_scale; + float max_scale = 1.0; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); if (rect) { graphene_rect_t capture_rect; + g_autoptr (GList) views = NULL; + GList *l; _clutter_util_rect_from_rectangle (rect, &capture_rect); - if (!_clutter_stage_get_max_view_scale_factor_for_rect (stage, - &capture_rect, - &max_scale)) + views = clutter_stage_get_views_for_rect (stage, &capture_rect); + + if (!views) return FALSE; + for (l = views; l; l = l->next) + { + ClutterStageView *view = l->data; + + max_scale = MAX (clutter_stage_view_get_scale (view), max_scale); + } + if (out_width) *out_width = (gint) roundf (rect->width * max_scale); @@ -4376,35 +4385,6 @@ clutter_stage_update_resource_scales (ClutterStage *stage) _clutter_actor_queue_update_resource_scale_recursive (CLUTTER_ACTOR (stage)); } -gboolean -_clutter_stage_get_max_view_scale_factor_for_rect (ClutterStage *stage, - graphene_rect_t *rect, - float *view_scale) -{ - ClutterStagePrivate *priv = stage->priv; - float scale = 0.0f; - GList *l; - - for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next) - { - ClutterStageView *view = l->data; - cairo_rectangle_int_t view_layout; - graphene_rect_t view_rect; - - clutter_stage_view_get_layout (view, &view_layout); - _clutter_util_rect_from_rectangle (&view_layout, &view_rect); - - if (graphene_rect_intersection (&view_rect, rect, NULL)) - scale = MAX (clutter_stage_view_get_scale (view), scale); - } - - if (scale == 0.0) - return FALSE; - - *view_scale = scale; - return TRUE; -} - GList * clutter_stage_get_views_for_rect (ClutterStage *stage, const graphene_rect_t *rect) -- GitLab From 8127494e5245b5ddf22918cc4ab1dfbc761e1375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Fri, 10 Apr 2020 12:33:35 +0200 Subject: [PATCH 3/7] clutter/stage: Rename update_resource_scales to clear_stage_views When the stage views the stage is shown on are changed, ClutterStage currently provides a clutter_stage_update_resource_scales() method that allows invalidating the resource scales of all actors. With the new stage-views API that's going to be added to ClutterActor, we also need a method to invalidate the stage-views lists of actors in case the stage views are rebuilt and fortunately we can re-use the infrastructure for invalidating resource scales for that. So since resource scales depend on the stage views an actor is on, rename clutter_stage_update_resource_scales() and related methods to clutter_stage_clear_stage_views(), which also covers resource scales. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196 --- clutter/clutter/clutter-actor-private.h | 2 +- clutter/clutter/clutter-actor.c | 10 +++++----- clutter/clutter/clutter-mutter.h | 2 +- clutter/clutter/clutter-stage.c | 4 ++-- src/backends/native/meta-stage-native.c | 2 +- src/backends/x11/nested/meta-backend-x11-nested.c | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h index 9ff4b2d4bd8..3b6a4153bd1 100644 --- a/clutter/clutter/clutter-actor-private.h +++ b/clutter/clutter/clutter-actor-private.h @@ -313,7 +313,7 @@ void _clutter_actor_detach_clone void _clutter_actor_queue_redraw_on_clones (ClutterActor *actor); void _clutter_actor_queue_relayout_on_clones (ClutterActor *actor); void _clutter_actor_queue_only_relayout (ClutterActor *actor); -void _clutter_actor_queue_update_resource_scale_recursive (ClutterActor *actor); +void clutter_actor_clear_stage_views_recursive (ClutterActor *actor); gboolean _clutter_actor_get_real_resource_scale (ClutterActor *actor, float *resource_scale); diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 4d79e88bac7..5ea4cc1c9e4 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -17517,20 +17517,20 @@ _clutter_actor_compute_resource_scale (ClutterActor *self, } static ClutterActorTraverseVisitFlags -queue_update_resource_scale_cb (ClutterActor *actor, - int depth, - void *user_data) +clear_stage_views_cb (ClutterActor *actor, + int depth, + gpointer user_data) { actor->priv->needs_compute_resource_scale = TRUE; return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; } void -_clutter_actor_queue_update_resource_scale_recursive (ClutterActor *self) +clutter_actor_clear_stage_views_recursive (ClutterActor *self) { _clutter_actor_traverse (self, CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST, - queue_update_resource_scale_cb, + clear_stage_views_cb, NULL, NULL); } diff --git a/clutter/clutter/clutter-mutter.h b/clutter/clutter/clutter-mutter.h index d5dbf89e6c9..cc1ba9a9725 100644 --- a/clutter/clutter/clutter-mutter.h +++ b/clutter/clutter/clutter-mutter.h @@ -75,7 +75,7 @@ CLUTTER_EXPORT void clutter_stage_thaw_updates (ClutterStage *stage); CLUTTER_EXPORT -void clutter_stage_update_resource_scales (ClutterStage *stage); +void clutter_stage_clear_stage_views (ClutterStage *stage); CLUTTER_EXPORT void clutter_stage_view_assign_next_scanout (ClutterStageView *stage_view, diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 1c5f5685afa..5326b1cc26b 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -4380,9 +4380,9 @@ clutter_stage_peek_stage_views (ClutterStage *stage) } void -clutter_stage_update_resource_scales (ClutterStage *stage) +clutter_stage_clear_stage_views (ClutterStage *stage) { - _clutter_actor_queue_update_resource_scale_recursive (CLUTTER_ACTOR (stage)); + clutter_actor_clear_stage_views_recursive (CLUTTER_ACTOR (stage)); } GList * diff --git a/src/backends/native/meta-stage-native.c b/src/backends/native/meta-stage-native.c index 9b9c45ef30e..9a3d11cb9dc 100644 --- a/src/backends/native/meta-stage-native.c +++ b/src/backends/native/meta-stage-native.c @@ -140,7 +140,7 @@ meta_stage_native_rebuild_views (MetaStageNative *stage_native) ClutterActor *stage = meta_backend_get_stage (backend); meta_renderer_rebuild_views (renderer); - clutter_stage_update_resource_scales (CLUTTER_STAGE (stage)); + clutter_stage_clear_stage_views (CLUTTER_STAGE (stage)); ensure_frame_callbacks (stage_native); } diff --git a/src/backends/x11/nested/meta-backend-x11-nested.c b/src/backends/x11/nested/meta-backend-x11-nested.c index 37d611eb340..009a809787c 100644 --- a/src/backends/x11/nested/meta-backend-x11-nested.c +++ b/src/backends/x11/nested/meta-backend-x11-nested.c @@ -87,7 +87,7 @@ meta_backend_x11_nested_update_screen_size (MetaBackend *backend, if (meta_is_stage_views_enabled ()) { meta_renderer_rebuild_views (renderer); - clutter_stage_update_resource_scales (CLUTTER_STAGE (stage)); + clutter_stage_clear_stage_views (CLUTTER_STAGE (stage)); } clutter_actor_set_size (stage, width, height); } -- GitLab From 675a97d58e3d4602a7905d49fe06c7b2ca60feea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Fri, 10 Apr 2020 00:34:49 +0200 Subject: [PATCH 4/7] clutter/actor: Add API to get the stage-views an actor is painted on There are certain rendering techniques and optimizations, for example the unredirection of non-fullscreen windows, where information about the output/stage-view an actor is on is needed to determine whether the optimization can be enabled. So add a new method to ClutterActor that allows listing the stage-views the actor is being painted on: clutter_actor_peek_stage_views() With the way Clutter works, the only point where we can reliably get this information is during or right before the paint phase, when the layout phase of the stage has been completed and no more changes to the actors transformation matrices happen. So to get the stage views the actor is on, introduce a new step that's done on every master clock tick between layout and paint cycle: Traversing through the actor tree and updating the stage-views the mapped actors are going to be painted on. We're doing this in a separate step instead of inside clutter_actor_paint() itself for a few reasons: It keeps the code separate from the painting code, making profiling easier and issues easier to track down (hopefully), it allows for a new "stage-views-changed" signal that doesn't interfere with painting, and finally, it will make it very easy to update the resource scales in the same step in the future. Currently, this list is only invalidated on allocation changes of actors, but not on changes to the transformation matrices. That's because there's no proper API to invalidate the transformation matrices ClutterActor implementations can apply through the apply_transform() vfunc. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196 --- clutter/clutter/clutter-actor-private.h | 2 + clutter/clutter/clutter-actor.c | 114 +++++++++++++++++++++++- clutter/clutter/clutter-actor.h | 3 + clutter/clutter/clutter-stage.c | 12 +++ 4 files changed, 129 insertions(+), 2 deletions(-) diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h index 3b6a4153bd1..ed398bf2100 100644 --- a/clutter/clutter/clutter-actor-private.h +++ b/clutter/clutter/clutter-actor-private.h @@ -321,6 +321,8 @@ gboolean _clutter_actor_get_real_resource_scale ClutterPaintNode * clutter_actor_create_texture_paint_node (ClutterActor *self, CoglTexture *texture); +void clutter_actor_update_stage_views (ClutterActor *self); + G_END_DECLS #endif /* __CLUTTER_ACTOR_PRIVATE_H__ */ diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 5ea4cc1c9e4..1d3f98d4150 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -813,6 +813,8 @@ struct _ClutterActorPrivate gulong font_changed_id; gulong layout_changed_id; + GList *stage_views; + /* bitfields: KEEP AT THE END */ /* fixed position and sizes */ @@ -855,6 +857,7 @@ struct _ClutterActorPrivate guint had_effects_on_last_paint_volume_update : 1; guint needs_compute_resource_scale : 1; guint absolute_origin_changed : 1; + guint needs_update_stage_views : 1; }; enum @@ -2565,6 +2568,7 @@ static void absolute_allocation_changed (ClutterActor *actor) { actor->priv->needs_compute_resource_scale = TRUE; + actor->priv->needs_update_stage_views = TRUE; } static ClutterActorTraverseVisitFlags @@ -4282,6 +4286,7 @@ typedef enum REMOVE_CHILD_FLUSH_QUEUE = 1 << 4, REMOVE_CHILD_NOTIFY_FIRST_LAST = 1 << 5, REMOVE_CHILD_STOP_TRANSITIONS = 1 << 6, + REMOVE_CHILD_CLEAR_STAGE_VIEWS = 1 << 7, /* default flags for public API */ REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS | @@ -4290,14 +4295,16 @@ typedef enum REMOVE_CHILD_EMIT_ACTOR_REMOVED | REMOVE_CHILD_CHECK_STATE | REMOVE_CHILD_FLUSH_QUEUE | - REMOVE_CHILD_NOTIFY_FIRST_LAST, + REMOVE_CHILD_NOTIFY_FIRST_LAST | + REMOVE_CHILD_CLEAR_STAGE_VIEWS, /* flags for legacy/deprecated API */ REMOVE_CHILD_LEGACY_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS | REMOVE_CHILD_CHECK_STATE | REMOVE_CHILD_FLUSH_QUEUE | REMOVE_CHILD_EMIT_PARENT_SET | - REMOVE_CHILD_NOTIFY_FIRST_LAST + REMOVE_CHILD_NOTIFY_FIRST_LAST | + REMOVE_CHILD_CLEAR_STAGE_VIEWS } ClutterActorRemoveChildFlags; /*< private > @@ -4319,6 +4326,7 @@ clutter_actor_remove_child_internal (ClutterActor *self, gboolean notify_first_last; gboolean was_mapped; gboolean stop_transitions; + gboolean clear_stage_views; GObject *obj; if (self == child) @@ -4335,6 +4343,7 @@ clutter_actor_remove_child_internal (ClutterActor *self, flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0; notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0; stop_transitions = (flags & REMOVE_CHILD_STOP_TRANSITIONS) != 0; + clear_stage_views = (flags & REMOVE_CHILD_CLEAR_STAGE_VIEWS) != 0; obj = G_OBJECT (self); g_object_freeze_notify (obj); @@ -4408,6 +4417,13 @@ clutter_actor_remove_child_internal (ClutterActor *self, clutter_actor_queue_compute_expand (self); } + /* Only actors which are attached to a stage get notified about changes + * to the stage views, so make sure all the stage-views lists are + * cleared as the child and its children leave the actor tree. + */ + if (clear_stage_views) + clutter_actor_clear_stage_views_recursive (child); + if (emit_parent_set && !CLUTTER_ACTOR_IN_DESTRUCTION (child)) { child->priv->needs_compute_resource_scale = TRUE; @@ -6080,6 +6096,8 @@ clutter_actor_dispose (GObject *object) priv->clones = NULL; } + g_clear_pointer (&priv->stage_views, g_list_free); + G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object); } @@ -8634,6 +8652,7 @@ clutter_actor_init (ClutterActor *self) priv->needs_allocation = TRUE; priv->needs_paint_volume_update = TRUE; priv->needs_compute_resource_scale = TRUE; + priv->needs_update_stage_views = TRUE; priv->cached_width_age = 1; priv->cached_height_age = 1; @@ -17521,7 +17540,11 @@ clear_stage_views_cb (ClutterActor *actor, int depth, gpointer user_data) { + actor->priv->needs_update_stage_views = TRUE; actor->priv->needs_compute_resource_scale = TRUE; + + g_clear_pointer (&actor->priv->stage_views, g_list_free); + return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; } @@ -17622,6 +17645,93 @@ clutter_actor_get_resource_scale (ClutterActor *self, return FALSE; } +static void +update_stage_views (ClutterActor *self) +{ + ClutterActorPrivate *priv = self->priv; + ClutterStage *stage; + graphene_rect_t bounding_rect; + + g_clear_pointer (&priv->stage_views, g_list_free); + + if (priv->needs_allocation) + { + g_warning ("Can't update stage views actor %s is on because it needs an " + "allocation.", _clutter_actor_get_debug_name (self)); + return; + } + + stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); + g_return_if_fail (stage); + + clutter_actor_get_transformed_position (self, + &bounding_rect.origin.x, + &bounding_rect.origin.y); + clutter_actor_get_transformed_size (self, + &bounding_rect.size.width, + &bounding_rect.size.height); + + if (bounding_rect.size.width == 0.0 || + bounding_rect.size.height == 0.0) + return; + + priv->stage_views = clutter_stage_get_views_for_rect (stage, + &bounding_rect); +} + +void +clutter_actor_update_stage_views (ClutterActor *self) +{ + ClutterActorPrivate *priv = self->priv; + ClutterActor *child; + + if (!CLUTTER_ACTOR_IS_MAPPED (self) || + CLUTTER_ACTOR_IN_DESTRUCTION (self)) + return; + + if (priv->needs_update_stage_views) + { + update_stage_views (self); + + priv->needs_update_stage_views = FALSE; + } + + for (child = priv->first_child; child; child = child->priv->next_sibling) + clutter_actor_update_stage_views (child); +} + +/** + * clutter_actor_peek_stage_views: + * @self: A #ClutterActor + * + * Retrieves the list of #ClutterStageViews the actor is being + * painted on. + * + * If this function is called during the paint cycle, the list is guaranteed + * to be up-to-date, if called outside the paint cycle, the list will + * contain the views the actor was painted on last. + * + * The list returned by this function is not updated when the actors + * visibility changes: If an actor gets hidden and is not being painted + * anymore, this function will return the list of views the actor was + * painted on last. + * + * If an actor is not attached to a stage (realized), this function will + * always return an empty list. + * + * Returns: (transfer none) (element-type Clutter.StageView): The list of + * #ClutterStageViews the actor is being painted on. The list and + * its contents are owned by the #ClutterActor and the list may not be + * freed or modified. + */ +GList * +clutter_actor_peek_stage_views (ClutterActor *self) +{ + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); + + return self->priv->stage_views; +} + /** * clutter_actor_has_overlaps: * @self: A #ClutterActor diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h index b5d4b7845e1..616e8016999 100644 --- a/clutter/clutter/clutter-actor.h +++ b/clutter/clutter/clutter-actor.h @@ -919,6 +919,9 @@ void clutter_actor_pick_box (ClutterActor *self, ClutterPickContext *pick_context, const ClutterActorBox *box); +CLUTTER_EXPORT +GList * clutter_actor_peek_stage_views (ClutterActor *self); + G_END_DECLS #endif /* __CLUTTER_ACTOR_H__ */ diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 5326b1cc26b..ff079c1adc6 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -1469,6 +1469,14 @@ _clutter_stage_check_updated_pointers (ClutterStage *stage) return updating; } +static void +update_actor_stage_views (ClutterStage *stage) +{ + ClutterActor *actor = CLUTTER_ACTOR (stage); + + clutter_actor_update_stage_views (actor); +} + /** * _clutter_stage_do_update: * @stage: A #ClutterStage @@ -1516,6 +1524,10 @@ _clutter_stage_do_update (ClutterStage *stage) if (stage_was_relayout) pointers = _clutter_stage_check_updated_pointers (stage); + COGL_TRACE_BEGIN (ClutterStageUpdateActorStageViews, "Actor stage-views"); + update_actor_stage_views (stage); + COGL_TRACE_END (ClutterStageUpdateActorStageViews); + COGL_TRACE_BEGIN (ClutterStagePaint, "Paint"); clutter_stage_maybe_finish_queue_redraws (stage); -- GitLab From 18c792d6f8a15bfd36edfb9751c63556190eeded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Fri, 10 Apr 2020 00:59:05 +0200 Subject: [PATCH 5/7] clutter/actor: Emit a signal when the stage-views an actor is on change Add a new signal that's emitted when the stage views an actor being painted on have changed, "stage-views-changed". For example this signal can be helpful when tracking whether an actor is painted on multiple stage views or only one. Since we must clear the stage-views list when an actor leaves the stage (actors that aren't attached to a stage don't get notified about the stage views being changed/replaced), we also emit the new signal when an actor gets detached from the stage (otherwise there would be an edge case where no signal is emitted but it really should: An actor is visible on a stage view, then detached from the stage, and then attached again and immeditely moved outside the view). Also skip the comparison of the old stage-views list and the new one if nobody is listening to the signal to save some resources. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196 --- clutter/clutter/clutter-actor.c | 69 ++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 1d3f98d4150..dd7b1ba740b 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -1020,6 +1020,7 @@ enum TRANSITIONS_COMPLETED, TOUCH_EVENT, TRANSITION_STOPPED, + STAGE_VIEWS_CHANGED, LAST_SIGNAL }; @@ -4421,7 +4422,7 @@ clutter_actor_remove_child_internal (ClutterActor *self, * to the stage views, so make sure all the stage-views lists are * cleared as the child and its children leave the actor tree. */ - if (clear_stage_views) + if (clear_stage_views && !CLUTTER_ACTOR_IN_DESTRUCTION (child)) clutter_actor_clear_stage_views_recursive (child); if (emit_parent_set && !CLUTTER_ACTOR_IN_DESTRUCTION (child)) @@ -8634,6 +8635,27 @@ clutter_actor_class_init (ClutterActorClass *klass) g_signal_set_va_marshaller (actor_signals[TOUCH_EVENT], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__BOXEDv); + + /** + * ClutterActor::stage-views-changed: + * @actor: a #ClutterActor + * + * The ::stage-views-changed signal is emitted when the position or + * size an actor is being painted at have changed so that it's visible + * on different stage views. + * + * This signal is also emitted when the actor gets detached from the stage + * or when the views of the stage have been invalidated and will be + * replaced; it's not emitted when the actor gets hidden. + */ + actor_signals[STAGE_VIEWS_CHANGED] = + g_signal_new (I_("stage-views-changed"), + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + } static void @@ -17540,10 +17562,15 @@ clear_stage_views_cb (ClutterActor *actor, int depth, gpointer user_data) { + g_autoptr (GList) old_stage_views = NULL; + actor->priv->needs_update_stage_views = TRUE; actor->priv->needs_compute_resource_scale = TRUE; - g_clear_pointer (&actor->priv->stage_views, g_list_free); + old_stage_views = g_steal_pointer (&actor->priv->stage_views); + + if (old_stage_views) + g_signal_emit (actor, actor_signals[STAGE_VIEWS_CHANGED], 0); return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; } @@ -17645,20 +17672,44 @@ clutter_actor_get_resource_scale (ClutterActor *self, return FALSE; } +static gboolean +sorted_lists_equal (GList *list_a, + GList *list_b) +{ + GList *a, *b; + + if (!list_a && !list_b) + return TRUE; + + for (a = list_a, b = list_b; + a && b; + a = a->next, b = b->next) + { + if (a->data != b->data) + break; + + if (!a->next && !b->next) + return TRUE; + } + + return FALSE; +} + static void update_stage_views (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; + g_autoptr (GList) old_stage_views = NULL; ClutterStage *stage; graphene_rect_t bounding_rect; - g_clear_pointer (&priv->stage_views, g_list_free); + old_stage_views = g_steal_pointer (&priv->stage_views); if (priv->needs_allocation) { g_warning ("Can't update stage views actor %s is on because it needs an " "allocation.", _clutter_actor_get_debug_name (self)); - return; + goto out; } stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); @@ -17673,10 +17724,18 @@ update_stage_views (ClutterActor *self) if (bounding_rect.size.width == 0.0 || bounding_rect.size.height == 0.0) - return; + goto out; priv->stage_views = clutter_stage_get_views_for_rect (stage, &bounding_rect); + +out: + if (g_signal_has_handler_pending (self, actor_signals[STAGE_VIEWS_CHANGED], + 0, TRUE)) + { + if (!sorted_lists_equal (old_stage_views, priv->stage_views)) + g_signal_emit (self, actor_signals[STAGE_VIEWS_CHANGED], 0); + } } void -- GitLab From 0f97196d84c038562fe3c8d11f9ef0e7593c412c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Fri, 10 Apr 2020 01:19:00 +0200 Subject: [PATCH 6/7] clutter/actor: Don't traverse whole actor tree for updating stage-views We currently go through the whole tree of mapped actors on every paint cycle to update the stage views actors are on. Even if no actors need updating of their stage views, traversing the actor tree is still quite expensive and shows up when using a profiler. So tone down the amounts of full-tree traversals we have to do on every paint cycle and only traverse a subtree if it includes an actor which actually needs updating of its stage views. We do that by setting the `needs_update_stage_views` flag to TRUE recursively for all parents up to the stage when the stage-views list of an actor gets invalidated. This way we end up updating a few more actors than necessary, but can avoid searching the whole actor tree for actors which have `needs_update_stage_views` set to TRUE. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196 --- clutter/clutter/clutter-actor.c | 40 ++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index dd7b1ba740b..fe02d3abddb 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -1618,6 +1618,22 @@ clutter_actor_update_map_state (ClutterActor *self, #endif } +static void +queue_update_stage_views (ClutterActor *actor) +{ + while (actor && !actor->priv->needs_update_stage_views) + { + actor->priv->needs_update_stage_views = TRUE; + + /* We don't really need to update the stage-views of the actors up the + * hierarchy, we set the flag anyway though so we can avoid traversing + * the whole scenegraph when looking for actors which need an update + * in clutter_actor_update_stage_views(). + */ + actor = actor->priv->parent; + } +} + static void clutter_actor_real_map (ClutterActor *self) { @@ -1632,6 +1648,18 @@ clutter_actor_real_map (ClutterActor *self) self->priv->needs_paint_volume_update = TRUE; + /* We skip unmapped actors when updating the stage-views list, so if + * an actors list got invalidated while it was unmapped make sure to + * set priv->needs_update_stage_views to TRUE for all actors up the + * hierarchy now. + */ + if (self->priv->needs_update_stage_views) + { + /* Avoid the early return in queue_update_stage_views() */ + self->priv->needs_update_stage_views = FALSE; + queue_update_stage_views (self); + } + clutter_actor_ensure_resource_scale (self); /* notify on parent mapped before potentially mapping @@ -2569,7 +2597,7 @@ static void absolute_allocation_changed (ClutterActor *actor) { actor->priv->needs_compute_resource_scale = TRUE; - actor->priv->needs_update_stage_views = TRUE; + queue_update_stage_views (actor); } static ClutterActorTraverseVisitFlags @@ -17748,12 +17776,12 @@ clutter_actor_update_stage_views (ClutterActor *self) CLUTTER_ACTOR_IN_DESTRUCTION (self)) return; - if (priv->needs_update_stage_views) - { - update_stage_views (self); + if (!priv->needs_update_stage_views) + return; - priv->needs_update_stage_views = FALSE; - } + update_stage_views (self); + + priv->needs_update_stage_views = FALSE; for (child = priv->first_child; child; child = child->priv->next_sibling) clutter_actor_update_stage_views (child); -- GitLab From 49e983b06e0899b4d66738f5e969d332e56044f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Sat, 25 Apr 2020 00:14:14 +0200 Subject: [PATCH 7/7] tests: Add a test for ClutterActors stage_views() API Test that the stage-views list of ClutterActor is correct when moving an actor, reparenting it, or hiding an actor up the hierarchy. Also test that the "stage-views-changed" signal works as expected. Don't test actor transforms for now because those aren't supported yet. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196 --- src/tests/meson.build | 28 ++ src/tests/stage-view-tests.c | 504 +++++++++++++++++++++++++++++++++++ 2 files changed, 532 insertions(+) create mode 100644 src/tests/stage-view-tests.c diff --git a/src/tests/meson.build b/src/tests/meson.build index 66b7f6222a5..92d110375ee 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -123,6 +123,27 @@ headless_start_test = executable('mutter-headless-start-test', install_dir: mutter_installed_tests_libexecdir, ) +stage_view_tests = executable('mutter-stage-view-tests', + sources: [ + 'meta-backend-test.c', + 'meta-backend-test.h', + 'meta-gpu-test.c', + 'meta-gpu-test.h', + 'meta-monitor-manager-test.c', + 'meta-monitor-manager-test.h', + 'monitor-test-utils.c', + 'monitor-test-utils.h', + 'stage-view-tests.c', + 'test-utils.c', + 'test-utils.h', + ], + include_directories: tests_includepath, + c_args: tests_c_args, + dependencies: [tests_deps], + install: have_installed_tests, + install_dir: mutter_installed_tests_libexecdir, +) + stacking_tests = [ 'basic-x11', 'basic-wayland', @@ -173,3 +194,10 @@ test('headless-start', headless_start_test, is_parallel: false, timeout: 60, ) + +test('stage-view', stage_view_tests, + suite: ['core', 'mutter/unit'], + env: test_env, + is_parallel: false, + timeout: 60, +) diff --git a/src/tests/stage-view-tests.c b/src/tests/stage-view-tests.c new file mode 100644 index 00000000000..47eb4bc9916 --- /dev/null +++ b/src/tests/stage-view-tests.c @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2020 Jonas Dreßler + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "config.h" + +#include "compositor/meta-plugin-manager.h" +#include "core/main-private.h" +#include "meta/main.h" +#include "tests/meta-backend-test.h" +#include "tests/monitor-test-utils.h" +#include "tests/test-utils.h" + +#define FRAME_WARNING "Frame has assigned frame counter but no frame drawn time" + +static gboolean +run_tests (gpointer data) +{ + MetaBackend *backend = meta_get_backend (); + MetaSettings *settings = meta_backend_get_settings (backend); + gboolean ret; + + g_test_log_set_fatal_handler (NULL, NULL); + + meta_settings_override_experimental_features (settings); + + meta_settings_enable_experimental_feature ( + settings, + META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER); + + ret = g_test_run (); + + meta_quit (ret != 0); + + return G_SOURCE_REMOVE; +} + +static gboolean +ignore_frame_counter_warning (const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer user_data) +{ + if ((log_level & G_LOG_LEVEL_WARNING) && + g_strcmp0 (log_domain, "mutter") == 0 && + g_str_has_suffix (message, FRAME_WARNING)) + return FALSE; + + return TRUE; +} + +static MonitorTestCaseSetup initial_test_case_setup = { + .modes = { + { + .width = 1024, + .height = 768, + .refresh_rate = 60.0 + } + }, + .n_modes = 1, + .outputs = { + { + .crtc = 0, + .modes = { 0 }, + .n_modes = 1, + .preferred_mode = 0, + .possible_crtcs = { 0 }, + .n_possible_crtcs = 1, + .width_mm = 222, + .height_mm = 125 + }, + { + .crtc = 1, + .modes = { 0 }, + .n_modes = 1, + .preferred_mode = 0, + .possible_crtcs = { 1 }, + .n_possible_crtcs = 1, + .width_mm = 220, + .height_mm = 124 + } + }, + .n_outputs = 2, + .crtcs = { + { + .current_mode = 0 + }, + { + .current_mode = 0 + } + }, + .n_crtcs = 2 +}; + +static void +meta_test_stage_views_exist (void) +{ + MetaBackend *backend = meta_get_backend (); + ClutterActor *stage; + GList *stage_views; + + stage = meta_backend_get_stage (backend); + g_assert_cmpint (clutter_actor_get_width (stage), ==, 1024 * 2); + g_assert_cmpint (clutter_actor_get_height (stage), ==, 768); + + stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage)); + g_assert_cmpint (g_list_length (stage_views), ==, 2); +} + +static void +on_after_paint (ClutterStage *stage, + gboolean *was_painted) +{ + *was_painted = TRUE; +} + +static void +wait_for_paint (ClutterActor *stage) +{ + gboolean was_painted = FALSE; + gulong was_painted_id; + + was_painted_id = g_signal_connect (CLUTTER_STAGE (stage), + "after-paint", + G_CALLBACK (on_after_paint), + &was_painted); + + while (!was_painted) + g_main_context_iteration (NULL, FALSE); + + g_signal_handler_disconnect (stage, was_painted_id); +} + +static void +on_stage_views_changed (ClutterActor *actor, + gboolean *stage_views_changed) +{ + *stage_views_changed = TRUE; +} + +static void +is_on_stage_views (ClutterActor *actor, + unsigned int n_views, + ...) +{ + va_list valist; + int i = 0; + GList *stage_views = clutter_actor_peek_stage_views (actor); + + va_start (valist, n_views); + for (i = 0; i < n_views; i++) + { + ClutterStageView *view = va_arg (valist, ClutterStageView*); + g_assert_nonnull (g_list_find (stage_views, view)); + } + + va_end (valist); + g_assert (g_list_length (stage_views) == n_views); +} + +static void +meta_test_actor_stage_views (void) +{ + MetaBackend *backend = meta_get_backend (); + ClutterActor *stage, *container, *test_actor; + GList *stage_views; + gboolean stage_views_changed_container = FALSE; + gboolean stage_views_changed_test_actor = FALSE; + gboolean *stage_views_changed_container_ptr = + &stage_views_changed_container; + gboolean *stage_views_changed_test_actor_ptr = + &stage_views_changed_test_actor; + + stage = meta_backend_get_stage (backend); + stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage)); + + container = clutter_actor_new (); + clutter_actor_set_size (container, 100, 100); + clutter_actor_add_child (stage, container); + + test_actor = clutter_actor_new (); + clutter_actor_set_size (test_actor, 50, 50); + clutter_actor_add_child (container, test_actor); + + g_signal_connect (container, "stage-views-changed", + G_CALLBACK (on_stage_views_changed), + stage_views_changed_container_ptr); + g_signal_connect (test_actor, "stage-views-changed", + G_CALLBACK (on_stage_views_changed), + stage_views_changed_test_actor_ptr); + + clutter_actor_show (stage); + + wait_for_paint (stage); + + is_on_stage_views (container, 1, stage_views->data); + is_on_stage_views (test_actor, 1, stage_views->data); + + /* The signal was emitted for the initial change */ + g_assert (stage_views_changed_container); + g_assert (stage_views_changed_test_actor); + stage_views_changed_container = FALSE; + stage_views_changed_test_actor = FALSE; + + /* Move the container to the second stage view */ + clutter_actor_set_x (container, 1040); + + wait_for_paint (stage); + + is_on_stage_views (container, 1, stage_views->next->data); + is_on_stage_views (test_actor, 1, stage_views->next->data); + + /* The signal was emitted again */ + g_assert (stage_views_changed_container); + g_assert (stage_views_changed_test_actor); + stage_views_changed_container = FALSE; + stage_views_changed_test_actor = FALSE; + + /* Move the container so it's on both stage views while the test_actor + * is only on the first one. + */ + clutter_actor_set_x (container, 940); + + wait_for_paint (stage); + + is_on_stage_views (container, 2, stage_views->data, stage_views->next->data); + is_on_stage_views (test_actor, 1, stage_views->data); + + /* The signal was emitted again */ + g_assert (stage_views_changed_container); + g_assert (stage_views_changed_test_actor); + + g_signal_handlers_disconnect_by_func (container, on_stage_views_changed, + stage_views_changed_container_ptr); + g_signal_handlers_disconnect_by_func (test_actor, on_stage_views_changed, + stage_views_changed_test_actor_ptr); + clutter_actor_destroy (container); +} + +static void +meta_test_actor_stage_views_reparent (void) +{ + MetaBackend *backend = meta_get_backend (); + ClutterActor *stage, *container, *test_actor; + GList *stage_views; + gboolean stage_views_changed_container = FALSE; + gboolean stage_views_changed_test_actor = FALSE; + gboolean *stage_views_changed_container_ptr = + &stage_views_changed_container; + gboolean *stage_views_changed_test_actor_ptr = + &stage_views_changed_test_actor; + + stage = meta_backend_get_stage (backend); + stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage)); + + container = clutter_actor_new (); + clutter_actor_set_size (container, 100, 100); + clutter_actor_set_x (container, 1020); + clutter_actor_add_child (stage, container); + + test_actor = clutter_actor_new (); + clutter_actor_set_size (test_actor, 20, 20); + clutter_actor_add_child (container, test_actor); + + g_signal_connect (container, "stage-views-changed", + G_CALLBACK (on_stage_views_changed), + stage_views_changed_container_ptr); + g_signal_connect (test_actor, "stage-views-changed", + G_CALLBACK (on_stage_views_changed), + stage_views_changed_test_actor_ptr); + + clutter_actor_show (stage); + + wait_for_paint (stage); + + is_on_stage_views (container, 2, stage_views->data, stage_views->next->data); + is_on_stage_views (test_actor, 2, stage_views->data, stage_views->next->data); + + /* The signal was emitted for both actors */ + g_assert (stage_views_changed_container); + g_assert (stage_views_changed_test_actor); + stage_views_changed_container = FALSE; + stage_views_changed_test_actor = FALSE; + + /* Remove the test_actor from the scene-graph */ + g_object_ref (test_actor); + clutter_actor_remove_child (container, test_actor); + + /* While the test_actor is not on stage, it must be on no stage views */ + is_on_stage_views (test_actor, 0); + + /* When the test_actor left the stage, the signal was emitted */ + g_assert (!stage_views_changed_container); + g_assert (stage_views_changed_test_actor); + stage_views_changed_test_actor = FALSE; + + /* Add the test_actor again as a child of the stage */ + clutter_actor_add_child (stage, test_actor); + g_object_unref (test_actor); + + wait_for_paint (stage); + + /* The container is still on both stage views... */ + is_on_stage_views (container, 2, stage_views->data, stage_views->next->data); + + /* ...while the test_actor is only on the first one now */ + is_on_stage_views (test_actor, 1, stage_views->data); + + /* The signal was emitted for the test_actor again */ + g_assert (!stage_views_changed_container); + g_assert (stage_views_changed_test_actor); + stage_views_changed_test_actor = FALSE; + + /* Move the container out of the stage... */ + clutter_actor_set_y (container, 2000); + g_object_ref (test_actor); + clutter_actor_remove_child (stage, test_actor); + + /* When the test_actor left the stage, the signal was emitted */ + g_assert (!stage_views_changed_container); + g_assert (stage_views_changed_test_actor); + stage_views_changed_test_actor = FALSE; + + /* ...and reparent the test_actor to the container again */ + clutter_actor_add_child (container, test_actor); + g_object_unref (test_actor); + + wait_for_paint (stage); + + /* Now both actors are on no stage views */ + is_on_stage_views (container, 0); + is_on_stage_views (test_actor, 0); + + /* The signal was emitted only for the container, the test_actor already + * has no stage-views. + */ + g_assert (stage_views_changed_container); + g_assert (!stage_views_changed_test_actor); + + g_signal_handlers_disconnect_by_func (container, on_stage_views_changed, + stage_views_changed_container_ptr); + g_signal_handlers_disconnect_by_func (test_actor, on_stage_views_changed, + stage_views_changed_test_actor_ptr); + clutter_actor_destroy (container); +} + +static void +meta_test_actor_stage_views_hide_parent (void) +{ + MetaBackend *backend = meta_get_backend (); + ClutterActor *stage, *outer_container, *inner_container, *test_actor; + GList *stage_views; + gboolean stage_views_changed_outer_container = FALSE; + gboolean stage_views_changed_inner_container = FALSE; + gboolean stage_views_changed_test_actor = FALSE; + gboolean *stage_views_changed_outer_container_ptr = + &stage_views_changed_outer_container; + gboolean *stage_views_changed_inner_container_ptr = + &stage_views_changed_inner_container; + gboolean *stage_views_changed_test_actor_ptr = + &stage_views_changed_test_actor; + + stage = meta_backend_get_stage (backend); + stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage)); + + outer_container = clutter_actor_new (); + clutter_actor_add_child (stage, outer_container); + + inner_container = clutter_actor_new (); + clutter_actor_add_child (outer_container, inner_container); + + test_actor = clutter_actor_new (); + clutter_actor_set_size (test_actor, 20, 20); + clutter_actor_add_child (inner_container, test_actor); + + g_signal_connect (outer_container, "stage-views-changed", + G_CALLBACK (on_stage_views_changed), + stage_views_changed_outer_container_ptr); + g_signal_connect (inner_container, "stage-views-changed", + G_CALLBACK (on_stage_views_changed), + stage_views_changed_inner_container_ptr); + g_signal_connect (test_actor, "stage-views-changed", + G_CALLBACK (on_stage_views_changed), + stage_views_changed_test_actor_ptr); + + clutter_actor_show (stage); + + wait_for_paint (stage); + + /* The containers and the test_actor are on all on the first view */ + is_on_stage_views (outer_container, 1, stage_views->data); + is_on_stage_views (inner_container, 1, stage_views->data); + is_on_stage_views (test_actor, 1, stage_views->data); + + /* The signal was emitted for all three */ + g_assert (stage_views_changed_outer_container); + g_assert (stage_views_changed_inner_container); + g_assert (stage_views_changed_test_actor); + stage_views_changed_outer_container = FALSE; + stage_views_changed_inner_container = FALSE; + stage_views_changed_test_actor = FALSE; + + /* Hide the inner_container */ + clutter_actor_hide (inner_container); + + /* Move the outer_container so it's still on the first view */ + clutter_actor_set_x (outer_container, 1023); + + wait_for_paint (stage); + + /* The outer_container is still expanded so it should be on both views */ + is_on_stage_views (outer_container, 2, + stage_views->data, stage_views->next->data); + + /* The inner_container and test_actor aren't updated because they're hidden */ + is_on_stage_views (inner_container, 1, stage_views->data); + is_on_stage_views (test_actor, 1, stage_views->data); + + /* The signal was emitted for the outer_container */ + g_assert (stage_views_changed_outer_container); + g_assert (!stage_views_changed_inner_container); + g_assert (!stage_views_changed_test_actor); + stage_views_changed_outer_container = FALSE; + + /* Show the inner_container again */ + clutter_actor_show (inner_container); + + wait_for_paint (stage); + + /* All actors are on both views now */ + is_on_stage_views (outer_container, 2, + stage_views->data, stage_views->next->data); + is_on_stage_views (inner_container, 2, + stage_views->data, stage_views->next->data); + is_on_stage_views (test_actor, 2, + stage_views->data, stage_views->next->data); + + /* The signal was emitted for the inner_container and test_actor */ + g_assert (!stage_views_changed_outer_container); + g_assert (stage_views_changed_inner_container); + g_assert (stage_views_changed_test_actor); + + g_signal_handlers_disconnect_by_func (outer_container, on_stage_views_changed, + stage_views_changed_outer_container_ptr); + g_signal_handlers_disconnect_by_func (inner_container, on_stage_views_changed, + stage_views_changed_inner_container_ptr); + g_signal_handlers_disconnect_by_func (test_actor, on_stage_views_changed, + stage_views_changed_test_actor_ptr); + clutter_actor_destroy (outer_container); +} + +static void +init_tests (int argc, char **argv) +{ + MetaMonitorTestSetup *test_setup; + + test_setup = create_monitor_test_setup (&initial_test_case_setup, + MONITOR_TEST_FLAG_NO_STORED); + + meta_monitor_manager_test_init_test_setup (test_setup); + + g_test_add_func ("/stage-view/stage-views-exist", + meta_test_stage_views_exist); + g_test_add_func ("/stage-views/actor-stage-views", + meta_test_actor_stage_views); + g_test_add_func ("/stage-views/actor-stage-views-reparent", + meta_test_actor_stage_views_reparent); + g_test_add_func ("/stage-views/actor-stage-views-hide-parent", + meta_test_actor_stage_views_hide_parent); +} + +int +main (int argc, char *argv[]) +{ + test_init (&argc, &argv); + init_tests (argc, argv); + + meta_plugin_manager_load (test_get_plugin_name ()); + + meta_override_compositor_configuration (META_COMPOSITOR_TYPE_WAYLAND, + META_TYPE_BACKEND_TEST); + + meta_init (); + meta_register_with_session (); + + g_test_log_set_fatal_handler (ignore_frame_counter_warning, NULL); + + g_idle_add (run_tests, NULL); + + return meta_run (); +} -- GitLab