1 /* GDK - The GIMP Drawing Kit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20 #include "config.h"
21 #include <string.h>
22 #include "gdkinputprivate.h"
23 #include "gdkdisplay-x11.h"
24 #include "gdkalias.h"
25
26 /*
27 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
28 * file for a list of people on the GTK+ Team. See the ChangeLog
29 * files for a list of changes. These files are distributed with
30 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
31 */
32
33 /* forward declarations */
34
35 static void gdk_input_check_proximity (GdkDisplay *display);
36
37 void
_gdk_input_init(GdkDisplay * display)38 _gdk_input_init(GdkDisplay *display)
39 {
40 _gdk_init_input_core (display);
41 display->ignore_core_events = FALSE;
42 _gdk_input_common_init (display, FALSE);
43 }
44
45 gboolean
gdk_device_set_mode(GdkDevice * device,GdkInputMode mode)46 gdk_device_set_mode (GdkDevice *device,
47 GdkInputMode mode)
48 {
49 GList *tmp_list;
50 GdkDevicePrivate *gdkdev;
51 GdkInputWindow *input_window;
52 GdkDisplayX11 *display_impl;
53
54 if (GDK_IS_CORE (device))
55 return FALSE;
56
57 gdkdev = (GdkDevicePrivate *)device;
58
59 if (device->mode == mode)
60 return TRUE;
61
62 device->mode = mode;
63
64 if (mode == GDK_MODE_WINDOW)
65 device->has_cursor = FALSE;
66 else if (mode == GDK_MODE_SCREEN)
67 device->has_cursor = TRUE;
68
69 display_impl = GDK_DISPLAY_X11 (gdkdev->display);
70 for (tmp_list = display_impl->input_windows; tmp_list; tmp_list = tmp_list->next)
71 {
72 input_window = (GdkInputWindow *)tmp_list->data;
73 _gdk_input_select_events (input_window->impl_window, gdkdev);
74 }
75
76 return TRUE;
77 }
78
79 static int
ignore_errors(Display * display,XErrorEvent * event)80 ignore_errors (Display *display, XErrorEvent *event)
81 {
82 return True;
83 }
84
85 static void
gdk_input_check_proximity(GdkDisplay * display)86 gdk_input_check_proximity (GdkDisplay *display)
87 {
88 GdkDisplayX11 *display_impl = GDK_DISPLAY_X11 (display);
89 GList *tmp_list = display_impl->input_devices;
90 gint new_proximity = 0;
91
92 while (tmp_list && !new_proximity)
93 {
94 GdkDevicePrivate *gdkdev = (GdkDevicePrivate *)(tmp_list->data);
95
96 if (gdkdev->info.mode != GDK_MODE_DISABLED
97 && !GDK_IS_CORE (gdkdev)
98 && gdkdev->xdevice)
99 {
100 int (*old_handler) (Display *, XErrorEvent *);
101 XDeviceState *state = NULL;
102 XInputClass *xic;
103 int i;
104
105 /* From X11 doc: "XQueryDeviceState can generate a BadDevice error."
106 * This would occur in particular when a device is unplugged,
107 * which would cause the program to crash (see bug 575767).
108 *
109 * To handle this case gracefully, we simply ignore the device.
110 * GTK+ 3 handles this better with XInput 2's hotplugging support;
111 * but this is better than a crash in GTK+ 2.
112 */
113 old_handler = XSetErrorHandler (ignore_errors);
114 state = XQueryDeviceState(display_impl->xdisplay, gdkdev->xdevice);
115 XSetErrorHandler (old_handler);
116
117 if (! state)
118 {
119 /* Broken device. It may have been disconnected.
120 * Ignore it.
121 */
122 tmp_list = tmp_list->next;
123 continue;
124 }
125
126 xic = state->data;
127 for (i=0; i<state->num_classes; i++)
128 {
129 if (xic->class == ValuatorClass)
130 {
131 XValuatorState *xvs = (XValuatorState *)xic;
132 if ((xvs->mode & ProximityState) == InProximity)
133 {
134 new_proximity = TRUE;
135 }
136 break;
137 }
138 xic = (XInputClass *)((char *)xic + xic->length);
139 }
140
141 XFreeDeviceState (state);
142 }
143 tmp_list = tmp_list->next;
144 }
145
146 display->ignore_core_events = new_proximity;
147 }
148
149 void
_gdk_input_configure_event(XConfigureEvent * xevent,GdkWindow * window)150 _gdk_input_configure_event (XConfigureEvent *xevent,
151 GdkWindow *window)
152 {
153 GdkWindowObject *priv = (GdkWindowObject *)window;
154 GdkInputWindow *input_window;
155 gint root_x, root_y;
156
157 input_window = priv->input_window;
158 if (input_window != NULL)
159 {
160 _gdk_input_get_root_relative_geometry (window, &root_x, &root_y);
161 input_window->root_x = root_x;
162 input_window->root_y = root_y;
163 }
164 }
165
166 void
_gdk_input_crossing_event(GdkWindow * window,gboolean enter)167 _gdk_input_crossing_event (GdkWindow *window,
168 gboolean enter)
169 {
170 GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
171 GdkDisplayX11 *display_impl = GDK_DISPLAY_X11 (display);
172 GdkWindowObject *priv = (GdkWindowObject *)window;
173 GdkInputWindow *input_window;
174 gint root_x, root_y;
175
176 if (enter)
177 {
178 gdk_input_check_proximity(display);
179
180 input_window = priv->input_window;
181 if (input_window != NULL)
182 {
183 _gdk_input_get_root_relative_geometry (window, &root_x, &root_y);
184 input_window->root_x = root_x;
185 input_window->root_y = root_y;
186 }
187 }
188 else
189 display->ignore_core_events = FALSE;
190 }
191
192 static GdkEventType
get_input_event_type(GdkDevicePrivate * gdkdev,XEvent * xevent,int * core_x,int * core_y)193 get_input_event_type (GdkDevicePrivate *gdkdev,
194 XEvent *xevent,
195 int *core_x, int *core_y)
196 {
197 if (xevent->type == gdkdev->buttonpress_type)
198 {
199 XDeviceButtonEvent *xie = (XDeviceButtonEvent *)(xevent);
200 *core_x = xie->x;
201 *core_y = xie->y;
202 return GDK_BUTTON_PRESS;
203 }
204
205 if (xevent->type == gdkdev->buttonrelease_type)
206 {
207 XDeviceButtonEvent *xie = (XDeviceButtonEvent *)(xevent);
208 *core_x = xie->x;
209 *core_y = xie->y;
210 return GDK_BUTTON_RELEASE;
211 }
212
213 if (xevent->type == gdkdev->keypress_type)
214 {
215 XDeviceKeyEvent *xie = (XDeviceKeyEvent *)(xevent);
216 *core_x = xie->x;
217 *core_y = xie->y;
218 return GDK_KEY_PRESS;
219 }
220
221 if (xevent->type == gdkdev->keyrelease_type)
222 {
223 XDeviceKeyEvent *xie = (XDeviceKeyEvent *)(xevent);
224 *core_x = xie->x;
225 *core_y = xie->y;
226 return GDK_KEY_RELEASE;
227 }
228
229 if (xevent->type == gdkdev->motionnotify_type)
230 {
231 XDeviceMotionEvent *xie = (XDeviceMotionEvent *)(xevent);
232 *core_x = xie->x;
233 *core_y = xie->y;
234 return GDK_MOTION_NOTIFY;
235 }
236
237 if (xevent->type == gdkdev->proximityin_type)
238 {
239 XProximityNotifyEvent *xie = (XProximityNotifyEvent *)(xevent);
240 *core_x = xie->x;
241 *core_y = xie->y;
242 return GDK_PROXIMITY_IN;
243 }
244
245 if (xevent->type == gdkdev->proximityout_type)
246 {
247 XProximityNotifyEvent *xie = (XProximityNotifyEvent *)(xevent);
248 *core_x = xie->x;
249 *core_y = xie->y;
250 return GDK_PROXIMITY_OUT;
251 }
252
253 *core_x = 0;
254 *core_y = 0;
255 return GDK_NOTHING;
256 }
257
258
259 gboolean
_gdk_input_other_event(GdkEvent * event,XEvent * xevent,GdkWindow * event_window)260 _gdk_input_other_event (GdkEvent *event,
261 XEvent *xevent,
262 GdkWindow *event_window)
263 {
264 GdkWindow *window;
265 GdkWindowObject *priv;
266 GdkInputWindow *iw;
267 GdkDevicePrivate *gdkdev;
268 GdkEventType event_type;
269 int x, y;
270 GdkDisplay *display = GDK_WINDOW_DISPLAY (event_window);
271 GdkDisplayX11 *display_impl = GDK_DISPLAY_X11 (display);
272
273 /* This is a sort of a hack, as there isn't any XDeviceAnyEvent -
274 but it's potentially faster than scanning through the types of
275 every device. If we were deceived, then it won't match any of
276 the types for the device anyways */
277 gdkdev = _gdk_input_find_device (display,
278 ((XDeviceButtonEvent *)xevent)->deviceid);
279 if (!gdkdev)
280 return FALSE; /* we don't handle it - not an XInput event */
281
282 event_type = get_input_event_type (gdkdev, xevent, &x, &y);
283 if (event_type == GDK_NOTHING)
284 return FALSE;
285
286 /* If we're not getting any event window its likely because we're outside the
287 window and there is no grab. We should still report according to the
288 implicit grab though. */
289 iw = ((GdkWindowObject *)event_window)->input_window;
290
291 if (iw->button_down_window)
292 window = iw->button_down_window;
293 else
294 window = _gdk_window_get_input_window_for_event (event_window,
295 event_type,
296 /* TODO: Seems wrong, but the code used to ignore button motion handling here... */
297 0,
298 x, y,
299 xevent->xany.serial);
300 priv = (GdkWindowObject *)window;
301 if (window == NULL)
302 return FALSE;
303
304 if (gdkdev->info.mode == GDK_MODE_DISABLED ||
305 priv->extension_events == 0 ||
306 !(gdkdev->info.has_cursor || (priv->extension_events & GDK_ALL_DEVICES_MASK)))
307 return FALSE;
308
309 if (!display->ignore_core_events && priv->extension_events != 0)
310 gdk_input_check_proximity (GDK_WINDOW_DISPLAY (window));
311
312 if (!_gdk_input_common_other_event (event, xevent, window, gdkdev))
313 return FALSE;
314
315 if (event->type == GDK_BUTTON_PRESS)
316 iw->button_down_window = window;
317 if (event->type == GDK_BUTTON_RELEASE && !gdkdev->button_count)
318 iw->button_down_window = NULL;
319
320 if (event->type == GDK_PROXIMITY_OUT &&
321 display->ignore_core_events)
322 gdk_input_check_proximity (GDK_WINDOW_DISPLAY (window));
323
324 return _gdk_input_common_event_selected(event, window, gdkdev);
325 }
326
327 gint
_gdk_input_grab_pointer(GdkWindow * window,GdkWindow * native_window,gint owner_events,GdkEventMask event_mask,GdkWindow * confine_to,guint32 time)328 _gdk_input_grab_pointer (GdkWindow *window,
329 GdkWindow *native_window, /* This is the toplevel */
330 gint owner_events,
331 GdkEventMask event_mask,
332 GdkWindow * confine_to,
333 guint32 time)
334 {
335 GdkInputWindow *input_window;
336 GdkWindowObject *priv, *impl_window;
337 gboolean need_ungrab;
338 GdkDevicePrivate *gdkdev;
339 GList *tmp_list;
340 XEventClass event_classes[GDK_MAX_DEVICE_CLASSES];
341 gint num_classes;
342 gint result;
343 GdkDisplayX11 *display_impl = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
344
345 tmp_list = display_impl->input_windows;
346 need_ungrab = FALSE;
347
348 while (tmp_list)
349 {
350 input_window = (GdkInputWindow *)tmp_list->data;
351
352 if (input_window->grabbed)
353 {
354 input_window->grabbed = FALSE;
355 need_ungrab = TRUE;
356 break;
357 }
358 tmp_list = tmp_list->next;
359 }
360
361 priv = (GdkWindowObject *)window;
362 impl_window = (GdkWindowObject *)_gdk_window_get_impl_window (window);
363 input_window = impl_window->input_window;
364 if (priv->extension_events)
365 {
366 g_assert (input_window != NULL);
367 input_window->grabbed = TRUE;
368
369 tmp_list = display_impl->input_devices;
370 while (tmp_list)
371 {
372 gdkdev = (GdkDevicePrivate *)tmp_list->data;
373 if (!GDK_IS_CORE (gdkdev) && gdkdev->xdevice)
374 {
375 _gdk_input_common_find_events (gdkdev, event_mask,
376 event_classes, &num_classes);
377 #ifdef G_ENABLE_DEBUG
378 if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
379 result = GrabSuccess;
380 else
381 #endif
382 result = XGrabDevice (display_impl->xdisplay, gdkdev->xdevice,
383 GDK_WINDOW_XWINDOW (native_window),
384 owner_events, num_classes, event_classes,
385 GrabModeAsync, GrabModeAsync, time);
386
387 /* FIXME: if failure occurs on something other than the first
388 device, things will be badly inconsistent */
389 if (result != Success)
390 return result;
391 }
392 tmp_list = tmp_list->next;
393 }
394 }
395 else
396 {
397 tmp_list = display_impl->input_devices;
398 while (tmp_list)
399 {
400 gdkdev = (GdkDevicePrivate *)tmp_list->data;
401 if (!GDK_IS_CORE (gdkdev) && gdkdev->xdevice &&
402 ((gdkdev->button_count != 0) || need_ungrab))
403 {
404 XUngrabDevice (display_impl->xdisplay, gdkdev->xdevice, time);
405 memset (gdkdev->button_state, 0, sizeof (gdkdev->button_state));
406 gdkdev->button_count = 0;
407 }
408
409 tmp_list = tmp_list->next;
410 }
411 }
412
413 return Success;
414 }
415
416 void
_gdk_input_ungrab_pointer(GdkDisplay * display,guint32 time)417 _gdk_input_ungrab_pointer (GdkDisplay *display,
418 guint32 time)
419 {
420 GdkInputWindow *input_window = NULL; /* Quiet GCC */
421 GdkDevicePrivate *gdkdev;
422 GList *tmp_list;
423 GdkDisplayX11 *display_impl = GDK_DISPLAY_X11 (display);
424
425 tmp_list = display_impl->input_windows;
426 while (tmp_list)
427 {
428 input_window = (GdkInputWindow *)tmp_list->data;
429 if (input_window->grabbed)
430 break;
431 tmp_list = tmp_list->next;
432 }
433
434 if (tmp_list) /* we found a grabbed window */
435 {
436 input_window->grabbed = FALSE;
437
438 tmp_list = display_impl->input_devices;
439 while (tmp_list)
440 {
441 gdkdev = (GdkDevicePrivate *)tmp_list->data;
442 if (!GDK_IS_CORE (gdkdev) && gdkdev->xdevice)
443 XUngrabDevice( display_impl->xdisplay, gdkdev->xdevice, time);
444
445 tmp_list = tmp_list->next;
446 }
447 }
448 }
449
450 #define __GDK_INPUT_XFREE_C__
451 #include "gdkaliasdef.c"
452