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