[gtk+] Add gdk_frame_clock_begin/end_updating()



commit 1db87c897f930171646351c99af7df09dc7ec949
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Fri Feb 15 17:04:39 2013 -0500

    Add gdk_frame_clock_begin/end_updating()
    
    Add an API to start or stop continually updating the frame clock.
    This is a slight convenience for applcations and avoids the problem
    of getting one more frame run after an animation stops, but the
    primary motivation for this is because it looks like we might have
    to use timeBeginPeriod()/timeEndPeriod() on Windows to get reasonably
    accurate timing, and for that we'll need to know if there is an
    animation running.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=693934

 gdk/gdk.symbols            |    2 +
 gdk/gdkframeclock.c        |   44 ++++++++++++++++++++-
 gdk/gdkframeclock.h        |    5 ++
 gdk/gdkframeclockidle.c    |   93 ++++++++++++++++++++++++++++++++-----------
 gdk/gdkframeclockprivate.h |    2 +
 gtk/gtkstylecontext.c      |    6 +--
 gtk/gtkwidget.c            |   20 ++++-----
 tests/video-timer.c        |    4 +-
 8 files changed, 133 insertions(+), 43 deletions(-)
---
diff --git a/gdk/gdk.symbols b/gdk/gdk.symbols
index b01f3e5..1f99c64 100644
--- a/gdk/gdk.symbols
+++ b/gdk/gdk.symbols
@@ -188,6 +188,8 @@ gdk_events_pending
 gdk_event_type_get_type
 gdk_filter_return_get_type
 gdk_flush
+gdk_frame_clock_begin_updating
+gdk_frame_clock_end_updating
 gdk_frame_clock_get_current_timings
 gdk_frame_clock_get_frame_counter
 gdk_frame_clock_get_frame_time
diff --git a/gdk/gdkframeclock.c b/gdk/gdkframeclock.c
index 4d1c765..1994b00 100644
--- a/gdk/gdkframeclock.c
+++ b/gdk/gdkframeclock.c
@@ -285,7 +285,12 @@ gdk_frame_clock_get_frame_time (GdkFrameClock *frame_clock)
  * corresponding the requested phase will be emitted the next
  * time the frame clock processes. Multiple calls to
  * gdk_frame_clock_request_phase() will be combined togethe
- * and only one frame processed.
+ * and only one frame processed. If you are displaying animated
+ * content and want to continually request the
+ * %GDK_FRAME_CLOCK_PHASE_UPDATE phase for a period of time,
+ * you should use gdk_frame_clock_begin_updating() instead, since
+ * this allows GTK+ to adjust system parameters to get maximally
+ * smooth animations.
  *
  * Since: 3.8
  */
@@ -298,6 +303,43 @@ gdk_frame_clock_request_phase (GdkFrameClock      *frame_clock,
   GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->request_phase (frame_clock, phase);
 }
 
+/**
+ * gdk_frame_clock_begin_updating:
+ * @frame_clock: a #GdkFrameClock
+ *
+ * Starts updates for an animation. Until a matching call to
+ * gdk_frame_clock_end_updating() is made, the frame clock will continually
+ * request a new frame with the %GDK_FRAME_CLOCK_PHASE_UPDATE phase.
+ * This function may be called multiple times and frames will be
+ * requested until gdk_frame_clock_end_updating() is called the same
+ * number of times.
+ *
+ * Since: 3.8
+ */
+void
+gdk_frame_clock_begin_updating (GdkFrameClock *frame_clock)
+{
+  g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
+
+  GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->begin_updating (frame_clock);
+}
+
+/**
+ * gdk_frame_clock_end_updating:
+ * @frame_clock: a #GdkFrameClock
+ *
+ * Stops updates for an animation. See the documentation for
+ * gdk_frame_clock_begin_updating().
+ *
+ * Since: 3.8
+ */
+void
+gdk_frame_clock_end_updating (GdkFrameClock *frame_clock)
+{
+  g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
+
+  GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->end_updating (frame_clock);
+}
 
 void
 _gdk_frame_clock_freeze (GdkFrameClock *clock)
