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