[gtk+/wip/frame-synchronization: 19/19] Compress motion synchronized with the paint cycle



commit 55bad81d5730c9aac90568c48b9a4c9e76537114
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Wed Sep 26 10:28:06 2012 -0400

    Compress motion synchronized with the paint cycle
    
    When we have pending motion events, instead of delivering them
    directly, request the new FLUSH_EVENTS phase of the paint clock.
    This allows us to compress repeated motion events sent to the
    same window.
    
    In the FLUSH_EVENTS phase, which occur at priority GDK_PRIORITY_EVENTS + 1,
    we deliver any pending motion events then turn off event delivery
    until the end of the next frame. Turning off event delivery means
    that we'll reliably paint the compressed motion events even if more
    have arrived.
    
    Add a motion-compression test case which demonstrates behavior when
    an application takes too long handle motion events. It is unusable
    without this patch but behaves fine with the patch.

 gdk/gdkdisplay.c           |   31 +++++++++++-
 gdk/gdkdisplayprivate.h    |    5 ++
 gdk/gdkevents.c            |   69 ++++++++++++++++++++++++-
 gdk/gdkinternals.h         |    3 +
 gdk/gdkpaintclock.c        |   32 ++++++++++++
 gdk/gdkpaintclock.h        |   16 ++++--
 gdk/gdkpaintclockidle.c    |  121 ++++++++++++++++++++++++++++++++++++--------
 gdk/gdkwindow.c            |   59 +++++++++++++++++++--
 tests/Makefile.am          |    2 +
 tests/motion-compression.c |   72 ++++++++++++++++++++++++++
 10 files changed, 373 insertions(+), 37 deletions(-)
---
diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c
index 60bbb44..31b0282 100644
--- a/gdk/gdkdisplay.c
+++ b/gdk/gdkdisplay.c
@@ -308,7 +308,11 @@ gdk_display_get_event (GdkDisplay *display)
   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
 
   GDK_DISPLAY_GET_CLASS (display)->queue_events (display);
-  return _gdk_event_unqueue (display);
+
+  if (display->events_paused)
+    return NULL;
+  else
+    return _gdk_event_unqueue (display);
 }
 
 /**
@@ -2003,6 +2007,31 @@ gdk_display_notify_startup_complete (GdkDisplay  *display,
 }
 
 void
+_gdk_display_set_events_paused (GdkDisplay       *display,
+                                gboolean          events_paused)
+{
+  display->events_paused = !!events_paused;
+}
+
+void
+_gdk_display_flush_events (GdkDisplay *display)
+{
+  display->flushing_events = TRUE;
+
+  while (TRUE)
+    {
+      GdkEvent *event = _gdk_event_unqueue (display);
+      if (event == NULL)
+        break;
+
+      _gdk_event_emit (event);
+      gdk_event_free (event);
+    }
+
+  display->flushing_events = FALSE;
+}
+
+void
 _gdk_display_event_data_copy (GdkDisplay     *display,
                               const GdkEvent *event,
                               GdkEvent       *new_event)
diff --git a/gdk/gdkdisplayprivate.h b/gdk/gdkdisplayprivate.h
index 6549f21..8f448eb 100644
--- a/gdk/gdkdisplayprivate.h
+++ b/gdk/gdkdisplayprivate.h
@@ -114,6 +114,8 @@ struct _GdkDisplay
   GdkDevice *core_pointer;  /* Core pointer device */
 
   guint closed             : 1;  /* Whether this display has been closed */
+  guint events_paused      : 1;  /* Whether events are blocked */
+  guint flushing_events    : 1;  /* Inside gdk_display_flush_events */
 
   GArray *touch_implicit_grabs;
   GHashTable *device_grabs;
@@ -296,6 +298,9 @@ void                _gdk_display_pointer_info_foreach (GdkDisplay       *display
                                                        GdkDisplayPointerInfoForeach func,
                                                        gpointer          user_data);
 gulong              _gdk_display_get_next_serial      (GdkDisplay       *display);
