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