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
38typedef struct _ScrollValuator ScrollValuator;
39
40struct _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
49struct _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
59struct _GdkX11DeviceXI2Class
60{
61 GdkDeviceClass parent_class;
62};
63
64G_DEFINE_TYPE (GdkX11DeviceXI2, gdk_x11_device_xi2, GDK_TYPE_DEVICE)
65
66
67static void gdk_x11_device_xi2_finalize (GObject *object);
68static void gdk_x11_device_xi2_get_property (GObject *object,
69 guint prop_id,
70 GValue *value,
71 GParamSpec *pspec);
72static void gdk_x11_device_xi2_set_property (GObject *object,
73 guint prop_id,
74 const GValue *value,
75 GParamSpec *pspec);
76
77static void gdk_x11_device_xi2_set_surface_cursor (GdkDevice *device,
78 GdkSurface *surface,
79 GdkCursor *cursor);
80
81static 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_);
88static void gdk_x11_device_xi2_ungrab (GdkDevice *device,
89 guint32 time_);
90
91static GdkSurface * gdk_x11_device_xi2_surface_at_position (GdkDevice *device,
92 double *win_x,
93 double *win_y,
94 GdkModifierType *mask);
95
96
97enum {
98 PROP_0,
99 PROP_DEVICE_ID
100};
101
102static void
103gdk_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
127static void
128gdk_x11_device_xi2_init (GdkX11DeviceXI2 *device)
129{
130 device->scroll_valuators = g_array_new (FALSE, FALSE, element_size: sizeof (ScrollValuator));
131}
132
133static void
134gdk_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
144static void
145gdk_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
163static void
164gdk_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
182static void
183gdk_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
204void
205gdk_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
281static GdkGrabStatus
282gdk_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
302static GdkGrabStatus
303gdk_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
359static void
360gdk_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
375static GdkSurface *
376gdk_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
555guchar *
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
639guint
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
685guint
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
714void
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
733gboolean
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
779void
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
793void
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
801int
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
809double
810gdk_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
822void
823gdk_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
835GdkX11DeviceType
836gdk_x11_device_xi2_get_device_type (GdkX11DeviceXI2 *device)
837{
838 return device->device_type;
839}
840
841void
842gdk_x11_device_xi2_set_device_type (GdkX11DeviceXI2 *device,
843 GdkX11DeviceType type)
844{
845 device->device_type = type;
846}
847

source code of gtk/gdk/x11/gdkdevice-xi2.c