+void                _gdk_display_set_events_paused    (GdkDisplay       *display,
+                                                       gboolean          events_paused);
+void                _gdk_display_flush_events         (GdkDisplay       *display);
 void                _gdk_display_event_data_copy      (GdkDisplay       *display,
                                                        const GdkEvent   *event,
                                                        GdkEvent         *new_event);
diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c
index 204863c..41c519c 100644
--- a/gdk/gdkevents.c
+++ b/gdk/gdkevents.c
@@ -85,13 +85,27 @@ _gdk_event_emit (GdkEvent *event)
 GList*
 _gdk_event_queue_find_first (GdkDisplay *display)
 {
-  GList *tmp_list = display->queued_events;
+  GList *tmp_list;
+  GList *pending_motion = NULL;
+
+  if (display->events_paused)
+    return NULL;
 
+  tmp_list = display->queued_events;
   while (tmp_list)
     {
       GdkEventPrivate *event = tmp_list->data;
-      if (!(event->flags & GDK_EVENT_PENDING))
-	return tmp_list;
+
+      if (event->flags & GDK_EVENT_PENDING)
+        continue;
+
+      if (pending_motion)
+        return pending_motion;
+
+      if (event->event.type == GDK_MOTION_NOTIFY && !display->flushing_events)
+        pending_motion = tmp_list;
+      else
+        return tmp_list;
 
       tmp_list = g_list_next (tmp_list);
     }
@@ -248,6 +262,55 @@ _gdk_event_unqueue (GdkDisplay *display)
   return event;
 }
 
