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