Add repaint functions
authorEmmanuele Bassi <ebassi@linux.intel.com>
Mon, 30 Mar 2009 12:49:03 +0000 (13:49 +0100)
committerEmmanuele Bassi <ebassi@linux.intel.com>
Sat, 23 May 2009 18:33:04 +0000 (19:33 +0100)
Sometimes it is necessary for third party code to have a
function called during the redraw process, so that you can
update the scenegraph before it is painted.

clutter/clutter-main.c
clutter/clutter-main.h
clutter/clutter-private.h
clutter/clutter-stage.c

index 922b5fb..b5f5c37 100644 (file)
@@ -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;
+}
index 43f3455..a5365f0 100644 (file)
@@ -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);
index fe0c9b3..6b62cee 100644 (file)
@@ -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 */
index 86d22c5..c6f736e 100644 (file)
@@ -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