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