1 /* $Id$
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., Inc., 51 Franklin Street, Fifth Floor, Boston,
16 MA 02110-1301, USA.
17
18
19 device.c - (c) 2017 Viktor Odintsev
20
21 */
22
23 #include "device.h"
24
25 #include <gdk/gdkx.h>
26 #ifdef HAVE_XI2
27 #include <X11/extensions/XInput2.h>
28 #endif
29
30 #include "display.h"
31
32 #ifdef HAVE_XI2
33 static const struct
34 {
35 guint core_mask;
36 guint xi2_event;
37 } core_to_xi2[] =
38 {
39 { KeyPressMask, XI_KeyPress },
40 { KeyReleaseMask, XI_KeyRelease },
41 { ButtonPressMask, XI_ButtonPress },
42 { ButtonReleaseMask, XI_ButtonRelease },
43 { PointerMotionMask | ButtonMotionMask, XI_Motion },
44 { EnterWindowMask, XI_Enter },
45 { LeaveWindowMask, XI_Leave }
46 };
47 #endif
48
49 #define xfwm_device_fill_meta(evtype, evwindow, evdevice) \
50 { \
51 if (event == NULL) \
52 { \
53 event = g_new0 (XfwmEvent, 1); \
54 } \
55 event->meta.type = evtype; \
56 event->meta.window = evwindow; \
57 event->meta.device = evdevice; \
58 event->meta.xevent = xevent; \
59 }
60
61 #ifdef HAVE_XI2
62 static guint
xfwm_device_obtain_state_xi2(XIButtonState * buttons,XIModifierState * mods,XIGroupState * group)63 xfwm_device_obtain_state_xi2 (XIButtonState *buttons, XIModifierState *mods, XIGroupState *group)
64 {
65 guint result;
66 gint i, count;
67
68 result = mods->effective | (group->effective << 13);
69 count = MIN (3, buttons->mask_len / 8);
70 for (i = 0; i < count; i++)
71 {
72 /* check first 3 buttons as GDK does */
73 if (XIMaskIsSet (buttons->mask, i + 1))
74 {
75 result |= 1 << (8 + i);
76 }
77 }
78
79 return result;
80 }
81 #endif
82
83 static XfwmEvent *
xfwm_device_translate_event_key_core(XEvent * xevent,XfwmEvent * event)84 xfwm_device_translate_event_key_core (XEvent *xevent, XfwmEvent *event)
85 {
86 xfwm_device_fill_meta (XFWM_EVENT_KEY, xevent->xany.window, None);
87
88 event->key.root = xevent->xkey.root;
89 event->key.pressed = xevent->type == KeyPress;
90 event->key.keycode = xevent->xkey.keycode;
91 event->key.state = xevent->xkey.state;
92 event->key.time = xevent->xkey.time;
93
94 return (XfwmEvent *)event;
95 }
96
97 #ifdef HAVE_XI2
98 static XfwmEvent *
xfwm_device_translate_event_key_xi2(XEvent * xevent,XIDeviceEvent * xievent,XfwmEvent * event)99 xfwm_device_translate_event_key_xi2 (XEvent *xevent, XIDeviceEvent *xievent, XfwmEvent *event)
100 {
101 xfwm_device_fill_meta (XFWM_EVENT_KEY, xievent->event, xievent->deviceid);
102
103 event->key.root = xievent->root;
104 event->key.pressed = xievent->evtype == XI_KeyPress;
105 event->key.keycode = xievent->detail;
106 event->key.state = xfwm_device_obtain_state_xi2 (&xievent->buttons,
107 &xievent->mods,
108 &xievent->group);
109 event->key.time = xievent->time;
110
111 return (XfwmEvent *)event;
112 }
113 #endif
114
115 static XfwmEvent *
xfwm_device_translate_event_button_core(XEvent * xevent,XfwmEvent * event)116 xfwm_device_translate_event_button_core (XEvent *xevent, XfwmEvent *event)
117 {
118 xfwm_device_fill_meta (XFWM_EVENT_BUTTON, xevent->xany.window, None);
119
120 event->button.root = xevent->xbutton.root;
121 event->button.subwindow = xevent->xbutton.subwindow;
122 event->button.pressed = xevent->type == ButtonPress;
123 event->button.button = xevent->xbutton.button;
124 event->button.state = xevent->xbutton.state;
125 event->button.x = xevent->xbutton.x;
126 event->button.y = xevent->xbutton.y;
127 event->button.x_root = xevent->xbutton.x_root;
128 event->button.y_root = xevent->xbutton.y_root;
129 event->button.time = xevent->xbutton.time;
130
131 return (XfwmEvent *)event;
132 }
133
134 #ifdef HAVE_XI2
135 static XfwmEvent *
xfwm_device_translate_event_button_xi2(XEvent * xevent,XIDeviceEvent * xievent,XfwmEvent * event)136 xfwm_device_translate_event_button_xi2 (XEvent *xevent, XIDeviceEvent *xievent, XfwmEvent *event)
137 {
138 xfwm_device_fill_meta (XFWM_EVENT_BUTTON, xievent->event, xievent->deviceid);
139
140 event->button.root = xievent->root;
141 event->button.subwindow = xievent->child;
142 event->button.pressed = xievent->evtype == XI_ButtonPress;
143 event->button.button = xievent->detail;
144 event->button.state = xfwm_device_obtain_state_xi2 (&xievent->buttons,
145 &xievent->mods,
146 &xievent->group);
147 event->button.x = xievent->event_x;
148 event->button.y = xievent->event_y;
149 event->button.x_root = xievent->root_x;
150 event->button.y_root = xievent->root_y;
151 event->button.time = xievent->time;
152
153 return (XfwmEvent *)event;
154 }
155 #endif
156
157 static XfwmEvent *
xfwm_device_translate_event_motion_core(XEvent * xevent,XfwmEvent * event)158 xfwm_device_translate_event_motion_core (XEvent *xevent, XfwmEvent *event)
159 {
160 xfwm_device_fill_meta (XFWM_EVENT_MOTION, xevent->xany.window, None);
161
162 event->motion.x = xevent->xbutton.x;
163 event->motion.y = xevent->xbutton.y;
164 event->motion.x_root = xevent->xbutton.x_root;
165 event->motion.y_root = xevent->xbutton.y_root;
166 event->motion.time = xevent->xbutton.time;
167
168 return (XfwmEvent *)event;
169 }
170
171 #ifdef HAVE_XI2
172 static XfwmEvent *
xfwm_device_translate_event_motion_xi2(XEvent * xevent,XIDeviceEvent * xievent,XfwmEvent * event)173 xfwm_device_translate_event_motion_xi2 (XEvent *xevent, XIDeviceEvent *xievent, XfwmEvent *event)
174 {
175 xfwm_device_fill_meta (XFWM_EVENT_MOTION, xievent->event, xievent->deviceid);
176
177 event->motion.x = xievent->event_x;
178 event->motion.y = xievent->event_y;
179 event->motion.x_root = xievent->root_x;
180 event->motion.y_root = xievent->root_y;
181 event->motion.time = xievent->time;
182
183 return (XfwmEvent *)event;
184 }
185 #endif
186
187 static XfwmEvent *
xfwm_device_translate_event_crossing_core(XEvent * xevent,XfwmEvent * event)188 xfwm_device_translate_event_crossing_core (XEvent *xevent, XfwmEvent *event)
189 {
190 xfwm_device_fill_meta (XFWM_EVENT_CROSSING, xevent->xany.window, None);
191
192 event->crossing.root = xevent->xcrossing.root;
193 event->crossing.enter = xevent->type == EnterNotify;
194 event->crossing.mode = xevent->xcrossing.mode;
195 event->crossing.detail = xevent->xcrossing.detail;
196 event->crossing.x_root = xevent->xcrossing.x_root;
197 event->crossing.y_root = xevent->xcrossing.y_root;
198 event->crossing.time = xevent->xcrossing.time;
199
200 return (XfwmEvent *)event;
201 }
202
203 #ifdef HAVE_XI2
204 static XfwmEvent *
xfwm_device_translate_event_crossing_xi2(XEvent * xevent,XIEnterEvent * xievent,XfwmEvent * event)205 xfwm_device_translate_event_crossing_xi2 (XEvent *xevent, XIEnterEvent *xievent, XfwmEvent *event)
206 {
207 xfwm_device_fill_meta (XFWM_EVENT_CROSSING, xievent->event, xievent->deviceid);
208
209 event->crossing.root = xievent->root;
210 event->crossing.enter = xievent->evtype == XI_Enter;
211 event->crossing.mode = xievent->mode;
212 event->crossing.detail = xievent->detail;
213 event->crossing.x_root = xievent->root_x;
214 event->crossing.y_root = xievent->root_y;
215 event->crossing.time = xievent->time;
216
217 return (XfwmEvent *)event;
218 }
219 #endif
220
221 static XfwmEvent *
xfwm_device_translate_event_common(XEvent * xevent,XfwmEvent * event)222 xfwm_device_translate_event_common (XEvent *xevent, XfwmEvent *event)
223 {
224 xfwm_device_fill_meta (XFWM_EVENT_XEVENT, xevent->xany.window, None);
225
226 return event;
227 }
228
229 XfwmEvent *
xfwm_device_translate_event(XfwmDevices * devices,XEvent * xevent,XfwmEvent * event)230 xfwm_device_translate_event (XfwmDevices *devices, XEvent *xevent, XfwmEvent *event)
231 {
232 switch (xevent->type)
233 {
234 case KeyPress:
235 case KeyRelease:
236 return xfwm_device_translate_event_key_core (xevent, event);
237 case ButtonPress:
238 case ButtonRelease:
239 return xfwm_device_translate_event_button_core (xevent, event);
240 case MotionNotify:
241 return xfwm_device_translate_event_motion_core (xevent, event);
242 case EnterNotify:
243 case LeaveNotify:
244 return xfwm_device_translate_event_crossing_core (xevent, event);
245 #ifdef HAVE_XI2
246 case GenericEvent:
247 if (devices->xi2_available &&
248 xevent->xgeneric.extension == devices->xi2_opcode &&
249 xevent->xcookie.data != NULL)
250 {
251 XIEvent *xievent = xevent->xcookie.data;
252
253 switch (xievent->evtype)
254 {
255 case XI_KeyPress:
256 case XI_KeyRelease:
257 return xfwm_device_translate_event_key_xi2 (xevent, (XIDeviceEvent *)xievent, event);
258 case XI_ButtonPress:
259 case XI_ButtonRelease:
260 return xfwm_device_translate_event_button_xi2 (xevent, (XIDeviceEvent *)xievent, event);
261 case XI_Motion:
262 return xfwm_device_translate_event_motion_xi2 (xevent, (XIDeviceEvent *)xievent, event);
263 case XI_Enter:
264 case XI_Leave:
265 return xfwm_device_translate_event_crossing_xi2 (xevent, (XIEnterEvent *)xievent, event);
266 }
267 }
268 break;
269 #endif
270 }
271
272 return xfwm_device_translate_event_common (xevent, event);
273 }
274
275 void
xfwm_device_free_event(XfwmEvent * event)276 xfwm_device_free_event (XfwmEvent *event)
277 {
278 g_free (event);
279 }
280
281 void
xfwm_device_button_update_window(XfwmEventButton * event,Window window)282 xfwm_device_button_update_window (XfwmEventButton *event, Window window)
283 {
284 event->meta.window = window;
285 #ifdef HAVE_XI2
286 if (event->meta.device != None)
287 {
288 ((XIDeviceEvent *)event->meta.xevent->xcookie.data)->event = window;
289 }
290 else
291 #endif
292 {
293 event->meta.xevent->xany.window = window;
294 }
295 }
296
297 #ifdef HAVE_XI2
298 static void
xfwm_device_fill_xi2_event_mask(XIEventMask * xievent_mask,gulong core_mask)299 xfwm_device_fill_xi2_event_mask (XIEventMask *xievent_mask, gulong core_mask)
300 {
301 gint len = XIMaskLen (XI_LASTEVENT);
302 guchar *mask = g_new0 (guchar, len);
303 guint i;
304
305 xievent_mask->deviceid = XIAllMasterDevices;
306 xievent_mask->mask_len = len;
307 xievent_mask->mask = mask;
308
309 for (i = 0; i < G_N_ELEMENTS (core_to_xi2); i++)
310 {
311 if ((core_mask & core_to_xi2[i].core_mask) == core_to_xi2[i].core_mask)
312 {
313 XISetMask (mask, core_to_xi2[i].xi2_event);
314 }
315 }
316
317 #undef xi2_set_mask
318 }
319 #endif
320
321 #ifdef HAVE_XI2
322 void
xfwm_device_configure_xi2_event_mask(XfwmDevices * devices,Display * dpy,Window window,gulong core_mask)323 xfwm_device_configure_xi2_event_mask (XfwmDevices *devices, Display *dpy,
324 Window window, gulong core_mask)
325 {
326 if (devices->xi2_available)
327 {
328 XIEventMask xievent_mask;
329 xfwm_device_fill_xi2_event_mask (&xievent_mask, core_mask);
330 XISelectEvents (dpy, window, &xievent_mask, 1);
331 g_free (xievent_mask.mask);
332 }
333 }
334 #endif
335
336 #ifdef HAVE_XI2
337 #define xi2_modifier_mask(core_mask) \
338 ((((core_mask) & AnyModifier) == AnyModifier) \
339 ? (((core_mask) & ~AnyModifier) | XIAnyModifier) \
340 : (core_mask))
341 #endif
342
343 gboolean
xfwm_device_grab(XfwmDevices * devices,XfwmDevice * device,Display * display,Window grab_window,gboolean owner_events,guint event_mask,gint grab_mode,Window confine_to,Cursor cursor,Time time)344 xfwm_device_grab (XfwmDevices *devices, XfwmDevice *device, Display *display,
345 Window grab_window, gboolean owner_events, guint event_mask,
346 gint grab_mode, Window confine_to, Cursor cursor, Time time)
347 {
348 gboolean result;
349 Status status;
350 #ifdef HAVE_XI2
351 XIEventMask xievent_mask;
352 #endif
353
354 #ifdef HAVE_XI2
355 if (device->xi2_device != None)
356 {
357 xfwm_device_fill_xi2_event_mask (&xievent_mask, event_mask);
358 status = XIGrabDevice (display, device->xi2_device, grab_window, time, cursor,
359 grab_mode, grab_mode, owner_events, &xievent_mask);
360 g_free (xievent_mask.mask);
361 result = (status == XIGrabSuccess);
362 }
363 else
364 #endif
365 if (device->keyboard)
366 {
367 status = XGrabKeyboard (display, grab_window, owner_events,
368 grab_mode, grab_mode, time);
369 result = (status == GrabSuccess);
370 }
371 else
372 {
373 status = XGrabPointer (display, grab_window, owner_events, event_mask,
374 grab_mode, grab_mode, confine_to, cursor, time);
375 result = (status == GrabSuccess);
376 }
377 return result;
378 }
379
380 void
xfwm_device_ungrab(XfwmDevices * devices,XfwmDevice * device,Display * display,Time time)381 xfwm_device_ungrab (XfwmDevices *devices, XfwmDevice *device, Display *display, Time time)
382 {
383 #ifdef HAVE_XI2
384 if (device->xi2_device != None)
385 {
386 XIUngrabDevice (display, device->xi2_device, time);
387 }
388 else
389 #endif
390 if (device->keyboard)
391 {
392 XUngrabKeyboard (display, time);
393 }
394 else
395 {
396 XUngrabPointer (display, time);
397 }
398 }
399
400 gboolean
xfwm_device_grab_button(XfwmDevices * devices,Display * display,guint button,guint modifiers,Window grab_window,gboolean owner_events,guint event_mask,gint grab_mode,gint paired_device_mode,Window confine_to,Cursor cursor)401 xfwm_device_grab_button (XfwmDevices *devices, Display *display,
402 guint button, guint modifiers, Window grab_window,
403 gboolean owner_events, guint event_mask,
404 gint grab_mode, gint paired_device_mode,
405 Window confine_to, Cursor cursor)
406 {
407 gboolean result;
408 DisplayInfo *display_info;
409 #ifdef HAVE_XI2
410 Status status;
411 XIGrabModifiers xi2_modifiers;
412 XIEventMask xievent_mask;
413 #endif
414
415 display_info = myDisplayGetDefault ();
416 myDisplayErrorTrapPush (display_info);
417 result = XGrabButton (display, button, modifiers, grab_window,
418 owner_events, event_mask, grab_mode, paired_device_mode,
419 confine_to, cursor);
420 if (myDisplayErrorTrapPop (display_info) || !result)
421 {
422 return FALSE;
423 }
424
425 #ifdef HAVE_XI2
426 if (devices->xi2_available)
427 {
428 xi2_modifiers.modifiers = xi2_modifier_mask (modifiers);
429 xi2_modifiers.status = 0;
430
431 xfwm_device_fill_xi2_event_mask (&xievent_mask, event_mask);
432 myDisplayErrorTrapPush (display_info);
433 status = XIGrabButton (display, devices->pointer.xi2_device, button, grab_window,
434 cursor, grab_mode, paired_device_mode, owner_events,
435 &xievent_mask, 1, &xi2_modifiers);
436 g_free (xievent_mask.mask);
437 if (myDisplayErrorTrapPop (display_info) || status != XIGrabSuccess)
438 {
439 return FALSE;
440 }
441 }
442 #endif
443
444 return TRUE;
445 }
446
447 void
xfwm_device_ungrab_button(XfwmDevices * devices,Display * display,guint button,guint modifiers,Window grab_window)448 xfwm_device_ungrab_button (XfwmDevices *devices, Display *display,
449 guint button, guint modifiers, Window grab_window)
450 {
451 DisplayInfo *display_info;
452 #ifdef HAVE_XI2
453 XIGrabModifiers xi2_modifiers;
454 #endif
455
456 display_info = myDisplayGetDefault ();
457 myDisplayErrorTrapPush (display_info);
458
459 XUngrabButton (display, button, modifiers, grab_window);
460
461 #ifdef HAVE_XI2
462 if (devices->xi2_available)
463 {
464 xi2_modifiers.modifiers = xi2_modifier_mask (modifiers);
465 xi2_modifiers.status = 0;
466
467 XIUngrabButton (display, devices->pointer.xi2_device, button,
468 grab_window, 1, &xi2_modifiers);
469 }
470 #endif
471
472 myDisplayErrorTrapPopIgnored (display_info);
473 }
474
475 gboolean
xfwm_device_grab_keycode(XfwmDevices * devices,Display * display,gint keycode,guint modifiers,Window grab_window,gboolean owner_events,guint event_mask,gint grab_mode,gint paired_device_mode)476 xfwm_device_grab_keycode (XfwmDevices *devices, Display *display,
477 gint keycode, guint modifiers, Window grab_window,
478 gboolean owner_events, guint event_mask,
479 gint grab_mode, gint paired_device_mode)
480 {
481 gboolean result;
482 DisplayInfo *display_info;
483 #ifdef HAVE_XI2
484 Status status;
485 XIGrabModifiers xi2_modifiers;
486 XIEventMask xievent_mask;
487 #endif
488
489 display_info = myDisplayGetDefault ();
490 myDisplayErrorTrapPush (display_info);
491 result = XGrabKey (display, keycode, modifiers, grab_window,
492 owner_events, grab_mode, paired_device_mode);
493 if (myDisplayErrorTrapPop (display_info) || !result)
494 {
495 return FALSE;
496 }
497
498 #ifdef HAVE_XI2
499 if (devices->xi2_available)
500 {
501 xi2_modifiers.modifiers = xi2_modifier_mask (modifiers);
502 xi2_modifiers.status = 0;
503
504 xfwm_device_fill_xi2_event_mask (&xievent_mask, event_mask);
505 myDisplayErrorTrapPush (display_info);
506 status = XIGrabKeycode (display, devices->keyboard.xi2_device, keycode, grab_window,
507 grab_mode, paired_device_mode, owner_events,
508 &xievent_mask, 1, &xi2_modifiers);
509 g_free (xievent_mask.mask);
510 if (myDisplayErrorTrapPop (display_info) || status != XIGrabSuccess)
511 {
512 return FALSE;
513 }
514 }
515 #endif
516
517 return TRUE;
518 }
519
520 void
xfwm_device_ungrab_keycode(XfwmDevices * devices,Display * display,gint keycode,guint modifiers,Window grab_window)521 xfwm_device_ungrab_keycode (XfwmDevices *devices, Display *display,
522 gint keycode, guint modifiers, Window grab_window)
523 {
524 DisplayInfo *display_info;
525 #ifdef HAVE_XI2
526 XIGrabModifiers xi2_modifiers;
527 #endif
528
529 display_info = myDisplayGetDefault ();
530 myDisplayErrorTrapPush (display_info);
531
532 XUngrabKey (display, keycode, modifiers, grab_window);
533
534 #ifdef HAVE_XI2
535 if (devices->xi2_available)
536 {
537 xi2_modifiers.modifiers = xi2_modifier_mask (modifiers);
538 xi2_modifiers.status = 0;
539
540 XIUngrabKeycode (display, devices->keyboard.xi2_device, keycode,
541 grab_window, 1, &xi2_modifiers);
542 }
543 #endif
544
545 myDisplayErrorTrapPopIgnored (display_info);
546 }
547
548 #ifdef HAVE_XI2
549 typedef struct
550 {
551 XfwmDevices *devices;
552 XfwmEvent *event;
553 XIEventMask xievent_mask;
554 } XI2CheckMaskContext;
555
556 static gboolean
xfwm_device_check_mask_event_xi2_predicate(Display * display,XEvent * xevent,XPointer user_data)557 xfwm_device_check_mask_event_xi2_predicate (Display *display, XEvent *xevent, XPointer user_data)
558 {
559 XI2CheckMaskContext *context = (void *)user_data;
560
561 if (xevent->type == GenericEvent &&
562 xevent->xgeneric.extension == context->devices->xi2_opcode &&
563 XIMaskIsSet (context->xievent_mask.mask, xevent->xgeneric.evtype))
564 {
565 /* GDK holds XI2 event data which we are replacing so it should be released here */
566 XFreeEventData (display, &context->event->meta.xevent->xcookie);
567 return TRUE;
568 }
569
570 return FALSE;
571 }
572 #endif
573
574 gboolean
xfwm_device_check_mask_event(XfwmDevices * devices,Display * display,guint event_mask,XfwmEvent * event)575 xfwm_device_check_mask_event (XfwmDevices *devices, Display *display,
576 guint event_mask, XfwmEvent *event)
577 {
578 gboolean result;
579 #ifdef HAVE_XI2
580 XI2CheckMaskContext context;
581 #endif
582
583 #ifdef HAVE_XI2
584 if (devices->xi2_available && event->meta.device != None)
585 {
586 context.devices = devices;
587 context.event = event;
588 xfwm_device_fill_xi2_event_mask (&context.xievent_mask, event_mask);
589 result = XCheckIfEvent (display, event->meta.xevent,
590 xfwm_device_check_mask_event_xi2_predicate, (XPointer)&context);
591 g_free (context.xievent_mask.mask);
592
593 if (result)
594 {
595 /* Previos data was released in predicate, allocate a new data for the new event */
596 XGetEventData (display, &event->meta.xevent->xcookie);
597 }
598 }
599 else
600 #endif
601 {
602 result = XCheckMaskEvent (display, event_mask, event->meta.xevent);
603 }
604
605 if (result)
606 {
607 xfwm_device_translate_event (devices, event->meta.xevent, event);
608 }
609
610 return result;
611 }
612
613 XfwmDevices *
xfwm_devices_new(GdkDisplay * display)614 xfwm_devices_new (GdkDisplay *display)
615 {
616 XfwmDevices *devices;
617 #ifdef HAVE_XI2
618 GdkSeat *seat;
619 GdkDevice *pointer_device;
620 GdkDevice *keyboard_device;
621 gint firstevent, firsterror;
622 #endif
623
624 devices = g_new0 (XfwmDevices, 1);
625 devices->xi2_available = FALSE;
626 devices->xi2_opcode = 0;
627
628 devices->pointer.keyboard = FALSE;
629 devices->pointer.xi2_device = None;
630
631 devices->keyboard.keyboard = TRUE;
632 devices->keyboard.xi2_device = None;
633
634 #ifdef HAVE_XI2
635 seat = gdk_display_get_default_seat (display);
636 pointer_device = gdk_seat_get_pointer (seat);
637 keyboard_device = gdk_seat_get_keyboard (seat);
638
639 if (GDK_IS_X11_DEVICE_XI2 (pointer_device) || GDK_IS_X11_DEVICE_XI2 (keyboard_device))
640 {
641 /* GDK uses XI2, let's use it too */
642
643 /* Obtain XI2 opcode */
644 if (XQueryExtension (gdk_x11_display_get_xdisplay (display), "XInputExtension",
645 &devices->xi2_opcode, &firstevent, &firsterror))
646 {
647 devices->xi2_available = TRUE;
648 devices->pointer.xi2_device = gdk_x11_device_get_id (pointer_device);
649 devices->keyboard.xi2_device = gdk_x11_device_get_id (keyboard_device);
650 }
651 }
652 #endif
653
654 return devices;
655 }
656