+void
+_gdk_event_queue_handle_motion_compression (GdkDisplay *display)
+{
+  GList *tmp_list;
+  GList *pending_motions = NULL;
+  GdkWindow *pending_motion_window = NULL;
+
+  /* If the last N events in the event queue are motion notify
+   * events for the same window, drop all but the last */
+
+  tmp_list = display->queued_tail;
+
+  while (tmp_list)
+    {
+      GdkEventPrivate *event = tmp_list->data;
+
+      if (event->flags & GDK_EVENT_PENDING)
+        break;
+
+      if (event->event.type != GDK_MOTION_NOTIFY)
+        break;
+
+      if (pending_motion_window != NULL &&
+          pending_motion_window != event->event.motion.window)
+        break;
+
+      pending_motion_window = event->event.motion.window;
+      pending_motions = tmp_list;
+
+      tmp_list = tmp_list->prev;
+    }
+
+  while (pending_motions && pending_motions->next != NULL)
+    {
+      GList *next = pending_motions->next;
+      display->queued_events = g_list_delete_link (display->queued_events,
+                                                   pending_motions);
+      pending_motions = next;
+    }
+
+  if (pending_motions &&
+      pending_motions == display->queued_events &&
+      pending_motions == display->queued_tail)
+    {
+      GdkPaintClock *clock = gdk_window_get_paint_clock (pending_motion_window);
+      gdk_paint_clock_request_phase (clock, GDK_PAINT_CLOCK_PHASE_FLUSH_EVENTS);
+    }
+}
+
 /**
  * gdk_event_handler_set:
  * @func: the function to call to handle events from GDK.
diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h
index 6760535..99745c2 100644
--- a/gdk/gdkinternals.h
+++ b/gdk/gdkinternals.h
@@ -298,6 +298,9 @@ GList* _gdk_event_queue_insert_after (GdkDisplay *display,
 GList* _gdk_event_queue_insert_before(GdkDisplay *display,
                                       GdkEvent   *after_event,
                                       GdkEvent   *event);
+
+void    _gdk_event_queue_handle_motion_compression (GdkDisplay *display);
+
 void   _gdk_event_button_generate    (GdkDisplay *display,
                                       GdkEvent   *event);
 
diff --git a/gdk/gdkpaintclock.c b/gdk/gdkpaintclock.c
index 4f06633..ef3eaf4 100644
--- a/gdk/gdkpaintclock.c
+++ b/gdk/gdkpaintclock.c
@@ -91,11 +91,13 @@ G_DEFINE_INTERFACE (GdkPaintClock, gdk_paint_clock, G_TYPE_OBJECT)
 
 enum {
   FRAME_REQUESTED,
+  FLUSH_EVENTS,
   BEFORE_PAINT,
   UPDATE,
   LAYOUT,
   PAINT,
   AFTER_PAINT,
+  RESUME_EVENTS,
   LAST_SIGNAL
 };
 
@@ -121,6 +123,21 @@ gdk_paint_clock_default_init (GdkPaintClockInterface *iface)
                   G_TYPE_NONE, 0);
 
   /**
+   * GdkPaintClock::flush-events:
+   * @clock: the paint clock emitting the signal
+   *
+   * FIXME.
+   */
+  signals[FLUSH_EVENTS] =
+    g_signal_new (g_intern_static_string ("flush-events"),
+                  GDK_TYPE_PAINT_CLOCK,
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
+  /**
    * GdkPaintClock::before-paint:
    * @clock: the paint clock emitting the signal
    *
@@ -202,6 +219,21 @@ gdk_paint_clock_default_init (GdkPaintClockInterface *iface)
                   NULL, NULL,
                   g_cclosure_marshal_VOID__VOID,
                   G_TYPE_NONE, 0);
+
+  /**
+   * GdkPaintClock::resume-events:
+   * @clock: the paint clock emitting the signal
+   *
+   * FIXME.
+   */
+  signals[RESUME_EVENTS] =
+    g_signal_new (g_intern_static_string ("resume-events"),
+                  GDK_TYPE_PAINT_CLOCK,
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
 }
 
 /**
diff --git a/gdk/gdkpaintclock.h b/gdk/gdkpaintclock.h
index 17dc45f..f2b6b6c 100644
--- a/gdk/gdkpaintclock.h
+++ b/gdk/gdkpaintclock.h
@@ -64,12 +64,14 @@ void gdk_paint_clock_target_set_clock (GdkPaintClockTarget *target,
 #define GDK_PAINT_CLOCK_GET_IFACE(inst)  (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GDK_TYPE_PAINT_CLOCK, GdkPaintClockInterface))
 
 typedef enum {
-  GDK_PAINT_CLOCK_PHASE_NONE         = 0,
-  GDK_PAINT_CLOCK_PHASE_BEFORE_PAINT = 1 << 0,
-  GDK_PAINT_CLOCK_PHASE_UPDATE       = 1 << 1,
-  GDK_PAINT_CLOCK_PHASE_LAYOUT       = 1 << 2,
-  GDK_PAINT_CLOCK_PHASE_PAINT        = 1 << 3,
-  GDK_PAINT_CLOCK_PHASE_AFTER_PAINT  = 1 << 4
+  GDK_PAINT_CLOCK_PHASE_NONE          = 0,
+  GDK_PAINT_CLOCK_PHASE_FLUSH_EVENTS  = 1 << 0,
+  GDK_PAINT_CLOCK_PHASE_BEFORE_PAINT  = 1 << 1,
+  GDK_PAINT_CLOCK_PHASE_UPDATE        = 1 << 2,
+  GDK_PAINT_CLOCK_PHASE_LAYOUT        = 1 << 3,
+  GDK_PAINT_CLOCK_PHASE_PAINT         = 1 << 4,
+  GDK_PAINT_CLOCK_PHASE_AFTER_PAINT   = 1 << 5,
+  GDK_PAINT_CLOCK_PHASE_RESUME_EVENTS = 1 << 6
 } GdkPaintClockPhase;
 
 struct _GdkPaintClockInterface
@@ -87,11 +89,13 @@ struct _GdkPaintClockInterface
 
   /* signals */
   /* void (* frame_requested)    (GdkPaintClock *clock); */
+  /* void (* flush_events)       (GdkPaintClock *clock); */
   /* void (* before_paint)       (GdkPaintClock *clock); */
   /* void (* update)             (GdkPaintClock *clock); */
   /* void (* layout)             (GdkPaintClock *clock); */
   /* void (* paint)              (GdkPaintClock *clock); */
   /* void (* after_paint)        (GdkPaintClock *clock); */
+  /* void (* resume_events)      (GdkPaintClock *clock); */
 };
 
 GType    gdk_paint_clock_get_type             (void) G_GNUC_CONST;