diff --git a/gdk/gdkframeclock.h b/gdk/gdkframeclock.h
index f652979..a82d5cf 100644
--- a/gdk/gdkframeclock.h
+++ b/gdk/gdkframeclock.h
@@ -82,6 +82,11 @@ GDK_AVAILABLE_IN_3_8
 void               gdk_frame_clock_request_phase (GdkFrameClock      *frame_clock,
                                                   GdkFrameClockPhase  phase);
 
+GDK_AVAILABLE_IN_3_8
+void               gdk_frame_clock_begin_updating (GdkFrameClock      *frame_clock);
+GDK_AVAILABLE_IN_3_8
+void               gdk_frame_clock_end_updating   (GdkFrameClock      *frame_clock);
+
 /* Frame history */
 GDK_AVAILABLE_IN_3_8
 gint64           gdk_frame_clock_get_frame_counter (GdkFrameClock *frame_clock);
diff --git a/gdk/gdkframeclockidle.c b/gdk/gdkframeclockidle.c
index d07c1d8..0db5bfd 100644
--- a/gdk/gdkframeclockidle.c
+++ b/gdk/gdkframeclockidle.c
@@ -45,6 +45,7 @@ struct _GdkFrameClockIdlePrivate
   guint flush_idle_id;
   guint paint_idle_id;
   guint freeze_count;
+  guint updating_count;
 
   GdkFrameClockPhase requested;
   GdkFrameClockPhase phase;
@@ -192,12 +193,26 @@ gdk_frame_clock_idle_get_frame_time (GdkFrameClock *clock)
   return priv->frame_time;
 }
 
