update to 1.10.4
[profile/ivi/clutter.git] / clutter / x11 / clutter-device-manager-core-x11.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright © 2009, 2010, 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 "clutter-device-manager-core-x11.h"
27
28 #include "clutter-backend-x11.h"
29 #include "clutter-input-device-core-x11.h"
30 #include "clutter-stage-x11.h"
31
32 #include "clutter-backend.h"
33 #include "clutter-debug.h"
34 #include "clutter-device-manager-private.h"
35 #include "clutter-event-private.h"
36 #include "clutter-event-translator.h"
37 #include "clutter-stage-private.h"
38 #include "clutter-private.h"
39
40 #ifdef HAVE_XINPUT
41 #include <X11/extensions/XInput.h>
42
43 /* old versions of XI.h don't define these */
44 #ifndef IsXExtensionKeyboard
45 #define IsXExtensionKeyboard 3
46 #define IsXExtensionPointer  4
47 #endif
48
49 #endif /* HAVE_XINPUT */
50
51 enum
52 {
53   PROP_0,
54
55   PROP_EVENT_BASE,
56
57   PROP_LAST
58 };
59
60 static GParamSpec *obj_props[PROP_LAST] = { NULL, };
61
62 static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface);
63
64 #define clutter_device_manager_x11_get_type     _clutter_device_manager_x11_get_type
65
66 G_DEFINE_TYPE_WITH_CODE (ClutterDeviceManagerX11,
67                          clutter_device_manager_x11,
68                          CLUTTER_TYPE_DEVICE_MANAGER,
69                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_EVENT_TRANSLATOR,
70                                                 clutter_event_translator_iface_init));
71
72 #ifdef HAVE_XINPUT
73 static void
74 translate_class_info (ClutterInputDevice *device,
75                       XDeviceInfo        *info)
76 {
77   XAnyClassPtr any_class;
78   gint i;
79
80   any_class = info->inputclassinfo;
81
82   for (i = 0; i < info->num_classes; i++)
83     {
84       switch (any_class->class)
85         {
86         case ButtonClass:
87           break;
88
89         case KeyClass:
90           {
91             XKeyInfo *xk_info = (XKeyInfo *) any_class;
92             ClutterInputDeviceX11 *device_x11;
93             guint n_keys;
94
95             device_x11 = CLUTTER_INPUT_DEVICE_X11 (device);
96
97             n_keys = xk_info->max_keycode - xk_info->min_keycode + 1;
98
99             _clutter_input_device_set_n_keys (device, n_keys);
100             _clutter_input_device_x11_set_keycodes (device_x11,
101                                                     xk_info->min_keycode,
102                                                     xk_info->max_keycode);
103           }
104           break;
105
106         case ValuatorClass:
107           {
108             XValuatorInfo *xv_info = (XValuatorInfo *) any_class;
109             gint j;
110
111             for (j = 0; j < xv_info->num_axes; j++)
112               {
113                 ClutterInputAxis axis;
114
115                 switch (j)
116                   {
117                   case 0:
118                     axis = CLUTTER_INPUT_AXIS_X;
119                     break;
120
121                   case 1:
122                     axis = CLUTTER_INPUT_AXIS_Y;
123                     break;
124
125                   case 2:
126                     axis = CLUTTER_INPUT_AXIS_PRESSURE;
127                     break;
128
129                   case 3:
130                     axis = CLUTTER_INPUT_AXIS_XTILT;
131                     break;
132
133                   case 4:
134                     axis = CLUTTER_INPUT_AXIS_YTILT;
135                     break;
136
137                   case 5:
138                     axis = CLUTTER_INPUT_AXIS_WHEEL;
139                     break;
140
141                   default:
142                     axis = CLUTTER_INPUT_AXIS_IGNORE;
143                     break;
144                   }
145
146                 _clutter_input_device_add_axis (device, axis,
147                                                 xv_info->axes[j].min_value,
148                                                 xv_info->axes[j].max_value,
149                                                 xv_info->axes[j].resolution);
150               }
151           }
152           break;
153         }
154
155       any_class = (XAnyClassPtr) (((char *) any_class) + any_class->length);
156     }
157 }
158
159 static ClutterInputDevice *
160 create_device (ClutterDeviceManagerX11 *manager_x11,
161                ClutterBackendX11       *backend_x11,
162                XDeviceInfo             *info)
163 {
164   ClutterInputDeviceType source;
165   ClutterInputDevice *retval;
166
167   if (info->use != IsXExtensionPointer &&
168       info->use != IsXExtensionKeyboard)
169     return NULL;
170
171   if (info->use == IsXExtensionKeyboard)
172     source = CLUTTER_KEYBOARD_DEVICE;
173   else
174     {
175       gchar *name;
176
177       name = g_ascii_strdown (info->name, -1);
178
179       if (strstr (name, "eraser") != NULL)
180         source = CLUTTER_ERASER_DEVICE;
181       else if (strstr (name, "cursor") != NULL)
182         source = CLUTTER_CURSOR_DEVICE;
183       else if (strstr (name, "wacom") != NULL || strstr (name, "pen") != NULL)
184         source = CLUTTER_PEN_DEVICE;
185       else
186         source = CLUTTER_POINTER_DEVICE;
187
188       g_free (name);
189     }
190
191   retval = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
192                          "name", info->name,
193                          "id", info->id,
194                          "has-cursor", FALSE,
195                          "device-manager", manager_x11,
196                          "device-type", source,
197                          "device-mode", CLUTTER_INPUT_MODE_FLOATING,
198                          "backend", backend_x11,
199                          "enabled", FALSE,
200                          NULL);
201   translate_class_info (retval, info);
202
203   CLUTTER_NOTE (BACKEND,
204                 "XI Device '%s' (id: %d) created",
205                 info->name,
206                 (int) info->id);
207
208   return retval;
209 }
210 #endif /* HAVE_XINPUT */
211
212 static inline void
213 translate_key_event (ClutterBackendX11       *backend_x11,
214                      ClutterDeviceManagerX11 *manager_x11,
215                      ClutterEvent            *event,
216                      XEvent                  *xevent)
217 {
218   ClutterEventX11 *event_x11;
219   char buffer[256 + 1];
220   int n;
221
222   event->key.type = xevent->xany.type == KeyPress ? CLUTTER_KEY_PRESS
223                                                   : CLUTTER_KEY_RELEASE;
224   event->key.time = xevent->xkey.time;
225
226   clutter_event_set_device (event, manager_x11->core_keyboard);
227
228   /* KeyEvents have platform specific data associated to them */
229   event_x11 = _clutter_event_x11_new ();
230   _clutter_event_set_platform_data (event, event_x11);
231
232   event->key.modifier_state = (ClutterModifierType) xevent->xkey.state;
233   event->key.hardware_keycode = xevent->xkey.keycode;
234
235   /* keyval is the key ignoring all modifiers ('1' vs. '!') */
236   event->key.keyval =
237     _clutter_keymap_x11_translate_key_state (backend_x11->keymap,
238                                              event->key.hardware_keycode,
239                                              event->key.modifier_state,
240                                              NULL);
241
242   event_x11->key_group =
243     _clutter_keymap_x11_get_key_group (backend_x11->keymap,
244                                        event->key.modifier_state);
245   event_x11->key_is_modifier =
246     _clutter_keymap_x11_get_is_modifier (backend_x11->keymap,
247                                          event->key.hardware_keycode);
248   event_x11->num_lock_set =
249     _clutter_keymap_x11_get_num_lock_state (backend_x11->keymap);
250   event_x11->caps_lock_set =
251     _clutter_keymap_x11_get_caps_lock_state (backend_x11->keymap);
252
253   /* unicode_value is the printable representation */
254   n = XLookupString (&xevent->xkey, buffer, sizeof (buffer) - 1, NULL, NULL);
255
256   if (n != NoSymbol)
257     {
258       event->key.unicode_value = g_utf8_get_char_validated (buffer, n);
259       if ((event->key.unicode_value != (gunichar) -1) &&
260           (event->key.unicode_value != (gunichar) -2))
261         goto out;
262     }
263   else
264     event->key.unicode_value = (gunichar)'\0';
265
266 out:
267   CLUTTER_NOTE (EVENT,
268                 "%s: win:0x%x, key: %12s (%d)",
269                 event->any.type == CLUTTER_KEY_PRESS
270                   ? "key press  "
271                   : "key release",
272                 (unsigned int) xevent->xkey.window,
273                 event->key.keyval ? buffer : "(none)",
274                 event->key.keyval);
275   return;
276 }
277
278 #ifdef HAVE_XINPUT
279 static ClutterInputDevice *
280 get_device_from_event (ClutterDeviceManagerX11 *manager_x11,
281                        XEvent                  *xevent)
282 {
283   guint32 device_id;
284
285   device_id = ((XDeviceButtonEvent *) xevent)->deviceid;
286
287   return g_hash_table_lookup (manager_x11->devices_by_id,
288                               GINT_TO_POINTER (device_id));
289 }
290 #endif /* HAVE_XINPUT */
291
292 static ClutterTranslateReturn
293 clutter_device_manager_x11_translate_event (ClutterEventTranslator *translator,
294                                             gpointer                native,
295                                             ClutterEvent           *event)
296 {
297   ClutterDeviceManagerX11 *manager_x11;
298   ClutterBackendX11 *backend_x11;
299   ClutterStageX11 *stage_x11;
300   ClutterTranslateReturn res;
301   ClutterStage *stage;
302   XEvent *xevent;
303 #ifdef HAVE_XINPUT
304   ClutterInputDevice *device;
305 #endif
306
307   manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (translator);
308   backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ());
309
310   xevent = native;
311
312   stage = clutter_x11_get_stage_from_window (xevent->xany.window);
313   if (stage == NULL)
314     return CLUTTER_TRANSLATE_CONTINUE;
315
316   if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
317     return CLUTTER_TRANSLATE_CONTINUE;
318
319   stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage));
320
321   event->any.stage = stage;
322
323   res = CLUTTER_TRANSLATE_CONTINUE;
324
325 #ifdef HAVE_XINPUT
326   device = get_device_from_event (manager_x11, xevent);
327   if (device != NULL)
328     {
329       ClutterInputDeviceX11 *device_x11;
330       gboolean retval;
331
332       device_x11 = CLUTTER_INPUT_DEVICE_X11 (device);
333       retval = _clutter_input_device_x11_translate_xi_event (device_x11,
334                                                              stage_x11,
335                                                              xevent,
336                                                              event);
337       if (retval)
338         return CLUTTER_TRANSLATE_QUEUE;
339     }
340 #endif /* HAVE_XINPUT */
341
342   switch (xevent->type)
343     {
344     case KeyPress:
345       translate_key_event (backend_x11, manager_x11, event, xevent);
346       _clutter_stage_x11_set_user_time (stage_x11, xevent->xkey.time);
347       res = CLUTTER_TRANSLATE_QUEUE;
348       break;
349
350     case KeyRelease:
351       /* old-style X11 terminals require that even modern X11 send
352        * KeyPress/KeyRelease pairs when auto-repeating. for this
353        * reason modern(-ish) API like XKB has a way to detect
354        * auto-repeat and do a single KeyRelease at the end of a
355        * KeyPress sequence.
356        *
357        * this check emulates XKB's detectable auto-repeat; we peek
358        * the next event and check if it's a KeyPress for the same key
359        * and timestamp - and then ignore it if it matches the
360        * KeyRelease
361        *
362        * if we have XKB, and autorepeat is enabled, then this becomes
363        * a no-op
364        */
365       if (!backend_x11->have_xkb_autorepeat && XPending (xevent->xkey.display))
366         {
367           XEvent next_event;
368
369           XPeekEvent (xevent->xkey.display, &next_event);
370
371           if (next_event.type == KeyPress &&
372               next_event.xkey.keycode == xevent->xkey.keycode &&
373               next_event.xkey.time == xevent->xkey.time)
374             {
375               res = CLUTTER_TRANSLATE_REMOVE;
376               break;
377             }
378         }
379
380       translate_key_event (backend_x11, manager_x11, event, xevent);
381       res = CLUTTER_TRANSLATE_QUEUE;
382       break;
383
384     case ButtonPress:
385       CLUTTER_NOTE (EVENT,
386                     "button press: win: 0x%x, coords: %d, %d, button: %d",
387                     (unsigned int) stage_x11->xwin,
388                     xevent->xbutton.x,
389                     xevent->xbutton.y,
390                     xevent->xbutton.button);
391
392       switch (xevent->xbutton.button)
393         {
394         case 4: /* up */
395         case 5: /* down */
396         case 6: /* left */
397         case 7: /* right */
398           event->scroll.type = CLUTTER_SCROLL;
399
400           if (xevent->xbutton.button == 4)
401             event->scroll.direction = CLUTTER_SCROLL_UP;
402           else if (xevent->xbutton.button == 5)
403             event->scroll.direction = CLUTTER_SCROLL_DOWN;
404           else if (xevent->xbutton.button == 6)
405             event->scroll.direction = CLUTTER_SCROLL_LEFT;
406           else
407             event->scroll.direction = CLUTTER_SCROLL_RIGHT;
408
409           event->scroll.time = xevent->xbutton.time;
410           event->scroll.x = xevent->xbutton.x;
411           event->scroll.y = xevent->xbutton.y;
412           event->scroll.modifier_state = xevent->xbutton.state;
413           event->scroll.axes = NULL;
414           break;
415
416         default:
417           event->button.type = event->type = CLUTTER_BUTTON_PRESS;
418           event->button.time = xevent->xbutton.time;
419           event->button.x = xevent->xbutton.x;
420           event->button.y = xevent->xbutton.y;
421           event->button.modifier_state = xevent->xbutton.state;
422           event->button.button = xevent->xbutton.button;
423           event->button.axes = NULL;
424           break;
425         }
426
427       clutter_event_set_device (event, manager_x11->core_pointer);
428
429       _clutter_stage_x11_set_user_time (stage_x11, xevent->xbutton.time);
430       res = CLUTTER_TRANSLATE_QUEUE;
431       break;
432
433     case ButtonRelease:
434       CLUTTER_NOTE (EVENT,
435                     "button press: win: 0x%x, coords: %d, %d, button: %d",
436                     (unsigned int) stage_x11->xwin,
437                     xevent->xbutton.x,
438                     xevent->xbutton.y,
439                     xevent->xbutton.button);
440
441       /* scroll events don't have a corresponding release */
442       if (xevent->xbutton.button == 4 ||
443           xevent->xbutton.button == 5 ||
444           xevent->xbutton.button == 6 ||
445           xevent->xbutton.button == 7)
446         {
447           res = CLUTTER_TRANSLATE_REMOVE;
448           break;
449         }
450
451       event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
452       event->button.time = xevent->xbutton.time;
453       event->button.x = xevent->xbutton.x;
454       event->button.y = xevent->xbutton.y;
455       event->button.modifier_state = xevent->xbutton.state;
456       event->button.button = xevent->xbutton.button;
457       event->button.axes = NULL;
458       clutter_event_set_device (event, manager_x11->core_pointer);
459       res = CLUTTER_TRANSLATE_QUEUE;
460       break;
461
462     case MotionNotify:
463       CLUTTER_NOTE (EVENT,
464                     "motion: win: 0x%x, coords: %d, %d",
465                     (unsigned int) stage_x11->xwin,
466                     xevent->xmotion.x,
467                     xevent->xmotion.y);
468
469       event->motion.type = event->type = CLUTTER_MOTION;
470       event->motion.time = xevent->xmotion.time;
471       event->motion.x = xevent->xmotion.x;
472       event->motion.y = xevent->xmotion.y;
473       event->motion.modifier_state = xevent->xmotion.state;
474       event->motion.axes = NULL;
475       clutter_event_set_device (event, manager_x11->core_pointer);
476       res = CLUTTER_TRANSLATE_QUEUE;
477       break;
478
479     case EnterNotify:
480       CLUTTER_NOTE (EVENT, "Entering the stage (time:%u)",
481                     (unsigned int) xevent->xcrossing.time);
482
483       event->crossing.type = CLUTTER_ENTER;
484       event->crossing.time = xevent->xcrossing.time;
485       event->crossing.x = xevent->xcrossing.x;
486       event->crossing.y = xevent->xcrossing.y;
487       event->crossing.source = CLUTTER_ACTOR (stage);
488       event->crossing.related = NULL;
489       clutter_event_set_device (event, manager_x11->core_pointer);
490
491       _clutter_stage_add_device (stage, manager_x11->core_pointer);
492
493       res = CLUTTER_TRANSLATE_QUEUE;
494       break;
495
496     case LeaveNotify:
497       if (manager_x11->core_pointer->stage == NULL)
498         {
499           CLUTTER_NOTE (EVENT, "Discarding LeaveNotify for "
500                                "ButtonRelease event off-stage");
501           res = CLUTTER_TRANSLATE_REMOVE;
502           break;
503         }
504
505       /* we know that we are leaving the stage here */
506       CLUTTER_NOTE (EVENT, "Leaving the stage (time:%u)",
507                     (unsigned int) xevent->xcrossing.time);
508
509       event->crossing.type = CLUTTER_LEAVE;
510       event->crossing.time = xevent->xcrossing.time;
511       event->crossing.x = xevent->xcrossing.x;
512       event->crossing.y = xevent->xcrossing.y;
513       event->crossing.source = CLUTTER_ACTOR (stage);
514       event->crossing.related = NULL;
515       clutter_event_set_device (event, manager_x11->core_pointer);
516
517       _clutter_stage_remove_device (stage, manager_x11->core_pointer);
518
519       res = CLUTTER_TRANSLATE_QUEUE;
520       break;
521
522     default:
523       break;
524     }
525
526   return res;
527 }
528
529 static void
530 clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
531 {
532   iface->translate_event = clutter_device_manager_x11_translate_event;
533 }
534
535 static void
536 clutter_device_manager_x11_constructed (GObject *gobject)
537 {
538   ClutterDeviceManagerX11 *manager_x11;
539   ClutterBackendX11 *backend_x11;
540 #ifdef HAVE_XINPUT
541   ClutterDeviceManager *manager;
542   XDeviceInfo *x_devices = NULL;
543   int i, n_devices;
544 #endif /* HAVE_XINPUT */
545
546   manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject);
547
548   g_object_get (gobject, "backend", &backend_x11, NULL);
549   g_assert (backend_x11 != NULL);
550
551 #ifdef HAVE_XINPUT
552   manager = CLUTTER_DEVICE_MANAGER (gobject);
553   x_devices = XListInputDevices (backend_x11->xdpy, &n_devices);
554   if (n_devices == 0)
555     {
556       CLUTTER_NOTE (BACKEND, "No XInput devices found");
557       goto default_device;
558     }
559
560   for (i = 0; i < n_devices; i++)
561     {
562       XDeviceInfo *info = x_devices + i;
563       ClutterInputDevice *device;
564
565       CLUTTER_NOTE (BACKEND,
566                     "Considering device %li with type %d, %d of %d",
567                     info->id,
568                     info->use,
569                     i, n_devices);
570
571       device = create_device (manager_x11, backend_x11, info);
572       if (device != NULL)
573         _clutter_device_manager_add_device (manager, device);
574     }
575
576   XFreeDeviceList (x_devices);
577
578 default_device:
579 #endif /* HAVE_XINPUT */
580
581   /* fallback code in case:
582    *
583    *  - we do not have XInput support compiled in
584    *  - we do not have the XInput extension
585    *
586    * we register two default devices, one for the pointer
587    * and one for the keyboard. this block must also be
588    * executed for the XInput support because XI does not
589    * cover core devices
590    */
591   manager_x11->core_pointer =
592     g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
593                   "name", "Core Pointer",
594                   "has-cursor", TRUE,
595                   "device-type", CLUTTER_POINTER_DEVICE,
596                   "device-manager", manager_x11,
597                   "device-mode", CLUTTER_INPUT_MODE_MASTER,
598                   "backend", backend_x11,
599                   "enabled", TRUE,
600                   NULL);
601   CLUTTER_NOTE (BACKEND, "Added core pointer device");
602
603   manager_x11->core_keyboard =
604     g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
605                   "name", "Core Keyboard",
606                   "has-cursor", FALSE,
607                   "device-type", CLUTTER_KEYBOARD_DEVICE,
608                   "device-manager", manager_x11,
609                   "device-mode", CLUTTER_INPUT_MODE_MASTER,
610                   "backend", backend_x11,
611                   "enabled", TRUE,
612                   NULL);
613   CLUTTER_NOTE (BACKEND, "Added core keyboard device");
614
615   /* associate core devices */
616   _clutter_input_device_set_associated_device (manager_x11->core_pointer,
617                                                manager_x11->core_keyboard);
618   _clutter_input_device_set_associated_device (manager_x11->core_keyboard,
619                                                manager_x11->core_pointer);
620
621   if (G_OBJECT_CLASS (clutter_device_manager_x11_parent_class)->constructed)
622     G_OBJECT_CLASS (clutter_device_manager_x11_parent_class)->constructed (gobject);
623 }
624
625 static void
626 clutter_device_manager_x11_add_device (ClutterDeviceManager *manager,
627                                        ClutterInputDevice   *device)
628 {
629   ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
630
631   manager_x11->devices = g_slist_prepend (manager_x11->devices, device);
632   g_hash_table_replace (manager_x11->devices_by_id,
633                         GINT_TO_POINTER (device->id),
634                         device);
635
636   /* blow the cache */
637   g_slist_free (manager_x11->all_devices);
638   manager_x11->all_devices = NULL;
639 }
640
641 static void
642 clutter_device_manager_x11_remove_device (ClutterDeviceManager *manager,
643                                           ClutterInputDevice   *device)
644 {
645   ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
646
647   g_hash_table_remove (manager_x11->devices_by_id,
648                        GINT_TO_POINTER (device->id));
649   manager_x11->devices = g_slist_remove (manager_x11->devices, device);
650
651   /* blow the cache */
652   g_slist_free (manager_x11->all_devices);
653   manager_x11->all_devices = NULL;
654 }
655
656 static const GSList *
657 clutter_device_manager_x11_get_devices (ClutterDeviceManager *manager)
658 {
659   ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
660
661   /* cache the devices list so that we can keep the core pointer
662    * and keyboard outside of the ManagerX11:devices list
663    */
664   if (manager_x11->all_devices == NULL)
665     {
666       GSList *all_devices = manager_x11->devices;
667
668       all_devices = g_slist_prepend (all_devices, manager_x11->core_keyboard);
669       all_devices = g_slist_prepend (all_devices, manager_x11->core_pointer);
670
671       manager_x11->all_devices = all_devices;
672     }
673     
674   return CLUTTER_DEVICE_MANAGER_X11 (manager)->all_devices;
675 }
676
677 static ClutterInputDevice *
678 clutter_device_manager_x11_get_core_device (ClutterDeviceManager   *manager,
679                                             ClutterInputDeviceType  type)
680 {
681   ClutterDeviceManagerX11 *manager_x11;
682
683   manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
684
685   switch (type)
686     {
687     case CLUTTER_POINTER_DEVICE:
688       return manager_x11->core_pointer;
689
690     case CLUTTER_KEYBOARD_DEVICE:
691       return manager_x11->core_keyboard;
692
693     default:
694       return NULL;
695     }
696
697   return NULL;
698 }
699
700 static ClutterInputDevice *
701 clutter_device_manager_x11_get_device (ClutterDeviceManager *manager,
702                                        gint                  id)
703 {
704   ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
705
706   return g_hash_table_lookup (manager_x11->devices_by_id,
707                               GINT_TO_POINTER (id));
708 }
709
710 static void
711 clutter_device_manager_x11_set_property (GObject      *gobject,
712                                          guint         prop_id,
713                                          const GValue *value,
714                                          GParamSpec   *pspec)
715 {
716   ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject);
717
718   switch (prop_id)
719     {
720     case PROP_EVENT_BASE:
721       manager_x11->xi_event_base = g_value_get_int (value);
722       break;
723
724     default:
725       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
726       break;
727     }
728 }
729
730 static void
731 clutter_device_manager_x11_class_init (ClutterDeviceManagerX11Class *klass)
732 {
733   ClutterDeviceManagerClass *manager_class;
734   GObjectClass *gobject_class;
735
736   obj_props[PROP_EVENT_BASE] =
737     g_param_spec_int ("event-base",
738                       "Event Base",
739                       "The first XI event",
740                       -1, G_MAXINT,
741                       -1,
742                       CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
743
744   gobject_class = G_OBJECT_CLASS (klass);
745   gobject_class->constructed = clutter_device_manager_x11_constructed;
746   gobject_class->set_property = clutter_device_manager_x11_set_property;
747
748   g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
749   
750   manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass);
751   manager_class->add_device = clutter_device_manager_x11_add_device;
752   manager_class->remove_device = clutter_device_manager_x11_remove_device;
753   manager_class->get_devices = clutter_device_manager_x11_get_devices;
754   manager_class->get_core_device = clutter_device_manager_x11_get_core_device;
755   manager_class->get_device = clutter_device_manager_x11_get_device;
756 }
757
758 static void
759 clutter_device_manager_x11_init (ClutterDeviceManagerX11 *self)
760 {
761   self->devices_by_id = g_hash_table_new (NULL, NULL);
762 }