diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 922b5fb0e..b5f5c3785 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -2816,3 +2816,162 @@ clutter_get_font_map (void) return NULL; } + +typedef struct _ClutterRepaintFunction +{ + guint id; + GSourceFunc func; + gpointer data; + GDestroyNotify notify; +} ClutterRepaintFunction; + +/** + * clutter_threads_remove_repaint_func: + * @handle_id: an unsigned integer greater than zero + * + * Removes the repaint function with @handle_id as its id + * + * Since: 1.0 + */ +void +clutter_threads_remove_repaint_func (guint handle_id) +{ + ClutterRepaintFunction *repaint_func; + ClutterMainContext *context; + GList *l; + + g_return_if_fail (handle_id > 0); + + context = CLUTTER_CONTEXT (); + l = context->repaint_funcs; + while (l != NULL) + { + repaint_func = l->data; + + if (repaint_func->id == handle_id) + { + context->repaint_funcs = + g_list_remove_link (context->repaint_funcs, l); + + g_list_free (l); + + if (repaint_func->notify) + repaint_func->notify (repaint_func->data); + + g_slice_free (ClutterRepaintFunction, repaint_func); + + return; + } + + l = l->next; + } +} + +/** + * clutter_threads_add_repaint_func: + * @func: the function to be called within the paint cycle + * @data: data to be passed to the function, or %NULL + * @notify: function to be called when removing the repaint + * function, or %NULL + * + * Adds a function to be called whenever Clutter is repainting a Stage. + * If the function returns %FALSE it is automatically removed from the + * list of repaint functions and will not be called again. + * + * This function is guaranteed to be called from within the same thread + * that called clutter_main(), and while the Clutter lock is being held. + * + * A repaint function is useful to ensure that an update of the scenegraph + * is performed before the scenegraph is repainted; for instance, uploading + * a frame from a video into a #ClutterTexture. + * + * When the repaint function is removed (either because it returned %FALSE + * or because clutter_threads_remove_repaint_func() has been called) the + * @notify function will be called, if any is set. + * + * Return value: the ID (greater than 0) of the repaint function. You + * can use the returned integer to remove the repaint function by + * calling clutter_threads_remove_repaint_func(). + * + * Since: 1.0 + */ +guint +clutter_threads_add_repaint_func (GSourceFunc func, + gpointer data, + GDestroyNotify notify) +{ + static guint repaint_id = 1; + ClutterMainContext *context; + ClutterRepaintFunction *repaint_func; + + g_return_val_if_fail (func != NULL, 0); + + context = CLUTTER_CONTEXT (); + + /* XXX lock the context */ + + repaint_func = g_slice_new (ClutterRepaintFunction); + + repaint_func->id = repaint_id++; + repaint_func->func = func; + repaint_func->data = data; + repaint_func->notify = notify; + + context->repaint_funcs = g_list_prepend (context->repaint_funcs, + repaint_func); + + /* XXX unlock the context */ + + return repaint_func->id; +} + +/* + * _clutter_run_repaint_functions: + * + * Executes the repaint functions added using the + * clutter_threads_add_repaint_func() function. + * + * Must be called before calling clutter_redraw() and + * with the Clutter thread lock held. + */ +void +_clutter_run_repaint_functions (void) +{ + ClutterMainContext *context = CLUTTER_CONTEXT (); + ClutterRepaintFunction *repaint_func; + GList *reinvoke_list, *l; + + if (context->repaint_funcs == NULL) + return; + + reinvoke_list = NULL; + + /* consume the whole list while we execute the functions */ + while (context->repaint_funcs) + { + gboolean res = FALSE; + + repaint_func = context->repaint_funcs->data; + + l = context->repaint_funcs; + context->repaint_funcs = + g_list_remove_link (context->repaint_funcs, context->repaint_funcs); + + g_list_free (l); + + res = repaint_func->func (repaint_func->data); + + if (res) + reinvoke_list = g_list_prepend (reinvoke_list, repaint_func); + else + { + if (repaint_func->notify) + repaint_func->notify (repaint_func->data); + + g_slice_free (ClutterRepaintFunction, repaint_func); + } + } + + if (reinvoke_list) + context->repaint_funcs = reinvoke_list; +} diff --git a/clutter/clutter-main.h b/clutter/clutter-main.h index 43f345592..a5365f0d9 100644 --- a/clutter/clutter-main.h +++ b/clutter/clutter-main.h @@ -109,34 +109,38 @@ gboolean clutter_get_show_fps (void); gulong clutter_get_timestamp (void); /* Threading functions */ -void clutter_threads_init (void); -void clutter_threads_enter (void); -void clutter_threads_leave (void); -void clutter_threads_set_lock_functions (GCallback enter_fn, - GCallback leave_fn); -guint clutter_threads_add_idle (GSourceFunc func, - gpointer data); -guint clutter_threads_add_idle_full (gint priority, - GSourceFunc func, - gpointer data, - GDestroyNotify notify); -guint clutter_threads_add_timeout (guint interval, - GSourceFunc func, - gpointer data); -guint clutter_threads_add_timeout_full (gint priority, - guint interval, - GSourceFunc func, - gpointer data, - GDestroyNotify notify); -guint clutter_threads_add_frame_source (guint fps, - GSourceFunc func, - gpointer data); -guint clutter_threads_add_frame_source_full - (gint priority, - guint fps, - GSourceFunc func, - gpointer data, - GDestroyNotify notify); +void clutter_threads_init (void); +void clutter_threads_enter (void); +void clutter_threads_leave (void); +void clutter_threads_set_lock_functions (GCallback enter_fn, + GCallback leave_fn); +guint clutter_threads_add_idle (GSourceFunc func, + gpointer data); +guint clutter_threads_add_idle_full (gint priority, + GSourceFunc func, + gpointer data, + GDestroyNotify notify); +guint clutter_threads_add_timeout (guint interval, + GSourceFunc func, + gpointer data); +guint clutter_threads_add_timeout_full (gint priority, + guint interval, + GSourceFunc func, + gpointer data, + GDestroyNotify notify); +guint clutter_threads_add_frame_source (guint fps, + GSourceFunc func, + gpointer data); +guint clutter_threads_add_frame_source_full (gint priority, + guint fps, + GSourceFunc func, + gpointer data, + GDestroyNotify notify); + +guint clutter_threads_add_repaint_func (GSourceFunc func, + gpointer data, + GDestroyNotify notify); +void clutter_threads_remove_repaint_func (guint handler_id); void clutter_set_motion_events_enabled (gboolean enable); gboolean clutter_get_motion_events_enabled (void); @@ -158,7 +162,7 @@ void clutter_clear_glyph_cache (void); void clutter_set_font_flags (ClutterFontFlags flags); ClutterFontFlags clutter_get_font_flags (void); -ClutterInputDevice* clutter_get_input_device_for_id (gint id); +ClutterInputDevice *clutter_get_input_device_for_id (gint id); void clutter_grab_pointer_for_device (ClutterActor *actor, gint id); diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index fe0c9b39a..6b62cee48 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -132,6 +132,8 @@ struct _ClutterMainContext ClutterMasterClock *master_clock; gulong redraw_count; + + GList *repaint_funcs; }; #define CLUTTER_CONTEXT() (clutter_context_get_default ()) @@ -238,6 +240,8 @@ void _clutter_actor_set_enable_model_view_transform (ClutterActor *self, void _clutter_actor_set_enable_paint_unmapped (ClutterActor *self, gboolean enable); +void _clutter_run_repaint_functions (void); + G_END_DECLS #endif /* _HAVE_CLUTTER_PRIVATE_H */ diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 86d22c569..c6f736e4e 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -403,6 +403,12 @@ redraw_update_idle (gpointer user_data) master_clock = _clutter_master_clock_get_default (); _clutter_master_clock_advance (master_clock); + /* run the (eventual) repaint functions; since those might end up queuing + * a relayout or a redraw we need to execute them before maybe_relayout() + */ + CLUTTER_NOTE (PAINT, "Repaint functions"); + _clutter_run_repaint_functions (); + /* clutter_redraw() will also call maybe_relayout(), but since a relayout * can queue a redraw, we want to do the relayout before we clear the * update_idle to avoid painting the stage twice. Calling maybe_relayout() @@ -457,8 +463,7 @@ static void set_offscreen_while_unrealized (ClutterActor *actor, void *data) { - CLUTTER_STAGE (actor)->priv->is_offscreen = - GPOINTER_TO_INT (data); + CLUTTER_STAGE (actor)->priv->is_offscreen = GPOINTER_TO_INT (data); } static void