1 /* GDK - The GIMP Drawing Kit
2 * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
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, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20
21 #include "gdkx11device-core.h"
22 #include "gdkdeviceprivate.h"
23
24 #include "gdkinternals.h"
25 #include "gdkwindow.h"
26 #include "gdkprivate-x11.h"
27 #include "gdkasync.h"
28
29 #include <math.h>
30
31 /* for the use of round() */
32 #include "fallback-c89.c"
33
34 struct _GdkX11DeviceCore
35 {
36 GdkDevice parent_instance;
37 };
38
39 struct _GdkX11DeviceCoreClass
40 {
41 GdkDeviceClass parent_class;
42 };
43
44 static gboolean gdk_x11_device_core_get_history (GdkDevice *device,
45 GdkWindow *window,
46 guint32 start,
47 guint32 stop,
48 GdkTimeCoord ***events,
49 gint *n_events);
50 static void gdk_x11_device_core_get_state (GdkDevice *device,
51 GdkWindow *window,
52 gdouble *axes,
53 GdkModifierType *mask);
54 static void gdk_x11_device_core_set_window_cursor (GdkDevice *device,
55 GdkWindow *window,
56 GdkCursor *cursor);
57 static void gdk_x11_device_core_warp (GdkDevice *device,
58 GdkScreen *screen,
59 gdouble x,
60 gdouble y);
61 static void gdk_x11_device_core_query_state (GdkDevice *device,
62 GdkWindow *window,
63 GdkWindow **root_window,
64 GdkWindow **child_window,
65 gdouble *root_x,
66 gdouble *root_y,
67 gdouble *win_x,
68 gdouble *win_y,
69 GdkModifierType *mask);
70 static GdkGrabStatus gdk_x11_device_core_grab (GdkDevice *device,
71 GdkWindow *window,
72 gboolean owner_events,
73 GdkEventMask event_mask,
74 GdkWindow *confine_to,
75 GdkCursor *cursor,
76 guint32 time_);
77 static void gdk_x11_device_core_ungrab (GdkDevice *device,
78 guint32 time_);
79 static GdkWindow * gdk_x11_device_core_window_at_position (GdkDevice *device,
80 gdouble *win_x,
81 gdouble *win_y,
82 GdkModifierType *mask,
83 gboolean get_toplevel);
84 static void gdk_x11_device_core_select_window_events (GdkDevice *device,
85 GdkWindow *window,
86 GdkEventMask event_mask);
87
G_DEFINE_TYPE(GdkX11DeviceCore,gdk_x11_device_core,GDK_TYPE_DEVICE)88 G_DEFINE_TYPE (GdkX11DeviceCore, gdk_x11_device_core, GDK_TYPE_DEVICE)
89
90 static void
91 gdk_x11_device_core_class_init (GdkX11DeviceCoreClass *klass)
92 {
93 GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
94
95 device_class->get_history = gdk_x11_device_core_get_history;
96 device_class->get_state = gdk_x11_device_core_get_state;
97 device_class->set_window_cursor = gdk_x11_device_core_set_window_cursor;
98 device_class->warp = gdk_x11_device_core_warp;
99 device_class->query_state = gdk_x11_device_core_query_state;
100 device_class->grab = gdk_x11_device_core_grab;
101 device_class->ungrab = gdk_x11_device_core_ungrab;
102 device_class->window_at_position = gdk_x11_device_core_window_at_position;
103 device_class->select_window_events = gdk_x11_device_core_select_window_events;
104 }
105
106 static void
gdk_x11_device_core_init(GdkX11DeviceCore * device_core)107 gdk_x11_device_core_init (GdkX11DeviceCore *device_core)
108 {
109 GdkDevice *device;
110
111 device = GDK_DEVICE (device_core);
112
113 _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_X, 0, 0, 1);
114 _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_Y, 0, 0, 1);
115 }
116
117 static gboolean
impl_coord_in_window(GdkWindow * window,int impl_x,int impl_y)118 impl_coord_in_window (GdkWindow *window,
119 int impl_x,
120 int impl_y)
121 {
122 if (impl_x < window->abs_x ||
123 impl_x >= window->abs_x + window->width)
124 return FALSE;
125
126 if (impl_y < window->abs_y ||
127 impl_y >= window->abs_y + window->height)
128 return FALSE;
129
130 return TRUE;
131 }
132
133 static gboolean
gdk_x11_device_core_get_history(GdkDevice * device,GdkWindow * window,guint32 start,guint32 stop,GdkTimeCoord *** events,gint * n_events)134 gdk_x11_device_core_get_history (GdkDevice *device,
135 GdkWindow *window,
136 guint32 start,
137 guint32 stop,
138 GdkTimeCoord ***events,
139 gint *n_events)
140 {
141 XTimeCoord *xcoords;
142 GdkTimeCoord **coords;
143 GdkWindow *impl_window;
144 GdkWindowImplX11 *impl;
145 int tmp_n_events;
146 int i, j;
147
148 impl_window = _gdk_window_get_impl_window (window);
149 impl = GDK_WINDOW_IMPL_X11 (impl_window->impl);
150 xcoords = XGetMotionEvents (GDK_WINDOW_XDISPLAY (window),
151 GDK_WINDOW_XID (impl_window),
152 start, stop, &tmp_n_events);
153 if (!xcoords)
154 return FALSE;
155
156 coords = _gdk_device_allocate_history (device, tmp_n_events);
157
158 for (i = 0, j = 0; i < tmp_n_events; i++)
159 {
160 if (impl_coord_in_window (window,
161 xcoords[i].x / impl->window_scale,
162 xcoords[i].y / impl->window_scale))
163 {
164 coords[j]->time = xcoords[i].time;
165 coords[j]->axes[0] = (double)xcoords[i].x / impl->window_scale - window->abs_x;
166 coords[j]->axes[1] = (double)xcoords[i].y / impl->window_scale - window->abs_y;
167 j++;
168 }
169 }
170
171 XFree (xcoords);
172
173 /* free the events we allocated too much */
174 for (i = j; i < tmp_n_events; i++)
175 {
176 g_free (coords[i]);
177 coords[i] = NULL;
178 }
179
180 tmp_n_events = j;
181
182 if (tmp_n_events == 0)
183 {
184 gdk_device_free_history (coords, tmp_n_events);
185 return FALSE;
186 }
187
188 if (n_events)
189 *n_events = tmp_n_events;
190
191 if (events)
192 *events = coords;
193 else if (coords)
194 gdk_device_free_history (coords, tmp_n_events);
195
196 return TRUE;
197 }
198
199 static void
gdk_x11_device_core_get_state(GdkDevice * device,GdkWindow * window,gdouble * axes,GdkModifierType * mask)200 gdk_x11_device_core_get_state (GdkDevice *device,
201 GdkWindow *window,
202 gdouble *axes,
203 GdkModifierType *mask)
204 {
205 gdouble x, y;
206
207 gdk_window_get_device_position_double (window, device, &x, &y, mask);
208
209 if (axes)
210 {
211 axes[0] = x;
212 axes[1] = y;
213 }
214 }
215
216 static void
gdk_x11_device_core_set_window_cursor(GdkDevice * device,GdkWindow * window,GdkCursor * cursor)217 gdk_x11_device_core_set_window_cursor (GdkDevice *device,
218 GdkWindow *window,
219 GdkCursor *cursor)
220 {
221 Cursor xcursor;
222
223 if (!cursor)
224 xcursor = None;
225 else
226 xcursor = gdk_x11_cursor_get_xcursor (cursor);
227
228 XDefineCursor (GDK_WINDOW_XDISPLAY (window),
229 GDK_WINDOW_XID (window),
230 xcursor);
231 }
232
233 static void
gdk_x11_device_core_warp(GdkDevice * device,GdkScreen * screen,gdouble x,gdouble y)234 gdk_x11_device_core_warp (GdkDevice *device,
235 GdkScreen *screen,
236 gdouble x,
237 gdouble y)
238 {
239 Display *xdisplay;
240 Window dest;
241
242 xdisplay = GDK_DISPLAY_XDISPLAY (gdk_device_get_display (device));
243 dest = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
244
245 XWarpPointer (xdisplay, None, dest, 0, 0, 0, 0,
246 round (x * GDK_X11_SCREEN (screen)->window_scale),
247 round (y * GDK_X11_SCREEN (screen)->window_scale));
248 }
249
250 static void
gdk_x11_device_core_query_state(GdkDevice * device,GdkWindow * window,GdkWindow ** root_window,GdkWindow ** child_window,gdouble * root_x,gdouble * root_y,gdouble * win_x,gdouble * win_y,GdkModifierType * mask)251 gdk_x11_device_core_query_state (GdkDevice *device,
252 GdkWindow *window,
253 GdkWindow **root_window,
254 GdkWindow **child_window,
255 gdouble *root_x,
256 gdouble *root_y,
257 gdouble *win_x,
258 gdouble *win_y,
259 GdkModifierType *mask)
260 {
261 GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (window->impl);
262 GdkDisplay *display;
263 GdkScreen *default_screen;
264 Window xroot_window, xchild_window;
265 int xroot_x, xroot_y, xwin_x, xwin_y;
266 unsigned int xmask;
267
268 display = gdk_window_get_display (window);
269 default_screen = gdk_display_get_default_screen (display);
270
271 if (!GDK_X11_DISPLAY (display)->trusted_client ||
272 !XQueryPointer (GDK_WINDOW_XDISPLAY (window),
273 GDK_WINDOW_XID (window),
274 &xroot_window,
275 &xchild_window,
276 &xroot_x, &xroot_y,
277 &xwin_x, &xwin_y,
278 &xmask))
279 {
280 XSetWindowAttributes attributes;
281 Display *xdisplay;
282 Window xwindow, w;
283
284 /* FIXME: untrusted clients not multidevice-safe */
285 xdisplay = GDK_SCREEN_XDISPLAY (default_screen);
286 xwindow = GDK_SCREEN_XROOTWIN (default_screen);
287
288 w = XCreateWindow (xdisplay, xwindow, 0, 0, 1, 1, 0,
289 CopyFromParent, InputOnly, CopyFromParent,
290 0, &attributes);
291 XQueryPointer (xdisplay, w,
292 &xroot_window,
293 &xchild_window,
294 &xroot_x, &xroot_y,
295 &xwin_x, &xwin_y,
296 &xmask);
297 XDestroyWindow (xdisplay, w);
298 }
299
300 if (root_window)
301 *root_window = gdk_x11_window_lookup_for_display (display, xroot_window);
302
303 if (child_window)
304 *child_window = gdk_x11_window_lookup_for_display (display, xchild_window);
305
306 if (root_x)
307 *root_x = (double)xroot_x / impl->window_scale;
308
309 if (root_y)
310 *root_y = (double)xroot_y / impl->window_scale;
311
312 if (win_x)
313 *win_x = (double)xwin_x / impl->window_scale;
314
315 if (win_y)
316 *win_y = (double)xwin_y / impl->window_scale;
317
318 if (mask)
319 *mask = xmask;
320 }
321
322 static GdkGrabStatus
gdk_x11_device_core_grab(GdkDevice * device,GdkWindow * window,gboolean owner_events,GdkEventMask event_mask,GdkWindow * confine_to,GdkCursor * cursor,guint32 time_)323 gdk_x11_device_core_grab (GdkDevice *device,
324 GdkWindow *window,
325 gboolean owner_events,
326 GdkEventMask event_mask,
327 GdkWindow *confine_to,
328 GdkCursor *cursor,
329 guint32 time_)
330 {
331 GdkDisplay *display;
332 Window xwindow, xconfine_to;
333 gint status;
334
335 display = gdk_device_get_display (device);
336
337 xwindow = GDK_WINDOW_XID (window);
338
339 if (confine_to)
340 confine_to = _gdk_window_get_impl_window (confine_to);
341
342 if (!confine_to || GDK_WINDOW_DESTROYED (confine_to))
343 xconfine_to = None;
344 else
345 xconfine_to = GDK_WINDOW_XID (confine_to);
346
347 #ifdef G_ENABLE_DEBUG
348 if (GDK_DEBUG_CHECK (NOGRABS))
349 status = GrabSuccess;
350 else
351 #endif
352 if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
353 {
354 /* Device is a keyboard */
355 status = XGrabKeyboard (GDK_DISPLAY_XDISPLAY (display),
356 xwindow,
357 owner_events,
358 GrabModeAsync, GrabModeAsync,
359 time_);
360 }
361 else
362 {
363 Cursor xcursor;
364 guint xevent_mask;
365 gint i;
366
367 /* Device is a pointer */
368 if (!cursor)
369 xcursor = None;
370 else
371 {
372 _gdk_x11_cursor_update_theme (cursor);
373 xcursor = gdk_x11_cursor_get_xcursor (cursor);
374 }
375
376 xevent_mask = 0;
377
378 for (i = 0; i < _gdk_x11_event_mask_table_size; i++)
379 {
380 if (event_mask & (1 << (i + 1)))
381 xevent_mask |= _gdk_x11_event_mask_table[i];
382 }
383
384 /* We don't want to set a native motion hint mask, as we're emulating motion
385 * hints. If we set a native one we just wouldn't get any events.
386 */
387 xevent_mask &= ~PointerMotionHintMask;
388
389 status = XGrabPointer (GDK_DISPLAY_XDISPLAY (display),
390 xwindow,
391 owner_events,
392 xevent_mask,
393 GrabModeAsync, GrabModeAsync,
394 xconfine_to,
395 xcursor,
396 time_);
397 }
398
399 _gdk_x11_display_update_grab_info (display, device, status);
400
401 return _gdk_x11_convert_grab_status (status);
402 }
403
404 static void
gdk_x11_device_core_ungrab(GdkDevice * device,guint32 time_)405 gdk_x11_device_core_ungrab (GdkDevice *device,
406 guint32 time_)
407 {
408 GdkDisplay *display;
409 gulong serial;
410
411 display = gdk_device_get_display (device);
412 serial = NextRequest (GDK_DISPLAY_XDISPLAY (display));
413
414 if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
415 XUngrabKeyboard (GDK_DISPLAY_XDISPLAY (display), time_);
416 else
417 XUngrabPointer (GDK_DISPLAY_XDISPLAY (display), time_);
418
419 _gdk_x11_display_update_grab_info_ungrab (display, device, time_, serial);
420 }
421
422 static GdkWindow *
gdk_x11_device_core_window_at_position(GdkDevice * device,gdouble * win_x,gdouble * win_y,GdkModifierType * mask,gboolean get_toplevel)423 gdk_x11_device_core_window_at_position (GdkDevice *device,
424 gdouble *win_x,
425 gdouble *win_y,
426 GdkModifierType *mask,
427 gboolean get_toplevel)
428 {
429 GdkWindowImplX11 *impl;
430 GdkDisplay *display;
431 GdkScreen *screen;
432 Display *xdisplay;
433 GdkWindow *window;
434 Window xwindow, root, child, last;
435 int xroot_x, xroot_y, xwin_x, xwin_y;
436 unsigned int xmask;
437
438 last = None;
439 display = gdk_device_get_display (device);
440 screen = gdk_display_get_default_screen (display);
441
442 /* This function really only works if the mouse pointer is held still
443 * during its operation. If it moves from one leaf window to another
444 * than we'll end up with inaccurate values for win_x, win_y
445 * and the result.
446 */
447 gdk_x11_display_grab (display);
448
449 xdisplay = GDK_SCREEN_XDISPLAY (screen);
450 xwindow = GDK_SCREEN_XROOTWIN (screen);
451
452 if (G_LIKELY (GDK_X11_DISPLAY (display)->trusted_client))
453 {
454 XQueryPointer (xdisplay, xwindow,
455 &root, &child,
456 &xroot_x, &xroot_y,
457 &xwin_x, &xwin_y,
458 &xmask);
459
460 if (root == xwindow)
461 xwindow = child;
462 else
463 xwindow = root;
464 }
465 else
466 {
467 gint width, height;
468 GList *toplevels, *list;
469 Window pointer_window;
470 int rootx = -1, rooty = -1;
471 int winx, winy;
472
473 /* FIXME: untrusted clients case not multidevice-safe */
474 pointer_window = None;
475 screen = gdk_display_get_default_screen (display);
476 toplevels = gdk_screen_get_toplevel_windows (screen);
477 for (list = toplevels; list != NULL; list = list->next)
478 {
479 window = GDK_WINDOW (list->data);
480 impl = GDK_WINDOW_IMPL_X11 (window->impl);
481 xwindow = GDK_WINDOW_XID (window);
482 gdk_x11_display_error_trap_push (display);
483 XQueryPointer (xdisplay, xwindow,
484 &root, &child,
485 &rootx, &rooty,
486 &winx, &winy,
487 &xmask);
488 if (gdk_x11_display_error_trap_pop (display))
489 continue;
490 if (child != None)
491 {
492 pointer_window = child;
493 break;
494 }
495 gdk_window_get_geometry (window, NULL, NULL, &width, &height);
496 if (winx >= 0 && winy >= 0 && winx < width * impl->window_scale && winy < height * impl->window_scale)
497 {
498 /* A childless toplevel, or below another window? */
499 XSetWindowAttributes attributes;
500 Window w;
501
502 w = XCreateWindow (xdisplay, xwindow, winx, winy, 1, 1, 0,
503 CopyFromParent, InputOnly, CopyFromParent,
504 0, &attributes);
505 XMapWindow (xdisplay, w);
506 XQueryPointer (xdisplay, xwindow,
507 &root, &child,
508 &rootx, &rooty,
509 &winx, &winy,
510 &xmask);
511 XDestroyWindow (xdisplay, w);
512 if (child == w)
513 {
514 pointer_window = xwindow;
515 break;
516 }
517 }
518 }
519
520 g_list_free (toplevels);
521
522 xwindow = pointer_window;
523 }
524
525 while (xwindow)
526 {
527 last = xwindow;
528 gdk_x11_display_error_trap_push (display);
529 XQueryPointer (xdisplay, xwindow,
530 &root, &xwindow,
531 &xroot_x, &xroot_y,
532 &xwin_x, &xwin_y,
533 &xmask);
534 if (gdk_x11_display_error_trap_pop (display))
535 break;
536
537 if (get_toplevel && last != root &&
538 (window = gdk_x11_window_lookup_for_display (display, last)) != NULL &&
539 window->window_type != GDK_WINDOW_FOREIGN)
540 {
541 xwindow = last;
542 break;
543 }
544 }
545
546 gdk_x11_display_ungrab (display);
547
548 window = gdk_x11_window_lookup_for_display (display, last);
549 impl = NULL;
550 if (window)
551 impl = GDK_WINDOW_IMPL_X11 (window->impl);
552
553 if (win_x)
554 *win_x = (window) ? (double)xwin_x / impl->window_scale : -1;
555
556 if (win_y)
557 *win_y = (window) ? (double)xwin_y / impl->window_scale : -1;
558
559 if (mask)
560 *mask = xmask;
561
562 return window;
563 }
564
565 static void
gdk_x11_device_core_select_window_events(GdkDevice * device,GdkWindow * window,GdkEventMask event_mask)566 gdk_x11_device_core_select_window_events (GdkDevice *device,
567 GdkWindow *window,
568 GdkEventMask event_mask)
569 {
570 GdkEventMask filter_mask, window_mask;
571 guint xmask = 0;
572 gint i;
573
574 window_mask = gdk_window_get_events (window);
575 filter_mask = GDK_POINTER_MOTION_MASK
576 | GDK_POINTER_MOTION_HINT_MASK
577 | GDK_BUTTON_MOTION_MASK
578 | GDK_BUTTON1_MOTION_MASK
579 | GDK_BUTTON2_MOTION_MASK
580 | GDK_BUTTON3_MOTION_MASK
581 | GDK_BUTTON_PRESS_MASK
582 | GDK_BUTTON_RELEASE_MASK
583 | GDK_KEY_PRESS_MASK
584 | GDK_KEY_RELEASE_MASK
585 | GDK_ENTER_NOTIFY_MASK
586 | GDK_LEAVE_NOTIFY_MASK
587 | GDK_FOCUS_CHANGE_MASK
588 | GDK_PROXIMITY_IN_MASK
589 | GDK_PROXIMITY_OUT_MASK
590 | GDK_SCROLL_MASK;
591
592 /* Filter out non-device events */
593 event_mask &= filter_mask;
594
595 /* Unset device events on window mask */
596 window_mask &= ~filter_mask;
597
598 /* Combine masks */
599 event_mask |= window_mask;
600
601 for (i = 0; i < _gdk_x11_event_mask_table_size; i++)
602 {
603 if (event_mask & (1 << (i + 1)))
604 xmask |= _gdk_x11_event_mask_table[i];
605 }
606
607 if (GDK_WINDOW_XID (window) != GDK_WINDOW_XROOTWIN (window))
608 xmask |= StructureNotifyMask | PropertyChangeMask;
609
610 XSelectInput (GDK_WINDOW_XDISPLAY (window),
611 GDK_WINDOW_XID (window),
612 xmask);
613 }
614