[gnome-shell] Add "leisure function" capability



commit a9a513c62166075d71f4a8e99106977b143e5f66
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Sun May 9 00:34:15 2010 -0400

    Add "leisure function" capability
    
    To support scheduling performance-measurement scripts that want to run
    a number of actions in series, add shell_global_run_at_leisure() to run
    a callback when all work is finished.
    
    The initial implementation of this is not that accurate: we track
    business in Tweener.js via new shell_global_begin_work(),
    shell_global_end_work() functions, and we also handle the case
    where the main loop is continually busy.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=618189

 js/ui/tweener.js   |    2 +
 src/shell-global.c |  135 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/shell-global.h |   10 ++++
 3 files changed, 147 insertions(+), 0 deletions(-)
---
diff --git a/js/ui/tweener.js b/js/ui/tweener.js
index f686375..60aaa8d 100644
--- a/js/ui/tweener.js
+++ b/js/ui/tweener.js
@@ -243,12 +243,14 @@ ClutterFrameTicker.prototype = {
 
     start : function() {
         this._timeline.start();
+        global.begin_work();
     },
 
     stop : function() {
         this._timeline.stop();
         this._startTime = -1;
         this._currentTime = -1;
+        global.end_work();
     }
 };
 
diff --git a/src/shell-global.c b/src/shell-global.c
index 5d61e41..84f7716 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -57,6 +57,10 @@ struct _ShellGlobal {
   ClutterActor *root_pixmap;
 
   gint last_change_screen_width, last_change_screen_height;
+
+  guint work_count;
+  GSList *leisure_closures;
+  guint leisure_function_id;
 };
 
 enum {
@@ -1392,3 +1396,134 @@ shell_global_set_property_mutable (ShellGlobal *global,
   JS_RemoveRoot (context, &val);
   return !gjs_log_exception (context, NULL);
 }
+
+typedef struct
+{
+  ShellLeisureFunction func;
+  gpointer user_data;
+  GDestroyNotify notify;
+} LeisureClosure;
+
+static gboolean
+run_leisure_functions (gpointer data)
+{
+  ShellGlobal *global = data;
+  GSList *closures;
+  GSList *iter;
+
+  global->leisure_function_id = 0;
+
+  /* We started more work since we scheduled the idle */
+  if (global->work_count > 0)
+    return FALSE;
+
+  closures = global->leisure_closures;
+  global->leisure_closures = NULL;
+
+  for (iter = closures; iter; iter = iter->next)
+    {
+      LeisureClosure *closure = closures->data;
+      closure->func (closure->user_data);
+
+      if (closure->notify)
+        closure->notify (closure->user_data);
+
+      g_slice_free (LeisureClosure, closure);
+    }
+
+  g_slist_free (closures);
+
+  return FALSE;
+}
+
+static void
+schedule_leisure_functions (ShellGlobal *global)
+{
+  /* This is called when we think we are ready to run leisure functions
+   * by our own accounting. We try to handle other types of business
+   * (like ClutterAnimation) by adding a low priority idle function.
+   *
+   * This won't work properly if the mainloop goes idle waiting for
+   * the vertical blanking interval or waiting for work being done
+   * in another thread.
+   */
+  if (!global->leisure_function_id)
+    global->leisure_function_id = g_idle_add_full (G_PRIORITY_LOW,
+                                                   run_leisure_functions,
+                                                   global, NULL);
+}
+
+/**
+ * shell_global_begin_work:
+ * @global: the #ShellGlobal
+ *
+ * Marks that we are currently doing work. This is used to to track
+ * whether we are busy for the purposes of shell_global_run_at_leisure().
+ * A count is kept and shell_global_end_work() must be called exactly
+ * as many times as shell_global_begin_work().
+ */
+void
+shell_global_begin_work (ShellGlobal *global)
+{
+  global->work_count++;
+}
+
+/**
+ * shell_global_end_work:
+ * @global: the #ShellGlobal
+ *
+ * Marks the end of work that we started with shell_global_begin_work().
+ * If no other work is ongoing and functions have been added with
+ * shell_global_run_at_leisure(), they will be run at the next
+ * opportunity.
+ */
+void
+shell_global_end_work (ShellGlobal *global)
+{
+  g_return_if_fail (global->work_count > 0);
+
+  global->work_count--;
+  if (global->work_count == 0 && global->leisure_closures != NULL)
+    schedule_leisure_functions (global);
+
+}
+
+/**
+ * shell_global_run_at_leisure:
+ * @global: the #ShellGlobal
+ * @func: function to call at leisure
+ * @user_data: data to pass to @func
+ * @notify: function to call to free @user_data
+ *
+ * Schedules a function to be called the next time the shell is idle.
+ * Idle means here no animations, no redrawing, and no ongoing background
+ * work. Since there is currently no way to hook into the Clutter master
+ * clock and know when is running, the implementation here is somewhat
+ * approximation. Animations done through the shell's Tweener module will
+ * be handled properly, but other animations may be detected as terminating
+ * early if they can be drawn fast enough so that the event loop goes idle
+ * between frames.
+ *
+ * The intent of this function is for performance measurement runs
+ * where a number of actions should be run serially and each action is
+ * timed individually. Using this function for other purposes will
+ * interfere with the ability to use it for performance measurement so
+ * should be avoided.
+ */
+void
+shell_global_run_at_leisure (ShellGlobal         *global,
+                             ShellLeisureFunction func,
+                             gpointer             user_data,
+                             GDestroyNotify       notify)
+{
+  LeisureClosure *closure = g_slice_new (LeisureClosure);
+  closure->func = func;
+  closure->user_data = user_data;
+  closure->notify = notify;
+
+  global->leisure_closures = g_slist_append (global->leisure_closures,
+                                             closure);
+
+  if (global->work_count == 0)
+    schedule_leisure_functions (global);
+}
diff --git a/src/shell-global.h b/src/shell-global.h
index 27c43be..696b439 100644
--- a/src/shell-global.h
+++ b/src/shell-global.h
@@ -101,6 +101,16 @@ gboolean shell_global_set_property_mutable (ShellGlobal *global,
                                             const char  *property,
                                             gboolean     mutable);
 
+void shell_global_begin_work     (ShellGlobal         *global);
+void shell_global_end_work       (ShellGlobal         *global);
+
+typedef void (*ShellLeisureFunction) (gpointer data);
+
+void shell_global_run_at_leisure (ShellGlobal         *global,
+                                  ShellLeisureFunction func,
+                                  gpointer             user_data,
+                                  GDestroyNotify       notify);
+
 G_END_DECLS
 
 #endif /* __SHELL_GLOBAL_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]