Blame SOURCES/Stop-confusing-GDK-s-grab-tracking.patch

8063f1
From ab0428a82f8233829c36e2a3ac0ed0848571c59d Mon Sep 17 00:00:00 2001
8063f1
From: "Owen W. Taylor" <otaylor@fishsoup.net>
8063f1
Date: Wed, 9 Jun 2010 19:38:35 -0400
8063f1
Subject: [PATCH] Stop confusing GDK's grab tracking
8063f1
8063f1
With client side windows, mixing GDK event delivery with explicit calls
8063f1
to XUngrabPointer() can result in GDK losing button release events
8063f1
it expects to get. This means that GDK thinks there is an implicit
8063f1
grab in effect when there is none and send events to the wrong window.
8063f1
8063f1
Avoid this by bypassing GDK's event handling for most mouse events.
8063f1
We do a simplified conversion of the X event into a GdkEvent and send
8063f1
it to directly to libgtk for delivery.
8063f1
8063f1
We make an exception when a GDK grab is already in effect - this is
8063f1
needed for the correct operation of menus.
8063f1
8063f1
http://bugzilla.gnome.org/show_bug.cgi?id=599181
8063f1
---
8063f1
 src/core/display-private.h |    7 ++
8063f1
 src/core/display.c         |  131 ++++++++++++++++++++++++++++++++++++++++++++
8063f1
 2 files changed, 138 insertions(+), 0 deletions(-)
8063f1
8063f1
diff --git a/src/core/display-private.h b/src/core/display-private.h
8063f1
index fee321c..7f779fd 100644
8063f1
--- a/src/core/display-private.h
8063f1
+++ b/src/core/display-private.h
8063f1
@@ -232,6 +232,13 @@ struct _MetaDisplay
8063f1
   /* Closing down the display */
8063f1
   int closing;
8063f1
 
8063f1
+  /* To detect double clicks */
8063f1
+  guint button_click_number;
8063f1
+  Window button_click_window;
8063f1
+  int button_click_x;
8063f1
+  int button_click_y;
8063f1
+  guint32 button_click_time;
8063f1
+
8063f1
   /* Managed by group.c */
8063f1
   GHashTable *groups_by_leader;
8063f1
 
8063f1
diff --git a/src/core/display.c b/src/core/display.c
8063f1
index 5bcf025..0c5f61d 100644
8063f1
--- a/src/core/display.c
8063f1
+++ b/src/core/display.c
8063f1
@@ -77,6 +77,7 @@
8063f1
 #include <X11/extensions/Xfixes.h>
8063f1
 #endif
8063f1
 #include <string.h>
8063f1
+#include <gtk/gtk.h>
8063f1
 
8063f1
 #define GRAB_OP_IS_WINDOW_SWITCH(g)                     \
