[gtk+] Handle recursion from motion event handlers



commit f50a3af1b7a24836c784797484cae14052cbfcdd
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Mon Nov 11 18:04:34 2013 -0500

    Handle recursion from motion event handlers
    
    If a motion event handler (or other handler running from the flush-events
    phase of the frame clock) recursed the main loop then flushing wouldn't
    complete until after the recursed main loop returned, and various aspects
    of the state would get out of sync.
    
    To fix this, change flushing of the event queue to simply mark events as
    ready to flush, and let normal event delivery handle the rest.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=705176

 gdk/broadway/gdkeventsource.c    |   10 +++-------
 gdk/gdkdisplay.c                 |   24 ++----------------------
 gdk/gdkdisplayprivate.h          |    2 --
 gdk/gdkevents.c                  |   20 ++++++++++++++++----
 gdk/gdkinternals.h               |    9 ++++++++-
 gdk/gdkwindow.c                  |    2 +-
 gdk/quartz/gdkeventloop-quartz.c |    4 ++--
 gdk/wayland/gdkeventsource.c     |    4 ++--
 gdk/win32/gdkevents-win32.c      |    4 ++--
 gdk/x11/gdkeventsource.c         |    4 ++--
 10 files changed, 38 insertions(+), 45 deletions(-)
---
diff --git a/gdk/broadway/gdkeventsource.c b/gdk/broadway/gdkeventsource.c
index e6c0a7f..d7cdede 100644
--- a/gdk/broadway/gdkeventsource.c
+++ b/gdk/broadway/gdkeventsource.c
@@ -62,10 +62,7 @@ gdk_event_source_prepare (GSource *source,
 
   *timeout = -1;
 
-  if (display->event_pause_count > 0)
-    retval = FALSE;
-  else
-    retval = (_gdk_event_queue_find_first (display) != NULL);
+  retval = (_gdk_event_queue_find_first (display) != NULL);
 
   gdk_threads_leave ();
 
@@ -80,9 +77,8 @@ gdk_event_source_check (GSource *source)
 
   gdk_threads_enter ();
 
-  if (event_source->display->event_pause_count > 0)
-    retval = FALSE;
-  else if (event_source->event_poll_fd.revents & G_IO_IN)
+  if (event_source->display->event_pause_count > 0 ||
+      event_source->event_poll_fd.revents & G_IO_IN)
     retval = (_gdk_event_queue_find_first (event_source->display) != NULL);
   else
     retval = FALSE;
diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c
index 1092869..f19319d 100644
--- a/gdk/gdkdisplay.c
+++ b/gdk/gdkdisplay.c
@@ -321,10 +321,8 @@ gdk_display_get_event (GdkDisplay *display)
 {
   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
 
-  if (display->event_pause_count > 0)
-    return NULL;
-
-  GDK_DISPLAY_GET_CLASS (display)->queue_events (display);
+  if (display->event_pause_count == 0)
+    GDK_DISPLAY_GET_CLASS (display)->queue_events (display);
 
   return _gdk_event_unqueue (display);
 }
@@ -2033,24 +2031,6 @@ _gdk_display_unpause_events (GdkDisplay *display)
 }
 
 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 365b429..df79f59 100644
--- a/gdk/gdkdisplayprivate.h
+++ b/gdk/gdkdisplayprivate.h
@@ -116,7 +116,6 @@ struct _GdkDisplay
   guint event_pause_count;       /* How many times events are blocked */
 
   guint closed             : 1;  /* Whether this display has been closed */
-  guint flushing_events    : 1;  /* Inside gdk_display_flush_events */
 
   GArray *touch_implicit_grabs;
   GHashTable *device_grabs;
@@ -300,7 +299,6 @@ void                _gdk_display_pointer_info_foreach (GdkDisplay       *display
 gulong              _gdk_display_get_next_serial      (GdkDisplay       *display);
 void                _gdk_display_pause_events         (GdkDisplay       *display);
 void                _gdk_display_unpause_events       (GdkDisplay       *display);
-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 83c313c..86ea357 100644
--- a/gdk/gdkevents.c
+++ b/gdk/gdkevents.c
@@ -88,20 +88,20 @@ _gdk_event_queue_find_first (GdkDisplay *display)
   GList *tmp_list;
   GList *pending_motion = NULL;
 
-  if (display->event_pause_count > 0)
-    return NULL;
+  gboolean paused = display->event_pause_count > 0;
 
   tmp_list = display->queued_events;
   while (tmp_list)
     {
       GdkEventPrivate *event = tmp_list->data;
 
-      if (!(event->flags & GDK_EVENT_PENDING))
+      if ((event->flags & GDK_EVENT_PENDING) == 0 &&
+         (!paused || (event->flags & GDK_EVENT_FLUSHED) != 0))
         {
           if (pending_motion)
             return pending_motion;
 
-          if (event->event.type == GDK_MOTION_NOTIFY && !display->flushing_events)
+          if (event->event.type == GDK_MOTION_NOTIFY && (event->flags & GDK_EVENT_FLUSHED) == 0)
             pending_motion = tmp_list;
           else
             return tmp_list;
@@ -321,6 +321,18 @@ _gdk_event_queue_handle_motion_compression (GdkDisplay *display)
     }
 }
 
+void
+_gdk_event_queue_flush (GdkDisplay *display)
+{
+  GList *tmp_list;
+
+  for (tmp_list = display->queued_events; tmp_list; tmp_list = tmp_list->next)
+    {
+      GdkEventPrivate *event = tmp_list->data;
+      event->flags |= GDK_EVENT_FLUSHED;
+    }
+}
+
 /**
  * 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 b245666..bebdec0 100644
--- a/gdk/gdkinternals.h
+++ b/gdk/gdkinternals.h
@@ -156,7 +156,13 @@ typedef enum
    * 1) touch events emulating pointer events
    * 2) pointer events being emulated by a touch sequence.
    */
-  GDK_EVENT_POINTER_EMULATED = 1 << 1
+  GDK_EVENT_POINTER_EMULATED = 1 << 1,
+
+  /* When we are ready to draw a frame, we pause event delivery,
+   * mark all events in the queue with this flag, and deliver
+   * only those events until we finish the frame.
+   */
+  GDK_EVENT_FLUSHED = 1 << 2
 } GdkEventFlags;
 
 struct _GdkEventPrivate
