]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkdevice-xi2.c
Clean up gdkx.h a bit
[~andy/gtk] / gdk / x11 / gdkdevice-xi2.c
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, 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
22 #include "gdkdevice-xi2.h"
23
24 #include "gdkintl.h"
25 #include "gdkasync.h"
26 #include "gdkprivate-x11.h"
27
28 #include <X11/extensions/XInput2.h>
29
30 struct _GdkDeviceXI2Private
31 {
32   int device_id;
33 };
34
35 static void gdk_device_xi2_get_property (GObject      *object,
36                                          guint         prop_id,
37                                          GValue       *value,
38                                          GParamSpec   *pspec);
39 static void gdk_device_xi2_set_property (GObject      *object,
40                                          guint         prop_id,
41                                          const GValue *value,
42                                          GParamSpec   *pspec);
43
44 static void gdk_device_xi2_get_state (GdkDevice       *device,
45                                       GdkWindow       *window,
46                                       gdouble         *axes,
47                                       GdkModifierType *mask);
48 static void gdk_device_xi2_set_window_cursor (GdkDevice *device,
49                                               GdkWindow *window,
50                                               GdkCursor *cursor);
51 static void gdk_device_xi2_warp (GdkDevice *device,
52                                  GdkScreen *screen,
53                                  gint       x,
54                                  gint       y);
55 static gboolean gdk_device_xi2_query_state (GdkDevice        *device,
56                                             GdkWindow        *window,
57                                             GdkWindow       **root_window,
58                                             GdkWindow       **child_window,
59                                             gint             *root_x,
60                                             gint             *root_y,
61                                             gint             *win_x,
62                                             gint             *win_y,
63                                             GdkModifierType  *mask);
64
65 static GdkGrabStatus gdk_device_xi2_grab   (GdkDevice     *device,
66                                             GdkWindow     *window,
67                                             gboolean       owner_events,
68                                             GdkEventMask   event_mask,
69                                             GdkWindow     *confine_to,
70                                             GdkCursor     *cursor,
71                                             guint32        time_);
72 static void          gdk_device_xi2_ungrab (GdkDevice     *device,
73                                             guint32        time_);
74
75 static GdkWindow * gdk_device_xi2_window_at_position (GdkDevice       *device,
76                                                       gint            *win_x,
77                                                       gint            *win_y,
78                                                       GdkModifierType *mask,
79                                                       gboolean         get_toplevel);
80 static void  gdk_device_xi2_select_window_events (GdkDevice    *device,
81                                                   GdkWindow    *window,
82                                                   GdkEventMask  event_mask);
83
84
85 G_DEFINE_TYPE (GdkDeviceXI2, gdk_device_xi2, GDK_TYPE_DEVICE)
86
87 enum {
88   PROP_0,
89   PROP_DEVICE_ID
90 };
91
92 static void
93 gdk_device_xi2_class_init (GdkDeviceXI2Class *klass)
94 {
95   GObjectClass *object_class = G_OBJECT_CLASS (klass);
96   GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
97
98   object_class->get_property = gdk_device_xi2_get_property;
99   object_class->set_property = gdk_device_xi2_set_property;
100
101   device_class->get_state = gdk_device_xi2_get_state;
102   device_class->set_window_cursor = gdk_device_xi2_set_window_cursor;
103   device_class->warp = gdk_device_xi2_warp;
104   device_class->query_state = gdk_device_xi2_query_state;
105   device_class->grab = gdk_device_xi2_grab;
106   device_class->ungrab = gdk_device_xi2_ungrab;
107   device_class->window_at_position = gdk_device_xi2_window_at_position;
108   device_class->select_window_events = gdk_device_xi2_select_window_events;
109
110   g_object_class_install_property (object_class,
111                                    PROP_DEVICE_ID,
112                                    g_param_spec_int ("device-id",
113                                                      P_("Device ID"),
114                                                      P_("Device identifier"),
115                                                      0, G_MAXINT, 0,
116                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
117
118   g_type_class_add_private (object_class, sizeof (GdkDeviceXI2Private));
119 }
120
121 static void
122 gdk_device_xi2_init (GdkDeviceXI2 *device)
123 {
124   GdkDeviceXI2Private *priv;
125
126   device->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
127                                                      GDK_TYPE_DEVICE_XI2,
128                                                      GdkDeviceXI2Private);
129 }
130
131 static void
132 gdk_device_xi2_get_property (GObject    *object,
133                              guint       prop_id,
134                              GValue     *value,
135                              GParamSpec *pspec)
136 {
137   GdkDeviceXI2Private *priv;
138
139   priv = GDK_DEVICE_XI2 (object)->priv;
140
141   switch (prop_id)
142     {
143     case PROP_DEVICE_ID:
144       g_value_set_int (value, priv->device_id);
145       break;
146     default:
147       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
148       break;
149     }
150 }
151
152 static void
153 gdk_device_xi2_set_property (GObject      *object,
154                              guint         prop_id,
155                              const GValue *value,
156                              GParamSpec   *pspec)
157 {
158   GdkDeviceXI2Private *priv;
159
160   priv = GDK_DEVICE_XI2 (object)->priv;
161
162   switch (prop_id)
163     {
164     case PROP_DEVICE_ID:
165       priv->device_id = g_value_get_int (value);
166       break;
167     default:
168       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
169       break;
170     }
171 }
172
173 static void
174 gdk_device_xi2_get_state (GdkDevice       *device,
175                           GdkWindow       *window,
176                           gdouble         *axes,
177                           GdkModifierType *mask)
178 {
179   GdkDeviceXI2Private *priv;
180   GdkDisplay *display;
181   XIDeviceInfo *info;
182   gint i, j, ndevices;
183
184   priv = GDK_DEVICE_XI2 (device)->priv;
185   display = gdk_device_get_display (device);
186
187   if (axes)
188     {
189       info = XIQueryDevice(GDK_DISPLAY_XDISPLAY (display),
190                            priv->device_id, &ndevices);
191
192       for (i = 0, j = 0; i < info->num_classes; i++)
193         {
194           XIAnyClassInfo *class_info = info->classes[i];
195           GdkAxisUse use;
196           gdouble value;
197
198           if (class_info->type != XIValuatorClass)
199             continue;
200
201           value = ((XIValuatorClassInfo *) class_info)->value;
202           use = _gdk_device_get_axis_use (device, j);
203
204           switch (use)
205             {
206             case GDK_AXIS_X:
207             case GDK_AXIS_Y:
208             case GDK_AXIS_IGNORE:
209               if (gdk_device_get_mode (device) == GDK_MODE_WINDOW)
210                 _gdk_device_translate_window_coord (device, window, j, value, &axes[j]);
211               else
212                 {
213                   gint root_x, root_y;
214
215                   /* FIXME: Maybe root coords chaching should happen here */
216                   gdk_window_get_origin (window, &root_x, &root_y);
217                   _gdk_device_translate_screen_coord (device, window,
218                                                       root_x, root_y,
219                                                       j, value,
220                                                       &axes[j]);
221                 }
222               break;
223             default:
224               _gdk_device_translate_axis (device, j, value, &axes[j]);
225               break;
226             }
227
228           j++;
229         }
230
231       XIFreeDeviceInfo (info);
232     }
233
234   if (mask)
235     gdk_device_xi2_query_state (device, window,
236                                 NULL, NULL,
237                                 NULL, NULL,
238                                 NULL, NULL,
239                                 mask);
240 }
241
242 static void
243 gdk_device_xi2_set_window_cursor (GdkDevice *device,
244                                   GdkWindow *window,
245                                   GdkCursor *cursor)
246 {
247   GdkDeviceXI2Private *priv;
248   GdkCursorPrivate *cursor_private;
249
250   priv = GDK_DEVICE_XI2 (device)->priv;
251
252   /* Non-master devices don't have a cursor */
253   if (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER)
254     return;
255
256   if (cursor)
257     {
258       cursor_private = (GdkCursorPrivate*) cursor;
259
260       XIDefineCursor (GDK_WINDOW_XDISPLAY (window),
261                       priv->device_id,
262                       GDK_WINDOW_XID (window),
263                       cursor_private->xcursor);
264     }
265   else
266     XIUndefineCursor (GDK_WINDOW_XDISPLAY (window),
267                       priv->device_id,
268                       GDK_WINDOW_XID (window));
269 }
270
271 static void
272 gdk_device_xi2_warp (GdkDevice *device,
273                      GdkScreen *screen,
274                      gint       x,
275                      gint       y)
276 {
277   GdkDeviceXI2Private *priv;
278   Window dest;
279
280   priv = GDK_DEVICE_XI2 (device)->priv;
281   dest = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
282
283   XIWarpPointer (GDK_SCREEN_XDISPLAY (screen),
284                  priv->device_id,
285                  None, dest,
286                  0, 0, 0, 0, x, y);
287 }
288
289 static gboolean
290 gdk_device_xi2_query_state (GdkDevice        *device,
291                             GdkWindow        *window,
292                             GdkWindow       **root_window,
293                             GdkWindow       **child_window,
294                             gint             *root_x,
295                             gint             *root_y,
296                             gint             *win_x,
297                             gint             *win_y,
298                             GdkModifierType  *mask)
299 {
300   GdkDisplay *display;
301   GdkScreen *default_screen;
302   GdkDeviceXI2Private *priv;
303   Window xroot_window, xchild_window;
304   gdouble xroot_x, xroot_y, xwin_x, xwin_y;
305   XIButtonState button_state;
306   XIModifierState mod_state;
307   XIGroupState group_state;
308
309   if (!window || GDK_WINDOW_DESTROYED (window))
310     return FALSE;
311
312   priv = GDK_DEVICE_XI2 (device)->priv;
313   display = gdk_window_get_display (window);
314   default_screen = gdk_display_get_default_screen (display);
315
316   if (G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
317     {
318       if (!XIQueryPointer (GDK_WINDOW_XDISPLAY (window),
319                            priv->device_id,
320                            GDK_WINDOW_XID (window),
321                            &xroot_window,
322                            &xchild_window,
323                            &xroot_x,
324                            &xroot_y,
325                            &xwin_x,
326                            &xwin_y,
327                            &button_state,
328                            &mod_state,
329                            &group_state))
330         return FALSE;
331     }
332   else
333     {
334       XSetWindowAttributes attributes;
335       Display *xdisplay;
336       Window xwindow, w;
337
338       /* FIXME: untrusted clients not multidevice-safe */
339       xdisplay = GDK_SCREEN_XDISPLAY (default_screen);
340       xwindow = GDK_SCREEN_XROOTWIN (default_screen);
341
342       w = XCreateWindow (xdisplay, xwindow, 0, 0, 1, 1, 0,
343                          CopyFromParent, InputOnly, CopyFromParent,
344                          0, &attributes);
345       XIQueryPointer (xdisplay, priv->device_id,
346                       w,
347                       &xroot_window,
348                       &xchild_window,
349                       &xroot_x,
350                       &xroot_y,
351                       &xwin_x,
352                       &xwin_y,
353                       &button_state,
354                       &mod_state,
355                       &group_state);
356       XDestroyWindow (xdisplay, w);
357     }
358
359   if (root_window)
360     *root_window = gdk_x11_window_lookup_for_display (display, xroot_window);
361
362   if (child_window)
363     *child_window = gdk_x11_window_lookup_for_display (display, xchild_window);
364
365   if (root_x)
366     *root_x = (gint) xroot_x;
367
368   if (root_y)
369     *root_y = (gint) xroot_y;
370
371   if (win_x)
372     *win_x = (gint) xwin_x;
373
374   if (win_y)
375     *win_y = (gint) xwin_y;
376
377   if (mask)
378     *mask = gdk_device_xi2_translate_state (&mod_state, &button_state);
379
380   return TRUE;
381 }
382
383 static GdkGrabStatus
384 gdk_device_xi2_grab (GdkDevice    *device,
385                      GdkWindow    *window,
386                      gboolean      owner_events,
387                      GdkEventMask  event_mask,
388                      GdkWindow    *confine_to,
389                      GdkCursor    *cursor,
390                      guint32       time_)
391 {
392   GdkDeviceXI2Private *priv;
393   GdkDisplay *display;
394   XIEventMask mask;
395   Window xwindow;
396   Cursor xcursor;
397   gint status;
398
399   priv = GDK_DEVICE_XI2 (device)->priv;
400   display = gdk_device_get_display (device);
401
402   /* FIXME: confine_to is actually unused */
403
404   xwindow = GDK_WINDOW_XID (window);
405
406   if (!cursor)
407     xcursor = None;
408   else
409     {
410       _gdk_x11_cursor_update_theme (cursor);
411       xcursor = ((GdkCursorPrivate *) cursor)->xcursor;
412     }
413
414   mask.deviceid = priv->device_id;
415   mask.mask = gdk_device_xi2_translate_event_mask (event_mask, &mask.mask_len);
416
417 #ifdef G_ENABLE_DEBUG
418   if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
419     status = GrabSuccess;
420   else
421 #endif
422   status = XIGrabDevice (GDK_DISPLAY_XDISPLAY (display),
423                          priv->device_id,
424                          xwindow,
425                          time_,
426                          xcursor,
427                          GrabModeAsync, GrabModeAsync,
428                          owner_events,
429                          &mask);
430
431   g_free (mask.mask);
432
433   _gdk_x11_display_update_grab_info (display, device, status);
434
435   return _gdk_x11_convert_grab_status (status);
436 }
437
438 static void
439 gdk_device_xi2_ungrab (GdkDevice *device,
440                        guint32    time_)
441 {
442   GdkDeviceXI2Private *priv;
443   GdkDisplay *display;
444   gulong serial;
445
446   priv = GDK_DEVICE_XI2 (device)->priv;
447   display = gdk_device_get_display (device);
448   serial = NextRequest (GDK_DISPLAY_XDISPLAY (display));
449
450   XIUngrabDevice (GDK_DISPLAY_XDISPLAY (display), priv->device_id, time_);
451
452   _gdk_x11_display_update_grab_info_ungrab (display, device, time_, serial);
453 }
454
455 static GdkWindow *
456 gdk_device_xi2_window_at_position (GdkDevice       *device,
457                                    gint            *win_x,
458                                    gint            *win_y,
459                                    GdkModifierType *mask,
460                                    gboolean         get_toplevel)
461 {
462   GdkDeviceXI2Private *priv;
463   GdkDisplay *display;
464   GdkScreen *screen;
465   Display *xdisplay;
466   GdkWindow *window;
467   Window xwindow, root, child, last = None;
468   gdouble xroot_x, xroot_y, xwin_x, xwin_y;
469   XIButtonState button_state;
470   XIModifierState mod_state;
471   XIGroupState group_state;
472
473   priv = GDK_DEVICE_XI2 (device)->priv;
474   display = gdk_device_get_display (device);
475   screen = gdk_display_get_default_screen (display);
476
477   /* This function really only works if the mouse pointer is held still
478    * during its operation. If it moves from one leaf window to another
479    * than we'll end up with inaccurate values for win_x, win_y
480    * and the result.
481    */
482   gdk_x11_display_grab (display);
483
484   xdisplay = GDK_SCREEN_XDISPLAY (screen);
485   xwindow = GDK_SCREEN_XROOTWIN (screen);
486
487   if (G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
488     {
489       XIQueryPointer (xdisplay,
490                       priv->device_id,
491                       xwindow,
492                       &root, &child,
493                       &xroot_x, &xroot_y,
494                       &xwin_x, &xwin_y,
495                       &button_state,
496                       &mod_state,
497                       &group_state);
498
499       if (root == xwindow)
500         xwindow = child;
501       else
502         xwindow = root;
503     }
504   else
505     {
506       gint i, screens, width, height;
507       GList *toplevels, *list;
508       Window pointer_window, root, child;
509
510       /* FIXME: untrusted clients case not multidevice-safe */
511       pointer_window = None;
512       screens = gdk_display_get_n_screens (display);
513
514       for (i = 0; i < screens; ++i)
515         {
516           screen = gdk_display_get_screen (display, i);
517           toplevels = gdk_screen_get_toplevel_windows (screen);
518           for (list = toplevels; list != NULL; list = g_list_next (list))
519             {
520               window = GDK_WINDOW (list->data);
521               xwindow = GDK_WINDOW_XID (window);
522               gdk_x11_display_error_trap_push (display);
523               XIQueryPointer (xdisplay,
524                               priv->device_id,
525                               xwindow,
526                               &root, &child,
527                               &xroot_x, &xroot_y,
528                               &xwin_x, &xwin_y,
529                               &button_state,
530                               &mod_state,
531                               &group_state);
532               if (gdk_x11_display_error_trap_pop (display))
533                 continue;
534               if (child != None)
535                 {
536                   pointer_window = child;
537                   break;
538                 }
539               gdk_window_get_geometry (window, NULL, NULL, &width, &height);
540               if (xwin_x >= 0 && xwin_y >= 0 && xwin_x < width && xwin_y < height)
541                 {
542                   /* A childless toplevel, or below another window? */
543                   XSetWindowAttributes attributes;
544                   Window w;
545
546                   w = XCreateWindow (xdisplay, xwindow, (int)xwin_x, (int)xwin_y, 1, 1, 0,
547                                      CopyFromParent, InputOnly, CopyFromParent,
548                                      0, &attributes);
549                   XMapWindow (xdisplay, w);
550                   XIQueryPointer (xdisplay,
551                                   priv->device_id,
552                                   xwindow,
553                                   &root, &child,
554                                   &xroot_x, &xroot_y,
555                                   &xwin_x, &xwin_y,
556                                   &button_state,
557                                   &mod_state,
558                                   &group_state);
559                   XDestroyWindow (xdisplay, w);
560                   if (child == w)
561                     {
562                       pointer_window = xwindow;
563                       break;
564                     }
565                 }
566             }
567
568           g_list_free (toplevels);
569           if (pointer_window != None)
570             break;
571         }
572
573       xwindow = pointer_window;
574     }
575
576   while (xwindow)
577     {
578       last = xwindow;
579       gdk_x11_display_error_trap_push (display);
580       XIQueryPointer (xdisplay,
581                       priv->device_id,
582                       xwindow,
583                       &root, &xwindow,
584                       &xroot_x, &xroot_y,
585                       &xwin_x, &xwin_y,
586                       &button_state,
587                       &mod_state,
588                       &group_state);
589       if (gdk_x11_display_error_trap_pop (display))
590         break;
591
592       if (get_toplevel && last != root &&
593           (window = gdk_x11_window_lookup_for_display (display, last)) != NULL &&
594           GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
595         {
596           xwindow = last;
597           break;
598         }
599     }
600
601   gdk_x11_display_ungrab (display);
602
603   window = gdk_x11_window_lookup_for_display (display, last);
604
605   if (win_x)
606     *win_x = (window) ? (gint) xwin_x : -1;
607
608   if (win_y)
609     *win_y = (window) ? (gint) xwin_y : -1;
610
611   if (mask)
612     *mask = gdk_device_xi2_translate_state (&mod_state, &button_state);
613
614   return window;
615 }
616
617 static void
618 gdk_device_xi2_select_window_events (GdkDevice    *device,
619                                      GdkWindow    *window,
620                                      GdkEventMask  event_mask)
621 {
622   GdkDeviceXI2Private *priv;
623   XIEventMask evmask;
624
625   priv = GDK_DEVICE_XI2 (device)->priv;
626
627   evmask.deviceid = priv->device_id;
628   evmask.mask = gdk_device_xi2_translate_event_mask (event_mask, &evmask.mask_len);
629
630   XISelectEvents (GDK_WINDOW_XDISPLAY (window),
631                   GDK_WINDOW_XID (window),
632                   &evmask, 1);
633
634   g_free (evmask.mask);
635 }
636
637 guchar *
638 gdk_device_xi2_translate_event_mask (GdkEventMask  event_mask,
639                                      int          *len)
640 {
641   guchar *mask;
642
643   *len = XIMaskLen (XI_LASTEVENT);
644   mask = g_new0 (guchar, *len);
645
646   if (event_mask & GDK_POINTER_MOTION_MASK ||
647       event_mask & GDK_POINTER_MOTION_HINT_MASK)
648     XISetMask (mask, XI_Motion);
649
650   if (event_mask & GDK_BUTTON_MOTION_MASK ||
651       event_mask & GDK_BUTTON1_MOTION_MASK ||
652       event_mask & GDK_BUTTON2_MOTION_MASK ||
653       event_mask & GDK_BUTTON3_MOTION_MASK)
654     {
655       XISetMask (mask, XI_ButtonPress);
656       XISetMask (mask, XI_ButtonRelease);
657       XISetMask (mask, XI_Motion);
658     }
659
660   if (event_mask & GDK_SCROLL_MASK)
661     {
662       XISetMask (mask, XI_ButtonPress);
663       XISetMask (mask, XI_ButtonRelease);
664     }
665
666   if (event_mask & GDK_BUTTON_PRESS_MASK)
667     XISetMask (mask, XI_ButtonPress);
668
669   if (event_mask & GDK_BUTTON_RELEASE_MASK)
670     XISetMask (mask, XI_ButtonRelease);
671
672   if (event_mask & GDK_KEY_PRESS_MASK)
673     XISetMask (mask, XI_KeyPress);
674
675   if (event_mask & GDK_KEY_RELEASE_MASK)
676     XISetMask (mask, XI_KeyRelease);
677
678   if (event_mask & GDK_ENTER_NOTIFY_MASK)
679     XISetMask (mask, XI_Enter);
680
681   if (event_mask & GDK_LEAVE_NOTIFY_MASK)
682     XISetMask (mask, XI_Leave);
683
684   if (event_mask & GDK_FOCUS_CHANGE_MASK)
685     {
686       XISetMask (mask, XI_FocusIn);
687       XISetMask (mask, XI_FocusOut);
688     }
689
690   return mask;
691 }
692
693 guint
694 gdk_device_xi2_translate_state (XIModifierState *mods_state,
695                                 XIButtonState   *buttons_state)
696 {
697   guint state = 0;
698
699   if (mods_state)
700     state = (guint) mods_state->effective;
701
702   if (buttons_state)
703     {
704       gint len, i;
705
706       /* We're only interested in the first 5 buttons */
707       len = MIN (5, buttons_state->mask_len * 8);
708
709       for (i = 0; i < len; i++)
710         {
711           if (!XIMaskIsSet (buttons_state->mask, i))
712             continue;
713
714           switch (i)
715             {
716             case 1:
717               state |= GDK_BUTTON1_MASK;
718               break;
719             case 2:
720               state |= GDK_BUTTON2_MASK;
721               break;
722             case 3:
723               state |= GDK_BUTTON3_MASK;
724               break;
725             case 4:
726               state |= GDK_BUTTON4_MASK;
727               break;
728             case 5:
729               state |= GDK_BUTTON5_MASK;
730               break;
731             default:
732               break;
733             }
734         }
735     }
736
737   return state;
738 }