diff --git a/.gitignore b/.gitignore index c8c08ea0f..730677e36 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ debian/muffin-common/ debian/muffin-dbg/ debian/muffin/ debian/tmp/ + +.cache diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h index 9ff4b2d4b..e85a99431 100644 --- a/clutter/clutter/clutter-actor-private.h +++ b/clutter/clutter/clutter-actor-private.h @@ -313,14 +313,18 @@ 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); +float clutter_actor_get_real_resource_scale (ClutterActor *actor); ClutterPaintNode * clutter_actor_create_texture_paint_node (ClutterActor *self, CoglTexture *texture); +void clutter_actor_update_stage_views (ClutterActor *self, + int phase); + +void clutter_actor_queue_immediate_relayout (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 9d4d36ec3..93dd13ead 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -698,7 +698,6 @@ struct _ClutterActorPrivate * allocation */ ClutterActorBox allocation; - ClutterAllocationFlags allocation_flags; /* clip, in actor coordinates */ graphene_rect_t clip; @@ -813,6 +812,8 @@ struct _ClutterActorPrivate gulong resolution_changed_id; gulong font_changed_id; + GList *stage_views; + /* bitfields: KEEP AT THE END */ /* fixed position and sizes */ @@ -853,7 +854,8 @@ struct _ClutterActorPrivate guint needs_y_expand : 1; guint needs_paint_volume_update : 1; 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 @@ -925,7 +927,6 @@ enum PROP_SCALE_CENTER_X, /* XXX:2.0 remove */ PROP_SCALE_CENTER_Y, /* XXX:2.0 remove */ PROP_SCALE_GRAVITY, /* XXX:2.0 remove */ - PROP_RESOURCE_SCALE, PROP_ROTATION_ANGLE_X, /* XXX:2.0 rename to rotation-x */ PROP_ROTATION_ANGLE_Y, /* XXX:2.0 rename to rotation-y */ @@ -1013,10 +1014,11 @@ enum MOTION_EVENT, ENTER_EVENT, LEAVE_EVENT, - ALLOCATION_CHANGED, TRANSITIONS_COMPLETED, TOUCH_EVENT, TRANSITION_STOPPED, + STAGE_VIEWS_CHANGED, + RESOURCE_SCALE_CHANGED, LAST_SIGNAL }; @@ -1104,8 +1106,6 @@ static void clutter_actor_set_child_transform_internal (ClutterActor *sel static void clutter_actor_realize_internal (ClutterActor *self); static void clutter_actor_unrealize_internal (ClutterActor *self); -static gboolean clutter_actor_update_resource_scale (ClutterActor *self); -static void clutter_actor_ensure_resource_scale (ClutterActor *self); static void clutter_actor_push_in_cloned_branch (ClutterActor *self, gulong count); @@ -1637,6 +1637,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) { @@ -1651,7 +1667,17 @@ clutter_actor_real_map (ClutterActor *self) self->priv->needs_paint_volume_update = TRUE; - clutter_actor_ensure_resource_scale (self); + /* 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); + } /* notify on parent mapped before potentially mapping * children, so apps see a top-down notification. @@ -2584,6 +2610,22 @@ clutter_actor_notify_if_geometry_changed (ClutterActor *self, g_object_thaw_notify (obj); } +static void +absolute_allocation_changed (ClutterActor *actor) +{ + queue_update_stage_views (actor); +} + +static ClutterActorTraverseVisitFlags +absolute_allocation_changed_cb (ClutterActor *actor, + int depth, + gpointer user_data) +{ + absolute_allocation_changed (actor); + + return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; +} + /*< private > * clutter_actor_set_allocation_internal: * @self: a #ClutterActor @@ -2600,20 +2642,15 @@ clutter_actor_notify_if_geometry_changed (ClutterActor *self, * Return value: %TRUE if the allocation of the #ClutterActor has been * changed, and %FALSE otherwise */ -static inline gboolean +static inline void clutter_actor_set_allocation_internal (ClutterActor *self, - const ClutterActorBox *box, - ClutterAllocationFlags flags) + const ClutterActorBox *box) { ClutterActorPrivate *priv = self->priv; GObject *obj; gboolean x1_changed, y1_changed, x2_changed, y2_changed; - gboolean retval; ClutterActorBox old_alloc = { 0, }; - g_return_val_if_fail (!isnan (box->x1) && !isnan (box->x2) && - !isnan (box->y1) && !isnan (box->y2), FALSE); - obj = G_OBJECT (self); g_object_freeze_notify (obj); @@ -2626,13 +2663,17 @@ clutter_actor_set_allocation_internal (ClutterActor *self, y2_changed = priv->allocation.y2 != box->y2; priv->allocation = *box; - priv->allocation_flags = flags; /* allocation is authoritative */ priv->needs_width_request = FALSE; priv->needs_height_request = FALSE; priv->needs_allocation = FALSE; + priv->absolute_origin_changed |= x1_changed || y1_changed; + + if (priv->absolute_origin_changed || x2_changed || y2_changed) + absolute_allocation_changed (self); + if (x1_changed || y1_changed || x2_changed || @@ -2651,88 +2692,37 @@ clutter_actor_set_allocation_internal (ClutterActor *self, priv->content_box_valid = FALSE; g_object_notify_by_pspec (obj, obj_props[PROP_CONTENT_BOX]); } - - retval = TRUE; } - else - retval = FALSE; clutter_actor_notify_if_geometry_changed (self, &old_alloc); g_object_thaw_notify (obj); - - return retval; } -static void clutter_actor_real_allocate (ClutterActor *self, - const ClutterActorBox *box, - ClutterAllocationFlags flags); - -static inline void -clutter_actor_maybe_layout_children (ClutterActor *self, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags) +static void +clutter_actor_real_allocate (ClutterActor *self, + const ClutterActorBox *box) { ClutterActorPrivate *priv = self->priv; - /* this is going to be a bit hard to follow, so let's put an explanation - * here. - * - * we want ClutterActor to have a default layout manager if the actor was - * created using "g_object_new (CLUTTER_TYPE_ACTOR, NULL)". - * - * we also want any subclass of ClutterActor that does not override the - * ::allocate() virtual function to delegate to a layout manager. - * - * finally, we want to allow people subclassing ClutterActor and overriding - * the ::allocate() vfunc to let Clutter delegate to the layout manager. - * - * on the other hand, we want existing actor subclasses overriding the - * ::allocate() virtual function and chaining up to the parent's - * implementation to continue working without allocating their children - * twice, or without entering an allocation loop. - * - * for the first two points, we check if the class of the actor is - * overridding the ::allocate() virtual function; if it isn't, then we - * follow through with checking whether we have children and a layout - * manager, and eventually calling clutter_layout_manager_allocate(). - * - * for the third point, we check the CLUTTER_DELEGATE_LAYOUT flag in the - * allocation flags that we got passed, and if it is present, we continue - * with the check above. - * - * if neither of these two checks yields a positive result, we just - * assume that the ::allocate() virtual function that resulted in this - * function being called will also allocate the children of the actor. - */ - - if (CLUTTER_ACTOR_GET_CLASS (self)->allocate == clutter_actor_real_allocate) - goto check_layout; - - if ((flags & CLUTTER_DELEGATE_LAYOUT) != 0) - goto check_layout; + g_object_freeze_notify (G_OBJECT (self)); - return; + clutter_actor_set_allocation_internal (self, box); -check_layout: +/* we allocate our children before we notify changes in our geometry, + * so that people connecting to properties will be able to get valid + * data out of the sub-tree of the scene graph that has this actor at + * the root. + */ if (priv->n_children != 0 && priv->layout_manager != NULL) { - ClutterContainer *container = CLUTTER_CONTAINER (self); - ClutterAllocationFlags children_flags; ClutterActorBox children_box; /* normalize the box passed to the layout manager */ children_box.x1 = children_box.y1 = 0.f; - children_box.x2 = (allocation->x2 - allocation->x1); - children_box.y2 = (allocation->y2 - allocation->y1); - - /* remove the DELEGATE_LAYOUT flag; this won't be passed to - * the actor's children, since it refers only to the current - * actor's allocation. - */ - children_flags = flags; - children_flags &= ~CLUTTER_DELEGATE_LAYOUT; + children_box.x2 = box->x2 - box->x1; + children_box.y2 = box->y2 - box->y1; CLUTTER_NOTE (LAYOUT, "Allocating %d children of %s " @@ -2740,46 +2730,15 @@ clutter_actor_maybe_layout_children (ClutterActor *self, "using %s", priv->n_children, _clutter_actor_get_debug_name (self), - allocation->x1, - allocation->y1, - (allocation->x2 - allocation->x1), - (allocation->y2 - allocation->y1), + box->x1, + box->y1, + (box->x2 - box->x1), + (box->y2 - box->y1), G_OBJECT_TYPE_NAME (priv->layout_manager)); clutter_layout_manager_allocate (priv->layout_manager, - container, - &children_box, - children_flags); - } -} - -static void -clutter_actor_real_allocate (ClutterActor *self, - const ClutterActorBox *box, - ClutterAllocationFlags flags) -{ - ClutterActorPrivate *priv = self->priv; - gboolean changed; - - g_object_freeze_notify (G_OBJECT (self)); - - changed = clutter_actor_set_allocation_internal (self, box, flags); - - /* we allocate our children before we notify changes in our geometry, - * so that people connecting to properties will be able to get valid - * data out of the sub-tree of the scene graph that has this actor at - * the root. - */ - clutter_actor_maybe_layout_children (self, box, flags); - - if (changed) - { - ClutterActorBox signal_box = priv->allocation; - ClutterAllocationFlags signal_flags = priv->allocation_flags; - - g_signal_emit (self, actor_signals[ALLOCATION_CHANGED], 0, - &signal_box, - signal_flags); + CLUTTER_CONTAINER (self), + &children_box); } g_object_thaw_notify (G_OBJECT (self)); @@ -3989,8 +3948,6 @@ clutter_actor_paint (ClutterActor *self, if (!CLUTTER_ACTOR_IS_MAPPED (self)) return; - clutter_actor_ensure_resource_scale (self); - actor_node = clutter_actor_node_new (self); root_node = clutter_paint_node_ref (actor_node); @@ -4274,8 +4231,6 @@ clutter_actor_pick (ClutterActor *actor, if (!CLUTTER_ACTOR_IS_MAPPED (actor)) return; - clutter_actor_ensure_resource_scale (actor); - /* mark that we are in the paint process */ CLUTTER_SET_PRIVATE_FLAGS (actor, CLUTTER_IN_PICK); @@ -4471,6 +4426,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 | @@ -4479,14 +4435,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 > @@ -4508,6 +4466,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) @@ -4524,6 +4483,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); @@ -4597,12 +4557,16 @@ 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_IN_DESTRUCTION (child)) + clutter_actor_clear_stage_views_recursive (child); + if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child) && !CLUTTER_ACTOR_IN_DESTRUCTION (child)) - { - child->priv->needs_compute_resource_scale = TRUE; - g_signal_emit (child, actor_signals[PARENT_SET], 0, self); - } + g_signal_emit (child, actor_signals[PARENT_SET], 0, self); /* if the child was mapped then we need to relayout ourselves to account * for the removed child @@ -5903,16 +5867,6 @@ clutter_actor_get_property (GObject *object, g_value_set_enum (value, clutter_actor_get_scale_gravity (actor)); break; - case PROP_RESOURCE_SCALE: - if (priv->needs_compute_resource_scale) - { - if (!clutter_actor_update_resource_scale (actor)) - g_warning ("Getting invalid resource scale property"); - } - - g_value_set_float (value, priv->resource_scale); - break; - case PROP_REACTIVE: g_value_set_boolean (value, clutter_actor_get_reactive (actor)); break; @@ -6269,6 +6223,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); } @@ -6526,6 +6482,25 @@ clutter_actor_real_has_overlaps (ClutterActor *self) return TRUE; } +static float +clutter_actor_real_calculate_resource_scale (ClutterActor *self, + int phase) +{ + ClutterActorPrivate *priv = self->priv; + GList *l; + float new_resource_scale = -1.f; + + for (l = priv->stage_views; l; l = l->next) + { + ClutterStageView *view = l->data; + + new_resource_scale = MAX (clutter_stage_view_get_scale (view), + new_resource_scale); + } + + return new_resource_scale; +} + static void clutter_actor_real_destroy (ClutterActor *actor) { @@ -6621,6 +6596,7 @@ clutter_actor_class_init (ClutterActorClass *klass) klass->get_accessible = clutter_actor_real_get_accessible; klass->get_paint_volume = clutter_actor_real_get_paint_volume; klass->has_overlaps = clutter_actor_real_has_overlaps; + klass->calculate_resource_scale = clutter_actor_real_calculate_resource_scale; klass->paint = clutter_actor_real_paint; klass->destroy = clutter_actor_real_destroy; @@ -7375,19 +7351,6 @@ clutter_actor_class_init (ClutterActorClass *klass) G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED); - /** - * ClutterActor:resource-scale: - * - * The resource-scale of the #ClutterActor if any or -1 if not available - */ - obj_props[PROP_RESOURCE_SCALE] = - g_param_spec_float ("resource-scale", - P_("Resource Scale"), - P_("The Scaling factor for resources painting"), - -1.0f, G_MAXFLOAT, - 1.0f, - CLUTTER_PARAM_READABLE); - /** * ClutterActor:rotation-angle-x: * @@ -8745,35 +8708,6 @@ clutter_actor_class_init (ClutterActorClass *klass) G_TYPE_NONE, 1, CLUTTER_TYPE_PICK_CONTEXT); - /** - * ClutterActor::allocation-changed: - * @actor: the #ClutterActor that emitted the signal - * @box: a #ClutterActorBox with the new allocation - * @flags: #ClutterAllocationFlags for the allocation - * - * The ::allocation-changed signal is emitted when the - * #ClutterActor:allocation property changes. Usually, application - * code should just use the notifications for the :allocation property - * but if you want to track the allocation flags as well, for instance - * to know whether the absolute origin of @actor changed, then you might - * want use this signal instead. - * - * Since: 1.0 - */ - actor_signals[ALLOCATION_CHANGED] = - g_signal_new (I_("allocation-changed"), - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - _clutter_marshal_VOID__BOXED_FLAGS, - G_TYPE_NONE, 2, - CLUTTER_TYPE_ACTOR_BOX | G_SIGNAL_TYPE_STATIC_SCOPE, - CLUTTER_TYPE_ALLOCATION_FLAGS); - g_signal_set_va_marshaller (actor_signals[ALLOCATION_CHANGED], - G_TYPE_FROM_CLASS (object_class), - _clutter_marshal_VOID__BOXED_FLAGSv); - /** * ClutterActor::transitions-completed: * @actor: a #ClutterActor @@ -8845,6 +8779,44 @@ 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); + + /** + * ClutterActor::resource-scale-changed: + * @actor: a #ClutterActor + * + * The ::resource-scale-changed signal is emitted when the resource scale + * value returned by clutter_actor_get_resource_scale() changes. + * + * This signal can be used to get notified about the correct resource scale + * when the scale had to be queried outside of the paint cycle. + */ + actor_signals[RESOURCE_SCALE_CHANGED] = + g_signal_new (I_("resource-scale-changed"), + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterActorClass, resource_scale_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 0); } static void @@ -8862,7 +8834,7 @@ clutter_actor_init (ClutterActor *self) priv->needs_height_request = TRUE; 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; @@ -10311,8 +10283,7 @@ clutter_actor_adjust_allocation (ClutterActor *self, static void clutter_actor_allocate_internal (ClutterActor *self, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags) + const ClutterActorBox *allocation) { ClutterActorClass *klass; @@ -10322,7 +10293,7 @@ clutter_actor_allocate_internal (ClutterActor *self, _clutter_actor_get_debug_name (self)); klass = CLUTTER_ACTOR_GET_CLASS (self); - klass->allocate (self, allocation, flags); + klass->allocate (self, allocation); CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT); @@ -10335,7 +10306,6 @@ clutter_actor_allocate_internal (ClutterActor *self, * clutter_actor_allocate: * @self: A #ClutterActor * @box: new allocation of the actor, in parent-relative coordinates - * @flags: flags that control the allocation * * Assigns the size of a #ClutterActor from the given @box. * @@ -10361,13 +10331,11 @@ clutter_actor_allocate_internal (ClutterActor *self, * Since: 0.8 */ void -clutter_actor_allocate (ClutterActor *self, - const ClutterActorBox *box, - ClutterAllocationFlags flags) +clutter_actor_allocate (ClutterActor *self, + const ClutterActorBox *box) { ClutterActorBox old_allocation, real_allocation; - gboolean origin_changed, child_moved, size_changed; - gboolean stage_allocation_changed; + gboolean origin_changed, size_changed; ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); @@ -10379,11 +10347,25 @@ clutter_actor_allocate (ClutterActor *self, return; } - if (!clutter_actor_is_visible (self)) - return; - priv = self->priv; + priv->absolute_origin_changed = priv->parent + ? priv->parent->priv->absolute_origin_changed + : FALSE; + + if (!CLUTTER_ACTOR_IS_VISIBLE (self)) + { + if (priv->absolute_origin_changed) + { + _clutter_actor_traverse (self, + CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST, + absolute_allocation_changed_cb, + NULL, + NULL); + } + goto out; + } + old_allocation = priv->allocation; real_allocation = *box; @@ -10414,88 +10396,65 @@ clutter_actor_allocate (ClutterActor *self, real_allocation.x2 = MAX (real_allocation.x2, real_allocation.x1); real_allocation.y2 = MAX (real_allocation.y2, real_allocation.y1); - origin_changed = (flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED); - - child_moved = (real_allocation.x1 != old_allocation.x1 || - real_allocation.y1 != old_allocation.y1); + origin_changed = (real_allocation.x1 != old_allocation.x1 || + real_allocation.y1 != old_allocation.y1); size_changed = (real_allocation.x2 != old_allocation.x2 || real_allocation.y2 != old_allocation.y2); - if (origin_changed || child_moved || size_changed) - stage_allocation_changed = TRUE; - else - stage_allocation_changed = FALSE; - - /* If we get an allocation "out of the blue" - * (we did not queue relayout), then we want to - * ignore it. But if we have needs_allocation set, - * we want to guarantee that allocate() virtual - * method is always called, i.e. that queue_relayout() - * always results in an allocate() invocation on - * an actor. + /* When needs_allocation is set but we didn't move nor resize, we still + * want to call the allocate() vfunc because a child probably called + * queue_relayout() and needs a new allocation. * - * The optimization here is to avoid re-allocating - * actors that did not queue relayout and were - * not moved. + * In case needs_allocation isn't set and we didn't move nor resize, we + * can safely stop allocating, but we need to notify the sub-tree in case + * our absolute origin changed. */ - if (!priv->needs_allocation && !stage_allocation_changed) + if (!priv->needs_allocation && !origin_changed && !size_changed) { + if (priv->absolute_origin_changed) + { + _clutter_actor_traverse (self, + CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST, + absolute_allocation_changed_cb, + NULL, + NULL); + } + CLUTTER_NOTE (LAYOUT, "No allocation needed"); - return; + goto out; } if (CLUTTER_ACTOR_IS_MAPPED (self)) self->priv->needs_paint_volume_update = TRUE; - if (stage_allocation_changed) - priv->needs_compute_resource_scale = TRUE; - - if (!stage_allocation_changed) + if (!origin_changed && !size_changed) { /* If the actor didn't move but needs_allocation is set, we just - * need to allocate the children */ - clutter_actor_allocate_internal (self, &real_allocation, flags); - return; + * need to allocate the children (see comment above) */ + clutter_actor_allocate_internal (self, &real_allocation); + goto out; } - /* When ABSOLUTE_ORIGIN_CHANGED is passed in to - * clutter_actor_allocate(), it indicates whether the parent has its - * absolute origin moved; when passed in to ClutterActor::allocate() - * virtual method though, it indicates whether the child has its - * absolute origin moved. So we set it when child_moved is TRUE - */ - if (child_moved) - flags |= CLUTTER_ABSOLUTE_ORIGIN_CHANGED; - - /* store the flags here, so that they can be propagated by the - * transition code - */ - self->priv->allocation_flags = flags; - _clutter_actor_create_transition (self, obj_props[PROP_ALLOCATION], &priv->allocation, &real_allocation); +out: + priv->absolute_origin_changed = FALSE; } /** * clutter_actor_set_allocation: * @self: a #ClutterActor * @box: a #ClutterActorBox - * @flags: allocation flags * * Stores the allocation of @self as defined by @box. * * This function can only be called from within the implementation of * the #ClutterActorClass.allocate() virtual function. * - * The allocation should have been adjusted to take into account constraints, - * alignment, and margin properties. If you are implementing a #ClutterActor - * subclass that provides its own layout management policy for its children - * instead of using a #ClutterLayoutManager delegate, you should not call - * this function on the children of @self; instead, you should call - * clutter_actor_allocate(), which will adjust the allocation box for - * you. + * The allocation @box should have been adjusted to take into account + * constraints, alignment, and margin properties. * * This function should only be used by subclasses of #ClutterActor * that wish to store their allocation but cannot chain up to the @@ -10503,69 +10462,12 @@ clutter_actor_allocate (ClutterActor *self, * #ClutterActorClass.allocate() virtual function will call this * function. * - * It is important to note that, while chaining up was the recommended - * behaviour for #ClutterActor subclasses prior to the introduction of - * this function, it is recommended to call clutter_actor_set_allocation() - * instead. - * - * If the #ClutterActor is using a #ClutterLayoutManager delegate object - * to handle the allocation of its children, this function will call - * the clutter_layout_manager_allocate() function only if the - * %CLUTTER_DELEGATE_LAYOUT flag is set on @flags, otherwise it is - * expected that the subclass will call clutter_layout_manager_allocate() - * by itself. For instance, the following code: - * - * |[ - * static void - * my_actor_allocate (ClutterActor *actor, - * const ClutterActorBox *allocation, - * ClutterAllocationFlags flags) - * { - * ClutterActorBox new_alloc; - * ClutterAllocationFlags new_flags; - * - * adjust_allocation (allocation, &new_alloc); - * - * new_flags = flags | CLUTTER_DELEGATE_LAYOUT; - * - * // this will use the layout manager set on the actor - * clutter_actor_set_allocation (actor, &new_alloc, new_flags); - * } - * ]| - * - * is equivalent to this: - * - * |[ - * static void - * my_actor_allocate (ClutterActor *actor, - * const ClutterActorBox *allocation, - * ClutterAllocationFlags flags) - * { - * ClutterLayoutManager *layout; - * ClutterActorBox new_alloc; - * - * adjust_allocation (allocation, &new_alloc); - * - * clutter_actor_set_allocation (actor, &new_alloc, flags); - * - * layout = clutter_actor_get_layout_manager (actor); - * clutter_layout_manager_allocate (layout, - * CLUTTER_CONTAINER (actor), - * &new_alloc, - * flags); - * } - * ]| - * * Since: 1.10 */ void clutter_actor_set_allocation (ClutterActor *self, - const ClutterActorBox *box, - ClutterAllocationFlags flags) + const ClutterActorBox *box) { - ClutterActorPrivate *priv; - gboolean changed; - g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (box != NULL); @@ -10577,28 +10479,9 @@ clutter_actor_set_allocation (ClutterActor *self, return; } - priv = self->priv; - g_object_freeze_notify (G_OBJECT (self)); - changed = clutter_actor_set_allocation_internal (self, box, flags); - - /* we allocate our children before we notify changes in our geometry, - * so that people connecting to properties will be able to get valid - * data out of the sub-tree of the scene graph that has this actor at - * the root. - */ - clutter_actor_maybe_layout_children (self, box, flags); - - if (changed) - { - ClutterActorBox signal_box = priv->allocation; - ClutterAllocationFlags signal_flags = priv->allocation_flags; - - g_signal_emit (self, actor_signals[ALLOCATION_CHANGED], 0, - &signal_box, - signal_flags); - } + clutter_actor_set_allocation_internal (self, box); g_object_thaw_notify (G_OBJECT (self)); } @@ -13229,10 +13112,7 @@ clutter_actor_add_child_internal (ClutterActor *self, } if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child)) - { - child->priv->needs_compute_resource_scale = TRUE; - g_signal_emit (child, actor_signals[PARENT_SET], 0, NULL); - } + g_signal_emit (child, actor_signals[PARENT_SET], 0, NULL); if (check_state) { @@ -15083,9 +14963,7 @@ clutter_actor_set_animatable_property (ClutterActor *actor, break; case PROP_ALLOCATION: - clutter_actor_allocate_internal (actor, - g_value_get_boxed (value), - actor->priv->allocation_flags); + clutter_actor_allocate_internal (actor, g_value_get_boxed (value)); clutter_actor_queue_redraw (actor); break; @@ -15479,7 +15357,6 @@ clutter_actor_get_stage (ClutterActor *actor) * actor's natural width * @available_height: the maximum available height, or -1 to use the * actor's natural height - * @flags: flags controlling the allocation * * Allocates @self taking into account the #ClutterActor's * preferred size, but limiting it to the maximum available width @@ -15526,7 +15403,7 @@ clutter_actor_get_stage (ClutterActor *actor) * box.x1 = x; box.y1 = y; * box.x2 = box.x1 + available_width; * box.y2 = box.y1 + available_height; - * clutter_actor_allocate (self, &box, flags); + * clutter_actor_allocate (self, &box); * ]| * * This function can be used by fluid layout managers to allocate @@ -15540,8 +15417,7 @@ clutter_actor_allocate_available_size (ClutterActor *self, gfloat x, gfloat y, gfloat available_width, - gfloat available_height, - ClutterAllocationFlags flags) + gfloat available_height) { ClutterActorPrivate *priv; gfloat width, height; @@ -15597,13 +15473,12 @@ clutter_actor_allocate_available_size (ClutterActor *self, box.y1 = y; box.x2 = box.x1 + width; box.y2 = box.y1 + height; - clutter_actor_allocate (self, &box, flags); + clutter_actor_allocate (self, &box); } /** * clutter_actor_allocate_preferred_size: * @self: a #ClutterActor - * @flags: flags controlling the allocation * * Allocates the natural size of @self. * @@ -15621,8 +15496,7 @@ clutter_actor_allocate_available_size (ClutterActor *self, * Since: 0.8 */ void -clutter_actor_allocate_preferred_size (ClutterActor *self, - ClutterAllocationFlags flags) +clutter_actor_allocate_preferred_size (ClutterActor *self) { gfloat actor_x, actor_y; gfloat natural_width, natural_height; @@ -15656,7 +15530,7 @@ clutter_actor_allocate_preferred_size (ClutterActor *self, actor_box.x2 = actor_box.x1 + natural_width; actor_box.y2 = actor_box.y1 + natural_height; - clutter_actor_allocate (self, &actor_box, flags); + clutter_actor_allocate (self, &actor_box); } /** @@ -15667,7 +15541,6 @@ clutter_actor_allocate_preferred_size (ClutterActor *self, * @y_align: the vertical alignment, between 0 and 1 * @x_fill: whether the actor should fill horizontally * @y_fill: whether the actor should fill vertically - * @flags: allocation flags to be passed to clutter_actor_allocate() * * Allocates @self by taking into consideration the available allocation * area; an alignment factor on either axis; and whether the actor should @@ -15694,8 +15567,7 @@ clutter_actor_allocate_align_fill (ClutterActor *self, gdouble x_align, gdouble y_align, gboolean x_fill, - gboolean y_fill, - ClutterAllocationFlags flags) + gboolean y_fill) { ClutterActorPrivate *priv; ClutterActorBox allocation = CLUTTER_ACTOR_BOX_INIT_ZERO; @@ -15811,7 +15683,7 @@ clutter_actor_allocate_align_fill (ClutterActor *self, allocation.x2 = ceilf (allocation.x1 + MAX (child_width, 0)); allocation.y2 = ceilf (allocation.y1 + MAX (child_height, 0)); - clutter_actor_allocate (self, &allocation, flags); + clutter_actor_allocate (self, &allocation); } /** @@ -17766,201 +17638,277 @@ clutter_actor_get_paint_box (ClutterActor *self, return TRUE; } -static gboolean -_clutter_actor_get_resource_scale_for_rect (ClutterActor *self, - graphene_rect_t *bounding_rect, - float *resource_scale) +static ClutterActorTraverseVisitFlags +clear_stage_views_cb (ClutterActor *actor, + int depth, + gpointer user_data) { - ClutterActor *stage; - float max_scale = 0; + g_autoptr (GList) old_stage_views = NULL; - stage = _clutter_actor_get_stage_internal (self); - if (!stage) - return FALSE; + actor->priv->needs_update_stage_views = TRUE; - if (!_clutter_stage_get_max_view_scale_factor_for_rect (CLUTTER_STAGE (stage), - bounding_rect, - &max_scale)) - return FALSE; + old_stage_views = g_steal_pointer (&actor->priv->stage_views); - *resource_scale = max_scale; + if (old_stage_views) + g_signal_emit (actor, actor_signals[STAGE_VIEWS_CHANGED], 0); - return TRUE; + return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; } -static gboolean -_clutter_actor_compute_resource_scale (ClutterActor *self, - float *resource_scale) +void +clutter_actor_clear_stage_views_recursive (ClutterActor *self) +{ + _clutter_actor_traverse (self, + CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST, + clear_stage_views_cb, + NULL, + NULL); +} + +float +clutter_actor_get_real_resource_scale (ClutterActor *self) { - graphene_rect_t bounding_rect; ClutterActorPrivate *priv = self->priv; + float guessed_scale; - if (CLUTTER_ACTOR_IN_DESTRUCTION (self) || - CLUTTER_ACTOR_IN_PREF_SIZE (self) || - !clutter_actor_is_mapped (self)) - { - return FALSE; - } + if (priv->resource_scale != -1.f) + return priv->resource_scale; - 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 the scale hasn't been computed yet, we return a best guess */ - if (bounding_rect.size.width == 0.0 || - bounding_rect.size.height == 0.0 || - !_clutter_actor_get_resource_scale_for_rect (self, - &bounding_rect, - resource_scale)) + if (priv->parent != NULL) { - if (priv->parent) - { - gboolean in_clone_paint; - gboolean was_parent_in_clone_paint; - gboolean was_parent_unmapped; - gboolean was_parent_paint_unmapped; - gboolean ret; - - in_clone_paint = clutter_actor_is_in_clone_paint (self); - was_parent_unmapped = !clutter_actor_is_mapped (priv->parent); - was_parent_in_clone_paint = - clutter_actor_is_in_clone_paint (priv->parent); - was_parent_paint_unmapped = priv->parent->priv->enable_paint_unmapped; - - if (in_clone_paint && was_parent_unmapped) - { - _clutter_actor_set_in_clone_paint (priv->parent, TRUE); - _clutter_actor_set_enable_paint_unmapped (priv->parent, TRUE); - } - - ret = _clutter_actor_compute_resource_scale (priv->parent, - resource_scale); + /* If the scale hasn't been calculated yet, assume this actor is located + * inside its parents box and go up the hierarchy. + */ + guessed_scale = clutter_actor_get_real_resource_scale (priv->parent); + } + else if (CLUTTER_ACTOR_IS_TOPLEVEL (self)) + { + /* This must be the first allocation cycle and the resource scale of + * the stage has not been updated yet, so return it manually. + */ + GList *l; + ClutterStage *stage = CLUTTER_STAGE (self); + float max_scale = -1.f; - if (in_clone_paint && was_parent_unmapped) - { - _clutter_actor_set_in_clone_paint (priv->parent, - was_parent_in_clone_paint); - _clutter_actor_set_enable_paint_unmapped (priv->parent, - was_parent_paint_unmapped); - } - return ret; - } - else + for (l = clutter_stage_peek_stage_views (stage); l; l = l->next) { - return FALSE; + ClutterStageView *view = l->data; + max_scale = MAX (clutter_stage_view_get_scale (view), max_scale); } + guessed_scale = max_scale; } + else + { + ClutterBackend *backend = clutter_get_default_backend (); + guessed_scale = clutter_backend_get_fallback_resource_scale (backend); + } - return TRUE; -} + g_assert (guessed_scale >= 1.f); -static ClutterActorTraverseVisitFlags -queue_update_resource_scale_cb (ClutterActor *actor, - int depth, - void *user_data) -{ - actor->priv->needs_compute_resource_scale = TRUE; - return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; + /* Always return this value until we compute the correct one later. + * If our guess turns out to be wrong, we'll emit "resource-scale-changed" + * and correct it before painting. + */ + priv->resource_scale = guessed_scale; + + return priv->resource_scale; } -void -_clutter_actor_queue_update_resource_scale_recursive (ClutterActor *self) +/** + * clutter_actor_get_resource_scale: + * @self: A #ClutterActor + * + * Retrieves the resource scale for this actor. + * + * The resource scale refers to the scale the actor should use for its resources. + * For example if an actor draws a a picture of size 100 x 100 in the stage + * coordinate space, it should use a texture of twice the size (i.e. 200 x 200) + * if the resource scale is 2. + * + * The resource scale is determined by calculating the highest #ClutterStageView + * scale the actor will get painted on. + * + * Note that the scale returned by this function is only guaranteed to be + * correct when queried during the paint cycle, in all other cases this + * function will only return a best guess. If your implementation really + * needs to get a resource scale outside of the paint cycle, make sure to + * subscribe to the "resource-scale-changed" signal to get notified about + * the new, correct resource scale before painting. + * + * Also avoid getting the resource scale for actors that are not attached + * to a stage. There's no sane way for Clutter to guess which #ClutterStageView + * the actor is going to be painted on, so you'll probably end up receiving + * the "resource-scale-changed" signal and having to rebuild your resources. + * + * The best guess this function may return is usually just the last resource + * scale the actor got painted with. If this resource scale couldn't be found + * because the actor was never painted so far or Clutter was unable to + * determine its position and size, this function will return the resource + * scale of a parent. + * + * Returns: The resource scale the actor should use for its textures + */ +float +clutter_actor_get_resource_scale (ClutterActor *self) { - _clutter_actor_traverse (self, - CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST, - queue_update_resource_scale_cb, - NULL, - NULL); + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 1.f); + + return ceilf (clutter_actor_get_real_resource_scale (self)); } static gboolean -clutter_actor_update_resource_scale (ClutterActor *self) +sorted_lists_equal (GList *list_a, + GList *list_b) { - ClutterActorPrivate *priv; - float resource_scale; - float old_resource_scale; - priv = self->priv; - - g_return_val_if_fail (priv->needs_compute_resource_scale, FALSE); + GList *a, *b; - old_resource_scale = priv->resource_scale; - priv->resource_scale = -1.0f; + if (!list_a && !list_b) + return TRUE; - if (_clutter_actor_compute_resource_scale (self, &resource_scale)) + for (a = list_a, b = list_b; + a && b; + a = a->next, b = b->next) { - priv->resource_scale = resource_scale; - priv->needs_compute_resource_scale = FALSE; + if (a->data != b->data) + break; - return fabsf (old_resource_scale - resource_scale) > FLT_EPSILON; + if (!a->next && !b->next) + return TRUE; } return FALSE; } static void -clutter_actor_ensure_resource_scale (ClutterActor *self) +update_stage_views (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; + g_autoptr (GList) old_stage_views = NULL; + ClutterStage *stage; + graphene_rect_t bounding_rect; + + old_stage_views = g_steal_pointer (&priv->stage_views); - if (!priv->needs_compute_resource_scale) + 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)); + goto out; + } + + 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) + 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); + } +} + +static void +update_resource_scale (ClutterActor *self, + int phase) +{ + ClutterActorPrivate *priv = self->priv; + float new_resource_scale, old_resource_scale; + + new_resource_scale = + CLUTTER_ACTOR_GET_CLASS (self)->calculate_resource_scale (self, phase); + + if (priv->resource_scale == new_resource_scale) return; - if (clutter_actor_update_resource_scale (self)) - g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_RESOURCE_SCALE]); + /* If the actor moved out of the stage, simply keep the last scale */ + if (new_resource_scale == -1.f) + return; + + old_resource_scale = priv->resource_scale; + priv->resource_scale = new_resource_scale; + + /* Never notify the initial change, otherwise, to be consistent, + * we'd also have to notify if we guessed correctly in + * clutter_actor_get_real_resource_scale(). + */ + if (old_resource_scale == -1.f) + return; + + if (ceilf (old_resource_scale) != ceilf (priv->resource_scale)) + g_signal_emit (self, actor_signals[RESOURCE_SCALE_CHANGED], 0); } -gboolean -_clutter_actor_get_real_resource_scale (ClutterActor *self, - gfloat *resource_scale) +void +clutter_actor_update_stage_views (ClutterActor *self, + gboolean use_max_scale) { ClutterActorPrivate *priv = self->priv; + ClutterActor *child; - clutter_actor_ensure_resource_scale (self); + if (!CLUTTER_ACTOR_IS_MAPPED (self) || + CLUTTER_ACTOR_IN_DESTRUCTION (self)) + return; - if (!priv->needs_compute_resource_scale) - { - *resource_scale = priv->resource_scale; - return TRUE; - } + if (!priv->needs_update_stage_views) + return; - *resource_scale = -1.0f; - return FALSE; + update_stage_views (self); + update_resource_scale (self, use_max_scale); + + priv->needs_update_stage_views = FALSE; + + for (child = priv->first_child; child; child = child->priv->next_sibling) + clutter_actor_update_stage_views (child, use_max_scale); } /** - * clutter_actor_get_resource_scale: + * clutter_actor_peek_stage_views: * @self: A #ClutterActor - * @resource_scale: (out): return location for the resource scale * - * Retrieves the resource scale for this actor, if available. + * Retrieves the list of #ClutterStageViews the actor is being + * painted on. * - * The resource scale refers to the scale the actor should use for its resources. - * For example if an actor draws a a picture of size 100 x 100 in the stage - * coordinate space, it should use a texture of twice the size (i.e. 200 x 200) - * if the resource scale is 2. + * 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 resource scale is determined by calculating the highest #ClutterStageView - * scale the actor will get painted on. + * 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: TRUE if resource scale is set for the actor, otherwise FALSE + * 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. */ -gboolean -clutter_actor_get_resource_scale (ClutterActor *self, - gfloat *resource_scale) +GList * +clutter_actor_peek_stage_views (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); - g_return_val_if_fail (resource_scale != NULL, FALSE); - if (_clutter_actor_get_real_resource_scale (self, resource_scale)) - { - *resource_scale = ceilf (*resource_scale); - return TRUE; - } - - return FALSE; + return self->priv->stage_views; } /** @@ -21440,6 +21388,20 @@ clutter_actor_has_accessible (ClutterActor *actor) return TRUE; } +void +clutter_actor_queue_immediate_relayout (ClutterActor *self) +{ + ClutterStage *stage; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + + clutter_actor_queue_relayout (self); + + stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); + if (stage) + clutter_stage_set_actor_needs_immediate_relayout (stage); +} + /** * clutter_actor_class_set_layout_manager_type * @actor_class: A #ClutterActor class diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h index 0817c74bb..a06ac07cf 100644 --- a/clutter/clutter/clutter-actor.h +++ b/clutter/clutter/clutter-actor.h @@ -175,9 +175,12 @@ struct _ClutterActor * @get_preferred_height: virtual function, used when querying the minimum * and natural heights of an actor for a given width; it is used by * clutter_actor_get_preferred_height() - * @allocate: virtual function, used when settings the coordinates of an - * actor; it is used by clutter_actor_allocate(); it must chain up to - * the parent's implementation, or call clutter_actor_set_allocation() + * @allocate: virtual function, used when setting the coordinates of an + * actor; it is used by clutter_actor_allocate(); when overriding this + * function without chaining up, clutter_actor_set_allocation() must be + * called and children must be allocated by the implementation, when + * chaining up though, those things will be done by the parent's + * implementation. * @apply_transform: virtual function, used when applying the transformations * to an actor before painting it or when transforming coordinates or * the allocation; it must chain up to the parent's implementation @@ -253,8 +256,7 @@ struct _ClutterActorClass gfloat *min_height_p, gfloat *natural_height_p); void (* allocate) (ClutterActor *self, - const ClutterActorBox *box, - ClutterAllocationFlags flags); + const ClutterActorBox *box); /* transformations */ void (* apply_transform) (ClutterActor *actor, @@ -301,6 +303,10 @@ struct _ClutterActorClass ClutterTouchEvent *event); gboolean (* has_accessible) (ClutterActor *self); + void (* resource_scale_changed) (ClutterActor *self); + float (* calculate_resource_scale) (ClutterActor *self, + int phase); + /*< private >*/ /* padding for future expansion */ GType layout_manager_type; @@ -417,30 +423,25 @@ void clutter_actor_get_preferred_size gfloat *natural_height_p); CLUTTER_EXPORT void clutter_actor_allocate (ClutterActor *self, - const ClutterActorBox *box, - ClutterAllocationFlags flags); + const ClutterActorBox *box); CLUTTER_EXPORT -void clutter_actor_allocate_preferred_size (ClutterActor *self, - ClutterAllocationFlags flags); +void clutter_actor_allocate_preferred_size (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_allocate_available_size (ClutterActor *self, gfloat x, gfloat y, gfloat available_width, - gfloat available_height, - ClutterAllocationFlags flags); + gfloat available_height); CLUTTER_EXPORT void clutter_actor_allocate_align_fill (ClutterActor *self, const ClutterActorBox *box, gdouble x_align, gdouble y_align, gboolean x_fill, - gboolean y_fill, - ClutterAllocationFlags flags); + gboolean y_fill); CLUTTER_EXPORT void clutter_actor_set_allocation (ClutterActor *self, - const ClutterActorBox *box, - ClutterAllocationFlags flags); + const ClutterActorBox *box); CLUTTER_EXPORT void clutter_actor_get_allocation_box (ClutterActor *self, ClutterActorBox *box); @@ -602,8 +603,7 @@ gboolean clutter_actor_get_paint_box ClutterActorBox *box); CLUTTER_EXPORT -gboolean clutter_actor_get_resource_scale (ClutterActor *self, - gfloat *resource_scale); +float clutter_actor_get_resource_scale (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_has_overlaps (ClutterActor *self); @@ -935,6 +935,9 @@ void clutter_actor_class_set_layout_manager_type (ClutterActorClass *actor_class CLUTTER_EXPORT GType clutter_actor_class_get_layout_manager_type (ClutterActorClass *actor_class); +CLUTTER_EXPORT +GList * clutter_actor_peek_stage_views (ClutterActor *self); + G_END_DECLS #endif /* __CLUTTER_ACTOR_H__ */ diff --git a/clutter/clutter/clutter-align-constraint.c b/clutter/clutter/clutter-align-constraint.c index a2694aa63..ed10ece2e 100644 --- a/clutter/clutter/clutter-align-constraint.c +++ b/clutter/clutter/clutter-align-constraint.c @@ -85,8 +85,7 @@ G_DEFINE_TYPE (ClutterAlignConstraint, static void source_position_changed (ClutterActor *actor, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags, + GParamSpec *pspec, ClutterAlignConstraint *align) { if (align->actor != NULL) @@ -410,7 +409,7 @@ clutter_align_constraint_set_source (ClutterAlignConstraint *align, align->source = source; if (align->source != NULL) { - g_signal_connect (align->source, "allocation-changed", + g_signal_connect (align->source, "notify::allocation", G_CALLBACK (source_position_changed), align); g_signal_connect (align->source, "destroy", diff --git a/clutter/clutter/clutter-backend-private.h b/clutter/clutter/clutter-backend-private.h index c7c54f85b..d5dca0d25 100644 --- a/clutter/clutter/clutter-backend-private.h +++ b/clutter/clutter/clutter-backend-private.h @@ -53,6 +53,8 @@ struct _ClutterBackend gfloat units_per_em; gint32 units_serial; + float fallback_resource_scale; + ClutterStageWindow *stage_window; ClutterInputMethod *input_method; @@ -134,6 +136,12 @@ void clutter_set_allowed_drivers (const c CLUTTER_EXPORT ClutterStageWindow * clutter_backend_get_stage_window (ClutterBackend *backend); +CLUTTER_EXPORT +void clutter_backend_set_fallback_resource_scale (ClutterBackend *backend, + float fallback_resource_scale); + +float clutter_backend_get_fallback_resource_scale (ClutterBackend *backend); + G_END_DECLS #endif /* __CLUTTER_BACKEND_PRIVATE_H__ */ diff --git a/clutter/clutter/clutter-backend.c b/clutter/clutter/clutter-backend.c index ace8cd93a..37543e510 100644 --- a/clutter/clutter/clutter-backend.c +++ b/clutter/clutter/clutter-backend.c @@ -1031,3 +1031,16 @@ clutter_backend_get_default_seat (ClutterBackend *backend) return CLUTTER_BACKEND_GET_CLASS (backend)->get_default_seat (backend); } + +void +clutter_backend_set_fallback_resource_scale (ClutterBackend *backend, + float fallback_resource_scale) +{ + backend->fallback_resource_scale = fallback_resource_scale; +} + +float +clutter_backend_get_fallback_resource_scale (ClutterBackend *backend) +{ + return backend->fallback_resource_scale; +} diff --git a/clutter/clutter/clutter-bin-layout.c b/clutter/clutter/clutter-bin-layout.c index 8c63b802c..a17393c48 100644 --- a/clutter/clutter/clutter-bin-layout.c +++ b/clutter/clutter/clutter-bin-layout.c @@ -406,8 +406,7 @@ get_actor_align_factor (ClutterActorAlign alignment) static void clutter_bin_layout_allocate (ClutterLayoutManager *manager, ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags) + const ClutterActorBox *allocation) { gfloat allocation_x, allocation_y; gfloat available_w, available_h; @@ -515,8 +514,7 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager, clutter_actor_allocate_align_fill (child, &child_alloc, x_align, y_align, - x_fill, y_fill, - flags); + x_fill, y_fill); } } diff --git a/clutter/clutter/clutter-box-layout.c b/clutter/clutter/clutter-box-layout.c index 73eefe477..ed64d68db 100644 --- a/clutter/clutter/clutter-box-layout.c +++ b/clutter/clutter/clutter-box-layout.c @@ -739,8 +739,7 @@ static void allocate_box_child (ClutterBoxLayout *self, ClutterContainer *container, ClutterActor *child, - ClutterActorBox *child_box, - ClutterAllocationFlags flags) + ClutterActorBox *child_box) { ClutterBoxLayoutPrivate *priv = self->priv; ClutterBoxChild *box_child; @@ -769,14 +768,13 @@ allocate_box_child (ClutterBoxLayout *self, */ if (clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL) || clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL)) - clutter_actor_allocate (child, child_box, flags); + clutter_actor_allocate (child, child_box); else clutter_actor_allocate_align_fill (child, child_box, get_box_alignment_factor (box_child->x_align), get_box_alignment_factor (box_child->y_align), box_child->x_fill, - box_child->y_fill, - flags); + box_child->y_fill); if (priv->use_animations) clutter_actor_restore_easing_state (child); @@ -975,8 +973,7 @@ distribute_natural_allocation (float extra_space, static void clutter_box_layout_allocate (ClutterLayoutManager *layout, ClutterContainer *container, - const ClutterActorBox *box, - ClutterAllocationFlags flags) + const ClutterActorBox *box) { ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (layout)->priv; ClutterActor *actor, *child; @@ -1251,8 +1248,7 @@ clutter_box_layout_allocate (ClutterLayoutManager *layout, allocate_box_child (CLUTTER_BOX_LAYOUT (layout), container, child, - &child_allocation, - flags); + &child_allocation); i += 1; } diff --git a/clutter/clutter/clutter-clone.c b/clutter/clutter/clutter-clone.c index 8c805725a..fe34359d8 100644 --- a/clutter/clutter/clutter-clone.c +++ b/clutter/clutter/clutter-clone.c @@ -52,6 +52,8 @@ struct _ClutterClonePrivate { ClutterActor *clone_source; + float x_scale, y_scale; + gulong source_destroy_id; }; @@ -122,8 +124,6 @@ static void clutter_clone_apply_transform (ClutterActor *self, CoglMatrix *matrix) { ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv; - ClutterActorBox box, source_box; - gfloat x_scale, y_scale; /* First chain up and apply all the standard ClutterActor * transformations... */ @@ -134,21 +134,7 @@ clutter_clone_apply_transform (ClutterActor *self, CoglMatrix *matrix) if (priv->clone_source == NULL) return; - /* get our allocated size */ - clutter_actor_get_allocation_box (self, &box); - - /* and get the allocated size of the source */ - clutter_actor_get_allocation_box (priv->clone_source, &source_box); - - /* We need to scale what the clone-source actor paints to fill our own - * allocation... - */ - x_scale = clutter_actor_box_get_width (&box) - / clutter_actor_box_get_width (&source_box); - y_scale = clutter_actor_box_get_height (&box) - / clutter_actor_box_get_height (&source_box); - - cogl_matrix_scale (matrix, x_scale, y_scale, x_scale); + cogl_matrix_scale (matrix, priv->x_scale, priv->y_scale, 1.f); } static void @@ -240,15 +226,16 @@ clutter_clone_has_overlaps (ClutterActor *actor) static void clutter_clone_allocate (ClutterActor *self, - const ClutterActorBox *box, - ClutterAllocationFlags flags) + const ClutterActorBox *box) { ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv; ClutterActorClass *parent_class; + ClutterActorBox source_box; + float x_scale, y_scale; /* chain up */ parent_class = CLUTTER_ACTOR_CLASS (clutter_clone_parent_class); - parent_class->allocate (self, box, flags); + parent_class->allocate (self, box); if (priv->clone_source == NULL) return; @@ -258,7 +245,24 @@ clutter_clone_allocate (ClutterActor *self, */ if (clutter_actor_get_parent (priv->clone_source) != NULL && !clutter_actor_has_allocation (priv->clone_source)) - clutter_actor_allocate_preferred_size (priv->clone_source, flags); + clutter_actor_allocate_preferred_size (priv->clone_source); + + clutter_actor_get_allocation_box (priv->clone_source, &source_box); + + /* We need to scale what the clone-source actor paints to fill our own + * allocation... + */ + x_scale = clutter_actor_box_get_width (box) + / clutter_actor_box_get_width (&source_box); + y_scale = clutter_actor_box_get_height (box) + / clutter_actor_box_get_height (&source_box); + + if (!G_APPROX_VALUE (priv->x_scale, x_scale, FLT_EPSILON) || + !G_APPROX_VALUE (priv->y_scale, y_scale, FLT_EPSILON)) + { + priv->x_scale = x_scale; + priv->y_scale = y_scale; + } #if 0 /* XXX - this is wrong: ClutterClone cannot clone unparented @@ -273,7 +277,7 @@ clutter_clone_allocate (ClutterActor *self, * paint cycle, we can safely give it as much size as it requires */ if (clutter_actor_get_parent (priv->clone_source) == NULL) - clutter_actor_allocate_preferred_size (priv->clone_source, flags); + clutter_actor_allocate_preferred_size (priv->clone_source); #endif } @@ -365,6 +369,9 @@ static void clutter_clone_init (ClutterClone *self) { self->priv = clutter_clone_get_instance_private (self); + + self->priv->x_scale = 1.f; + self->priv->y_scale = 1.f; } /** diff --git a/clutter/clutter/clutter-deform-effect.c b/clutter/clutter/clutter-deform-effect.c index 59ef1ff75..a1b7c89bb 100644 --- a/clutter/clutter/clutter-deform-effect.c +++ b/clutter/clutter/clutter-deform-effect.c @@ -128,10 +128,9 @@ clutter_deform_effect_deform_vertex (ClutterDeformEffect *effect, } static void -vbo_invalidate (ClutterActor *actor, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags, - ClutterDeformEffect *effect) +vbo_invalidate (ClutterActor *actor, + GParamSpec *pspec, + ClutterDeformEffect *effect) { effect->priv->is_dirty = TRUE; } @@ -156,7 +155,7 @@ clutter_deform_effect_set_actor (ClutterActorMeta *meta, * changes */ if (actor != NULL) - priv->allocation_id = g_signal_connect (actor, "allocation-changed", + priv->allocation_id = g_signal_connect (actor, "notify::allocation", G_CALLBACK (vbo_invalidate), meta); diff --git a/clutter/clutter/clutter-enums.h b/clutter/clutter/clutter-enums.h index 33a16f126..17105f8d7 100644 --- a/clutter/clutter/clutter-enums.h +++ b/clutter/clutter/clutter-enums.h @@ -554,32 +554,6 @@ typedef enum /*< prefix=CLUTTER_OFFSCREEN_REDIRECT >*/ CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE = 1 << 2 } ClutterOffscreenRedirect; -/** - * ClutterAllocationFlags: - * @CLUTTER_ALLOCATION_NONE: No flag set - * @CLUTTER_ABSOLUTE_ORIGIN_CHANGED: Whether the absolute origin of the - * actor has changed; this implies that any ancestor of the actor has - * been moved. - * @CLUTTER_DELEGATE_LAYOUT: Whether the allocation should be delegated - * to the #ClutterLayoutManager instance stored inside the - * #ClutterActor:layout-manager property of #ClutterActor. This flag - * should only be used if you are subclassing #ClutterActor and - * overriding the #ClutterActorClass.allocate() virtual function, but - * you wish to use the default implementation of the virtual function - * inside #ClutterActor. Added in Clutter 1.10. - * - * Flags passed to the #ClutterActorClass.allocate() virtual function - * and to the clutter_actor_allocate() function. - * - * Since: 1.0 - */ -typedef enum -{ - CLUTTER_ALLOCATION_NONE = 0, - CLUTTER_ABSOLUTE_ORIGIN_CHANGED = 1 << 1, - CLUTTER_DELEGATE_LAYOUT = 1 << 2 -} ClutterAllocationFlags; - /** * ClutterAlignAxis: * @CLUTTER_ALIGN_X_AXIS: Maintain the alignment on the X axis diff --git a/clutter/clutter/clutter-fixed-layout.c b/clutter/clutter/clutter-fixed-layout.c index 959f7652e..324084809 100644 --- a/clutter/clutter/clutter-fixed-layout.c +++ b/clutter/clutter/clutter-fixed-layout.c @@ -131,8 +131,7 @@ clutter_fixed_layout_get_preferred_height (ClutterLayoutManager *manager, static void clutter_fixed_layout_allocate (ClutterLayoutManager *manager, ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags) + const ClutterActorBox *allocation) { ClutterActor *child; @@ -140,7 +139,7 @@ clutter_fixed_layout_allocate (ClutterLayoutManager *manager, child != NULL; child = clutter_actor_get_next_sibling (child)) { - clutter_actor_allocate_preferred_size (child, flags); + clutter_actor_allocate_preferred_size (child); } } diff --git a/clutter/clutter/clutter-flow-layout.c b/clutter/clutter/clutter-flow-layout.c index 365c5d9d8..f7ced9837 100644 --- a/clutter/clutter/clutter-flow-layout.c +++ b/clutter/clutter/clutter-flow-layout.c @@ -566,8 +566,7 @@ clutter_flow_layout_get_preferred_height (ClutterLayoutManager *manager, static void clutter_flow_layout_allocate (ClutterLayoutManager *manager, ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags) + const ClutterActorBox *allocation) { ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; ClutterActor *actor, *child; @@ -729,7 +728,7 @@ clutter_flow_layout_allocate (ClutterLayoutManager *manager, child_alloc.y1 = ceil (item_y); child_alloc.x2 = ceil (child_alloc.x1 + item_width); child_alloc.y2 = ceil (child_alloc.y1 + item_height); - clutter_actor_allocate (child, &child_alloc, flags); + clutter_actor_allocate (child, &child_alloc); if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) item_x = new_x; diff --git a/clutter/clutter/clutter-grid-layout.c b/clutter/clutter/clutter-grid-layout.c index 29ec54759..ebc022eb7 100644 --- a/clutter/clutter/clutter-grid-layout.c +++ b/clutter/clutter/clutter-grid-layout.c @@ -1391,8 +1391,7 @@ allocate_child (ClutterGridRequest *request, static void clutter_grid_layout_allocate (ClutterLayoutManager *layout, ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags) + const ClutterActorBox *allocation) { ClutterGridLayout *self = CLUTTER_GRID_LAYOUT (layout); ClutterOrientation orientation; @@ -1453,7 +1452,7 @@ clutter_grid_layout_allocate (ClutterLayoutManager *layout, child_allocation.x2 = child_allocation.x1 + width; child_allocation.y2 = child_allocation.y1 + height; - clutter_actor_allocate (child, &child_allocation, flags); + clutter_actor_allocate (child, &child_allocation); } } diff --git a/clutter/clutter/clutter-layout-manager.c b/clutter/clutter/clutter-layout-manager.c index 13dbd6c0b..5652a004e 100644 --- a/clutter/clutter/clutter-layout-manager.c +++ b/clutter/clutter/clutter-layout-manager.c @@ -255,8 +255,7 @@ layout_manager_real_get_preferred_height (ClutterLayoutManager *manager, static void layout_manager_real_allocate (ClutterLayoutManager *manager, ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags) + const ClutterActorBox *allocation) { LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "allocate"); } @@ -523,7 +522,6 @@ clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager, * @container: the #ClutterContainer using @manager * @allocation: the #ClutterActorBox containing the allocated area * of @container - * @flags: the allocation flags * * Allocates the children of @container given an area * @@ -534,8 +532,7 @@ clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager, void clutter_layout_manager_allocate (ClutterLayoutManager *manager, ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags) + const ClutterActorBox *allocation) { ClutterLayoutManagerClass *klass; @@ -544,7 +541,7 @@ clutter_layout_manager_allocate (ClutterLayoutManager *manager, g_return_if_fail (allocation != NULL); klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); - klass->allocate (manager, container, allocation, flags); + klass->allocate (manager, container, allocation); } /** diff --git a/clutter/clutter/clutter-layout-manager.h b/clutter/clutter/clutter-layout-manager.h index 00f711803..4bc268706 100644 --- a/clutter/clutter/clutter-layout-manager.h +++ b/clutter/clutter/clutter-layout-manager.h @@ -115,8 +115,7 @@ struct _ClutterLayoutManagerClass gfloat *nat_height_p); void (* allocate) (ClutterLayoutManager *manager, ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags); + const ClutterActorBox *allocation); void (* set_container) (ClutterLayoutManager *manager, ClutterContainer *container); @@ -167,8 +166,7 @@ void clutter_layout_manager_get_preferred_height (ClutterLayoutMa CLUTTER_EXPORT void clutter_layout_manager_allocate (ClutterLayoutManager *manager, ClutterContainer *container, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags); + const ClutterActorBox *allocation); CLUTTER_EXPORT void clutter_layout_manager_set_container (ClutterLayoutManager *manager, diff --git a/clutter/clutter/clutter-muffin.h b/clutter/clutter/clutter-muffin.h index c0d9015d8..606961a5f 100644 --- a/clutter/clutter/clutter-muffin.h +++ b/clutter/clutter/clutter-muffin.h @@ -73,7 +73,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 gboolean clutter_actor_has_damage (ClutterActor *actor); diff --git a/clutter/clutter/clutter-offscreen-effect.c b/clutter/clutter/clutter-offscreen-effect.c index 10a9cc24a..85ca187ae 100644 --- a/clutter/clutter/clutter-offscreen-effect.c +++ b/clutter/clutter/clutter-offscreen-effect.c @@ -238,8 +238,8 @@ clutter_offscreen_effect_pre_paint (ClutterEffect *effect, gfloat stage_width, stage_height; gfloat target_width = -1, target_height = -1; CoglFramebuffer *framebuffer; - gfloat resource_scale; - gfloat ceiled_resource_scale; + float resource_scale; + float ceiled_resource_scale; graphene_point3d_t local_offset; gfloat old_viewport[4]; @@ -254,17 +254,11 @@ clutter_offscreen_effect_pre_paint (ClutterEffect *effect, stage = _clutter_actor_get_stage_internal (priv->actor); clutter_actor_get_size (stage, &stage_width, &stage_height); - if (_clutter_actor_get_real_resource_scale (priv->actor, &resource_scale)) - { - ceiled_resource_scale = ceilf (resource_scale); - stage_width *= ceiled_resource_scale; - stage_height *= ceiled_resource_scale; - } - else - { - /* We are sure we have a resource scale set to a good value at paint */ - g_assert_not_reached (); - } + resource_scale = clutter_actor_get_real_resource_scale (priv->actor); + + ceiled_resource_scale = ceilf (resource_scale); + stage_width *= ceiled_resource_scale; + stage_height *= ceiled_resource_scale; /* Get the minimal bounding box for what we want to paint, relative to the * parent of priv->actor. Note that we may actually be painting a clone of @@ -417,8 +411,9 @@ clutter_offscreen_effect_paint_texture (ClutterOffscreenEffect *effect, */ cogl_framebuffer_get_modelview_matrix (framebuffer, &modelview); - if (clutter_actor_get_resource_scale (priv->actor, &resource_scale) && - resource_scale != 1.0f) + resource_scale = clutter_actor_get_resource_scale (priv->actor); + + if (resource_scale != 1.0f) { float paint_scale = 1.0f / resource_scale; cogl_matrix_scale (&modelview, paint_scale, paint_scale, 1); diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h index 448fd3ef7..2c3b4e62c 100644 --- a/clutter/clutter/clutter-stage-private.h +++ b/clutter/clutter/clutter-stage-private.h @@ -53,11 +53,7 @@ ClutterStageWindow *_clutter_stage_get_window (ClutterStage void _clutter_stage_get_projection_matrix (ClutterStage *stage, CoglMatrix *projection); void _clutter_stage_dirty_projection (ClutterStage *stage); -void _clutter_stage_set_viewport (ClutterStage *stage, - float x, - float y, - float width, - float height); + void _clutter_stage_get_viewport (ClutterStage *stage, float *x, float *y, @@ -134,19 +130,21 @@ 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, ClutterFrameInfo *frame_info); -GList * _clutter_stage_peek_stage_views (ClutterStage *stage); +GList * clutter_stage_peek_stage_views (ClutterStage *stage); void clutter_stage_queue_actor_relayout (ClutterStage *stage, ClutterActor *actor); +GList * clutter_stage_get_views_for_rect (ClutterStage *stage, + const graphene_rect_t *rect); + +void clutter_stage_set_actor_needs_immediate_relayout (ClutterStage *stage); + G_END_DECLS #endif /* __CLUTTER_STAGE_PRIVATE_H__ */ diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index f2894b56e..649920a0c 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -168,6 +168,7 @@ struct _ClutterStagePrivate guint motion_events_enabled : 1; guint has_custom_perspective : 1; guint stage_was_relayout : 1; + guint actor_needs_immediate_relayout : 1; }; enum @@ -215,6 +216,9 @@ static void capture_view_into (ClutterStage *stage, uint8_t *data, int stride); static void clutter_stage_update_view_perspective (ClutterStage *stage); +static void clutter_stage_set_viewport (ClutterStage *stage, + float width, + float height); static void clutter_container_iface_init (ClutterContainerIface *iface); @@ -580,7 +584,7 @@ clutter_stage_add_redraw_clip (ClutterStage *stage, { GList *l; - for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next) + for (l = clutter_stage_peek_stage_views (stage); l; l = l->next) { ClutterStageView *view = l->data; @@ -641,23 +645,18 @@ stage_is_default (ClutterStage *stage) static void clutter_stage_allocate (ClutterActor *self, - const ClutterActorBox *box, - ClutterAllocationFlags flags) + const ClutterActorBox *box) { ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv; ClutterActorBox alloc = CLUTTER_ACTOR_BOX_INIT_ZERO; - float old_width, old_height; float new_width, new_height; float width, height; cairo_rectangle_int_t window_size; + ClutterLayoutManager *layout_manager = clutter_actor_get_layout_manager (self); if (priv->impl == NULL) return; - /* our old allocation */ - clutter_actor_get_allocation_box (self, &alloc); - clutter_actor_box_get_size (&alloc, &old_width, &old_height); - /* the current allocation */ clutter_actor_box_get_size (box, &width, &height); @@ -671,15 +670,21 @@ clutter_stage_allocate (ClutterActor *self, */ if (!clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC)) { + ClutterActorBox children_box; + + children_box.x1 = children_box.y1 = 0.f; + children_box.x2 = box->x2 - box->x1; + children_box.y2 = box->y2 - box->y1; + CLUTTER_NOTE (LAYOUT, - "Following allocation to %.2fx%.2f (absolute origin %s)", - width, height, - (flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED) - ? "changed" - : "not changed"); + "Following allocation to %.2fx%.2f", + width, height); + + clutter_actor_set_allocation (self, box); - clutter_actor_set_allocation (self, box, - flags | CLUTTER_DELEGATE_LAYOUT); + clutter_layout_manager_allocate (layout_manager, + CLUTTER_CONTAINER (self), + &children_box); /* Ensure the window is sized correctly */ if (priv->min_size_changed) @@ -727,39 +732,23 @@ clutter_stage_allocate (ClutterActor *self, CLUTTER_NOTE (LAYOUT, "Overriding original allocation of %.2fx%.2f " - "with %.2fx%.2f (absolute origin %s)", + "with %.2fx%.2f", width, height, - override.x2, override.y2, - (flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED) - ? "changed" - : "not changed"); + override.x2, override.y2); /* and store the overridden allocation */ - clutter_actor_set_allocation (self, &override, - flags | CLUTTER_DELEGATE_LAYOUT); + clutter_actor_set_allocation (self, &override); + + clutter_layout_manager_allocate (layout_manager, + CLUTTER_CONTAINER (self), + &override); } - /* reset the viewport if the allocation effectively changed */ + /* set the viewport to the new allocation */ clutter_actor_get_allocation_box (self, &alloc); clutter_actor_box_get_size (&alloc, &new_width, &new_height); - if (CLUTTER_NEARBYINT (old_width) != CLUTTER_NEARBYINT (new_width) || - CLUTTER_NEARBYINT (old_height) != CLUTTER_NEARBYINT (new_height)) - { - int real_width = CLUTTER_NEARBYINT (new_width); - int real_height = CLUTTER_NEARBYINT (new_height); - - _clutter_stage_set_viewport (CLUTTER_STAGE (self), - 0, 0, - real_width, - real_height); - - /* Note: we don't assume that set_viewport will queue a full redraw - * since it may bail-out early if something preemptively set the - * viewport before the stage was really allocated its new size. - */ - queue_full_redraw (CLUTTER_STAGE (self)); - } + clutter_stage_set_viewport (CLUTTER_STAGE (self), new_width, new_height); } typedef struct _Vector4 @@ -1407,8 +1396,7 @@ _clutter_stage_maybe_relayout (ClutterActor *actor) CLUTTER_SET_PRIVATE_FLAGS (queued_actor, CLUTTER_IN_RELAYOUT); old_version = priv->pending_relayouts_version; - clutter_actor_allocate_preferred_size (queued_actor, - CLUTTER_ALLOCATION_NONE); + clutter_actor_allocate_preferred_size (queued_actor); CLUTTER_UNSET_PRIVATE_FLAGS (queued_actor, CLUTTER_IN_RELAYOUT); @@ -1525,6 +1513,40 @@ _clutter_stage_check_updated_pointers (ClutterStage *stage) return updating; } +static void +update_actor_stage_views (ClutterStage *stage) +{ + ClutterActor *actor = CLUTTER_ACTOR (stage); + ClutterStagePrivate *priv = stage->priv; + int phase; + + COGL_TRACE_BEGIN_SCOPED (ClutterStageUpdateActorStageViews, + "Actor stage-views"); + + /* If an actor needs an immediate relayout because its resource scale + * changed, we give it another chance to allocate correctly before + * the paint. + * + * We're doing the whole thing twice and pass the phase to + * clutter_actor_update_stage_views() to allow actors to detect loops: + * If the resource scale changes again after the relayout, the new + * allocation of an actor probably moved the actor onto another stage + * view, so if an actor sees phase == 1, it can choose a "final" scale. + */ + for (phase = 0; phase < 2; phase++) + { + clutter_actor_update_stage_views (actor, phase); + + if (!priv->actor_needs_immediate_relayout) + break; + + priv->actor_needs_immediate_relayout = FALSE; + _clutter_stage_maybe_relayout (actor); + } + + g_warn_if_fail (!priv->actor_needs_immediate_relayout); +} + /** * _clutter_stage_do_update: * @stage: A #ClutterStage @@ -1577,6 +1599,8 @@ _clutter_stage_do_update (ClutterStage *stage) if (stage_was_relayout) pointers = _clutter_stage_check_updated_pointers (stage); + update_actor_stage_views (stage); + COGL_TRACE_BEGIN (ClutterStagePaint, "Paint"); clutter_stage_maybe_finish_queue_redraws (stage); @@ -1630,7 +1654,7 @@ is_full_stage_redraw_queued (ClutterStage *stage) { GList *l; - for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next) + for (l = clutter_stage_peek_stage_views (stage); l; l = l->next) { ClutterStageView *view = l->data; @@ -2428,10 +2452,7 @@ clutter_stage_init (ClutterStage *self) g_signal_connect (self, "notify::min-height", G_CALLBACK (clutter_stage_notify_min_size), NULL); - _clutter_stage_set_viewport (self, - 0, 0, - geom.width, - geom.height); + clutter_stage_set_viewport (self, geom.width, geom.height); priv->paint_volume_stack = g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume)); @@ -2648,8 +2669,6 @@ _clutter_stage_dirty_projection (ClutterStage *stage) /* * clutter_stage_set_viewport: * @stage: A #ClutterStage - * @x: The X postition to render the stage at, in window coordinates - * @y: The Y position to render the stage at, in window coordinates * @width: The width to render the stage at, in window coordinates * @height: The height to render the stage at, in window coordinates * @@ -2682,19 +2701,22 @@ _clutter_stage_dirty_projection (ClutterStage *stage) * * Since: 1.6 */ -void -_clutter_stage_set_viewport (ClutterStage *stage, - float x, - float y, - float width, - float height) +static void +clutter_stage_set_viewport (ClutterStage *stage, + float width, + float height) { ClutterStagePrivate *priv; + float x, y; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; + x = 0.f; + y = 0.f; + width = roundf (width); + height = roundf (height); if (x == priv->viewport[0] && y == priv->viewport[1] && @@ -4518,20 +4540,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); @@ -4545,9 +4576,7 @@ clutter_stage_get_capture_final_size (ClutterStage *stage, clutter_actor_get_allocation_box (CLUTTER_ACTOR (stage), &alloc); clutter_actor_box_get_size (&alloc, &stage_width, &stage_height); - if (!_clutter_actor_get_real_resource_scale (CLUTTER_ACTOR (stage), - &max_scale)) - return FALSE; + max_scale = clutter_actor_get_real_resource_scale (CLUTTER_ACTOR (stage)); if (out_width) *out_width = (gint) roundf (stage_width * max_scale); @@ -4771,7 +4800,7 @@ clutter_stage_thaw_updates (ClutterStage *stage) } GList * -_clutter_stage_peek_stage_views (ClutterStage *stage) +clutter_stage_peek_stage_views (ClutterStage *stage) { ClutterStagePrivate *priv = stage->priv; @@ -4779,18 +4808,17 @@ _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)); } -gboolean -_clutter_stage_get_max_view_scale_factor_for_rect (ClutterStage *stage, - graphene_rect_t *rect, - float *view_scale) +GList * +clutter_stage_get_views_for_rect (ClutterStage *stage, + const graphene_rect_t *rect) { ClutterStagePrivate *priv = stage->priv; - float scale = 0.0f; + GList *views_for_rect = NULL; GList *l; for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next) @@ -4803,14 +4831,10 @@ _clutter_stage_get_max_view_scale_factor_for_rect (ClutterStage *stage, _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); + views_for_rect = g_list_prepend (views_for_rect, view); } - if (scale == 0.0) - return FALSE; - - *view_scale = scale; - return TRUE; + return views_for_rect; } static void @@ -4878,3 +4902,11 @@ clutter_stage_get_device_coords (ClutterStage *stage, if (entry && coords) *coords = entry->coords; } + +void +clutter_stage_set_actor_needs_immediate_relayout (ClutterStage *stage) +{ + ClutterStagePrivate *priv = stage->priv; + + priv->actor_needs_immediate_relayout = TRUE; +} diff --git a/clutter/clutter/clutter-text.c b/clutter/clutter/clutter-text.c index e18ed4c48..b7f97762f 100644 --- a/clutter/clutter/clutter-text.c +++ b/clutter/clutter/clutter-text.c @@ -187,9 +187,6 @@ struct _ClutterTextPrivate ClutterInputContentHintFlags input_hints; ClutterInputContentPurpose input_purpose; - /* Signal handler for when the :resource-scale changes */ - gulong resource_scale_changed_id; - /* bitfields */ guint alignment : 2; guint wrap : 1; @@ -598,9 +595,7 @@ ensure_effective_pango_scale_attribute (ClutterText *self) float resource_scale; ClutterTextPrivate *priv = self->priv; - if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (self), &resource_scale) || - resource_scale == 1.0) - return; + resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (self)); if (priv->effective_attrs != NULL) { @@ -922,18 +917,6 @@ clutter_text_direction_changed_cb (GObject *gobject, /* no need to queue a relayout: set_text_direction() will do that for us */ } -static void -clutter_text_resource_scale_changed_cb (GObject *gobject, - GParamSpec *pspec) -{ - ClutterText *self = CLUTTER_TEXT (gobject); - ClutterTextPrivate *priv = self->priv; - - g_clear_pointer (&priv->effective_attrs, pango_attr_list_unref); - clutter_text_dirty_cache (self); - clutter_actor_queue_relayout (CLUTTER_ACTOR (gobject)); -} - /* * clutter_text_create_layout: * @text: a #ClutterText @@ -1137,8 +1120,7 @@ maybe_create_text_layout_with_resource_scale (ClutterText *text, { float resource_scale; - if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (text), &resource_scale)) - return NULL; + resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (text)); return create_text_layout_with_scale (text, allocation_width, @@ -1170,8 +1152,7 @@ clutter_text_coords_to_position (ClutterText *self, g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); - if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (self), &resource_scale)) - return 0; + resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (self)); /* Take any offset due to scrolling into account, and normalize * the coordinates to PangoScale units @@ -1299,8 +1280,7 @@ clutter_text_position_to_coords (ClutterText *self, g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); - if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (self), &resource_scale)) - return FALSE; + resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (self)); ret = clutter_text_position_to_coords_internal (self, position, x, y, line_height); @@ -1776,7 +1756,6 @@ clutter_text_dispose (GObject *gobject) clutter_text_dirty_cache (self); g_clear_signal_handler (&priv->direction_changed_id, self); - g_clear_signal_handler (&priv->resource_scale_changed_id, self); g_clear_signal_handler (&priv->settings_changed_id, clutter_get_default_backend ()); @@ -2637,8 +2616,7 @@ clutter_text_paint (ClutterActor *self, !clutter_text_should_draw_cursor (text)) return; - if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (self), &resource_scale)) - return; + resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (self)); clutter_actor_box_scale (&alloc, resource_scale); clutter_actor_box_get_size (&alloc, &alloc_width, &alloc_height); @@ -2870,8 +2848,7 @@ clutter_text_get_paint_volume (ClutterActor *self, if (!clutter_actor_has_allocation (self)) return FALSE; - if (!clutter_actor_get_resource_scale (self, &resource_scale)) - return FALSE; + resource_scale = clutter_actor_get_resource_scale (self); _clutter_paint_volume_init_static (&priv->paint_volume, self); @@ -2928,8 +2905,7 @@ clutter_text_get_preferred_width (ClutterActor *self, gfloat layout_width; gfloat resource_scale; - if (!clutter_actor_get_resource_scale (self, &resource_scale)) - resource_scale = 1; + resource_scale = clutter_actor_get_resource_scale (self); layout = clutter_text_create_layout (text, -1, -1); pango_layout_get_extents (layout, NULL, &logical_rect); @@ -2985,8 +2961,7 @@ clutter_text_get_preferred_height (ClutterActor *self, gfloat layout_height; gfloat resource_scale; - if (!clutter_actor_get_resource_scale (self, &resource_scale)) - resource_scale = 1; + resource_scale = clutter_actor_get_resource_scale (self); if (priv->single_line_mode) for_width = -1; @@ -3033,8 +3008,7 @@ clutter_text_get_preferred_height (ClutterActor *self, static void clutter_text_allocate (ClutterActor *self, - const ClutterActorBox *box, - ClutterAllocationFlags flags) + const ClutterActorBox *box) { ClutterText *text = CLUTTER_TEXT (self); ClutterActorClass *parent_class; @@ -3054,7 +3028,7 @@ clutter_text_allocate (ClutterActor *self, box->y2 - box->y1); parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class); - parent_class->allocate (self, box, flags); + parent_class->allocate (self, box); } static gboolean @@ -3063,6 +3037,33 @@ clutter_text_has_overlaps (ClutterActor *self) return clutter_text_should_draw_cursor ((ClutterText *) self); } +static float +clutter_text_calculate_resource_scale (ClutterActor *actor, + int phase) +{ + ClutterActorClass *parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class); + float new_resource_scale; + + new_resource_scale = parent_class->calculate_resource_scale (actor, phase); + + if (phase == 1) + return MAX (new_resource_scale, clutter_actor_get_real_resource_scale (actor)); + + return new_resource_scale; +} + +static void +clutter_text_resource_scale_changed (ClutterActor *actor) +{ + ClutterText *text = CLUTTER_TEXT (actor); + ClutterTextPrivate *priv = text->priv; + + g_clear_pointer (&priv->effective_attrs, pango_attr_list_unref); + clutter_text_dirty_cache (text); + + clutter_actor_queue_immediate_relayout (actor); +} + static gboolean clutter_text_event (ClutterActor *self, ClutterEvent *event) @@ -3829,6 +3830,8 @@ clutter_text_class_init (ClutterTextClass *klass) actor_class->key_focus_in = clutter_text_key_focus_in; actor_class->key_focus_out = clutter_text_key_focus_out; actor_class->has_overlaps = clutter_text_has_overlaps; + actor_class->calculate_resource_scale = clutter_text_calculate_resource_scale; + actor_class->resource_scale_changed = clutter_text_resource_scale_changed; actor_class->event = clutter_text_event; /** @@ -4637,11 +4640,6 @@ clutter_text_init (ClutterText *self) NULL); priv->input_focus = clutter_text_input_focus_new (self); - - priv->resource_scale_changed_id = - g_signal_connect (self, "notify::resource-scale", - G_CALLBACK (clutter_text_resource_scale_changed_cb), - NULL); } /** diff --git a/clutter/clutter/deprecated/clutter-group.c b/clutter/clutter/deprecated/clutter-group.c index af602fd51..dce35cff8 100644 --- a/clutter/clutter/deprecated/clutter-group.c +++ b/clutter/clutter/deprecated/clutter-group.c @@ -333,21 +333,20 @@ clutter_group_real_get_preferred_height (ClutterActor *actor, static void clutter_group_real_allocate (ClutterActor *actor, - const ClutterActorBox *allocation, - ClutterAllocationFlags flags) + const ClutterActorBox *allocation) { ClutterGroupPrivate *priv = CLUTTER_GROUP (actor)->priv; ClutterActorClass *klass; klass = CLUTTER_ACTOR_CLASS (clutter_group_parent_class); - klass->allocate (actor, allocation, flags); + klass->allocate (actor, allocation); if (priv->children == NULL) return; clutter_layout_manager_allocate (priv->layout, CLUTTER_CONTAINER (actor), - allocation, flags); + allocation); } static void diff --git a/debian/libmuffin0.symbols b/debian/libmuffin0.symbols index 928320459..70e2edfd9 100644 --- a/debian/libmuffin0.symbols +++ b/debian/libmuffin0.symbols @@ -249,6 +249,7 @@ libmuffin-clutter-0.so.0 libmuffin0 #MINVER# clutter_actor_node_get_type@Base 5.3.0 clutter_actor_node_new@Base 5.3.0 clutter_actor_paint@Base 5.3.0 + clutter_actor_peek_stage_views@Base 6.6.3 clutter_actor_pick@Base 5.3.0 clutter_actor_pick_box@Base 5.3.0 clutter_actor_queue_redraw@Base 5.3.0 @@ -343,7 +344,6 @@ libmuffin-clutter-0.so.0 libmuffin0 #MINVER# clutter_align_constraint_set_align_axis@Base 5.3.0 clutter_align_constraint_set_factor@Base 5.3.0 clutter_align_constraint_set_source@Base 5.3.0 - clutter_allocation_flags_get_type@Base 5.3.0 clutter_alpha_get_alpha@Base 5.3.0 clutter_alpha_get_mode@Base 5.3.0 clutter_alpha_get_timeline@Base 5.3.0 @@ -381,6 +381,7 @@ libmuffin-clutter-0.so.0 libmuffin0 #MINVER# clutter_backend_get_resolution@Base 5.3.0 clutter_backend_get_stage_window@Base 5.3.0 clutter_backend_get_type@Base 5.3.0 + clutter_backend_set_fallback_resource_scale@Base 6.6.3 clutter_backend_set_font_options@Base 5.3.0 clutter_backend_set_input_method@Base 5.3.0 clutter_backend_x11_get_type@Base 5.3.0 @@ -587,7 +588,7 @@ libmuffin-clutter-0.so.0 libmuffin0 #MINVER# clutter_event_get_event_sequence@Base 5.3.0 clutter_event_get_flags@Base 5.3.0 clutter_event_get_gesture_motion_delta@Base 5.3.0 - clutter_event_get_gesture_motion_delta_unaccelerated@Base 6.7.0 + clutter_event_get_gesture_motion_delta_unaccelerated@Base 6.6.3 clutter_event_get_gesture_phase@Base 5.3.0 clutter_event_get_gesture_pinch_angle_delta@Base 5.3.0 clutter_event_get_gesture_pinch_scale@Base 5.3.0 @@ -1109,6 +1110,7 @@ libmuffin-clutter-0.so.0 libmuffin0 #MINVER# clutter_snap_edge_get_type@Base 5.3.0 clutter_stage_capture@Base 5.3.0 clutter_stage_capture_into@Base 5.3.0 + clutter_stage_clear_stage_views@Base 6.6.3 clutter_stage_ensure_current@Base 5.3.0 clutter_stage_ensure_redraw@Base 5.3.0 clutter_stage_ensure_viewport@Base 5.3.0 @@ -1158,7 +1160,6 @@ libmuffin-clutter-0.so.0 libmuffin0 #MINVER# clutter_stage_skip_sync_delay@Base 5.3.0 clutter_stage_state_get_type@Base 5.3.0 clutter_stage_thaw_updates@Base 5.3.0 - clutter_stage_update_resource_scales@Base 5.3.0 clutter_stage_view_cogl_get_type@Base 5.3.0 clutter_stage_view_get_framebuffer@Base 5.3.0 clutter_stage_view_get_layout@Base 5.3.0 diff --git a/src/backends/meta-renderer.c b/src/backends/meta-renderer.c index 983a570e1..046c7b2c7 100644 --- a/src/backends/meta-renderer.c +++ b/src/backends/meta-renderer.c @@ -153,6 +153,19 @@ meta_renderer_real_rebuild_views (MetaRenderer *renderer) { MetaLogicalMonitor *logical_monitor = l->data; + if (meta_logical_monitor_is_primary (logical_monitor)) + { + ClutterBackend *clutter_backend; + float scale; + + clutter_backend = meta_backend_get_clutter_backend (backend); + scale = meta_is_stage_views_scaled () + ? meta_logical_monitor_get_scale (logical_monitor) + : 1.f; + + clutter_backend_set_fallback_resource_scale (clutter_backend, scale); + } + meta_logical_monitor_foreach_crtc (logical_monitor, create_crtc_view, renderer); diff --git a/src/backends/native/meta-stage-native.c b/src/backends/native/meta-stage-native.c index 9b9c45ef3..9a3d11cb9 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 def74bb74..8b45eada7 100644 --- a/src/backends/x11/nested/meta-backend-x11-nested.c +++ b/src/backends/x11/nested/meta-backend-x11-nested.c @@ -85,7 +85,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); } diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 919da582a..c4c2b66ea 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -1288,8 +1288,7 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, if (width == 0 || height == 0) return FALSE; - if (!clutter_actor_get_resource_scale (actor, &resource_scale)) - return FALSE; + resource_scale = clutter_actor_get_resource_scale (actor); clutter_actor_inhibit_culling (actor); @@ -1445,8 +1444,7 @@ meta_window_actor_get_image (MetaWindowActor *self, if (width == 0 || height == 0) goto out; - if (!clutter_actor_get_resource_scale (actor, &resource_scale)) - goto out; + resource_scale = clutter_actor_get_resource_scale (actor); width = ceilf (width * resource_scale); height = ceilf (height * resource_scale); diff --git a/src/tests/clutter/conform/actor-anchors.c b/src/tests/clutter/conform/actor-anchors.c index 3cf3191f3..bd934ff14 100644 --- a/src/tests/clutter/conform/actor-anchors.c +++ b/src/tests/clutter/conform/actor-anchors.c @@ -716,8 +716,8 @@ actor_pivot (void) clutter_actor_add_child (stage, actor_explicit); /* Fake allocation or pivot-point will not have any effect */ - clutter_actor_allocate (actor_implicit, &allocation, CLUTTER_ALLOCATION_NONE); - clutter_actor_allocate (actor_explicit, &allocation, CLUTTER_ALLOCATION_NONE); + clutter_actor_allocate (actor_implicit, &allocation); + clutter_actor_allocate (actor_explicit, &allocation); clutter_actor_set_pivot_point (actor_implicit, 0.5, 0.5); clutter_actor_set_pivot_point (actor_explicit, 0.5, 0.5); diff --git a/src/tests/clutter/conform/actor-pick.c b/src/tests/clutter/conform/actor-pick.c index dcee96424..3511be7d2 100644 --- a/src/tests/clutter/conform/actor-pick.c +++ b/src/tests/clutter/conform/actor-pick.c @@ -82,7 +82,7 @@ on_timeout (gpointer data) /* Only allocated actors can be picked, so force an allocation * of the overlay actor here. */ - clutter_actor_allocate (over_actor, &over_actor_box, 0); + clutter_actor_allocate (over_actor, &over_actor_box); if (g_test_verbose ()) g_print ("Clipped covering actor:\n"); diff --git a/src/tests/clutter/conform/text.c b/src/tests/clutter/conform/text.c index ebe4b7bb1..d0525b6a4 100644 --- a/src/tests/clutter/conform/text.c +++ b/src/tests/clutter/conform/text.c @@ -475,8 +475,7 @@ validate_markup_attributes (ClutterText *text, PangoAttrFloat *scale = (PangoAttrFloat*) a; float resource_scale; - if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (text), &resource_scale)) - resource_scale = 1.0; + resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (text)); g_assert_cmpfloat (scale->value, ==, resource_scale); g_slist_free_full (attributes, (GDestroyNotify) pango_attribute_destroy); diff --git a/src/tests/clutter/interactive/test-layout.c b/src/tests/clutter/interactive/test-layout.c index 9666f15df..5f697c907 100644 --- a/src/tests/clutter/interactive/test-layout.c +++ b/src/tests/clutter/interactive/test-layout.c @@ -276,15 +276,14 @@ my_thing_get_preferred_height (ClutterActor *self, static void my_thing_allocate (ClutterActor *self, - const ClutterActorBox *box, - ClutterAllocationFlags flags) + const ClutterActorBox *box) { MyThingPrivate *priv; gfloat current_x, current_y, max_row_height; ClutterActorIter iter; ClutterActor *child; - clutter_actor_set_allocation (self, box, flags); + clutter_actor_set_allocation (self, box); priv = MY_THING (self)->priv; @@ -322,7 +321,7 @@ my_thing_allocate (ClutterActor *self, child_box.x2 = child_box.x1 + natural_width; child_box.y2 = child_box.y1 + natural_height; - clutter_actor_allocate (child, &child_box, flags); + clutter_actor_allocate (child, &child_box); /* if we take into account the transformation of the children * then we first check if it's transformed; then we get the @@ -338,17 +337,8 @@ my_thing_allocate (ClutterActor *self, graphene_point3d_t v1 = { 0, }, v2 = { 0, }; ClutterActorBox transformed_box = { 0, }; - /* origin */ - if (!(flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED)) - { - v1.x = 0; - v1.y = 0; - } - else - { - v1.x = box->x1; - v1.y = box->y1; - } + v1.x = box->x1; + v1.y = box->y1; clutter_actor_apply_transform_to_point (child, &v1, &v2); transformed_box.x1 = v2.x; diff --git a/src/tests/meson.build b/src/tests/meson.build index 72cc5aa05..fdbf43bcd 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -123,6 +123,27 @@ headless_start_test = executable('muffin-headless-start-test', install_dir: muffin_installed_tests_libexecdir, ) +stage_view_tests = executable('muffin-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: muffin_installed_tests_libexecdir, +) + stacking_tests = [ 'basic-x11', 'basic-wayland', @@ -172,3 +193,10 @@ test('headless-start', headless_start_test, is_parallel: false, timeout: 60, ) + +test('stage-view', stage_view_tests, + suite: ['core', 'muffin/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 000000000..47eb4bc99 --- /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 (); +}