diff --git a/gdk/gdkpaintclockidle.c b/gdk/gdkpaintclockidle.c
index 7d64501..730b8c2 100644
--- a/gdk/gdkpaintclockidle.c
+++ b/gdk/gdkpaintclockidle.c
@@ -39,7 +39,8 @@ struct _GdkPaintClockIdlePrivate
   guint64 frame_time;
   guint64 min_next_frame_time;
 
-  guint idle_id;
+  guint flush_idle_id;
+  guint paint_idle_id;
   guint freeze_count;
 
   GdkPaintClockPhase requested;
@@ -48,6 +49,7 @@ struct _GdkPaintClockIdlePrivate
   guint in_paint_idle : 1;
 };
 
+static gboolean gdk_paint_clock_flush_idle (void *data);
 static gboolean gdk_paint_clock_paint_idle (void *data);
 
 static void gdk_paint_clock_idle_finalize             (GObject                *object);
@@ -146,7 +148,7 @@ maybe_start_idle (GdkPaintClockIdle *clock_idle)
 {
   GdkPaintClockIdlePrivate *priv = clock_idle->priv;
 
-  if (priv->idle_id == 0 && priv->freeze_count == 0 && priv->requested != 0)
+  if (priv->freeze_count == 0)
     {
       guint min_interval = 0;
 
@@ -157,41 +159,89 @@ maybe_start_idle (GdkPaintClockIdle *clock_idle)
           min_interval = (min_interval_us + 500) / 1000;
         }
 
-      priv->idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_REDRAW,
-                                                    min_interval,
-                                                    gdk_paint_clock_paint_idle,
-                                                    g_object_ref (clock_idle),
-                                                    (GDestroyNotify) g_object_unref);
+      if (priv->flush_idle_id == 0 &&
+          (priv->requested & GDK_PAINT_CLOCK_PHASE_FLUSH_EVENTS) != 0)
+        {
+          priv->flush_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS + 1,
+                                                              min_interval,
+                                                              gdk_paint_clock_flush_idle,
+                                                              g_object_ref (clock_idle),
+                                                              (GDestroyNotify) g_object_unref);
+        }
+
+      if (priv->paint_idle_id == 0 &&
+          (priv->requested & ~GDK_PAINT_CLOCK_PHASE_FLUSH_EVENTS) != 0)
+        {
+          priv->paint_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_REDRAW,
+                                                              min_interval,
+                                                              gdk_paint_clock_paint_idle,
+                                                              g_object_ref (clock_idle),
+                                                              (GDestroyNotify) g_object_unref);
 
-      gdk_paint_clock_frame_requested (GDK_PAINT_CLOCK (clock_idle));
+          gdk_paint_clock_frame_requested (GDK_PAINT_CLOCK (clock_idle));
+        }
     }
 }
 
 static gboolean
