summaryrefslogtreecommitdiffstats
path: root/Stop-confusing-GDK-s-grab-tracking.patch
blob: abcca67dee4e17e17cd0baa63967c030cb5746f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
From c81be0bc98ff79fcdba7d60276ae63048e53b7c8 Mon Sep 17 00:00:00 2001
From: Owen W. Taylor <otaylor@fishsoup.net>
Date: Wed, 9 Jun 2010 19:38:35 -0400
Subject: [PATCH] Stop confusing GDK's grab tracking

With client side windows, mixing GDK event delivery with explicit calls
to XUngrabPointer() can result in GDK losing button release events
it expects to get. This means that GDK thinks there is an implicit
grab in effect when there is none and send events to the wrong window.

Avoid this by bypassing GDK's event handling for most mouse events.
We do a simplified conversion of the X event into a GdkEvent and send
it to directly to libgtk for delivery.

We make an exception when a GDK grab is already in effect - this is
needed for the correct operation of menus.

http://bugzilla.gnome.org/show_bug.cgi?id=599181
---
 src/core/display-private.h |    7 ++
 src/core/display.c         |  131 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 138 insertions(+), 0 deletions(-)

diff --git a/src/core/display-private.h b/src/core/display-private.h
index 19287f3..8bc00fb 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -223,6 +223,13 @@ struct _MetaDisplay
   /* Closing down the display */
   int closing;
 
+  /* To detect double clicks */
+  guint button_click_number;
+  Window button_click_window;
+  int button_click_x;
+  int button_click_y;
+  guint32 button_click_time;
+
   /* Managed by group.c */
   GHashTable *groups_by_leader;
 
diff --git a/src/core/display.c b/src/core/display.c
index 4c7b4c0..bdd3016 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -77,6 +77,7 @@
 #include <X11/extensions/Xfixes.h>
 #endif
 #include <string.h>
+#include <gtk/gtk.h>
 
 #define GRAB_OP_IS_WINDOW_SWITCH(g)                     \
         (g == META_GRAB_OP_KEYBOARD_TABBING_NORMAL  ||  \
@@ -1396,6 +1397,133 @@ handle_net_restack_window (MetaDisplay* display,
 }
 #endif
 
+/* We do some of our event handling in core/frames.c, which expects
+ * GDK events delivered by GTK+.  However, since the transition to
+ * client side windows, we can't let GDK see button events, since the
+ * client-side tracking of implicit and explicit grabs it does will
+ * get confused by our direct use of X grabs.
+ *
+ * So we do a very minimal GDK => GTK event conversion here and send on the
+ * events we care about, and then filter them out so they don't go
+ * through the normal GDK event handling.
+ *
+ * To reduce the amount of code, the only events fields filled out
+ * below are the ones that frames.c uses. If frames.c is modified to
+ * use more fields, more fields need to be filled out below.
+ */
+
+static gboolean
+maybe_send_event_to_gtk (MetaDisplay *display,
+                         XEvent      *xevent)
+{
+  /* We're always using the default display */
+  GdkDisplay *gdk_display = gdk_display_get_default ();
+  GdkEvent gdk_event;
+  GdkWindow *gdk_window;
+  Window window;
+
+  switch (xevent->type)
+    {
+    case ButtonPress:
+    case ButtonRelease:
+      window = xevent->xbutton.window;
+      break;
+    case MotionNotify:
+      window = xevent->xmotion.window;
+      break;
+    case EnterNotify:
+    case LeaveNotify:
+      window = xevent->xcrossing.window;
+      break;
+    default:
+      return FALSE;
+    }
+
+  gdk_window = gdk_window_lookup_for_display (gdk_display, window);
+  if (gdk_window == NULL)
+    return FALSE;
+
+  /* If GDK already things it has a grab, we better let it see events; this
+   * is the menu-navigation case and events need to get sent to the appropriate
+   * (client-side) subwindow for individual menu items.
+   */
+  if (gdk_display_pointer_is_grabbed (gdk_display))
+    return FALSE;
+
+  memset (&gdk_event, 0, sizeof (gdk_event));
+
+  switch (xevent->type)
+    {
+    case ButtonPress:
+    case ButtonRelease:
+      if (xevent->type == ButtonPress)
+        {
+          GtkSettings *settings = gtk_settings_get_default ();
+          int double_click_time;
+          int double_click_distance;
+
+          g_object_get (settings,
+                        "gtk-double-click-time", &double_click_time,
+                        "gtk-double-click-distance", &double_click_distance,
+                        NULL);
+
+          if (xevent->xbutton.button == display->button_click_number &&
+              xevent->xbutton.window == display->button_click_window &&
+              xevent->xbutton.time < display->button_click_time + double_click_time &&
+              ABS (xevent->xbutton.x - display->button_click_x) <= double_click_distance &&
+              ABS (xevent->xbutton.y - display->button_click_y) <= double_click_distance)
+            {
+              gdk_event.button.type = GDK_2BUTTON_PRESS;
+
+              display->button_click_number = 0;
+            }
+          else
+            {
+              gdk_event.button.type = GDK_BUTTON_PRESS;
+              display->button_click_number = xevent->xbutton.button;
+              display->button_click_window = xevent->xbutton.window;
+              display->button_click_time = xevent->xbutton.time;
+              display->button_click_x = xevent->xbutton.x;
+              display->button_click_y = xevent->xbutton.y;
+            }
+        }
+      else
+        {
+          gdk_event.button.type = GDK_BUTTON_RELEASE;
+        }
+
+      gdk_event.button.window = gdk_window;
+      gdk_event.button.button = xevent->xbutton.button;
+      gdk_event.button.time = xevent->xbutton.time;
+      gdk_event.button.x = xevent->xbutton.x;
+      gdk_event.button.y = xevent->xbutton.y;
+      gdk_event.button.x_root = xevent->xbutton.x_root;
+      gdk_event.button.y_root = xevent->xbutton.y_root;
+
+      break;
+    case MotionNotify:
+      gdk_event.motion.type = GDK_MOTION_NOTIFY;
+      gdk_event.motion.window = gdk_window;
+      break;
+    case EnterNotify:
+    case LeaveNotify:
+      gdk_event.crossing.type = xevent->type == EnterNotify ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
+      gdk_event.crossing.window = gdk_window;
+      gdk_event.crossing.x = xevent->xcrossing.x;
+      gdk_event.crossing.y = xevent->xcrossing.y;
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  /* If we've gotten here, we've filled in the gdk_event and should send it on */
+
+  gtk_main_do_event (&gdk_event);
+
+  return TRUE;
+}
+
 /**
  * This is the most important function in the whole program. It is the heart,
  * it is the nexus, it is the Grand Central Station of Metacity's world.
@@ -2403,6 +2531,9 @@ event_callback (XEvent   *event,
 				     event,
 				     window);
     }
+
+  if (maybe_send_event_to_gtk (display, event))
+    filter_out_event = TRUE;
   
   display->current_time = CurrentTime;
   return filter_out_event;
-- 
1.7.0.1