XI2: Avoid a crash in event translation
[profile/ivi/clutter.git] / clutter / x11 / clutter-device-manager-xi2.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright © 2011  Intel Corp.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  *
21  * Author: Emmanuele Bassi <ebassi@linux.intel.com>
22  */
23
24 #include "config.h"
25
26 #include <stdint.h>
27
28 #include "clutter-device-manager-xi2.h"
29
30 #include "clutter-backend-x11.h"
31 #include "clutter-input-device-xi2.h"
32 #include "clutter-stage-x11.h"
33
34 #include "clutter-backend.h"
35 #include "clutter-debug.h"
36 #include "clutter-device-manager-private.h"
37 #include "clutter-event-private.h"
38 #include "clutter-event-translator.h"
39 #include "clutter-stage-private.h"
40 #include "clutter-private.h"
41
42 #include <X11/extensions/XInput2.h>
43
44 enum
45 {
46   PROP_0,
47
48   PROP_OPCODE,
49
50   PROP_LAST
51 };
52
53 static GParamSpec *obj_props[PROP_LAST] = { NULL, };
54
55 static const char *clutter_input_axis_atom_names[] = {
56   "Abs X",              /* CLUTTER_INPUT_AXIS_X */
57   "Abs Y",              /* CLUTTER_INPUT_AXIS_Y */
58   "Abs Pressure",       /* CLUTTER_INPUT_AXIS_PRESSURE */
59   "Abs Tilt X",         /* CLUTTER_INPUT_AXIS_XTILT */
60   "Abs Tilt Y",         /* CLUTTER_INPUT_AXIS_YTILT */
61   "Abs Wheel",          /* CLUTTER_INPUT_AXIS_WHEEL */
62   "Abs Distance",       /* CLUTTER_INPUT_AXIS_DISTANCE */
63 };
64
65 #define N_AXIS_ATOMS    G_N_ELEMENTS (clutter_input_axis_atom_names)
66
67 static Atom clutter_input_axis_atoms[N_AXIS_ATOMS] = { 0, };
68
69 static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface);
70
71 #define clutter_device_manager_xi2_get_type     _clutter_device_manager_xi2_get_type
72
73 G_DEFINE_TYPE_WITH_CODE (ClutterDeviceManagerXI2,
74                          clutter_device_manager_xi2,
75                          CLUTTER_TYPE_DEVICE_MANAGER,
76                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_EVENT_TRANSLATOR,
77                                                 clutter_event_translator_iface_init));
78
79 static void
80 translate_valuator_class (Display             *xdisplay,
81                           ClutterInputDevice  *device,
82                           XIValuatorClassInfo *class)
83 {
84   static gboolean atoms_initialized = FALSE;
85   ClutterInputAxis i, axis = CLUTTER_INPUT_AXIS_IGNORE;
86
87   if (G_UNLIKELY (!atoms_initialized))
88     {
89       XInternAtoms (xdisplay,
90                     (char **) clutter_input_axis_atom_names, N_AXIS_ATOMS,
91                     False,
92                     clutter_input_axis_atoms);
93
94       atoms_initialized = TRUE;
95     }
96
97   for (i = CLUTTER_INPUT_AXIS_IGNORE;
98        i < CLUTTER_INPUT_AXIS_LAST;
99        i += 1)
100     {
101       if (clutter_input_axis_atoms[i] == class->label)
102         {
103           axis = i;
104           break;
105         }
106     }
107
108   _clutter_input_device_add_axis (device, axis,
109                                   class->min,
110                                   class->max,
111                                   class->resolution);
112
113   CLUTTER_NOTE (BACKEND,
114                 "Added axis '%s' (min:%.2f, max:%.2fd, res:%d) of device %d",
115                 clutter_input_axis_atom_names[axis],
116                 class->min,
117                 class->max,
118                 class->resolution,
119                 device->id);
120 }
121
122 static void
123 translate_device_classes (Display             *xdisplay,
124                           ClutterInputDevice  *device,
125                           XIAnyClassInfo     **classes,
126                           guint                n_classes)
127 {
128   gint i;
129
130   for (i = 0; i < n_classes; i++)
131     {
132       XIAnyClassInfo *class_info = classes[i];
133
134       switch (class_info->type)
135         {
136         case XIKeyClass:
137           {
138             XIKeyClassInfo *key_info = (XIKeyClassInfo *) class_info;
139             gint j;
140
141             _clutter_input_device_set_n_keys (device,
142                                               key_info->num_keycodes);
143
144             for (j = 0; j < key_info->num_keycodes; j++)
145               {
146                 clutter_input_device_set_key (device, j,
147                                               key_info->keycodes[i],
148                                               0);
149               }
150           }
151           break;
152
153         case XIValuatorClass:
154           translate_valuator_class (xdisplay, device,
155                                     (XIValuatorClassInfo *) class_info);
156           break;
157
158 #ifdef HAVE_XINPUT_2_2
159         case XIScrollClass:
160           {
161             XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info;
162             ClutterScrollDirection direction;
163
164             if (scroll_info->scroll_type == XIScrollTypeVertical)
165               direction = CLUTTER_SCROLL_DOWN;
166             else
167               direction = CLUTTER_SCROLL_RIGHT;
168
169             CLUTTER_NOTE (BACKEND, "Scroll valuator %d: %s, increment: %f",
170                           scroll_info->number,
171                           scroll_info->scroll_type == XIScrollTypeVertical
172                             ? "vertical"
173                             : "horizontal",
174                           scroll_info->increment);
175
176             _clutter_input_device_add_scroll_info (device,
177                                                    scroll_info->number,
178                                                    direction,
179                                                    scroll_info->increment);
180           }
181           break;
182 #endif /* HAVE_XINPUT_2_2 */
183
184         default:
185           break;
186         }
187     }
188 }
189
190 static gboolean
191 is_touch_device (XIAnyClassInfo         **classes,
192                  guint                    n_classes,
193                  ClutterInputDeviceType  *device_type,
194                  guint                   *n_touch_points)
195 {
196 #ifdef HAVE_XINPUT_2_2
197   guint i;
198
199   for (i = 0; i < n_classes; i++)
200     {
201       XITouchClassInfo *class = (XITouchClassInfo *) classes[i];
202
203       if (class->type != XITouchClass)
204         continue;
205
206       if (class->num_touches > 0)
207         {
208           if (class->mode == XIDirectTouch)
209             *device_type = CLUTTER_TOUCHSCREEN_DEVICE;
210           else if (class->mode == XIDependentTouch)
211             *device_type = CLUTTER_TOUCHPAD_DEVICE;
212           else
213             continue;
214
215           *n_touch_points = class->num_touches;
216
217           return TRUE;
218         }
219     }
220 #endif
221
222   return FALSE;
223 }
224
225 static ClutterInputDevice *
226 create_device (ClutterDeviceManagerXI2 *manager_xi2,
227                ClutterBackendX11       *backend_x11,
228                XIDeviceInfo            *info)
229 {
230   ClutterInputDeviceType source, touch_source;
231   ClutterInputDevice *retval;
232   ClutterInputMode mode;
233   gboolean is_enabled;
234   guint num_touches = 0;
235
236   if (info->use == XIMasterKeyboard || info->use == XISlaveKeyboard)
237     source = CLUTTER_KEYBOARD_DEVICE;
238   else if (info->use == XISlavePointer &&
239            is_touch_device (info->classes, info->num_classes,
240                             &touch_source,
241                             &num_touches))
242     {
243       source = touch_source;
244     }
245   else
246     {
247       gchar *name;
248
249       name = g_ascii_strdown (info->name, -1);
250
251       if (strstr (name, "eraser") != NULL)
252         source = CLUTTER_ERASER_DEVICE;
253       else if (strstr (name, "cursor") != NULL)
254         source = CLUTTER_CURSOR_DEVICE;
255       else if (strstr (name, "wacom") != NULL || strstr (name, "pen") != NULL)
256         source = CLUTTER_PEN_DEVICE;
257       else
258         source = CLUTTER_POINTER_DEVICE;
259
260       g_free (name);
261     }
262
263   switch (info->use)
264     {
265     case XIMasterKeyboard:
266     case XIMasterPointer:
267       mode = CLUTTER_INPUT_MODE_MASTER;
268       is_enabled = TRUE;
269       break;
270
271     case XISlaveKeyboard:
272     case XISlavePointer:
273       mode = CLUTTER_INPUT_MODE_SLAVE;
274       is_enabled = FALSE;
275       break;
276
277     case XIFloatingSlave:
278     default:
279       mode = CLUTTER_INPUT_MODE_FLOATING;
280       is_enabled = FALSE;
281       break;
282     }
283
284   retval = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_XI2,
285                          "name", info->name,
286                          "id", info->deviceid,
287                          "has-cursor", (info->use == XIMasterPointer),
288                          "device-manager", manager_xi2,
289                          "device-type", source,
290                          "device-mode", mode,
291                          "backend", backend_x11,
292                          "enabled", is_enabled,
293                          NULL);
294
295   translate_device_classes (backend_x11->xdpy, retval,
296                             info->classes,
297                             info->num_classes);
298
299   CLUTTER_NOTE (BACKEND, "Created device '%s' (id: %d, has-cursor: %s)",
300                 info->name,
301                 info->deviceid,
302                 info->use == XIMasterPointer ? "yes" : "no");
303
304   return retval;
305 }
306
307 static ClutterInputDevice *
308 add_device (ClutterDeviceManagerXI2 *manager_xi2,
309             ClutterBackendX11       *backend_x11,
310             XIDeviceInfo            *info,
311             gboolean                 in_construction)
312 {
313   ClutterInputDevice *device;
314
315   device = create_device (manager_xi2, backend_x11, info);
316
317   /* we don't go through the DeviceManager::add_device() vfunc because
318    * that emits the signal, and we only do it conditionally
319    */
320   g_hash_table_replace (manager_xi2->devices_by_id,
321                         GINT_TO_POINTER (info->deviceid),
322                         g_object_ref (device));
323
324   if (info->use == XIMasterPointer ||
325       info->use == XIMasterKeyboard)
326     {
327       manager_xi2->master_devices =
328         g_list_prepend (manager_xi2->master_devices, device);
329     }
330   else if (info->use == XISlavePointer ||
331            info->use == XISlaveKeyboard ||
332            info->use == XIFloatingSlave)
333     {
334       manager_xi2->slave_devices =
335         g_list_prepend (manager_xi2->slave_devices, device);
336     }
337   else
338     g_warning ("Unhandled device: %s",
339                clutter_input_device_get_device_name (device));
340
341   /* relationships between devices and signal emissions are not
342    * necessary while we're constructing the device manager instance
343    */
344   if (!in_construction)
345     {
346       if (info->use == XISlavePointer || info->use == XISlaveKeyboard)
347         {
348           ClutterInputDevice *master;
349
350           master = g_hash_table_lookup (manager_xi2->devices_by_id,
351                                         GINT_TO_POINTER (info->attachment));
352           _clutter_input_device_set_associated_device (device, master);
353           _clutter_input_device_add_slave (master, device);
354         }
355
356       /* blow the cache */
357       g_slist_free (manager_xi2->all_devices);
358       manager_xi2->all_devices = NULL;
359
360       g_signal_emit_by_name (manager_xi2, "device-added", device);
361     }
362
363   return device;
364 }
365
366 static void
367 remove_device (ClutterDeviceManagerXI2 *manager_xi2,
368                gint                     device_id)
369 {
370   ClutterInputDevice *device;
371
372   device = g_hash_table_lookup (manager_xi2->devices_by_id,
373                                 GINT_TO_POINTER (device_id));
374
375   if (device != NULL)
376     {
377       manager_xi2->master_devices =
378         g_list_remove (manager_xi2->master_devices, device);
379       manager_xi2->slave_devices =
380         g_list_remove (manager_xi2->slave_devices, device);
381
382       /* blow the cache */
383       g_slist_free (manager_xi2->all_devices);
384       manager_xi2->all_devices = NULL;
385
386       g_signal_emit_by_name (manager_xi2, "device-removed", device);
387
388       g_object_run_dispose (G_OBJECT (device));
389
390       g_hash_table_remove (manager_xi2->devices_by_id,
391                            GINT_TO_POINTER (device_id));
392     }
393 }
394
395 static void
396 translate_hierarchy_event (ClutterBackendX11       *backend_x11,
397                            ClutterDeviceManagerXI2 *manager_xi2,
398                            XIHierarchyEvent        *ev)
399 {
400   int i;
401
402   for (i = 0; i < ev->num_info; i++)
403     {
404       if (ev->info[i].flags & XIDeviceEnabled)
405         {
406           XIDeviceInfo *info;
407           int n_devices;
408
409           CLUTTER_NOTE (EVENT, "Hierarchy event: device enabled");
410
411           info = XIQueryDevice (backend_x11->xdpy,
412                                 ev->info[i].deviceid,
413                                 &n_devices);
414           add_device (manager_xi2, backend_x11, &info[0], FALSE);
415         }
416       else if (ev->info[i].flags & XIDeviceDisabled)
417         {
418           CLUTTER_NOTE (EVENT, "Hierarchy event: device disabled");
419
420           remove_device (manager_xi2, ev->info[i].deviceid);
421         }
422       else if ((ev->info[i].flags & XISlaveAttached) ||
423                (ev->info[i].flags & XISlaveDetached))
424         {
425           ClutterInputDevice *master, *slave;
426           XIDeviceInfo *info;
427           int n_devices;
428           gboolean send_changed = FALSE;
429
430           CLUTTER_NOTE (EVENT, "Hierarchy event: slave %s",
431                         (ev->info[i].flags & XISlaveAttached)
432                           ? "attached"
433                           : "detached");
434
435           slave = g_hash_table_lookup (manager_xi2->devices_by_id,
436                                        GINT_TO_POINTER (ev->info[i].deviceid));
437           master = clutter_input_device_get_associated_device (slave);
438
439           /* detach the slave in both cases */
440           if (master != NULL)
441             {
442               _clutter_input_device_remove_slave (master, slave);
443               _clutter_input_device_set_associated_device (slave, NULL);
444
445               send_changed = TRUE;
446             }
447
448           /* and attach the slave to the new master if needed */
449           if (ev->info[i].flags & XISlaveAttached)
450             {
451               info = XIQueryDevice (backend_x11->xdpy,
452                                     ev->info[i].deviceid,
453                                     &n_devices);
454               master = g_hash_table_lookup (manager_xi2->devices_by_id,
455                                             GINT_TO_POINTER (info->attachment));
456               _clutter_input_device_set_associated_device (slave, master);
457               _clutter_input_device_add_slave (master, slave);
458
459               send_changed = TRUE;
460               XIFreeDeviceInfo (info);
461             }
462
463           if (send_changed)
464             {
465               ClutterStage *stage = _clutter_input_device_get_stage (master);
466               if (stage != NULL)
467                 _clutter_stage_x11_events_device_changed (CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage)), 
468                                                           master,
469                                                           CLUTTER_DEVICE_MANAGER (manager_xi2));
470             }
471         }
472     }
473 }
474
475 static void
476 clutter_device_manager_xi2_select_events (ClutterDeviceManager *manager,
477                                           Window                xwindow,
478                                           XIEventMask          *event_mask)
479 {
480   Display *xdisplay;
481
482   xdisplay = clutter_x11_get_default_display ();
483
484   XISelectEvents (xdisplay, xwindow, event_mask, 1);
485 }
486
487 static ClutterStage *
488 get_event_stage (ClutterEventTranslator *translator,
489                  XIEvent                *xi_event)
490 {
491   Window xwindow = None;
492
493   switch (xi_event->evtype)
494     {
495     case XI_KeyPress:
496     case XI_KeyRelease:
497     case XI_ButtonPress:
498     case XI_ButtonRelease:
499     case XI_Motion:
500 #ifdef HAVE_XINPUT_2_2
501     case XI_TouchBegin:
502     case XI_TouchUpdate:
503     case XI_TouchEnd:
504 #endif /* HAVE_XINPUT_2_2 */
505       {
506         XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
507
508         xwindow = xev->event;
509       }
510       break;
511
512     case XI_Enter:
513     case XI_Leave:
514     case XI_FocusIn:
515     case XI_FocusOut:
516       {
517         XIEnterEvent *xev = (XIEnterEvent *) xi_event;
518
519         xwindow = xev->event;
520       }
521       break;
522
523     default:
524       break;
525     }
526
527   if (xwindow == None)
528     return NULL;
529
530   return clutter_x11_get_stage_from_window (xwindow);
531 }
532
533 /*
534  * print_key_sym: Translate a symbol to its printable form if any
535  * @symbol: the symbol to translate
536  * @buffer: the buffer where to put the translated string
537  * @len: size of the buffer
538  *
539  * Translates @symbol into a printable representation in @buffer, if possible.
540  *
541  * Return value: The number of bytes of the translated string, 0 if the
542  *               symbol can't be printed
543  *
544  * Note: The code is derived from libX11's src/KeyBind.c
545  *       Copyright 1985, 1987, 1998  The Open Group
546  *
547  * Note: This code works for Latin-1 symbols. clutter_keysym_to_unicode()
548  *       does the work for the other keysyms.
549  */
550 static int
551 print_keysym (uint32_t symbol,
552               char    *buffer,
553               int      len)
554 {
555   unsigned long high_bytes;
556   unsigned char c;
557
558   high_bytes = symbol >> 8;
559   if (!(len &&
560         ((high_bytes == 0) ||
561          ((high_bytes == 0xFF) &&
562           (((symbol >= CLUTTER_KEY_BackSpace) &&
563             (symbol <= CLUTTER_KEY_Clear)) ||
564            (symbol == CLUTTER_KEY_Return) ||
565            (symbol == CLUTTER_KEY_Escape) ||
566            (symbol == CLUTTER_KEY_KP_Space) ||
567            (symbol == CLUTTER_KEY_KP_Tab) ||
568            (symbol == CLUTTER_KEY_KP_Enter) ||
569            ((symbol >= CLUTTER_KEY_KP_Multiply) &&
570             (symbol <= CLUTTER_KEY_KP_9)) ||
571            (symbol == CLUTTER_KEY_KP_Equal) ||
572            (symbol == CLUTTER_KEY_Delete))))))
573     return 0;
574
575   /* if X keysym, convert to ascii by grabbing low 7 bits */
576   if (symbol == CLUTTER_KEY_KP_Space)
577     c = CLUTTER_KEY_space & 0x7F; /* patch encoding botch */
578   else if (high_bytes == 0xFF)
579     c = symbol & 0x7F;
580   else
581     c = symbol & 0xFF;
582
583   buffer[0] = c;
584   return 1;
585 }
586
587 static gdouble *
588 translate_axes (ClutterInputDevice *device,
589                 gdouble             x,
590                 gdouble             y,
591                 ClutterStageX11    *stage_x11,
592                 XIValuatorState    *valuators)
593 {
594   guint n_axes = clutter_input_device_get_n_axes (device);
595   guint i;
596   gdouble *retval;
597   double *values;
598
599   retval = g_new0 (gdouble, n_axes);
600   values = valuators->values;
601
602   for (i = 0; i < valuators->mask_len * 8; i++)
603     {
604       ClutterInputAxis axis;
605       gdouble val;
606
607       if (!XIMaskIsSet (valuators->mask, i))
608         continue;
609
610       axis = clutter_input_device_get_axis (device, i);
611       val = *values++;
612
613       switch (axis)
614         {
615         case CLUTTER_INPUT_AXIS_X:
616           retval[i] = x;
617           break;
618
619         case CLUTTER_INPUT_AXIS_Y:
620           retval[i] = y;
621           break;
622
623         default:
624           _clutter_input_device_translate_axis (device, i, val, &retval[i]);
625           break;
626         }
627     }
628
629   return retval;
630 }
631
632 static gdouble
633 scroll_valuators_changed (ClutterInputDevice *device,
634                           XIValuatorState    *valuators,
635                           gdouble            *dx_p,
636                           gdouble            *dy_p)
637 {
638   gboolean retval = FALSE;
639   guint n_axes, n_val, i;
640   double *values;
641
642   n_axes = clutter_input_device_get_n_axes (device);
643   values = valuators->values;
644
645   *dx_p = *dy_p = 0.0;
646
647   n_val = 0;
648
649   for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++)
650     {
651       ClutterScrollDirection direction;
652       gdouble delta;
653
654       if (!XIMaskIsSet (valuators->mask, i))
655         continue;
656
657       if (_clutter_input_device_get_scroll_delta (device, i,
658                                                   values[n_val],
659                                                   &direction,
660                                                   &delta))
661         {
662           retval = TRUE;
663
664           if (direction == CLUTTER_SCROLL_UP ||
665               direction == CLUTTER_SCROLL_DOWN)
666             *dy_p = delta;
667           else
668             *dx_p = delta;
669         }
670
671       n_val += 1;
672     }
673
674   return retval;
675 }
676
677 static ClutterTranslateReturn
678 clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator,
679                                             gpointer                native,
680                                             ClutterEvent           *event)
681 {
682   ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (translator);
683   ClutterTranslateReturn retval = CLUTTER_TRANSLATE_CONTINUE;
684   ClutterBackendX11 *backend_x11;
685   ClutterStageX11 *stage_x11 = NULL;
686   ClutterStage *stage = NULL;
687   ClutterInputDevice *device, *source_device;
688   XGenericEventCookie *cookie;
689   XIEvent *xi_event;
690   XEvent *xevent;
691
692   backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ());
693
694   xevent = native;
695
696   cookie = &xevent->xcookie;
697
698   if (cookie->type != GenericEvent ||
699       cookie->extension != manager_xi2->opcode)
700     return CLUTTER_TRANSLATE_CONTINUE;
701
702   xi_event = (XIEvent *) cookie->data;
703
704   if (!xi_event)
705     return CLUTTER_TRANSLATE_REMOVE;
706
707   if (!(xi_event->evtype == XI_HierarchyChanged ||
708         xi_event->evtype == XI_DeviceChanged))
709     {
710       stage = get_event_stage (translator, xi_event);
711       if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage))
712         return CLUTTER_TRANSLATE_CONTINUE;
713       else
714         stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage));
715     }
716
717   event->any.stage = stage;
718
719   switch (xi_event->evtype)
720     {
721     case XI_HierarchyChanged:
722       {
723         XIHierarchyEvent *xev = (XIHierarchyEvent *) xi_event;
724
725         translate_hierarchy_event (backend_x11, manager_xi2, xev);
726       }
727       retval = CLUTTER_TRANSLATE_REMOVE;
728       break;
729
730     case XI_DeviceChanged:
731       {
732         XIDeviceChangedEvent *xev = (XIDeviceChangedEvent *) xi_event;
733
734         device = g_hash_table_lookup (manager_xi2->devices_by_id,
735                                       GINT_TO_POINTER (xev->deviceid));
736         if (device)
737           {
738             _clutter_input_device_reset_axes (device);
739             _clutter_input_device_reset_scroll_info (device);
740             translate_device_classes (backend_x11->xdpy,
741                                       device,
742                                       xev->classes,
743                                       xev->num_classes);
744           }
745       }
746       retval = CLUTTER_TRANSLATE_REMOVE;
747       break;
748
749     case XI_KeyPress:
750     case XI_KeyRelease:
751       {
752         XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
753         ClutterEventX11 *event_x11;
754         char buffer[7] = { 0, };
755         gunichar n;
756
757         event->key.type = event->type = (xev->evtype == XI_KeyPress)
758                                       ? CLUTTER_KEY_PRESS
759                                       : CLUTTER_KEY_RELEASE;
760
761         event->key.time = xev->time;
762         event->key.stage = stage;
763         event->key.modifier_state =
764           _clutter_input_device_xi2_translate_state (&xev->mods, &xev->buttons);
765         event->key.hardware_keycode = xev->detail;
766
767           /* keyval is the key ignoring all modifiers ('1' vs. '!') */
768         event->key.keyval =
769           _clutter_keymap_x11_translate_key_state (backend_x11->keymap,
770                                                    event->key.hardware_keycode,
771                                                    event->key.modifier_state,
772                                                    NULL);
773
774         /* KeyEvents have platform specific data associated to them */
775         event_x11 = _clutter_event_x11_new ();
776         _clutter_event_set_platform_data (event, event_x11);
777
778         event_x11->key_group =
779           _clutter_keymap_x11_get_key_group (backend_x11->keymap,
780                                              event->key.modifier_state);
781         event_x11->key_is_modifier =
782           _clutter_keymap_x11_get_is_modifier (backend_x11->keymap,
783                                                event->key.hardware_keycode);
784         event_x11->num_lock_set =
785           _clutter_keymap_x11_get_num_lock_state (backend_x11->keymap);
786         event_x11->caps_lock_set =
787           _clutter_keymap_x11_get_caps_lock_state (backend_x11->keymap);
788
789         source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
790                                              GINT_TO_POINTER (xev->sourceid));
791         clutter_event_set_source_device (event, source_device);
792
793         device = g_hash_table_lookup (manager_xi2->devices_by_id,
794                                       GINT_TO_POINTER (xev->deviceid));
795         clutter_event_set_device (event, device);
796
797         /* XXX keep this in sync with the evdev device manager */
798         n = print_keysym (event->key.keyval, buffer, sizeof (buffer));
799         if (n == 0)
800           {
801             /* not printable */
802             event->key.unicode_value = (gunichar) '\0';
803           }
804         else
805           {
806             event->key.unicode_value = g_utf8_get_char_validated (buffer, n);
807             if (event->key.unicode_value == -1 ||
808                 event->key.unicode_value == -2)
809               event->key.unicode_value = (gunichar) '\0';
810           }
811
812         CLUTTER_NOTE (EVENT,
813                       "%s: win:0x%x device:%d source:%d, key: %12s (%d)",
814                       event->any.type == CLUTTER_KEY_PRESS
815                         ? "key press  "
816                         : "key release",
817                       (unsigned int) stage_x11->xwin,
818                       xev->deviceid,
819                       xev->sourceid,
820                       event->key.keyval ? buffer : "(none)",
821                       event->key.keyval);
822
823         if (xi_event->evtype == XI_KeyPress)
824           _clutter_stage_x11_set_user_time (stage_x11, event->key.time);
825
826         retval = CLUTTER_TRANSLATE_QUEUE;
827       }
828       break;
829
830     case XI_ButtonPress:
831     case XI_ButtonRelease:
832       {
833         XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
834
835         switch (xev->detail)
836           {
837           case 4:
838           case 5:
839           case 6:
840           case 7:
841             event->scroll.type = event->type = CLUTTER_SCROLL;
842
843             if (xev->detail == 4)
844               event->scroll.direction = CLUTTER_SCROLL_UP;
845             else if (xev->detail == 5)
846               event->scroll.direction = CLUTTER_SCROLL_DOWN;
847             else if (xev->detail == 6)
848               event->scroll.direction = CLUTTER_SCROLL_LEFT;
849             else
850               event->scroll.direction = CLUTTER_SCROLL_RIGHT;
851
852             event->scroll.stage = stage;
853
854             event->scroll.time = xev->time;
855             event->scroll.x = xev->event_x;
856             event->scroll.y = xev->event_y;
857             event->scroll.modifier_state =
858               _clutter_input_device_xi2_translate_state (&xev->mods,
859                                                          &xev->buttons);
860
861             source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
862                                                  GINT_TO_POINTER (xev->sourceid));
863             clutter_event_set_source_device (event, source_device);
864
865             device = g_hash_table_lookup (manager_xi2->devices_by_id,
866                                           GINT_TO_POINTER (xev->deviceid));
867             clutter_event_set_device (event, device);
868
869             event->scroll.axes = translate_axes (event->scroll.device,
870                                                  event->scroll.x,
871                                                  event->scroll.y,
872                                                  stage_x11,
873                                                  &xev->valuators);
874
875 #ifdef HAVE_XINPUT_2_2
876             if (xev->flags & XIPointerEmulated)
877               _clutter_event_set_pointer_emulated (event, TRUE);
878 #endif /* HAVE_XINPUT_2_2 */
879             break;
880
881           default:
882             event->button.type = event->type =
883               (xi_event->evtype == XI_ButtonPress) ? CLUTTER_BUTTON_PRESS
884                                                    : CLUTTER_BUTTON_RELEASE;
885
886             event->button.stage = stage;
887
888             event->button.time = xev->time;
889             event->button.x = xev->event_x;
890             event->button.y = xev->event_y;
891             event->button.button = xev->detail;
892             event->button.modifier_state =
893               _clutter_input_device_xi2_translate_state (&xev->mods,
894                                                          &xev->buttons);
895
896             source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
897                                                  GINT_TO_POINTER (xev->sourceid));
898             clutter_event_set_source_device (event, source_device);
899
900             device = g_hash_table_lookup (manager_xi2->devices_by_id,
901                                           GINT_TO_POINTER (xev->deviceid));
902             clutter_event_set_device (event, device);
903
904             event->button.axes = translate_axes (event->button.device,
905                                                  event->button.x,
906                                                  event->button.y,
907                                                  stage_x11,
908                                                  &xev->valuators);
909             break;
910           }
911
912         if (source_device != NULL && device->stage != NULL)
913           _clutter_input_device_set_stage (source_device, device->stage);
914
915         CLUTTER_NOTE (EVENT,
916                       "%s: win:0x%x, device:%s (button:%d, x:%.2f, y:%.2f, axes:%s)",
917                       event->any.type == CLUTTER_BUTTON_PRESS
918                         ? "button press  "
919                         : "button release",
920                       (unsigned int) stage_x11->xwin,
921                       event->button.device->device_name,
922                       event->button.button,
923                       event->button.x,
924                       event->button.y,
925                       event->button.axes != NULL ? "yes" : "no");
926
927 #ifdef HAVE_XINPUT_2_2
928         if (xev->flags & XIPointerEmulated)
929           _clutter_event_set_pointer_emulated (event, TRUE);
930 #endif /* HAVE_XINPUT_2_2 */
931
932         if (xi_event->evtype == XI_ButtonPress)
933           _clutter_stage_x11_set_user_time (stage_x11, event->button.time);
934
935         retval = CLUTTER_TRANSLATE_QUEUE;
936       }
937       break;
938
939     case XI_Motion:
940       {
941         XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
942         gdouble delta_x, delta_y;
943
944         source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
945                                              GINT_TO_POINTER (xev->sourceid));
946
947         if (scroll_valuators_changed (source_device,
948                                       &xev->valuators,
949                                       &delta_x, &delta_y))
950           {
951             event->scroll.type = event->type = CLUTTER_SCROLL;
952             event->scroll.direction = CLUTTER_SCROLL_SMOOTH;
953
954             event->scroll.stage = stage;
955             event->scroll.time = xev->time;
956             event->scroll.x = xev->event_x;
957             event->scroll.y = xev->event_y;
958             event->scroll.modifier_state =
959               _clutter_input_device_xi2_translate_state (&xev->mods,
960                                                          &xev->buttons);
961
962             clutter_event_set_scroll_delta (event, delta_x, delta_y);
963             clutter_event_set_source_device (event, source_device);
964
965             device = g_hash_table_lookup (manager_xi2->devices_by_id,
966                                           GINT_TO_POINTER (xev->deviceid));
967             clutter_event_set_device (event, device);
968
969             CLUTTER_NOTE (EVENT,
970                           "smooth scroll: win:0x%x device:%s (x:%.2f, y:%.2f, delta:%f, %f)",
971                           (unsigned int) stage_x11->xwin,
972                           event->scroll.device->device_name,
973                           event->scroll.x,
974                           event->scroll.y,
975                           delta_x, delta_y);
976
977             retval = CLUTTER_TRANSLATE_QUEUE;
978             break;
979           }
980
981         event->motion.type = event->type = CLUTTER_MOTION;
982
983         event->motion.stage = stage;
984
985         event->motion.time = xev->time;
986         event->motion.x = xev->event_x;
987         event->motion.y = xev->event_y;
988         event->motion.modifier_state =
989           _clutter_input_device_xi2_translate_state (&xev->mods,
990                                                      &xev->buttons);
991
992         clutter_event_set_source_device (event, source_device);
993
994         device = g_hash_table_lookup (manager_xi2->devices_by_id,
995                                       GINT_TO_POINTER (xev->deviceid));
996         clutter_event_set_device (event, device);
997
998         event->motion.axes = translate_axes (event->motion.device,
999                                              event->motion.x,
1000                                              event->motion.y,
1001                                              stage_x11,
1002                                              &xev->valuators);
1003
1004         if (source_device != NULL && device->stage != NULL)
1005           _clutter_input_device_set_stage (source_device, device->stage);
1006
1007 #ifdef HAVE_XINPUT_2_2
1008         if (xev->flags & XIPointerEmulated)
1009           _clutter_event_set_pointer_emulated (event, TRUE);
1010 #endif /* HAVE_XINPUT_2_2 */
1011
1012         CLUTTER_NOTE (EVENT, "motion: win:0x%x device:%s (x:%.2f, y:%.2f, axes:%s)",
1013                       (unsigned int) stage_x11->xwin,
1014                       event->motion.device->device_name,
1015                       event->motion.x,
1016                       event->motion.y,
1017                       event->motion.axes != NULL ? "yes" : "no");
1018
1019         retval = CLUTTER_TRANSLATE_QUEUE;
1020       }
1021       break;
1022
1023 #ifdef HAVE_XINPUT_2_2
1024     case XI_TouchBegin:
1025     case XI_TouchEnd:
1026       {
1027         XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
1028
1029         source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
1030                                              GINT_TO_POINTER (xev->sourceid));
1031
1032         if (xi_event->evtype == XI_TouchBegin)
1033           event->touch.type = event->type = CLUTTER_TOUCH_BEGIN;
1034         else
1035           event->touch.type = event->type = CLUTTER_TOUCH_END;
1036
1037         event->touch.stage = stage;
1038         event->touch.time = xev->time;
1039         event->touch.x = xev->event_x;
1040         event->touch.y = xev->event_y;
1041         event->touch.modifier_state =
1042           _clutter_input_device_xi2_translate_state (&xev->mods,
1043                                                      &xev->buttons);
1044
1045         clutter_event_set_source_device (event, source_device);
1046
1047         device = g_hash_table_lookup (manager_xi2->devices_by_id,
1048                                       GINT_TO_POINTER (xev->deviceid));
1049         clutter_event_set_device (event, device);
1050
1051         event->touch.axes = translate_axes (event->touch.device,
1052                                             event->motion.x,
1053                                             event->motion.y,
1054                                             stage_x11,
1055                                             &xev->valuators);
1056
1057         if (source_device != NULL && device->stage != NULL)
1058           _clutter_input_device_set_stage (source_device, device->stage);
1059
1060         if (xi_event->evtype == XI_TouchBegin)
1061           {
1062             event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;
1063
1064             _clutter_stage_x11_set_user_time (stage_x11, event->touch.time);
1065           }
1066
1067         event->touch.sequence = GUINT_TO_POINTER (xev->detail);
1068
1069         if (xev->flags & XITouchEmulatingPointer)
1070           _clutter_event_set_pointer_emulated (event, TRUE);
1071
1072         CLUTTER_NOTE (EVENT, "touch %s: win:0x%x device:%s (x:%.2f, y:%.2f, axes:%s)",
1073                       event->type == CLUTTER_TOUCH_BEGIN ? "begin" : "end",
1074                       (unsigned int) stage_x11->xwin,
1075                       event->touch.device->device_name,
1076                       event->touch.x,
1077                       event->touch.y,
1078                       event->touch.axes != NULL ? "yes" : "no");
1079
1080         retval = CLUTTER_TRANSLATE_QUEUE;
1081       }
1082       break;
1083
1084     case XI_TouchUpdate:
1085       {
1086         XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
1087
1088         source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
1089                                              GINT_TO_POINTER (xev->sourceid));
1090
1091         event->touch.type = event->type = CLUTTER_TOUCH_UPDATE;
1092         event->touch.stage = stage;
1093         event->touch.time = xev->time;
1094         event->touch.sequence = GUINT_TO_POINTER (xev->detail);
1095         event->touch.x = xev->event_x;
1096         event->touch.y = xev->event_y;
1097
1098         clutter_event_set_source_device (event, source_device);
1099
1100         device = g_hash_table_lookup (manager_xi2->devices_by_id,
1101                                       GINT_TO_POINTER (xev->deviceid));
1102         clutter_event_set_device (event, device);
1103
1104         event->touch.axes = translate_axes (event->touch.device,
1105                                             event->motion.x,
1106                                             event->motion.y,
1107                                             stage_x11,
1108                                             &xev->valuators);
1109
1110         if (source_device != NULL && device->stage != NULL)
1111           _clutter_input_device_set_stage (source_device, device->stage);
1112
1113         event->touch.modifier_state =
1114           _clutter_input_device_xi2_translate_state (&xev->mods,
1115                                                      &xev->buttons);
1116         event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;
1117
1118         if (xev->flags & XITouchEmulatingPointer)
1119           _clutter_event_set_pointer_emulated (event, TRUE);
1120
1121         CLUTTER_NOTE (EVENT, "touch update: win:0x%x device:%s (x:%.2f, y:%.2f, axes:%s)",
1122                       (unsigned int) stage_x11->xwin,
1123                       event->touch.device->device_name,
1124                       event->touch.x,
1125                       event->touch.y,
1126                       event->touch.axes != NULL ? "yes" : "no");
1127
1128         retval = CLUTTER_TRANSLATE_QUEUE;
1129       }
1130       break;
1131 #endif /* HAVE_XINPUT_2_2 */
1132
1133     case XI_Enter:
1134     case XI_Leave:
1135       {
1136         XIEnterEvent *xev = (XIEnterEvent *) xi_event;
1137
1138         device = g_hash_table_lookup (manager_xi2->devices_by_id,
1139                                       GINT_TO_POINTER (xev->deviceid));
1140
1141         source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
1142                                              GINT_TO_POINTER (xev->sourceid));
1143
1144         if (xi_event->evtype == XI_Enter)
1145           {
1146             event->crossing.type = event->type = CLUTTER_ENTER;
1147
1148             event->crossing.stage = stage;
1149             event->crossing.source = CLUTTER_ACTOR (stage);
1150             event->crossing.related = NULL;
1151
1152             event->crossing.time = xev->time;
1153             event->crossing.x = xev->event_x;
1154             event->crossing.y = xev->event_y;
1155
1156             _clutter_stage_add_device (stage, device);
1157           }
1158         else
1159           {
1160             if (device->stage == NULL)
1161               {
1162                 CLUTTER_NOTE (EVENT,
1163                               "Discarding Leave for ButtonRelease "
1164                               "event off-stage");
1165
1166                 retval = CLUTTER_TRANSLATE_REMOVE;
1167                 break;
1168               }
1169
1170             event->crossing.type = event->type = CLUTTER_LEAVE;
1171
1172             event->crossing.stage = stage;
1173             event->crossing.source = CLUTTER_ACTOR (stage);
1174             event->crossing.related = NULL;
1175
1176             event->crossing.time = xev->time;
1177             event->crossing.x = xev->event_x;
1178             event->crossing.y = xev->event_y;
1179
1180             _clutter_stage_remove_device (stage, device);
1181           }
1182
1183         _clutter_input_device_reset_scroll_info (source_device);
1184
1185         clutter_event_set_device (event, device);
1186         clutter_event_set_source_device (event, source_device);
1187
1188         retval = CLUTTER_TRANSLATE_QUEUE;
1189       }
1190       break;
1191
1192     case XI_FocusIn:
1193     case XI_FocusOut:
1194       retval = CLUTTER_TRANSLATE_CONTINUE;
1195       break;
1196     }
1197
1198   return retval;
1199 }
1200
1201 static void
1202 clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
1203 {
1204   iface->translate_event = clutter_device_manager_xi2_translate_event;
1205 }
1206
1207 static void
1208 clutter_device_manager_xi2_add_device (ClutterDeviceManager *manager,
1209                                        ClutterInputDevice   *device)
1210 {
1211   /* XXX implement */
1212 }
1213
1214 static void
1215 clutter_device_manager_xi2_remove_device (ClutterDeviceManager *manager,
1216                                           ClutterInputDevice   *device)
1217 {
1218   /* XXX implement */
1219 }
1220
1221 static const GSList *
1222 clutter_device_manager_xi2_get_devices (ClutterDeviceManager *manager)
1223 {
1224   ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager);
1225   GSList *all_devices = NULL;
1226   GList *l;
1227
1228   if (manager_xi2->all_devices != NULL)
1229     return manager_xi2->all_devices;
1230
1231   for (l = manager_xi2->master_devices; l != NULL; l = l->next)
1232     all_devices = g_slist_prepend (all_devices, l->data);
1233
1234   for (l = manager_xi2->slave_devices; l != NULL; l = l->next)
1235     all_devices = g_slist_prepend (all_devices, l->data);
1236
1237   manager_xi2->all_devices = g_slist_reverse (all_devices);
1238
1239   return manager_xi2->all_devices;
1240 }
1241
1242 static ClutterInputDevice *
1243 clutter_device_manager_xi2_get_device (ClutterDeviceManager *manager,
1244                                        gint                  id)
1245 {
1246   ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager);
1247
1248   return g_hash_table_lookup (manager_xi2->devices_by_id,
1249                               GINT_TO_POINTER (id));
1250 }
1251
1252 static ClutterInputDevice *
1253 clutter_device_manager_xi2_get_core_device (ClutterDeviceManager   *manager,
1254                                             ClutterInputDeviceType  device_type)
1255 {
1256   ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager);
1257   ClutterBackendX11 *backend_x11;
1258   ClutterInputDevice *device;
1259   int device_id;
1260
1261   backend_x11 =
1262     CLUTTER_BACKEND_X11 (_clutter_device_manager_get_backend (manager));
1263
1264   XIGetClientPointer (backend_x11->xdpy, None, &device_id);
1265
1266   device = g_hash_table_lookup (manager_xi2->devices_by_id,
1267                                 GINT_TO_POINTER (device_id));
1268
1269   switch (device_type)
1270     {
1271     case CLUTTER_POINTER_DEVICE:
1272       return device;
1273
1274     case CLUTTER_KEYBOARD_DEVICE:
1275       return clutter_input_device_get_associated_device (device);
1276
1277     default:
1278       break;
1279     }
1280
1281   return NULL;
1282 }
1283
1284 static void
1285 relate_masters (gpointer key,
1286                 gpointer value,
1287                 gpointer data)
1288 {
1289   ClutterDeviceManagerXI2 *manager_xi2 = data;
1290   ClutterInputDevice *device, *relative;
1291
1292   device = g_hash_table_lookup (manager_xi2->devices_by_id, key);
1293   relative = g_hash_table_lookup (manager_xi2->devices_by_id, value);
1294
1295   _clutter_input_device_set_associated_device (device, relative);
1296   _clutter_input_device_set_associated_device (relative, device);
1297 }
1298
1299 static void
1300 relate_slaves (gpointer key,
1301                gpointer value,
1302                gpointer data)
1303 {
1304   ClutterDeviceManagerXI2 *manager_xi2 = data;
1305   ClutterInputDevice *master, *slave;
1306
1307   master = g_hash_table_lookup (manager_xi2->devices_by_id, key);
1308   slave = g_hash_table_lookup (manager_xi2->devices_by_id, value);
1309
1310   _clutter_input_device_set_associated_device (slave, master);
1311   _clutter_input_device_add_slave (master, slave);
1312 }
1313
1314 static void
1315 clutter_device_manager_xi2_constructed (GObject *gobject)
1316 {
1317   ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (gobject);
1318   ClutterDeviceManager *manager = CLUTTER_DEVICE_MANAGER (gobject);
1319   ClutterBackendX11 *backend_x11;
1320   GHashTable *masters, *slaves;
1321   XIDeviceInfo *info;
1322   XIEventMask event_mask;
1323   unsigned char mask[2] = { 0, };
1324   int n_devices, i;
1325
1326   backend_x11 =
1327     CLUTTER_BACKEND_X11 (_clutter_device_manager_get_backend (manager));
1328
1329   masters = g_hash_table_new (NULL, NULL);
1330   slaves = g_hash_table_new (NULL, NULL);
1331
1332   info = XIQueryDevice (backend_x11->xdpy, XIAllDevices, &n_devices);
1333
1334   for (i = 0; i < n_devices; i++)
1335     {
1336       XIDeviceInfo *xi_device = &info[i];
1337
1338       add_device (manager_xi2, backend_x11, xi_device, TRUE);
1339
1340       if (xi_device->use == XIMasterPointer ||
1341           xi_device->use == XIMasterKeyboard)
1342         {
1343           g_hash_table_insert (masters,
1344                                GINT_TO_POINTER (xi_device->deviceid),
1345                                GINT_TO_POINTER (xi_device->attachment));
1346         }
1347       else if (xi_device->use == XISlavePointer ||
1348                xi_device->use == XISlaveKeyboard)
1349         {
1350           g_hash_table_insert (slaves,
1351                                GINT_TO_POINTER (xi_device->deviceid),
1352                                GINT_TO_POINTER (xi_device->attachment));
1353         }
1354     }
1355
1356   XIFreeDeviceInfo (info);
1357
1358   g_hash_table_foreach (masters, relate_masters, manager_xi2);
1359   g_hash_table_destroy (masters);
1360
1361   g_hash_table_foreach (slaves, relate_slaves, manager_xi2);
1362   g_hash_table_destroy (slaves);
1363
1364   XISetMask (mask, XI_HierarchyChanged);
1365   XISetMask (mask, XI_DeviceChanged);
1366
1367   event_mask.deviceid = XIAllDevices;
1368   event_mask.mask_len = sizeof (mask);
1369   event_mask.mask = mask;
1370
1371   clutter_device_manager_xi2_select_events (manager,
1372                                             clutter_x11_get_root_window (),
1373                                             &event_mask);
1374
1375   if (G_OBJECT_CLASS (clutter_device_manager_xi2_parent_class)->constructed)
1376     G_OBJECT_CLASS (clutter_device_manager_xi2_parent_class)->constructed (gobject);
1377 }
1378
1379 static void
1380 clutter_device_manager_xi2_set_property (GObject      *gobject,
1381                                          guint         prop_id,
1382                                          const GValue *value,
1383                                          GParamSpec   *pspec)
1384 {
1385   ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (gobject);
1386
1387   switch (prop_id)
1388     {
1389     case PROP_OPCODE:
1390       manager_xi2->opcode = g_value_get_int (value);
1391       break;
1392
1393     default:
1394       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1395       break;
1396     }
1397 }
1398
1399 static void
1400 clutter_device_manager_xi2_class_init (ClutterDeviceManagerXI2Class *klass)
1401 {
1402   ClutterDeviceManagerClass *manager_class;
1403   GObjectClass *gobject_class;
1404
1405   obj_props[PROP_OPCODE] =
1406     g_param_spec_int ("opcode",
1407                       "Opcode",
1408                       "The XI2 opcode",
1409                       -1, G_MAXINT,
1410                       -1,
1411                       CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
1412
1413   gobject_class = G_OBJECT_CLASS (klass);
1414   gobject_class->constructed = clutter_device_manager_xi2_constructed;
1415   gobject_class->set_property = clutter_device_manager_xi2_set_property;
1416
1417   g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
1418   
1419   manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass);
1420   manager_class->add_device = clutter_device_manager_xi2_add_device;
1421   manager_class->remove_device = clutter_device_manager_xi2_remove_device;
1422   manager_class->get_devices = clutter_device_manager_xi2_get_devices;
1423   manager_class->get_core_device = clutter_device_manager_xi2_get_core_device;
1424   manager_class->get_device = clutter_device_manager_xi2_get_device;
1425 }
1426
1427 static void
1428 clutter_device_manager_xi2_init (ClutterDeviceManagerXI2 *self)
1429 {
1430   self->devices_by_id = g_hash_table_new_full (NULL, NULL,
1431                                                NULL,
1432                                                (GDestroyNotify) g_object_unref);
1433 }