8063f1
         (g == META_GRAB_OP_KEYBOARD_TABBING_NORMAL  ||  \
8063f1
@@ -1362,6 +1363,133 @@ meta_display_queue_autoraise_callback (MetaDisplay *display,
8063f1
   display->autoraise_window = window;
8063f1
 }
8063f1
 
8063f1
+/* We do some of our event handling in core/frames.c, which expects
8063f1
+ * GDK events delivered by GTK+.  However, since the transition to
8063f1
+ * client side windows, we can't let GDK see button events, since the
8063f1
+ * client-side tracking of implicit and explicit grabs it does will
8063f1
+ * get confused by our direct use of X grabs.
8063f1
+ *
8063f1
+ * So we do a very minimal GDK => GTK event conversion here and send on the
8063f1
+ * events we care about, and then filter them out so they don't go
8063f1
+ * through the normal GDK event handling.
8063f1
+ *
8063f1
+ * To reduce the amount of code, the only events fields filled out
8063f1
+ * below are the ones that frames.c uses. If frames.c is modified to
8063f1
+ * use more fields, more fields need to be filled out below.
8063f1
+ */
8063f1
+
8063f1
+static gboolean
8063f1
+maybe_send_event_to_gtk (MetaDisplay *display,
8063f1
+                         XEvent      *xevent)
8063f1
+{
8063f1
+  /* We're always using the default display */
8063f1
+  GdkDisplay *gdk_display = gdk_display_get_default ();
8063f1
+  GdkEvent gdk_event;
8063f1
+  GdkWindow *gdk_window;
8063f1
+  Window window;
8063f1
+
8063f1
+  switch (xevent->type)
8063f1
+    {
8063f1
+    case ButtonPress:
8063f1
+    case ButtonRelease:
8063f1
+      window = xevent->xbutton.window;
8063f1
+      break;
8063f1
+    case MotionNotify:
8063f1
+      window = xevent->xmotion.window;
8063f1
+      break;
8063f1
+    case EnterNotify:
8063f1
+    case LeaveNotify:
8063f1
+      window = xevent->xcrossing.window;
8063f1
+      break;
8063f1
+    default:
8063f1
+      return FALSE;
8063f1
+    }
8063f1
+
8063f1
+  gdk_window = gdk_window_lookup_for_display (gdk_display, window);
8063f1
+  if (gdk_window == NULL)
8063f1
+    return FALSE;
8063f1
+
8063f1
+  /* If GDK already things it has a grab, we better let it see events; this
8063f1
+   * is the menu-navigation case and events need to get sent to the appropriate
8063f1
+   * (client-side) subwindow for individual menu items.
8063f1
+   */
8063f1
+  if (gdk_display_pointer_is_grabbed (gdk_display))
8063f1
+    return FALSE;
8063f1
+
8063f1
+  memset (&gdk_event, 0, sizeof (gdk_event));
8063f1
+
8063f1
+  switch (xevent->type)
8063f1
+    {
8063f1
+    case ButtonPress:
8063f1
+    case ButtonRelease:
8063f1
+      if (xevent->type == ButtonPress)
8063f1
+        {
8063f1
+          GtkSettings *settings = gtk_settings_get_default ();
8063f1
+          int double_click_time;
8063f1
+          int double_click_distance;
8063f1
+
8063f1
+          g_object_get (settings,
8063f1
+                        "gtk-double-click-time", &double_click_time,
8063f1
+                        "gtk-double-click-distance", &double_click_distance,
8063f1
+                        NULL);
8063f1
+
8063f1
+          if (xevent->xbutton.button == display->button_click_number &&
8063f1
+              xevent->xbutton.window == display->button_click_window &&
8063f1
+              xevent->xbutton.time < display->button_click_time + double_click_time &&
8063f1
+              ABS (xevent->xbutton.x - display->button_click_x) <= double_click_distance &&
8063f1
+              ABS (xevent->xbutton.y - display->button_click_y) <= double_click_distance)
8063f1
+            {
8063f1
+              gdk_event.button.type = GDK_2BUTTON_PRESS;
8063f1
+
8063f1
+              display->button_click_number = 0;
8063f1
+            }
8063f1
+          else
8063f1
+            {
8063f1
+              gdk_event.button.type = GDK_BUTTON_PRESS;
8063f1
+              display->button_click_number = xevent->xbutton.button;
8063f1
+              display->button_click_window = xevent->xbutton.window;
8063f1
+              display->button_click_time = xevent->xbutton.time;
8063f1
+              display->button_click_x = xevent->xbutton.x;
8063f1
+              display->button_click_y = xevent->xbutton.y;
8063f1
+            }
8063f1
+        }
8063f1
+      else
8063f1
+        {
8063f1
+          gdk_event.button.type = GDK_BUTTON_RELEASE;
8063f1
+        }
8063f1
+
8063f1
+      gdk_event.button.window = gdk_window;
8063f1
+      gdk_event.button.button = xevent->xbutton.button;
8063f1
+      gdk_event.button.time = xevent->xbutton.time;
8063f1
+      gdk_event.button.x = xevent->xbutton.x;
8063f1
+      gdk_event.button.y = xevent->xbutton.y;
8063f1
+      gdk_event.button.x_root = xevent->xbutton.x_root;
8063f1
+      gdk_event.button.y_root = xevent->xbutton.y_root;
8063f1
+
8063f1
+      break;
8063f1
+    case MotionNotify:
8063f1
+      gdk_event.motion.type = GDK_MOTION_NOTIFY;
8063f1
+      gdk_event.motion.window = gdk_window;
8063f1
+      break;
8063f1
+    case EnterNotify:
8063f1
+    case LeaveNotify:
8063f1
+      gdk_event.crossing.type = xevent->type == EnterNotify ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
8063f1
+      gdk_event.crossing.window = gdk_window;
8063f1
+      gdk_event.crossing.x = xevent->xcrossing.x;
8063f1
+      gdk_event.crossing.y = xevent->xcrossing.y;
8063f1
+      break;
8063f1
+    default:
8063f1
+      g_assert_not_reached ();
8063f1
+      break;
8063f1
+    }
8063f1
+
8063f1
+  /* If we've gotten here, we've filled in the gdk_event and should send it on */
8063f1
+
8063f1
+  gtk_main_do_event (&gdk_event);
8063f1
+
8063f1
+  return TRUE;
8063f1
+}
8063f1
+
8063f1
 /**
8063f1
  * This is the most important function in the whole program. It is the heart,
8063f1
  * it is the nexus, it is the Grand Central Station of Metacity's world.
8063f1
@@ -2387,6 +2515,9 @@ event_callback (XEvent   *event,
8063f1
 				     event,
8063f1
 				     window);
8063f1
     }
8063f1
+
8063f1
+  if (maybe_send_event_to_gtk (display, event))
8063f1
+    filter_out_event = TRUE;
8063f1
   
8063f1
   display->current_time = CurrentTime;
8063f1
   return filter_out_event;
8063f1
-- 
8063f1
1.7.9
8063f1