+#define RUN_FLUSH_IDLE(priv)                                            \
+  ((priv)->freeze_count == 0 &&                                         \
+   ((priv)->requested & GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0)
+
+/* The reason why we track updating_count separately here and don't
+ * just add GDK_FRAME_CLOCK_PHASE_UPDATE into ->request on every frame
+ * is so that we can avoid doing one more frame when an animation
+ * is cancelled.
+ */
+#define RUN_PAINT_IDLE(priv)                                            \
+  ((priv)->freeze_count == 0 &&                                         \
+   (((priv)->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0 ||   \
+    (priv)->updating_count > 0))
+
 static void
 maybe_start_idle (GdkFrameClockIdle *clock_idle)
 {
   GdkFrameClockIdlePrivate *priv = clock_idle->priv;
 
-  if (priv->freeze_count == 0 && priv->requested != 0)
+  if (RUN_FLUSH_IDLE (priv) || RUN_PAINT_IDLE (priv))
     {
       guint min_interval = 0;
 
@@ -208,8 +223,7 @@ maybe_start_idle (GdkFrameClockIdle *clock_idle)
           min_interval = (min_interval_us + 500) / 1000;
         }
 
-      if (priv->flush_idle_id == 0 &&
-          (priv->requested & GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0)
+      if (priv->flush_idle_id == 0 && RUN_FLUSH_IDLE (priv))
         {
           priv->flush_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS + 1,
                                                               min_interval,
@@ -218,9 +232,7 @@ maybe_start_idle (GdkFrameClockIdle *clock_idle)
                                                               (GDestroyNotify) g_object_unref);
         }
 
-      if (priv->paint_idle_id == 0 &&
-          !priv->in_paint_idle &&
-          (priv->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0)
+      if (priv->paint_idle_id == 0 && RUN_PAINT_IDLE (priv))
         {
           priv->paint_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_REDRAW,
                                                               min_interval,
@@ -231,6 +243,24 @@ maybe_start_idle (GdkFrameClockIdle *clock_idle)
     }
 }
 
+static void
+maybe_stop_idle (GdkFrameClockIdle *clock_idle)
+{
+  GdkFrameClockIdlePrivate *priv = clock_idle->priv;
+
+  if (priv->flush_idle_id != 0 && !RUN_FLUSH_IDLE (priv))
+    {
+      g_source_remove (priv->flush_idle_id);
+      priv->flush_idle_id = 0;
+    }
+
+  if (priv->paint_idle_id != 0 && !RUN_PAINT_IDLE (priv))
+    {
+      g_source_remove (priv->paint_idle_id);
+      priv->paint_idle_id = 0;
+    }
+}
+
 static gint64
 compute_min_next_frame_time (GdkFrameClockIdle *clock_idle,
                              gint64             last_frame_time)
@@ -265,7 +295,8 @@ gdk_frame_clock_flush_idle (void *data)
 
   g_signal_emit_by_name (G_OBJECT (clock), "flush-events");
 
-  if ((priv->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0)
+  if ((priv->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0 ||
+      priv->updating_count > 0)
     priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
   else
     priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
@@ -287,7 +318,8 @@ gdk_frame_clock_paint_idle (void *data)
   priv->min_next_frame_time = 0;
 
   skip_to_resume_events =
-    (priv->requested & ~(GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS | GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)) == 0;
+    (priv->requested & ~(GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS | GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)) == 0 &&
+    priv->updating_count == 0;
 
   if (priv->phase > GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT)
     {
@@ -325,7 +357,8 @@ gdk_frame_clock_paint_idle (void *data)
         case GDK_FRAME_CLOCK_PHASE_UPDATE:
           if (priv->freeze_count == 0)
             {
-              if (priv->requested & GDK_FRAME_CLOCK_PHASE_UPDATE)
+              if ((priv->requested & GDK_FRAME_CLOCK_PHASE_UPDATE) != 0 ||
+                  priv->updating_count > 0)
                 {
                   priv->requested &= ~GDK_FRAME_CLOCK_PHASE_UPDATE;
                   g_signal_emit_by_name (G_OBJECT (clock), "update");
@@ -436,25 +469,35 @@ gdk_frame_clock_idle_request_phase (GdkFrameClock      *clock,
 }
 
 static void
+gdk_frame_clock_idle_begin_updating (GdkFrameClock *clock)
+{
+  GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
+  GdkFrameClockIdlePrivate *priv = clock_idle->priv;
+
+  priv->updating_count++;
+  maybe_start_idle (clock_idle);
+}
+
+static void
+gdk_frame_clock_idle_end_updating (GdkFrameClock *clock)
+{
+  GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
+  GdkFrameClockIdlePrivate *priv = clock_idle->priv;
+
+  g_return_if_fail (priv->updating_count > 0);
+
+  priv->updating_count--;
+  maybe_stop_idle (clock_idle);
+}
+
+static void
 gdk_frame_clock_idle_freeze (GdkFrameClock *clock)
 {
-  GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
+  GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
+  GdkFrameClockIdlePrivate *priv = clock_idle->priv;
 
   priv->freeze_count++;
-
-  if (priv->freeze_count == 1)
-    {
-      if (priv->flush_idle_id)
-       {
-         g_source_remove (priv->flush_idle_id);
-         priv->flush_idle_id = 0;
-       }
-      if (priv->paint_idle_id)
-       {
-         g_source_remove (priv->paint_idle_id);
-         priv->paint_idle_id = 0;
-       }
-    }
+  maybe_stop_idle (clock_idle);
 }
 
 static void
@@ -489,6 +532,8 @@ gdk_frame_clock_idle_class_init (GdkFrameClockIdleClass *klass)
 
   frame_clock_class->get_frame_time = gdk_frame_clock_idle_get_frame_time;
   frame_clock_class->request_phase = gdk_frame_clock_idle_request_phase;
+  frame_clock_class->begin_updating = gdk_frame_clock_idle_begin_updating;
+  frame_clock_class->end_updating = gdk_frame_clock_idle_end_updating;
   frame_clock_class->freeze = gdk_frame_clock_idle_freeze;
   frame_clock_class->thaw = gdk_frame_clock_idle_thaw;
 
diff --git a/gdk/gdkframeclockprivate.h b/gdk/gdkframeclockprivate.h
index fd505be..27629e3 100644
--- a/gdk/gdkframeclockprivate.h
+++ b/gdk/gdkframeclockprivate.h
@@ -49,6 +49,8 @@ struct _GdkFrameClockClass
 
   void     (* request_phase)  (GdkFrameClock      *clock,
                                GdkFrameClockPhase  phase);
+  void     (* begin_updating) (GdkFrameClock      *clock);
+  void     (* end_updating)   (GdkFrameClock      *clock);
 
   void     (* freeze)         (GdkFrameClock *clock);
   void     (* thaw)           (GdkFrameClock *clock);
diff --git a/gtk/gtkstylecontext.c b/gtk/gtkstylecontext.c
index 7fa5ee7..f5d1e86 100644
--- a/gtk/gtkstylecontext.c
+++ b/gtk/gtkstylecontext.c
@@ -739,9 +739,6 @@ gtk_style_context_update (GdkFrameClock  *clock,
                           GtkStyleContext *context)
 {
   _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANIMATE);
-
-  /* A little blech to request one more than we need */
-  gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
 }
 
 static gboolean
@@ -762,6 +759,7 @@ gtk_style_context_disconnect_update (GtkStyleContext *context)
       g_signal_handler_disconnect (priv->frame_clock,
                                    priv->frame_clock_update_id);
       priv->frame_clock_update_id = 0;
+      gdk_frame_clock_end_updating (priv->frame_clock);
     }
 }
 
@@ -776,7 +774,7 @@ gtk_style_context_connect_update (GtkStyleContext *context)
                                                       "update",
                                                       G_CALLBACK (gtk_style_context_update),
                                                       context);
-      gdk_frame_clock_request_phase (priv->frame_clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
+      gdk_frame_clock_begin_updating (priv->frame_clock);
     }
 }
 
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 231d27b..8a7fb18 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -4554,6 +4554,7 @@ unref_tick_callback_info (GtkWidget           *widget,
       g_signal_handlers_disconnect_by_func (frame_clock,
                                             (gpointer) gtk_widget_on_frame_clock_update,
                                             widget);
+      gdk_frame_clock_end_updating (frame_clock);
     }
 }
 
@@ -4596,10 +4597,6 @@ gtk_widget_on_frame_clock_update (GdkFrameClock *frame_clock,
       unref_tick_callback_info (widget, info, l);
       l = next;
     }
-
-  if (priv->tick_callbacks != NULL)
-    gdk_frame_clock_request_phase (frame_clock,
-                                   GDK_FRAME_CLOCK_PHASE_UPDATE);
 }
 
 static guint tick_callback_id;
@@ -4651,8 +4648,7 @@ gtk_widget_add_tick_callback (GtkWidget       *widget,
       g_signal_connect (frame_clock, "update",
                         G_CALLBACK (gtk_widget_on_frame_clock_update),
                         widget);
-      gdk_frame_clock_request_phase (frame_clock,
-                                     GDK_FRAME_CLOCK_PHASE_UPDATE);
+      gdk_frame_clock_begin_updating (frame_clock);
     }
 
   info = g_slice_new0 (GtkTickCallbackInfo);
@@ -4702,8 +4698,7 @@ gtk_widget_connect_frame_clock (GtkWidget     *widget,
       g_signal_connect (frame_clock, "update",
                         G_CALLBACK (gtk_widget_on_frame_clock_update),
                         widget);
-      gdk_frame_clock_request_phase (frame_clock,
-                                     GDK_FRAME_CLOCK_PHASE_UPDATE);
+      gdk_frame_clock_begin_updating (frame_clock);
     }
 
   if (priv->context)
@@ -4720,9 +4715,12 @@ gtk_widget_disconnect_frame_clock (GtkWidget     *widget,
     _gtk_container_stop_idle_sizer (GTK_CONTAINER (widget));
 
   if (priv->tick_callbacks)
-    g_signal_handlers_disconnect_by_func (frame_clock,
-                                          (gpointer) gtk_widget_on_frame_clock_update,
-                                          widget);
+    {
+      g_signal_handlers_disconnect_by_func (frame_clock,
+                                            (gpointer) gtk_widget_on_frame_clock_update,
+                                            widget);
+      gdk_frame_clock_end_updating (frame_clock);
+    }
 
   if (priv->context)
     gtk_style_context_set_frame_clock (priv->context, NULL);
diff --git a/tests/video-timer.c b/tests/video-timer.c
index 1ddc260..f858240 100644
--- a/tests/video-timer.c
+++ b/tests/video-timer.c
@@ -350,8 +350,6 @@ on_update (GdkFrameClock *frame_clock,
 
       gtk_widget_queue_draw (window);
     }
-
-  gdk_frame_clock_request_phase (frame_clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
 }
 
 static GOptionEntry options[] = {
@@ -393,7 +391,7 @@ main(int argc, char **argv)
   frame_clock = gtk_widget_get_frame_clock (window);
   g_signal_connect (frame_clock, "update",
                     G_CALLBACK (on_update), NULL);
-  gdk_frame_clock_request_phase (frame_clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
+  gdk_frame_clock_begin_updating (frame_clock);
 
   gtk_main ();
 


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