[mutter] events: Sync pending pointer events without a window



commit 4b88c1832a2b8363d593a93e967502e4c864b80e
Author: Olivier Fourdan <ofourdan redhat com>
Date:   Wed Oct 2 16:49:28 2019 +0200

    events: Sync pending pointer events without a window
    
    Mutter issues a synchronous grab on the pointer for unfocused client
    windows to be able to catch the button events first and raise/focus
    client windows accordingly.
    
    When there is a synchronous grab in effect, all events are queued until
    the grabbing client releases the event queue as it processes the events.
    
    Mutter does release the events in its event handler function but does so
    only if it is able to find the window matching the event. If the window
    is a shell widget, that matching may fail and therefore Mutter will not
    release the events, hence causing a freeze in pointer events delivery.
    
    To avoid the issue, make sure we sync the pointer events in case we
    can't find a matching window.
    
    https://gitlab.gnome.org/GNOME/mutter/merge_requests/821

 src/core/events.c | 62 +++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 51 insertions(+), 11 deletions(-)
---
diff --git a/src/core/events.c b/src/core/events.c
index 8a046b398..d38377862 100644
--- a/src/core/events.c
+++ b/src/core/events.c
@@ -50,6 +50,12 @@
 #define IS_KEY_EVENT(e) ((e)->type == CLUTTER_KEY_PRESS || \
                          (e)->type == CLUTTER_KEY_RELEASE)
 
+typedef enum
+{
+  EVENTS_UNFREEZE_SYNC,
+  EVENTS_UNFREEZE_REPLAY,
+} EventsUnfreezeMethod;
+
 static gboolean
 stage_has_key_focus (void)
 {
@@ -169,6 +175,43 @@ sequence_is_pointer_emulated (MetaDisplay        *display,
   return FALSE;
 }
 
+static void
+maybe_unfreeze_pointer_events (MetaBackend          *backend,
+                               const ClutterEvent   *event,
+                               EventsUnfreezeMethod  unfreeze_method)
+{
+  Display *xdisplay;
+  int event_mode;
+  int device_id;
+
+  if (event->type != CLUTTER_BUTTON_PRESS)
+    return;
+
+  if (!META_IS_BACKEND_X11 (backend))
+    return;
+
+  device_id = clutter_event_get_device_id (event);
+  switch (unfreeze_method)
+    {
+    case EVENTS_UNFREEZE_SYNC:
+      event_mode = XISyncDevice;
+      meta_verbose ("Syncing events time %u device %i\n",
+                    (unsigned int) event->button.time, device_id);
+      break;
+    case EVENTS_UNFREEZE_REPLAY:
+      event_mode = XIReplayDevice;
+      meta_verbose ("Replaying events time %u device %i\n",
+                    (unsigned int) event->button.time, device_id);
+      break;
+    default:
+      g_assert_not_reached ();
+      return;
+    }
+
+  xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
+  XIAllowEvents (xdisplay, device_id, event_mode, event->button.time);
+}
+
 static gboolean
 meta_display_handle_event (MetaDisplay        *display,
                            const ClutterEvent *event)
@@ -382,17 +425,7 @@ meta_display_handle_event (MetaDisplay        *display,
         {
           /* Only replay button press events, since that's where we
            * have the synchronous grab. */
-          if (event->type == CLUTTER_BUTTON_PRESS)
-            {
-              if (META_IS_BACKEND_X11 (backend))
-                {
-                  Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
-                  meta_verbose ("Allowing events time %u\n",
-                                (unsigned int)event->button.time);
-                  XIAllowEvents (xdisplay, clutter_event_get_device_id (event),
-                                 XIReplayDevice, event->button.time);
-                }
-            }
+          maybe_unfreeze_pointer_events (backend, event, EVENTS_UNFREEZE_REPLAY);
 
           /* If the focus window has an active close dialog let clutter
            * events go through, so fancy clutter dialogs can get to handle
@@ -408,6 +441,13 @@ meta_display_handle_event (MetaDisplay        *display,
 
       goto out;
     }
+  else
+    {
+      /* We could not match the event with a window, make sure we sync
+       * the pointer to discard the sequence and don't keep events frozen.
+       */
+       maybe_unfreeze_pointer_events (backend, event, EVENTS_UNFREEZE_SYNC);
+    }
 
  out:
   /* If the compositor has a grab, don't pass that through to Wayland */


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