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 | #include "gdkx11device-xi2.h" |
21 | #include "gdkdeviceprivate.h" |
22 | #include "gdkdevice-xi2-private.h" |
23 | |
24 | #include "gdkintl.h" |
25 | #include "gdkasync.h" |
26 | #include "gdkprivate-x11.h" |
27 | #include "gdkdisplay-x11.h" |
28 | |
29 | #include "gdk-private.h" |
30 | |
31 | #include <stdlib.h> |
32 | #include <X11/Xlib.h> |
33 | #include <X11/Xutil.h> |
34 | #include <X11/extensions/XInput2.h> |
35 | |
36 | #include <math.h> |
37 | |
38 | typedef struct _ScrollValuator ScrollValuator; |
39 | |
40 | struct _ScrollValuator |
41 | { |
42 | guint n_valuator : 4; |
43 | guint direction : 4; |
44 | guint last_value_valid : 1; |
45 | double last_value; |
46 | double increment; |
47 | }; |
48 | |
49 | struct _GdkX11DeviceXI2 |
50 | { |
51 | GdkDevice parent_instance; |
52 | |
53 | int device_id; |
54 | GArray *scroll_valuators; |
55 | double *last_axes; |
56 | GdkX11DeviceType device_type; |
57 | }; |
58 | |
59 | struct _GdkX11DeviceXI2Class |
60 | { |
61 | GdkDeviceClass parent_class; |
62 | }; |
63 | |
64 | G_DEFINE_TYPE (GdkX11DeviceXI2, gdk_x11_device_xi2, GDK_TYPE_DEVICE) |
65 | |
66 | |
67 | static void gdk_x11_device_xi2_finalize (GObject *object); |
68 | static void gdk_x11_device_xi2_get_property (GObject *object, |
69 | guint prop_id, |
70 | GValue *value, |
71 | GParamSpec *pspec); |
72 | static void gdk_x11_device_xi2_set_property (GObject *object, |
73 | guint prop_id, |
74 | const GValue *value, |
75 | GParamSpec *pspec); |
76 | |
77 | static void gdk_x11_device_xi2_set_surface_cursor (GdkDevice *device, |
78 | GdkSurface *surface, |
79 | GdkCursor *cursor); |
80 | |
81 | static GdkGrabStatus gdk_x11_device_xi2_grab (GdkDevice *device, |
82 | GdkSurface *surface, |
83 | gboolean owner_events, |
84 | GdkEventMask event_mask, |
85 | GdkSurface *confine_to, |
86 | GdkCursor *cursor, |
87 | guint32 time_); |
88 | static void gdk_x11_device_xi2_ungrab (GdkDevice *device, |
89 | guint32 time_); |
90 | |
91 | static GdkSurface * gdk_x11_device_xi2_surface_at_position (GdkDevice *device, |
92 | double *win_x, |
93 | double *win_y, |
94 | GdkModifierType *mask); |
95 | |
96 | |
97 | enum { |
98 | PROP_0, |
99 | PROP_DEVICE_ID |
100 | }; |
101 | |
102 | static void |
103 | gdk_x11_device_xi2_class_init (GdkX11DeviceXI2Class *klass) |
104 | { |
105 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
106 | GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass); |
107 | |
108 | object_class->finalize = gdk_x11_device_xi2_finalize; |
109 | object_class->get_property = gdk_x11_device_xi2_get_property; |
110 | object_class->set_property = gdk_x11_device_xi2_set_property; |
111 | |
112 | device_class->set_surface_cursor = gdk_x11_device_xi2_set_surface_cursor; |
113 | device_class->grab = gdk_x11_device_xi2_grab; |
114 | device_class->ungrab = gdk_x11_device_xi2_ungrab; |
115 | device_class->surface_at_position = gdk_x11_device_xi2_surface_at_position; |
116 | |
117 | g_object_class_install_property (oclass: object_class, |
118 | property_id: PROP_DEVICE_ID, |
119 | pspec: g_param_spec_int (name: "device-id" , |
120 | P_("Device ID" ), |
121 | P_("Device identifier" ), |
122 | minimum: 0, G_MAXINT, default_value: 0, |
123 | flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | |
124 | G_PARAM_STATIC_STRINGS)); |
125 | } |
126 | |
127 | static void |
128 | gdk_x11_device_xi2_init (GdkX11DeviceXI2 *device) |
129 | { |
130 | device->scroll_valuators = g_array_new (FALSE, FALSE, element_size: sizeof (ScrollValuator)); |
131 | } |
132 | |
133 | static void |
134 | gdk_x11_device_xi2_finalize (GObject *object) |
135 | { |
136 | GdkX11DeviceXI2 *device = GDK_X11_DEVICE_XI2 (object); |
137 | |
138 | g_array_free (array: device->scroll_valuators, TRUE); |
139 | g_free (mem: device->last_axes); |
140 | |
141 | G_OBJECT_CLASS (gdk_x11_device_xi2_parent_class)->finalize (object); |
142 | } |
143 | |
144 | static void |
145 | gdk_x11_device_xi2_get_property (GObject *object, |
146 | guint prop_id, |
147 | GValue *value, |
148 | GParamSpec *pspec) |
149 | { |
150 | GdkX11DeviceXI2 *device_xi2 = GDK_X11_DEVICE_XI2 (object); |
151 | |
152 | switch (prop_id) |
153 | { |
154 | case PROP_DEVICE_ID: |
155 | g_value_set_int (value, v_int: device_xi2->device_id); |
156 | break; |
157 | default: |
158 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
159 | break; |
160 | } |
161 | } |
162 | |
163 | static void |
164 | gdk_x11_device_xi2_set_property (GObject *object, |
165 | guint prop_id, |
166 | const GValue *value, |
167 | GParamSpec *pspec) |
168 | { |
169 | GdkX11DeviceXI2 *device_xi2 = GDK_X11_DEVICE_XI2 (object); |
170 | |
171 | switch (prop_id) |
172 | { |
173 | case PROP_DEVICE_ID: |
174 | device_xi2->device_id = g_value_get_int (value); |
175 | break; |
176 | default: |
177 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
178 | break; |
179 | } |
180 | } |
181 | |
182 | static void |
183 | gdk_x11_device_xi2_set_surface_cursor (GdkDevice *device, |
184 | GdkSurface *surface, |
185 | GdkCursor *cursor) |
186 | { |
187 | GdkX11DeviceXI2 *device_xi2 = GDK_X11_DEVICE_XI2 (device); |
188 | |
189 | /* Non-logical devices don't have a cursor */ |
190 | if (device_xi2->device_type != GDK_X11_DEVICE_TYPE_LOGICAL) |
191 | return; |
192 | |
193 | if (cursor) |
194 | XIDefineCursor (GDK_SURFACE_XDISPLAY (surface), |
195 | deviceid: device_xi2->device_id, |
196 | GDK_SURFACE_XID (surface), |
197 | cursor: gdk_x11_display_get_xcursor (GDK_SURFACE_DISPLAY (surface), cursor)); |
198 | else |
199 | XIUndefineCursor (GDK_SURFACE_XDISPLAY (surface), |
200 | deviceid: device_xi2->device_id, |
201 | GDK_SURFACE_XID (surface)); |
202 | } |
203 | |
204 | void |
205 | gdk_x11_device_xi2_query_state (GdkDevice *device, |
206 | GdkSurface *surface, |
207 | double *win_x, |
208 | double *win_y, |
209 | GdkModifierType *mask) |
210 | { |
211 | GdkX11DeviceXI2 *device_xi2 = GDK_X11_DEVICE_XI2 (device); |
212 | GdkDisplay *display; |
213 | GdkX11Screen *default_screen; |
214 | Window xroot_window, xchild_window, xwindow; |
215 | double xroot_x, xroot_y, xwin_x, xwin_y; |
216 | XIButtonState button_state; |
217 | XIModifierState mod_state; |
218 | XIGroupState group_state; |
219 | int scale; |
220 | |
221 | display = gdk_device_get_display (device); |
222 | default_screen = GDK_X11_DISPLAY (display)->screen; |
223 | if (surface == NULL) |
224 | { |
225 | xwindow = GDK_DISPLAY_XROOTWIN (display); |
226 | scale = default_screen->surface_scale; |
227 | } |
228 | else |
229 | { |
230 | xwindow = GDK_SURFACE_XID (surface); |
231 | scale = GDK_X11_SURFACE (surface)->surface_scale; |
232 | } |
233 | |
234 | if (!GDK_X11_DISPLAY (display)->trusted_client || |
235 | !XIQueryPointer (GDK_DISPLAY_XDISPLAY (display), |
236 | deviceid: device_xi2->device_id, |
237 | win: xwindow, |
238 | root: &xroot_window, |
239 | child: &xchild_window, |
240 | root_x: &xroot_x, root_y: &xroot_y, |
241 | win_x: &xwin_x, win_y: &xwin_y, |
242 | buttons: &button_state, |
243 | mods: &mod_state, |
244 | group: &group_state)) |
245 | { |
246 | XSetWindowAttributes attributes; |
247 | Display *xdisplay; |
248 | Window w; |
249 | |
250 | /* FIXME: untrusted clients not multidevice-safe */ |
251 | xdisplay = GDK_SCREEN_XDISPLAY (default_screen); |
252 | xwindow = GDK_SCREEN_XROOTWIN (default_screen); |
253 | |
254 | w = XCreateWindow (xdisplay, xwindow, 0, 0, 1, 1, 0, |
255 | CopyFromParent, InputOnly, CopyFromParent, |
256 | 0, &attributes); |
257 | XIQueryPointer (display: xdisplay, deviceid: device_xi2->device_id, |
258 | win: w, |
259 | root: &xroot_window, |
260 | child: &xchild_window, |
261 | root_x: &xroot_x, root_y: &xroot_y, |
262 | win_x: &xwin_x, win_y: &xwin_y, |
263 | buttons: &button_state, |
264 | mods: &mod_state, |
265 | group: &group_state); |
266 | XDestroyWindow (xdisplay, w); |
267 | } |
268 | |
269 | if (win_x) |
270 | *win_x = xwin_x / scale; |
271 | |
272 | if (win_y) |
273 | *win_y = xwin_y / scale; |
274 | |
275 | if (mask) |
276 | *mask = _gdk_x11_device_xi2_translate_state (mods_state: &mod_state, buttons_state: &button_state, group_state: &group_state); |
277 | |
278 | free (ptr: button_state.mask); |
279 | } |
280 | |
281 | static GdkGrabStatus |
282 | gdk_x11_convert_grab_status (int status) |
283 | { |
284 | switch (status) |
285 | { |
286 | case GrabSuccess: |
287 | return GDK_GRAB_SUCCESS; |
288 | case AlreadyGrabbed: |
289 | return GDK_GRAB_ALREADY_GRABBED; |
290 | case GrabInvalidTime: |
291 | return GDK_GRAB_INVALID_TIME; |
292 | case GrabNotViewable: |
293 | return GDK_GRAB_NOT_VIEWABLE; |
294 | case GrabFrozen: |
295 | return GDK_GRAB_FROZEN; |
296 | default: |
297 | g_assert_not_reached(); |
298 | return 0; |
299 | } |
300 | } |
301 | |
302 | static GdkGrabStatus |
303 | gdk_x11_device_xi2_grab (GdkDevice *device, |
304 | GdkSurface *surface, |
305 | gboolean owner_events, |
306 | GdkEventMask event_mask, |
307 | GdkSurface *confine_to, |
308 | GdkCursor *cursor, |
309 | guint32 time_) |
310 | { |
311 | GdkX11DeviceXI2 *device_xi2 = GDK_X11_DEVICE_XI2 (device); |
312 | GdkX11DeviceManagerXI2 *device_manager_xi2; |
313 | GdkDisplay *display; |
314 | XIEventMask mask; |
315 | Window xwindow; |
316 | Cursor xcursor; |
317 | int status; |
318 | |
319 | display = gdk_device_get_display (device); |
320 | device_manager_xi2 = GDK_X11_DEVICE_MANAGER_XI2 (GDK_X11_DISPLAY (display)->device_manager); |
321 | |
322 | /* FIXME: confine_to is actually unused */ |
323 | |
324 | xwindow = GDK_SURFACE_XID (surface); |
325 | |
326 | if (!cursor) |
327 | xcursor = None; |
328 | else |
329 | { |
330 | xcursor = gdk_x11_display_get_xcursor (display, cursor); |
331 | } |
332 | |
333 | mask.deviceid = device_xi2->device_id; |
334 | mask.mask = _gdk_x11_device_xi2_translate_event_mask (device_manager_xi2, |
335 | event_mask, |
336 | len: &mask.mask_len); |
337 | |
338 | #ifdef G_ENABLE_DEBUG |
339 | if (GDK_DISPLAY_DEBUG_CHECK (display, NOGRABS)) |
340 | status = GrabSuccess; |
341 | else |
342 | #endif |
343 | status = XIGrabDevice (GDK_DISPLAY_XDISPLAY (display), |
344 | deviceid: device_xi2->device_id, |
345 | grab_window: xwindow, |
346 | time: time_, |
347 | cursor: xcursor, |
348 | GrabModeAsync, GrabModeAsync, |
349 | owner_events, |
350 | mask: &mask); |
351 | |
352 | g_free (mem: mask.mask); |
353 | |
354 | _gdk_x11_display_update_grab_info (display, device, status); |
355 | |
356 | return gdk_x11_convert_grab_status (status); |
357 | } |
358 | |
359 | static void |
360 | gdk_x11_device_xi2_ungrab (GdkDevice *device, |
361 | guint32 time_) |
362 | { |
363 | GdkX11DeviceXI2 *device_xi2 = GDK_X11_DEVICE_XI2 (device); |
364 | GdkDisplay *display; |
365 | gulong serial; |
366 | |
367 | display = gdk_device_get_display (device); |
368 | serial = NextRequest (GDK_DISPLAY_XDISPLAY (display)); |
369 | |
370 | XIUngrabDevice (GDK_DISPLAY_XDISPLAY (display), deviceid: device_xi2->device_id, time: time_); |
371 | |
372 | _gdk_x11_display_update_grab_info_ungrab (display, device, time: time_, serial); |
373 | } |
374 | |
375 | static GdkSurface * |
376 | gdk_x11_device_xi2_surface_at_position (GdkDevice *device, |
377 | double *win_x, |
378 | double *win_y, |
379 | GdkModifierType *mask) |
380 | { |
381 | GdkX11Surface *impl; |
382 | GdkX11DeviceXI2 *device_xi2 = GDK_X11_DEVICE_XI2 (device); |
383 | GdkDisplay *display; |
384 | GdkX11Screen *screen; |
385 | Display *xdisplay; |
386 | GdkSurface *surface; |
387 | Window xwindow, root, child, last = None; |
388 | double xroot_x, xroot_y, xwin_x, xwin_y; |
389 | XIButtonState button_state = { 0 }; |
390 | XIModifierState mod_state; |
391 | XIGroupState group_state; |
392 | Bool retval; |
393 | |
394 | display = gdk_device_get_display (device); |
395 | screen = GDK_X11_DISPLAY (display)->screen; |
396 | |
397 | gdk_x11_display_error_trap_push (display); |
398 | |
399 | /* This function really only works if the mouse pointer is held still |
400 | * during its operation. If it moves from one leaf window to another |
401 | * than we'll end up with inaccurate values for win_x, win_y |
402 | * and the result. |
403 | */ |
404 | gdk_x11_display_grab (display); |
405 | |
406 | xdisplay = GDK_SCREEN_XDISPLAY (screen); |
407 | xwindow = GDK_SCREEN_XROOTWIN (screen); |
408 | |
409 | if (G_LIKELY (GDK_X11_DISPLAY (display)->trusted_client)) |
410 | { |
411 | XIQueryPointer (display: xdisplay, |
412 | deviceid: device_xi2->device_id, |
413 | win: xwindow, |
414 | root: &root, child: &child, |
415 | root_x: &xroot_x, root_y: &xroot_y, |
416 | win_x: &xwin_x, win_y: &xwin_y, |
417 | buttons: &button_state, |
418 | mods: &mod_state, |
419 | group: &group_state); |
420 | |
421 | if (root == xwindow) |
422 | xwindow = child; |
423 | else |
424 | xwindow = root; |
425 | } |
426 | else |
427 | { |
428 | int width, height; |
429 | GList *toplevels, *list; |
430 | Window pointer_window; |
431 | |
432 | /* FIXME: untrusted clients case not multidevice-safe */ |
433 | pointer_window = None; |
434 | |
435 | toplevels = gdk_x11_display_get_toplevel_windows (display); |
436 | for (list = toplevels; list != NULL; list = list->next) |
437 | { |
438 | surface = GDK_SURFACE (list->data); |
439 | xwindow = GDK_SURFACE_XID (surface); |
440 | |
441 | /* Free previous button mask, if any */ |
442 | g_free (mem: button_state.mask); |
443 | |
444 | retval = XIQueryPointer (display: xdisplay, |
445 | deviceid: device_xi2->device_id, |
446 | win: xwindow, |
447 | root: &root, child: &child, |
448 | root_x: &xroot_x, root_y: &xroot_y, |
449 | win_x: &xwin_x, win_y: &xwin_y, |
450 | buttons: &button_state, |
451 | mods: &mod_state, |
452 | group: &group_state); |
453 | if (!retval) |
454 | continue; |
455 | |
456 | if (child != None) |
457 | { |
458 | pointer_window = child; |
459 | break; |
460 | } |
461 | gdk_surface_get_geometry (surface, NULL, NULL, width: &width, height: &height); |
462 | if (xwin_x >= 0 && xwin_y >= 0 && xwin_x < width && xwin_y < height) |
463 | { |
464 | /* A childless toplevel, or below another window? */ |
465 | XSetWindowAttributes attributes; |
466 | Window w; |
467 | |
468 | free (ptr: button_state.mask); |
469 | |
470 | w = XCreateWindow (xdisplay, xwindow, (int)xwin_x, (int)xwin_y, 1, 1, 0, |
471 | CopyFromParent, InputOnly, CopyFromParent, |
472 | 0, &attributes); |
473 | XMapWindow (xdisplay, w); |
474 | XIQueryPointer (display: xdisplay, |
475 | deviceid: device_xi2->device_id, |
476 | win: xwindow, |
477 | root: &root, child: &child, |
478 | root_x: &xroot_x, root_y: &xroot_y, |
479 | win_x: &xwin_x, win_y: &xwin_y, |
480 | buttons: &button_state, |
481 | mods: &mod_state, |
482 | group: &group_state); |
483 | XDestroyWindow (xdisplay, w); |
484 | if (child == w) |
485 | { |
486 | pointer_window = xwindow; |
487 | break; |
488 | } |
489 | } |
490 | |
491 | if (pointer_window != None) |
492 | break; |
493 | } |
494 | |
495 | xwindow = pointer_window; |
496 | } |
497 | |
498 | while (xwindow) |
499 | { |
500 | last = xwindow; |
501 | free (ptr: button_state.mask); |
502 | |
503 | retval = XIQueryPointer (display: xdisplay, |
504 | deviceid: device_xi2->device_id, |
505 | win: xwindow, |
506 | root: &root, child: &xwindow, |
507 | root_x: &xroot_x, root_y: &xroot_y, |
508 | win_x: &xwin_x, win_y: &xwin_y, |
509 | buttons: &button_state, |
510 | mods: &mod_state, |
511 | group: &group_state); |
512 | if (!retval) |
513 | break; |
514 | |
515 | if (last != root && |
516 | (surface = gdk_x11_surface_lookup_for_display (display, window: last)) != NULL) |
517 | { |
518 | xwindow = last; |
519 | break; |
520 | } |
521 | } |
522 | |
523 | gdk_x11_display_ungrab (display); |
524 | |
525 | if (gdk_x11_display_error_trap_pop (display) == 0) |
526 | { |
527 | surface = gdk_x11_surface_lookup_for_display (display, window: last); |
528 | impl = NULL; |
529 | if (surface) |
530 | impl = GDK_X11_SURFACE (surface); |
531 | |
532 | if (mask) |
533 | *mask = _gdk_x11_device_xi2_translate_state (mods_state: &mod_state, buttons_state: &button_state, group_state: &group_state); |
534 | |
535 | free (ptr: button_state.mask); |
536 | } |
537 | else |
538 | { |
539 | surface = NULL; |
540 | |
541 | if (mask) |
542 | *mask = 0; |
543 | } |
544 | |
545 | if (win_x) |
546 | *win_x = (surface) ? (xwin_x / impl->surface_scale) : -1; |
547 | |
548 | if (win_y) |
549 | *win_y = (surface) ? (xwin_y / impl->surface_scale) : -1; |
550 | |
551 | |
552 | return surface; |
553 | } |
554 | |
555 | guchar * |
556 | _gdk_x11_device_xi2_translate_event_mask (GdkX11DeviceManagerXI2 *device_manager_xi2, |
557 | GdkEventMask event_mask, |
558 | int *len) |
559 | { |
560 | guchar *mask; |
561 | int minor; |
562 | |
563 | g_object_get (object: device_manager_xi2, first_property_name: "minor" , &minor, NULL); |
564 | |
565 | *len = XIMaskLen (XI_LASTEVENT); |
566 | mask = g_new0 (guchar, *len); |
567 | |
568 | if (event_mask & GDK_POINTER_MOTION_MASK) |
569 | XISetMask (mask, XI_Motion); |
570 | |
571 | if (event_mask & GDK_BUTTON_MOTION_MASK || |
572 | event_mask & GDK_BUTTON1_MOTION_MASK || |
573 | event_mask & GDK_BUTTON2_MOTION_MASK || |
574 | event_mask & GDK_BUTTON3_MOTION_MASK) |
575 | { |
576 | XISetMask (mask, XI_ButtonPress); |
577 | XISetMask (mask, XI_ButtonRelease); |
578 | XISetMask (mask, XI_Motion); |
579 | } |
580 | |
581 | if (event_mask & GDK_SCROLL_MASK) |
582 | { |
583 | XISetMask (mask, XI_ButtonPress); |
584 | XISetMask (mask, XI_ButtonRelease); |
585 | } |
586 | |
587 | if (event_mask & GDK_BUTTON_PRESS_MASK) |
588 | XISetMask (mask, XI_ButtonPress); |
589 | |
590 | if (event_mask & GDK_BUTTON_RELEASE_MASK) |
591 | XISetMask (mask, XI_ButtonRelease); |
592 | |
593 | if (event_mask & GDK_KEY_PRESS_MASK) |
594 | XISetMask (mask, XI_KeyPress); |
595 | |
596 | if (event_mask & GDK_KEY_RELEASE_MASK) |
597 | XISetMask (mask, XI_KeyRelease); |
598 | |
599 | if (event_mask & GDK_ENTER_NOTIFY_MASK) |
600 | XISetMask (mask, XI_Enter); |
601 | |
602 | if (event_mask & GDK_LEAVE_NOTIFY_MASK) |
603 | XISetMask (mask, XI_Leave); |
604 | |
605 | if (event_mask & GDK_FOCUS_CHANGE_MASK) |
606 | { |
607 | XISetMask (mask, XI_FocusIn); |
608 | XISetMask (mask, XI_FocusOut); |
609 | } |
610 | |
611 | #ifdef XINPUT_2_2 |
612 | /* XInput 2.2 includes multitouch support */ |
613 | if (minor >= 2 && |
614 | event_mask & GDK_TOUCH_MASK) |
615 | { |
616 | XISetMask (mask, XI_TouchBegin); |
617 | XISetMask (mask, XI_TouchUpdate); |
618 | XISetMask (mask, XI_TouchEnd); |
619 | } |
620 | #endif /* XINPUT_2_2 */ |
621 | |
622 | #ifdef XINPUT_2_4 |
623 | /* XInput 2.4 includes touchpad gesture support */ |
624 | if (minor >= 4 && |
625 | event_mask & GDK_TOUCHPAD_GESTURE_MASK) |
626 | { |
627 | XISetMask (mask, XI_GesturePinchBegin); |
628 | XISetMask (mask, XI_GesturePinchUpdate); |
629 | XISetMask (mask, XI_GesturePinchEnd); |
630 | XISetMask (mask, XI_GestureSwipeBegin); |
631 | XISetMask (mask, XI_GestureSwipeUpdate); |
632 | XISetMask (mask, XI_GestureSwipeEnd); |
633 | } |
634 | #endif |
635 | |
636 | return mask; |
637 | } |
638 | |
639 | guint |
640 | _gdk_x11_device_xi2_translate_state (XIModifierState *mods_state, |
641 | XIButtonState *buttons_state, |
642 | XIGroupState *group_state) |
643 | { |
644 | guint state = 0; |
645 | |
646 | if (mods_state) |
647 | state = mods_state->effective; |
648 | |
649 | if (buttons_state) |
650 | { |
651 | int len, i; |
652 | |
653 | /* We're only interested in the first 3 buttons */ |
654 | len = MIN (3, buttons_state->mask_len * 8); |
655 | |
656 | for (i = 1; i <= len; i++) |
657 | { |
658 | if (!XIMaskIsSet (buttons_state->mask, i)) |
659 | continue; |
660 | |
661 | switch (i) |
662 | { |
663 | case 1: |
664 | state |= GDK_BUTTON1_MASK; |
665 | break; |
666 | case 2: |
667 | state |= GDK_BUTTON2_MASK; |
668 | break; |
669 | case 3: |
670 | state |= GDK_BUTTON3_MASK; |
671 | break; |
672 | default: |
673 | break; |
674 | } |
675 | } |
676 | } |
677 | |
678 | if (group_state) |
679 | state |= (group_state->effective) << 13; |
680 | |
681 | return state; |
682 | } |
683 | |
684 | #ifdef XINPUT_2_4 |
685 | guint |
686 | _gdk_x11_device_xi2_gesture_type_to_phase (int evtype, int flags) |
687 | { |
688 | switch (evtype) |
689 | { |
690 | case XI_GesturePinchBegin: |
691 | case XI_GestureSwipeBegin: |
692 | return GDK_TOUCHPAD_GESTURE_PHASE_BEGIN; |
693 | |
694 | case XI_GesturePinchUpdate: |
695 | case XI_GestureSwipeUpdate: |
696 | return GDK_TOUCHPAD_GESTURE_PHASE_UPDATE; |
697 | |
698 | case XI_GesturePinchEnd: |
699 | if (flags & XIGesturePinchEventCancelled) |
700 | return GDK_TOUCHPAD_GESTURE_PHASE_CANCEL; |
701 | return GDK_TOUCHPAD_GESTURE_PHASE_END; |
702 | |
703 | case XI_GestureSwipeEnd: |
704 | if (flags & XIGestureSwipeEventCancelled) |
705 | return GDK_TOUCHPAD_GESTURE_PHASE_CANCEL; |
706 | return GDK_TOUCHPAD_GESTURE_PHASE_END; |
707 | default: |
708 | g_assert_not_reached (); |
709 | return GDK_TOUCHPAD_GESTURE_PHASE_END; |
710 | } |
711 | } |
712 | #endif /* XINPUT_2_4 */ |
713 | |
714 | void |
715 | _gdk_x11_device_xi2_add_scroll_valuator (GdkX11DeviceXI2 *device, |
716 | guint n_valuator, |
717 | GdkScrollDirection direction, |
718 | double increment) |
719 | { |
720 | ScrollValuator scroll; |
721 | |
722 | g_return_if_fail (GDK_IS_X11_DEVICE_XI2 (device)); |
723 | g_return_if_fail (n_valuator < gdk_device_get_n_axes (GDK_DEVICE (device))); |
724 | |
725 | scroll.n_valuator = n_valuator; |
726 | scroll.direction = direction; |
727 | scroll.last_value_valid = FALSE; |
728 | scroll.increment = increment; |
729 | |
730 | g_array_append_val (device->scroll_valuators, scroll); |
731 | } |
732 | |
733 | gboolean |
734 | _gdk_x11_device_xi2_get_scroll_delta (GdkX11DeviceXI2 *device, |
735 | guint n_valuator, |
736 | double valuator_value, |
737 | GdkScrollDirection *direction_ret, |
738 | double *delta_ret) |
739 | { |
740 | guint i; |
741 | |
742 | g_return_val_if_fail (GDK_IS_X11_DEVICE_XI2 (device), FALSE); |
743 | g_return_val_if_fail (n_valuator < gdk_device_get_n_axes (GDK_DEVICE (device)), FALSE); |
744 | |
745 | for (i = 0; i < device->scroll_valuators->len; i++) |
746 | { |
747 | ScrollValuator *scroll; |
748 | |
749 | scroll = &g_array_index (device->scroll_valuators, ScrollValuator, i); |
750 | |
751 | if (scroll->n_valuator == n_valuator) |
752 | { |
753 | if (direction_ret) |
754 | *direction_ret = scroll->direction; |
755 | |
756 | if (delta_ret) |
757 | *delta_ret = 0; |
758 | |
759 | if (scroll->last_value_valid) |
760 | { |
761 | if (delta_ret) |
762 | *delta_ret = (valuator_value - scroll->last_value) / scroll->increment; |
763 | |
764 | scroll->last_value = valuator_value; |
765 | } |
766 | else |
767 | { |
768 | scroll->last_value = valuator_value; |
769 | scroll->last_value_valid = TRUE; |
770 | } |
771 | |
772 | return TRUE; |
773 | } |
774 | } |
775 | |
776 | return FALSE; |
777 | } |
778 | |
779 | void |
780 | _gdk_device_xi2_reset_scroll_valuators (GdkX11DeviceXI2 *device) |
781 | { |
782 | guint i; |
783 | |
784 | for (i = 0; i < device->scroll_valuators->len; i++) |
785 | { |
786 | ScrollValuator *scroll; |
787 | |
788 | scroll = &g_array_index (device->scroll_valuators, ScrollValuator, i); |
789 | scroll->last_value_valid = FALSE; |
790 | } |
791 | } |
792 | |
793 | void |
794 | _gdk_device_xi2_unset_scroll_valuators (GdkX11DeviceXI2 *device) |
795 | { |
796 | if (device->scroll_valuators->len > 0) |
797 | g_array_remove_range (array: device->scroll_valuators, index_: 0, |
798 | length: device->scroll_valuators->len); |
799 | } |
800 | |
801 | int |
802 | _gdk_x11_device_xi2_get_id (GdkX11DeviceXI2 *device) |
803 | { |
804 | g_return_val_if_fail (GDK_IS_X11_DEVICE_XI2 (device), 0); |
805 | |
806 | return device->device_id; |
807 | } |
808 | |
809 | double |
810 | gdk_x11_device_xi2_get_last_axis_value (GdkX11DeviceXI2 *device, |
811 | int n_axis) |
812 | { |
813 | if (n_axis >= gdk_device_get_n_axes (GDK_DEVICE (device))) |
814 | return 0; |
815 | |
816 | if (!device->last_axes) |
817 | return 0; |
818 | |
819 | return device->last_axes[n_axis]; |
820 | } |
821 | |
822 | void |
823 | gdk_x11_device_xi2_store_axes (GdkX11DeviceXI2 *device, |
824 | double *axes, |
825 | int n_axes) |
826 | { |
827 | g_free (mem: device->last_axes); |
828 | |
829 | if (axes && n_axes) |
830 | device->last_axes = g_memdup2 (mem: axes, byte_size: sizeof (double) * n_axes); |
831 | else |
832 | device->last_axes = NULL; |
833 | } |
834 | |
835 | GdkX11DeviceType |
836 | gdk_x11_device_xi2_get_device_type (GdkX11DeviceXI2 *device) |
837 | { |
838 | return device->device_type; |
839 | } |
840 | |
841 | void |
842 | gdk_x11_device_xi2_set_device_type (GdkX11DeviceXI2 *device, |
843 | GdkX11DeviceType type) |
844 | { |
845 | device->device_type = type; |
846 | } |
847 | |