+gdk_paint_clock_flush_idle (void *data)
+{
+  GdkPaintClock *clock = GDK_PAINT_CLOCK (data);
+  GdkPaintClockIdle *clock_idle = GDK_PAINT_CLOCK_IDLE (clock);
+  GdkPaintClockIdlePrivate *priv = clock_idle->priv;
+
+  priv->flush_idle_id = 0;
+
+  if (priv->phase != GDK_PAINT_CLOCK_PHASE_NONE)
+    return FALSE;
+
+  priv->phase = GDK_PAINT_CLOCK_PHASE_FLUSH_EVENTS;
+  priv->requested &= ~GDK_PAINT_CLOCK_PHASE_FLUSH_EVENTS;
+
+  g_signal_emit_by_name (G_OBJECT (clock), "flush-events");
+
+  if ((priv->requested & ~GDK_PAINT_CLOCK_PHASE_FLUSH_EVENTS) != 0)
+    priv->phase = GDK_PAINT_CLOCK_PHASE_BEFORE_PAINT;
+  else
+    priv->phase = GDK_PAINT_CLOCK_PHASE_NONE;
+
+  return FALSE;
+}
+
+static gboolean
 gdk_paint_clock_paint_idle (void *data)
 {
   GdkPaintClock *clock = GDK_PAINT_CLOCK (data);
   GdkPaintClockIdle *clock_idle = GDK_PAINT_CLOCK_IDLE (clock);
   GdkPaintClockIdlePrivate *priv = clock_idle->priv;
+  gboolean skip_to_resume_events;
 
-  priv->idle_id = 0;
+  priv->paint_idle_id = 0;
+
+  skip_to_resume_events =
+    (priv->requested & ~(GDK_PAINT_CLOCK_PHASE_FLUSH_EVENTS | GDK_PAINT_CLOCK_PHASE_RESUME_EVENTS)) == 0;
 
   switch (priv->phase)
     {
+    case GDK_PAINT_CLOCK_PHASE_FLUSH_EVENTS:
+      break;
     case GDK_PAINT_CLOCK_PHASE_NONE:
     case GDK_PAINT_CLOCK_PHASE_BEFORE_PAINT:
       if (priv->freeze_count == 0)
 	{
           priv->frame_time = compute_frame_time (clock_idle);
 
-	  priv->phase = GDK_PAINT_CLOCK_PHASE_BEFORE_PAINT;
-          priv->requested &= ~GDK_PAINT_CLOCK_PHASE_BEFORE_PAINT;
-          /* We always emit ::before-paint and ::after-paint even if
-           * not explicitly requested, and unlike other phases,
+          priv->phase = GDK_PAINT_CLOCK_PHASE_BEFORE_PAINT;
+          /* We always emit ::before-paint and ::after-paint if
+           * any of the intermediate phases are requested and
            * they don't get repeated if you freeze/thaw while
            * in them. */
-	  g_signal_emit_by_name (G_OBJECT (clock), "before-paint");
-	  priv->phase = GDK_PAINT_CLOCK_PHASE_UPDATE;
+          if (!skip_to_resume_events)
+            {
+              priv->requested &= ~GDK_PAINT_CLOCK_PHASE_BEFORE_PAINT;
+              g_signal_emit_by_name (G_OBJECT (clock), "before-paint");
+            }
+          priv->phase = GDK_PAINT_CLOCK_PHASE_UPDATE;
 	}
     case GDK_PAINT_CLOCK_PHASE_UPDATE:
       if (priv->freeze_count == 0)
@@ -226,9 +276,24 @@ gdk_paint_clock_paint_idle (void *data)
       if (priv->freeze_count == 0)
 	{
 	  priv->phase = GDK_PAINT_CLOCK_PHASE_AFTER_PAINT;
-          priv->requested &= ~GDK_PAINT_CLOCK_PHASE_AFTER_PAINT;
-	  g_signal_emit_by_name (G_OBJECT (clock), "after-paint");
-          /* the ::after-paint phase doesn't get repeated on freeze/thaw */
+          if (!skip_to_resume_events)
+            {
+              priv->requested &= ~GDK_PAINT_CLOCK_PHASE_AFTER_PAINT;
+              g_signal_emit_by_name (G_OBJECT (clock), "after-paint");
+            }
+          /* the ::after-paint phase doesn't get repeated on freeze/thaw,
+           */
+          priv->phase = GDK_PAINT_CLOCK_PHASE_RESUME_EVENTS;
+	}
+    case GDK_PAINT_CLOCK_PHASE_RESUME_EVENTS:
+      if (priv->freeze_count == 0)
+	{
+          if (priv->requested & GDK_PAINT_CLOCK_PHASE_RESUME_EVENTS)
+            {
+              priv->requested &= ~GDK_PAINT_CLOCK_PHASE_RESUME_EVENTS;
+              g_signal_emit_by_name (G_OBJECT (clock), "resume-events");
+            }
+          /* the ::resume-event phase doesn't get repeated on freeze/thaw */
 	  priv->phase = GDK_PAINT_CLOCK_PHASE_NONE;
 	}
     }
@@ -280,10 +345,15 @@ gdk_paint_clock_idle_freeze (GdkPaintClock *clock)
 
   if (priv->freeze_count == 1)
     {
-      if (priv->idle_id)
+      if (priv->flush_idle_id)
 	{
-	  g_source_remove (priv->idle_id);
-	  priv->idle_id = 0;
+	  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;
 	}
     }
 }