@@ -305,6 +311,7 @@ GList* _gdk_event_queue_insert_before(GdkDisplay *display,
                                       GdkEvent   *event);
 
 void    _gdk_event_queue_handle_motion_compression (GdkDisplay *display);
+void    _gdk_event_queue_flush                     (GdkDisplay       *display);
 
 void   _gdk_event_button_generate    (GdkDisplay *display,
                                       GdkEvent   *event);
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index 8c121ed..f6330e2 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -10696,7 +10696,7 @@ gdk_window_flush_events (GdkFrameClock *clock,
   window = GDK_WINDOW (data);
 
   display = gdk_window_get_display (window);
-  _gdk_display_flush_events (display);
+  _gdk_event_queue_flush (display);
   _gdk_display_pause_events (display);
 
   gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS);
diff --git a/gdk/quartz/gdkeventloop-quartz.c b/gdk/quartz/gdkeventloop-quartz.c
index 2c1ca0b..bc1ecbb 100644
--- a/gdk/quartz/gdkeventloop-quartz.c
+++ b/gdk/quartz/gdkeventloop-quartz.c
@@ -647,7 +647,7 @@ gdk_event_prepare (GSource *source,
   *timeout = -1;
 
   if (_gdk_display->event_pause_count > 0)
-    retval = FALSE;
+    retval = _gdk_event_queue_find_first (_gdk_display) != NULL;
   else
     retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
               _gdk_quartz_event_loop_check_pending ());
@@ -665,7 +665,7 @@ gdk_event_check (GSource *source)
   gdk_threads_enter ();
 
   if (_gdk_display->event_pause_count > 0)
-    retval = FALSE;
+    retval = _gdk_event_queue_find_first (_gdk_display) != NULL;
   else
     retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
               _gdk_quartz_event_loop_check_pending ());
diff --git a/gdk/wayland/gdkeventsource.c b/gdk/wayland/gdkeventsource.c
index 9dda53a..11e7b1e 100644
--- a/gdk/wayland/gdkeventsource.c
+++ b/gdk/wayland/gdkeventsource.c
@@ -40,7 +40,7 @@ gdk_event_source_prepare(GSource *base, gint *timeout)
   *timeout = -1;
 
   if (source->display->event_pause_count > 0)
-    return FALSE;
+    return _gdk_event_queue_find_first (source->display) != NULL;
 
   /* We have to add/remove the GPollFD if we want to update our
    * poll event mask dynamically.  Instead, let's just flush all
@@ -64,7 +64,7 @@ gdk_event_source_check(GSource *base)
   GdkWaylandEventSource *source = (GdkWaylandEventSource *) base;
 
   if (source->display->event_pause_count > 0)
-    return FALSE;
+    return _gdk_event_queue_find_first (source->display) != NULL;
 
   return _gdk_event_queue_find_first (source->display) != NULL ||
     source->pfd.revents;
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index e7524d0..2f751ee 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -3328,7 +3328,7 @@ gdk_event_prepare (GSource *source,
   *timeout = -1;
 
   if (_gdk_display->event_pause_count > 0)
-    retval = FALSE;
+    retval =_gdk_event_queue_find_first (_gdk_display) != NULL;
   else
     retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
               (modal_win32_dialog == NULL &&
@@ -3347,7 +3347,7 @@ gdk_event_check (GSource *source)
   gdk_threads_enter ();
 
   if (_gdk_display->event_pause_count > 0)
-    retval = FALSE;
+    retval = gdk_event_queue_find_first (_gdk_display) != NULL;
   else if (event_poll_fd.revents & G_IO_IN)
     retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
               (modal_win32_dialog == NULL &&
diff --git a/gdk/x11/gdkeventsource.c b/gdk/x11/gdkeventsource.c
index 4b1a546..7fff28f 100644
--- a/gdk/x11/gdkeventsource.c
+++ b/gdk/x11/gdkeventsource.c
@@ -278,7 +278,7 @@ gdk_event_source_prepare (GSource *source,
   *timeout = -1;
 
   if (display->event_pause_count > 0)
-    retval = FALSE;
+    retval = _gdk_event_queue_find_first (display) != NULL;
   else
     retval = (_gdk_event_queue_find_first (display) != NULL ||
               gdk_check_xpending (display));
@@ -297,7 +297,7 @@ gdk_event_source_check (GSource *source)
   gdk_threads_enter ();
 
   if (event_source->display->event_pause_count > 0)
-    retval = FALSE;
+    retval = _gdk_event_queue_find_first (event_source->display) != NULL;
   else if (event_source->event_poll_fd.revents & G_IO_IN)
     retval = (_gdk_event_queue_find_first (event_source->display) != NULL ||
               gdk_check_xpending (event_source->display));


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