Skip to content

Commit 263d10f

Browse files
committed
clutter: Emit ::resource-scale-changed outside the stage-views walk.
update_resource_scale() emitted the signal from inside the recursive clutter_actor_update_stage_views() traversal, so handlers that rebuild content — st_icon_update()'s destroy + add_child — modified the actor tree from within the layout operation, and the new child tripped "Can't update stage views ... needs an allocation" before it could be allocated. Reproducible on any non-primary monitor whose scale differs from the stage's max. Keep the resource_scale assignment in the walk so parents resolve before children guess from them, and queue only the emission, draining it once the walk has returned and before the actor_needs_immediate_relayout check.
1 parent 4ba0a2d commit 263d10f

4 files changed

Lines changed: 66 additions & 1 deletion

File tree

clutter/clutter/clutter-actor-private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ void _clutter_actor_queue_only_relayout
316316
void clutter_actor_clear_stage_views_recursive (ClutterActor *actor);
317317

318318
float clutter_actor_get_real_resource_scale (ClutterActor *actor);
319+
void clutter_actor_emit_resource_scale_changed (ClutterActor *actor);
319320

320321
ClutterPaintNode * clutter_actor_create_texture_paint_node (ClutterActor *self,
321322
CoglTexture *texture);

clutter/clutter/clutter-actor.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17873,7 +17873,20 @@ update_resource_scale (ClutterActor *self,
1787317873
return;
1787417874

1787517875
if (ceilf (old_resource_scale) != ceilf (priv->resource_scale))
17876-
g_signal_emit (self, actor_signals[RESOURCE_SCALE_CHANGED], 0);
17876+
{
17877+
ClutterActor *stage = _clutter_actor_get_stage_internal (self);
17878+
17879+
if (stage != NULL)
17880+
clutter_stage_queue_resource_scale_change (CLUTTER_STAGE (stage), self);
17881+
else
17882+
clutter_actor_emit_resource_scale_changed (self);
17883+
}
17884+
}
17885+
17886+
void
17887+
clutter_actor_emit_resource_scale_changed (ClutterActor *self)
17888+
{
17889+
g_signal_emit (self, actor_signals[RESOURCE_SCALE_CHANGED], 0);
1787717890
}
1787817891

1787917892
void

clutter/clutter/clutter-stage-private.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ void clutter_stage_queue_actor_relayout (ClutterStage *stage,
143143
void clutter_stage_dequeue_actor_relayout (ClutterStage *stage,
144144
ClutterActor *actor);
145145

146+
void clutter_stage_queue_resource_scale_change (ClutterStage *stage,
147+
ClutterActor *actor);
148+
146149
GList * clutter_stage_get_views_for_rect (ClutterStage *stage,
147150
const graphene_rect_t *rect);
148151

clutter/clutter/clutter-stage.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ struct _ClutterStagePrivate
129129
ClutterPlane current_clip_planes[4];
130130

131131
GSList *pending_relayouts;
132+
GPtrArray *pending_resource_scale_changes;
132133
GList *pending_queue_redraws;
133134

134135
gint sync_delay;
@@ -1382,6 +1383,47 @@ clutter_stage_dequeue_actor_relayout (ClutterStage *stage,
13821383
}
13831384
}
13841385

1386+
void
1387+
clutter_stage_queue_resource_scale_change (ClutterStage *stage,
1388+
ClutterActor *actor)
1389+
{
1390+
ClutterStagePrivate *priv = stage->priv;
1391+
1392+
g_ptr_array_add (priv->pending_resource_scale_changes, g_object_ref (actor));
1393+
}
1394+
1395+
/* Resource scales are resolved while walking the actor tree, but handlers of
1396+
* ::resource-scale-changed routinely rebuild content and add children, which
1397+
* must not happen from inside that walk. Emit once the walk has finished.
1398+
*/
1399+
static void
1400+
flush_resource_scale_changes (ClutterStage *stage)
1401+
{
1402+
ClutterStagePrivate *priv = stage->priv;
1403+
g_autoptr (GPtrArray) changes = NULL;
1404+
unsigned int i;
1405+
1406+
if (priv->pending_resource_scale_changes->len == 0)
1407+
return;
1408+
1409+
changes = g_steal_pointer (&priv->pending_resource_scale_changes);
1410+
priv->pending_resource_scale_changes =
1411+
g_ptr_array_new_with_free_func (g_object_unref);
1412+
1413+
for (i = 0; i < changes->len; i++)
1414+
{
1415+
ClutterActor *actor = g_ptr_array_index (changes, i);
1416+
1417+
if (CLUTTER_ACTOR_IN_DESTRUCTION (actor))
1418+
continue;
1419+
1420+
if (clutter_actor_get_stage (actor) != CLUTTER_ACTOR (stage))
1421+
continue;
1422+
1423+
clutter_actor_emit_resource_scale_changed (actor);
1424+
}
1425+
}
1426+
13851427
void
13861428
_clutter_stage_maybe_relayout (ClutterActor *actor)
13871429
{
@@ -1551,6 +1593,7 @@ update_actor_stage_views (ClutterStage *stage)
15511593
for (phase = 0; phase < 2; phase++)
15521594
{
15531595
clutter_actor_update_stage_views (actor, phase);
1596+
flush_resource_scale_changes (stage);
15541597

15551598
if (!priv->actor_needs_immediate_relayout)
15561599
break;
@@ -2048,6 +2091,8 @@ clutter_stage_dispose (GObject *object)
20482091
(GDestroyNotify) g_object_unref);
20492092
priv->pending_relayouts = NULL;
20502093

2094+
g_clear_pointer (&priv->pending_resource_scale_changes, g_ptr_array_unref);
2095+
20512096
/* this will release the reference on the stage */
20522097
stage_manager = clutter_stage_manager_get_default ();
20532098
_clutter_stage_manager_remove_stage (stage_manager, stage);
@@ -2454,6 +2499,9 @@ clutter_stage_init (ClutterStage *self)
24542499
clutter_actor_set_background_color (CLUTTER_ACTOR (self),
24552500
&default_stage_color);
24562501

2502+
priv->pending_resource_scale_changes =
2503+
g_ptr_array_new_with_free_func (g_object_unref);
2504+
24572505
clutter_stage_queue_actor_relayout (self, CLUTTER_ACTOR (self));
24582506

24592507
clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);

0 commit comments

Comments
 (0)