From 961aac3fb36f73d4a48720d93b8928a3e24b5b84 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Tue, 17 Feb 2009 12:22:02 -0500 Subject: [PATCH] [actor] Add ::queue-redraw signal Bug 1454 - move queue_redraw virtualization to ClutterActor The ClutterActor::queue-redraw signal allows parent containers to track whether their children need a redraw. Signed-off-by: Emmanuele Bassi --- clutter/clutter-actor.c | 124 +++++++++++++++++++++++++++++++++++----- clutter/clutter-actor.h | 3 + clutter/clutter-stage.c | 63 +++----------------- clutter/clutter-stage.h | 2 - 4 files changed, 121 insertions(+), 71 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 6fe86db1d..881c07416 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -244,6 +244,8 @@ struct _ClutterActorPrivate /* cached allocation is invalid (request has changed, probably) */ guint needs_allocation : 1; + guint queued_redraw : 1; + guint show_on_set_parent : 1; guint has_clip : 1; guint clip_to_allocation : 1; @@ -379,7 +381,7 @@ enum PICK, REALIZE, UNREALIZE, - + QUEUE_REDRAW, EVENT, CAPTURED_EVENT, BUTTON_PRESS_EVENT, @@ -881,6 +883,49 @@ clutter_actor_real_allocate (ClutterActor *self, g_object_thaw_notify (G_OBJECT (self)); } +static void +clutter_actor_queue_redraw_with_origin (ClutterActor *self, + ClutterActor *origin) +{ + /* short-circuit the trivial case */ + if (!CLUTTER_ACTOR_IS_VISIBLE(self)) + return; + + /* already queued since last paint() */ + if (self->priv->queued_redraw) + return; + + /* calls klass->queue_redraw in default handler */ + g_signal_emit (self, actor_signals[QUEUE_REDRAW], 0, origin); +} + +static void +clutter_actor_real_queue_redraw (ClutterActor *self, + ClutterActor *origin) +{ + ClutterActor *parent; + + /* short-circuit the trivial case */ + if (!CLUTTER_ACTOR_IS_VISIBLE (self)) + return; + + /* already queued since last paint() */ + if (self->priv->queued_redraw) + return; + + self->priv->queued_redraw = TRUE; + + /* notify parents, if they are all visible eventually we'll + * queue redraw on the stage, which queues the redraw idle. + */ + parent = clutter_actor_get_parent (self); + if (parent != NULL) + { + /* this will go up recursively */ + clutter_actor_queue_redraw_with_origin (parent, origin); + } +} + /* like ClutterVertex, but with a w component */ typedef struct { gfloat x; @@ -1571,6 +1616,7 @@ clutter_actor_paint (ClutterActor *self) { clutter_actor_shader_pre_paint (self, FALSE); + self->priv->queued_redraw = FALSE; g_signal_emit (self, actor_signals[PAINT], 0); clutter_actor_shader_post_paint (self); @@ -2890,6 +2936,66 @@ clutter_actor_class_init (ClutterActorClass *klass) G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); + /** + * ClutterActor::queue-redraw: + * @actor: the actor we're bubbling the redraw request through + * @origin: the actor which initiated the redraw request + * + * The ::queue_redraw signal is emitted when clutter_actor_queue_redraw() + * is called on @origin. + * + * The default implementation for #ClutterActor chains up to the + * parent actor and queues a redraw on the parent, thus "bubbling" + * the redraw queue up through the actor graph. The default + * implementation for #ClutterStage queues a clutter_redraw() in a + * main loop idle handler. + * + * Note that the @origin actor may be the stage, or a container; it + * does not have to be a leaf node in the actor graph. + * + * Toolkits embedding a #ClutterStage which require a redraw and + * relayout cycle can stop the emission of this signal using the + * GSignal API, redraw the UI and then call clutter_redraw() + * themselves, like: + * + * |[ + * static void + * on_redraw_complete (void) + * { + * /* execute the Clutter drawing pipeline */ + * clutter_redraw (); + * } + * + * static void + * on_stage_queue_redraw (ClutterStage *stage) + * { + * /* this prevents the default handler to run */ + * g_signal_stop_emission_by_name (stage, "queue-redraw"); + * + * /* queue a redraw with the host toolkit and call + * * a function when the redraw has been completed + * */ + * queue_a_redraw (G_CALLBACK (on_redraw_complete)); + * } + * ]| + * + * This signal is emitted before the Clutter paint + * pipeline is executed. If you want to know when the pipeline has + * been completed you should connect to the ::paint signal on the + * Stage with g_signal_connect_after(). + * + * Since: 1.0 + */ + actor_signals[QUEUE_REDRAW] = + g_signal_new (I_("queue-redraw"), + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterActorClass, queue_redraw), + NULL, NULL, + clutter_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + CLUTTER_TYPE_ACTOR); + /** * ClutterActor::event: * @actor: the actor which received the event @@ -3247,6 +3353,7 @@ clutter_actor_class_init (ClutterActorClass *klass) klass->get_preferred_width = clutter_actor_real_get_preferred_width; klass->get_preferred_height = clutter_actor_real_get_preferred_height; klass->allocate = clutter_actor_real_allocate; + klass->queue_redraw = clutter_actor_real_queue_redraw; } static void @@ -3329,22 +3436,9 @@ clutter_actor_destroy (ClutterActor *self) void clutter_actor_queue_redraw (ClutterActor *self) { - ClutterActor *stage; - g_return_if_fail (CLUTTER_IS_ACTOR (self)); - /* short-circuit the trivial case */ - if (!CLUTTER_ACTOR_IS_VISIBLE (self)) - return; - - /* check if any part of the scenegraph we're in - * is not visible - */ - if (!clutter_actor_get_paint_visibility (self)) - return; - - if ((stage = clutter_actor_get_stage (self)) != NULL) - clutter_stage_queue_redraw (CLUTTER_STAGE (stage)); + clutter_actor_queue_redraw_with_origin (self, self); } /** diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 26167da0e..7c194119c 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -227,6 +227,9 @@ struct _ClutterActorClass void (* pick) (ClutterActor *actor, const ClutterColor *color); + void (* queue_redraw) (ClutterActor *actor, + ClutterActor *leaf_that_queued); + /* size negotiation */ void (* get_preferred_width) (ClutterActor *actor, ClutterUnit for_height, diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 98172cb28..557a99c47 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -116,7 +116,6 @@ enum UNFULLSCREEN, ACTIVATE, DEACTIVATE, - QUEUE_REDRAW, LAST_SIGNAL }; @@ -376,8 +375,10 @@ redraw_update_idle (gpointer user_data) } static void -clutter_stage_real_queue_redraw (ClutterStage *stage) +clutter_stage_real_queue_redraw (ClutterActor *actor, + ClutterActor *leaf) { + ClutterStage *stage = CLUTTER_STAGE (actor); ClutterStagePrivate *priv = stage->priv; if (priv->update_idle == 0) @@ -585,6 +586,7 @@ clutter_stage_class_init (ClutterStageClass *klass) actor_class->unrealize = clutter_stage_unrealize; actor_class->show = clutter_stage_show; actor_class->hide = clutter_stage_hide; + actor_class->queue_redraw = clutter_stage_real_queue_redraw; /** * ClutterStage:fullscreen: @@ -777,58 +779,8 @@ clutter_stage_class_init (ClutterStageClass *klass) NULL, NULL, clutter_marshal_VOID__VOID, G_TYPE_NONE, 0); - /** - * ClutterStage::queue-redraw: - * @stage: the stage which was queued for redraw - * - * The ::queue-redraw signal is emitted each time a #ClutterStage - * has been queued for a redraw. You can use this signal to know - * when clutter_stage_queue_redraw() has been called. - * - * Toolkits embedding a #ClutterStage which require a redraw and - * relayout cycle can stop the emission of this signal using the - * GSignal API, redraw the UI and then call clutter_redraw() - * themselves, like: - * - * |[ - * static void - * on_redraw_complete (void) - * { - * /* execute the Clutter drawing pipeline */ - * clutter_redraw (); - * } - * - * static void - * on_stage_queue_redraw (ClutterStage *stage) - * { - * /* this prevents the default handler to run */ - * g_signal_stop_emission_by_name (stage, "queue-redraw"); - * - * /* queue a redraw with the host toolkit and call - * * a function when the redraw has been completed - * */ - * queue_a_redraw (G_CALLBACK (on_redraw_complete)); - * } - * ]| - * - * This signal is emitted before the Clutter paint - * pipeline is run. If you want to know when the pipeline has been - * completed you should connect to the ::paint signal on the Stage - * with g_signal_connect_after(). - * - * Since: 1.0 - */ - stage_signals[QUEUE_REDRAW] = - g_signal_new (I_("queue-redraw"), - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterStageClass, queue_redraw), - NULL, NULL, - clutter_marshal_VOID__VOID, - G_TYPE_NONE, 0); klass->fullscreen = clutter_stage_real_fullscreen; - klass->queue_redraw = clutter_stage_real_queue_redraw; g_type_class_add_private (gobject_class, sizeof (ClutterStagePrivate)); } @@ -1793,7 +1745,7 @@ clutter_stage_ensure_viewport (ClutterStage *stage) CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES); - clutter_stage_queue_redraw (stage); + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } /** @@ -1805,6 +1757,9 @@ clutter_stage_ensure_viewport (ClutterStage *stage) * Applications should call clutter_actor_queue_redraw() and not * this function. * + * This function is just a wrapper for clutter_actor_queue_redraw() + * and should probably go away. + * * Since: 0.8 */ void @@ -1812,7 +1767,7 @@ clutter_stage_queue_redraw (ClutterStage *stage) { g_return_if_fail (CLUTTER_IS_STAGE (stage)); - g_signal_emit (stage, stage_signals[QUEUE_REDRAW], 0); + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } /** diff --git a/clutter/clutter-stage.h b/clutter/clutter-stage.h index 82b1ea456..2f020c0d4 100644 --- a/clutter/clutter-stage.h +++ b/clutter/clutter-stage.h @@ -106,8 +106,6 @@ struct _ClutterStageClass void (* activate) (ClutterStage *stage); void (* deactivate) (ClutterStage *stage); - void (* queue_redraw) (ClutterStage *stage); - /*< private >*/ /* padding for future expansion */ gpointer _padding_dummy[32];