@@ -298,7 +368,14 @@ gdk_paint_clock_idle_thaw (GdkPaintClock *clock)
 
   priv->freeze_count--;
   if (priv->freeze_count == 0)
-    maybe_start_idle (clock_idle);
+    {
+      maybe_start_idle (clock_idle);
+      /* If nothing is requested so we didn't start an idle, we need
+       * to skip to the end of the state chain, since the idle won't
+       * run and do it for us. */
+      if (priv->paint_idle_id == 0)
+        priv->phase = GDK_PAINT_CLOCK_PHASE_NONE;
+    }
 }
 
 static void
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index 459d8dd..a8f0e17 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -9946,7 +9946,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
 {
   GdkWindow *event_window;
   gdouble x, y;
-  gboolean unlink_event;
+  gboolean unlink_event = FALSE;
   GdkDeviceGrabInfo *button_release_grab;
   GdkPointerWindowInfo *pointer_info = NULL;
   GdkDevice *device, *source_device;
@@ -9989,7 +9989,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
 
   event_window = event->any.window;
   if (!event_window)
-    return;
+    goto out;
 
 #ifdef DEBUG_WINDOW_PRINTING
   if (event->type == GDK_KEY_PRESS &&
@@ -10004,13 +10004,13 @@ _gdk_windowing_got_event (GdkDisplay *display,
     {
       event_window->native_visibility = event->visibility.state;
       gdk_window_update_visibility_recursively (event_window, event_window);
-      return;
+      goto out;
     }
 
   if (!(is_button_type (event->type) ||
         is_motion_type (event->type)) ||
       event_window->window_type == GDK_WINDOW_ROOT)
-    return;
+    goto out;
 
   is_toplevel = gdk_window_is_toplevel (event_window);
 
@@ -10103,7 +10103,6 @@ _gdk_windowing_got_event (GdkDisplay *display,
         _gdk_display_enable_motion_hints (display, device);
     }
 
-  unlink_event = FALSE;
   if (is_motion_type (event->type))
     unlink_event = proxy_pointer_event (display, event, serial);
   else if (is_button_type (event->type))
@@ -10145,6 +10144,13 @@ _gdk_windowing_got_event (GdkDisplay *display,
       g_list_free_1 (event_link);
       gdk_event_free (event);
     }
+
+  /* This does two things - first it sees if there are motions at the
+   * end of the queue that can be compressed. Second, if there is just
+   * a single motion that won't be dispatched because it is a compression
+   * candidate it queues up flushing the event queue.
+   */
+  _gdk_event_queue_handle_motion_compression (display);
 }
 
 /**
@@ -11431,6 +11437,22 @@ gdk_property_delete (GdkWindow *window,
 }
 
 static void
+gdk_window_flush_events (GdkPaintClock *clock,
+                         void          *data)
+{
+  GdkWindow *window;
+  GdkDisplay *display;
+
+  window = GDK_WINDOW (data);
+
+  display = gdk_window_get_display (window);
+  _gdk_display_flush_events (display);
+  _gdk_display_set_events_paused (display, TRUE);
+
+  gdk_paint_clock_request_phase (clock, GDK_PAINT_CLOCK_PHASE_RESUME_EVENTS);
+}
+
+static void
 gdk_window_paint_on_clock (GdkPaintClock *clock,
 			   void          *data)
 {
@@ -11443,6 +11465,19 @@ gdk_window_paint_on_clock (GdkPaintClock *clock,
   gdk_window_process_updates_with_mode (window, PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN);
 }
 
+static void
+gdk_window_resume_events (GdkPaintClock *clock,
+                          void          *data)
+{
+  GdkWindow *window;
+  GdkDisplay *display;
+
+  window = GDK_WINDOW (data);
+
+  display = gdk_window_get_display (window);
+  _gdk_display_set_events_paused (display, FALSE);
+}
+
 /**
  * gdk_window_set_paint_clock:
  * @window: window to set paint clock on
@@ -11479,16 +11514,30 @@ gdk_window_set_paint_clock (GdkWindow     *window,
     {
       g_object_ref (clock);
       g_signal_connect (G_OBJECT (clock),
+                        "flush-events",
+                        G_CALLBACK (gdk_window_flush_events),
+                        window);
+      g_signal_connect (G_OBJECT (clock),
                         "paint",
                         G_CALLBACK (gdk_window_paint_on_clock),
                         window);
+      g_signal_connect (G_OBJECT (clock),
+                        "resume-events",
+                        G_CALLBACK (gdk_window_resume_events),
+                        window);
     }
 
   if (window->paint_clock)
     {
       g_signal_handlers_disconnect_by_func (G_OBJECT (window->paint_clock),
+                                            G_CALLBACK (gdk_window_flush_events),
+                                            window);
+      g_signal_handlers_disconnect_by_func (G_OBJECT (window->paint_clock),
                                             G_CALLBACK (gdk_window_paint_on_clock),
                                             window);
+      g_signal_handlers_disconnect_by_func (G_OBJECT (window->paint_clock),
+                                            G_CALLBACK (gdk_window_resume_events),
+                                            window);
       g_object_unref (window->paint_clock);
     }
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0626078..e5b25cd 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -26,6 +26,7 @@ endif
 
 noinst_PROGRAMS =  $(TEST_PROGS)	\
 	animated-resizing		\
+	motion-compression		\
 	simple				\
 	flicker				\
 	print-editor			\
@@ -151,6 +152,7 @@ endif
 
 animated_resizing_DEPENDENCIES = $(TEST_DEPS)
 flicker_DEPENDENCIES = $(TEST_DEPS)
+motion_compression_DEPENDENCIES = $(TEST_DEPS)
 simple_DEPENDENCIES = $(TEST_DEPS)
 print_editor_DEPENDENCIES = $(TEST_DEPS)
 testheightforwidth_DEPENDENCIES = $(TEST_DEPS)
diff --git a/tests/motion-compression.c b/tests/motion-compression.c
new file mode 100644
index 0000000..c7effad
--- /dev/null
+++ b/tests/motion-compression.c
@@ -0,0 +1,72 @@
+#include <gtk/gtk.h>
+#include <math.h>
+
+GtkAdjustment *adjustment;
+int cursor_x, cursor_y;
+
+static void
+on_motion_notify (GtkWidget      *window,
+                  GdkEventMotion *event)
+{
+  if (event->window == gtk_widget_get_window (window))
+    {
+      float processing_ms = gtk_adjustment_get_value (adjustment);
+      g_usleep (processing_ms * 1000);
+      cursor_x = event->x;
+      cursor_y = event->y;
+      gtk_widget_queue_draw (window);
+    }
+}
+
+static void
+on_draw (GtkWidget *window,
+         cairo_t   *cr)
+{
+  cairo_set_source_rgb (cr, 1, 1, 1);
+  cairo_paint (cr);
+
+  cairo_set_source_rgb (cr, 0, 0.5, 0.5);
+
+  cairo_arc (cr, cursor_x, cursor_y, 10, 0, 2 * M_PI);
+  cairo_stroke (cr);
+}
+
+int
+main (int argc, char **argv)
+{
+  GtkWidget *window;
+  GtkWidget *vbox;
+  GtkWidget *label;
+  GtkWidget *scale;
+
+  gtk_init (&argc, &argv);
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_default_size (GTK_WINDOW (window), 300, 300);
+  gtk_widget_set_app_paintable (window, TRUE);
+  gtk_widget_add_events (window, GDK_POINTER_MOTION_MASK);
+  gtk_widget_set_app_paintable (window, TRUE);
+
+  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+
+  adjustment = gtk_adjustment_new (20, 0, 200, 1, 10, 0);
+  scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adjustment);
+  gtk_box_pack_end (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+
+  label = gtk_label_new ("Event processing time (ms):");
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+  gtk_box_pack_end (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+  g_signal_connect (window, "motion-notify-event",
+                    G_CALLBACK (on_motion_notify), NULL);
+  g_signal_connect (window, "draw",
+                    G_CALLBACK (on_draw), NULL);
+  g_signal_connect (window, "destroy",
+                    G_CALLBACK (gtk_main_quit), NULL);
+
+  gtk_widget_show_all (window);
+  gtk_main ();
+
+  return 0;
+}



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