4 * An OpenGL based 'interactive canvas' library.
6 * Copyright © 2009, 2010, 2011 Intel Corp.
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.
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.
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/>.
21 * Author: Emmanuele Bassi <ebassi@linux.intel.com>
26 #include "clutter-device-manager-core-x11.h"
28 #include "clutter-backend-x11.h"
29 #include "clutter-input-device-core-x11.h"
30 #include "clutter-stage-x11.h"
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"
41 #include <X11/extensions/XInput.h>
43 /* old versions of XI.h don't define these */
44 #ifndef IsXExtensionKeyboard
45 #define IsXExtensionKeyboard 3
46 #define IsXExtensionPointer 4
49 #endif /* HAVE_XINPUT */
60 static GParamSpec *obj_props[PROP_LAST] = { NULL, };
62 static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface);
64 #define clutter_device_manager_x11_get_type _clutter_device_manager_x11_get_type
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));
74 translate_class_info (ClutterInputDevice *device,
77 XAnyClassPtr any_class;
80 any_class = info->inputclassinfo;
82 for (i = 0; i < info->num_classes; i++)
84 switch (any_class->class)
91 XKeyInfo *xk_info = (XKeyInfo *) any_class;
92 ClutterInputDeviceX11 *device_x11;
95 device_x11 = CLUTTER_INPUT_DEVICE_X11 (device);
97 n_keys = xk_info->max_keycode - xk_info->min_keycode + 1;
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);
108 XValuatorInfo *xv_info = (XValuatorInfo *) any_class;
111 for (j = 0; j < xv_info->num_axes; j++)
113 ClutterInputAxis axis;
118 axis = CLUTTER_INPUT_AXIS_X;
122 axis = CLUTTER_INPUT_AXIS_Y;
126 axis = CLUTTER_INPUT_AXIS_PRESSURE;
130 axis = CLUTTER_INPUT_AXIS_XTILT;
134 axis = CLUTTER_INPUT_AXIS_YTILT;
138 axis = CLUTTER_INPUT_AXIS_WHEEL;
142 axis = CLUTTER_INPUT_AXIS_IGNORE;
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);
155 any_class = (XAnyClassPtr) (((char *) any_class) + any_class->length);
159 static ClutterInputDevice *
160 create_device (ClutterDeviceManagerX11 *manager_x11,
161 ClutterBackendX11 *backend_x11,
164 ClutterInputDeviceType source;
165 ClutterInputDevice *retval;
167 if (info->use != IsXExtensionPointer &&
168 info->use != IsXExtensionKeyboard)
171 if (info->use == IsXExtensionKeyboard)
172 source = CLUTTER_KEYBOARD_DEVICE;
177 name = g_ascii_strdown (info->name, -1);
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;
186 source = CLUTTER_POINTER_DEVICE;
191 retval = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
195 "device-manager", manager_x11,
196 "device-type", source,
197 "device-mode", CLUTTER_INPUT_MODE_FLOATING,
198 "backend", backend_x11,
201 translate_class_info (retval, info);
203 CLUTTER_NOTE (BACKEND,
204 "XI Device '%s' (id: %d) created",
210 #endif /* HAVE_XINPUT */
213 translate_key_event (ClutterBackendX11 *backend_x11,
214 ClutterDeviceManagerX11 *manager_x11,
218 ClutterEventX11 *event_x11;
219 char buffer[256 + 1];
222 event->key.type = xevent->xany.type == KeyPress ? CLUTTER_KEY_PRESS
223 : CLUTTER_KEY_RELEASE;
224 event->key.time = xevent->xkey.time;
226 clutter_event_set_device (event, manager_x11->core_keyboard);
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);
232 event->key.modifier_state = (ClutterModifierType) xevent->xkey.state;
233 event->key.hardware_keycode = xevent->xkey.keycode;
235 /* keyval is the key ignoring all modifiers ('1' vs. '!') */
237 _clutter_keymap_x11_translate_key_state (backend_x11->keymap,
238 event->key.hardware_keycode,
239 event->key.modifier_state,
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);
253 /* unicode_value is the printable representation */
254 n = XLookupString (&xevent->xkey, buffer, sizeof (buffer) - 1, NULL, NULL);
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))
264 event->key.unicode_value = (gunichar)'\0';
268 "%s: win:0x%x, key: %12s (%d)",
269 event->any.type == CLUTTER_KEY_PRESS
272 (unsigned int) xevent->xkey.window,
273 event->key.keyval ? buffer : "(none)",
279 static ClutterInputDevice *
280 get_device_from_event (ClutterDeviceManagerX11 *manager_x11,
285 device_id = ((XDeviceButtonEvent *) xevent)->deviceid;
287 return g_hash_table_lookup (manager_x11->devices_by_id,
288 GINT_TO_POINTER (device_id));
290 #endif /* HAVE_XINPUT */
292 static ClutterTranslateReturn
293 clutter_device_manager_x11_translate_event (ClutterEventTranslator *translator,
297 ClutterDeviceManagerX11 *manager_x11;
298 ClutterBackendX11 *backend_x11;
299 ClutterStageX11 *stage_x11;
300 ClutterTranslateReturn res;
304 ClutterInputDevice *device;
307 manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (translator);
308 backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ());
312 stage = clutter_x11_get_stage_from_window (xevent->xany.window);
314 return CLUTTER_TRANSLATE_CONTINUE;
316 if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
317 return CLUTTER_TRANSLATE_CONTINUE;
319 stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage));
321 event->any.stage = stage;
323 res = CLUTTER_TRANSLATE_CONTINUE;
326 device = get_device_from_event (manager_x11, xevent);
329 ClutterInputDeviceX11 *device_x11;
332 device_x11 = CLUTTER_INPUT_DEVICE_X11 (device);
333 retval = _clutter_input_device_x11_translate_xi_event (device_x11,
338 return CLUTTER_TRANSLATE_QUEUE;
340 #endif /* HAVE_XINPUT */
342 switch (xevent->type)
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;
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
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
362 * if we have XKB, and autorepeat is enabled, then this becomes
365 if (!backend_x11->have_xkb_autorepeat && XPending (xevent->xkey.display))
369 XPeekEvent (xevent->xkey.display, &next_event);
371 if (next_event.type == KeyPress &&
372 next_event.xkey.keycode == xevent->xkey.keycode &&
373 next_event.xkey.time == xevent->xkey.time)
375 res = CLUTTER_TRANSLATE_REMOVE;
380 translate_key_event (backend_x11, manager_x11, event, xevent);
381 res = CLUTTER_TRANSLATE_QUEUE;
386 "button press: win: 0x%x, coords: %d, %d, button: %d",
387 (unsigned int) stage_x11->xwin,
390 xevent->xbutton.button);
392 switch (xevent->xbutton.button)
398 event->scroll.type = CLUTTER_SCROLL;
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;
407 event->scroll.direction = CLUTTER_SCROLL_RIGHT;
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;
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;
427 clutter_event_set_device (event, manager_x11->core_pointer);
429 _clutter_stage_x11_set_user_time (stage_x11, xevent->xbutton.time);
430 res = CLUTTER_TRANSLATE_QUEUE;
435 "button press: win: 0x%x, coords: %d, %d, button: %d",
436 (unsigned int) stage_x11->xwin,
439 xevent->xbutton.button);
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)
447 res = CLUTTER_TRANSLATE_REMOVE;
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;
464 "motion: win: 0x%x, coords: %d, %d",
465 (unsigned int) stage_x11->xwin,
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;
480 CLUTTER_NOTE (EVENT, "Entering the stage (time:%u)",
481 (unsigned int) xevent->xcrossing.time);
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);
491 _clutter_stage_add_device (stage, manager_x11->core_pointer);
493 res = CLUTTER_TRANSLATE_QUEUE;
497 if (manager_x11->core_pointer->stage == NULL)
499 CLUTTER_NOTE (EVENT, "Discarding LeaveNotify for "
500 "ButtonRelease event off-stage");
501 res = CLUTTER_TRANSLATE_REMOVE;
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);
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);
517 _clutter_stage_remove_device (stage, manager_x11->core_pointer);
519 res = CLUTTER_TRANSLATE_QUEUE;
530 clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
532 iface->translate_event = clutter_device_manager_x11_translate_event;
536 clutter_device_manager_x11_constructed (GObject *gobject)
538 ClutterDeviceManagerX11 *manager_x11;
539 ClutterBackendX11 *backend_x11;
541 ClutterDeviceManager *manager;
542 XDeviceInfo *x_devices = NULL;
544 #endif /* HAVE_XINPUT */
546 manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject);
548 g_object_get (gobject, "backend", &backend_x11, NULL);
549 g_assert (backend_x11 != NULL);
552 manager = CLUTTER_DEVICE_MANAGER (gobject);
553 x_devices = XListInputDevices (backend_x11->xdpy, &n_devices);
556 CLUTTER_NOTE (BACKEND, "No XInput devices found");
560 for (i = 0; i < n_devices; i++)
562 XDeviceInfo *info = x_devices + i;
563 ClutterInputDevice *device;
565 CLUTTER_NOTE (BACKEND,
566 "Considering device %li with type %d, %d of %d",
571 device = create_device (manager_x11, backend_x11, info);
573 _clutter_device_manager_add_device (manager, device);
576 XFreeDeviceList (x_devices);
579 #endif /* HAVE_XINPUT */
581 /* fallback code in case:
583 * - we do not have XInput support compiled in
584 * - we do not have the XInput extension
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
591 manager_x11->core_pointer =
592 g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
593 "name", "Core Pointer",
595 "device-type", CLUTTER_POINTER_DEVICE,
596 "device-manager", manager_x11,
597 "device-mode", CLUTTER_INPUT_MODE_MASTER,
598 "backend", backend_x11,
601 CLUTTER_NOTE (BACKEND, "Added core pointer device");
603 manager_x11->core_keyboard =
604 g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
605 "name", "Core Keyboard",
607 "device-type", CLUTTER_KEYBOARD_DEVICE,
608 "device-manager", manager_x11,
609 "device-mode", CLUTTER_INPUT_MODE_MASTER,
610 "backend", backend_x11,
613 CLUTTER_NOTE (BACKEND, "Added core keyboard device");
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);
621 if (G_OBJECT_CLASS (clutter_device_manager_x11_parent_class)->constructed)
622 G_OBJECT_CLASS (clutter_device_manager_x11_parent_class)->constructed (gobject);
626 clutter_device_manager_x11_add_device (ClutterDeviceManager *manager,
627 ClutterInputDevice *device)
629 ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
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),
637 g_slist_free (manager_x11->all_devices);
638 manager_x11->all_devices = NULL;
642 clutter_device_manager_x11_remove_device (ClutterDeviceManager *manager,
643 ClutterInputDevice *device)
645 ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
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);
652 g_slist_free (manager_x11->all_devices);
653 manager_x11->all_devices = NULL;
656 static const GSList *
657 clutter_device_manager_x11_get_devices (ClutterDeviceManager *manager)
659 ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
661 /* cache the devices list so that we can keep the core pointer
662 * and keyboard outside of the ManagerX11:devices list
664 if (manager_x11->all_devices == NULL)
666 GSList *all_devices = manager_x11->devices;
668 all_devices = g_slist_prepend (all_devices, manager_x11->core_keyboard);
669 all_devices = g_slist_prepend (all_devices, manager_x11->core_pointer);
671 manager_x11->all_devices = all_devices;
674 return CLUTTER_DEVICE_MANAGER_X11 (manager)->all_devices;
677 static ClutterInputDevice *
678 clutter_device_manager_x11_get_core_device (ClutterDeviceManager *manager,
679 ClutterInputDeviceType type)
681 ClutterDeviceManagerX11 *manager_x11;
683 manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
687 case CLUTTER_POINTER_DEVICE:
688 return manager_x11->core_pointer;
690 case CLUTTER_KEYBOARD_DEVICE:
691 return manager_x11->core_keyboard;
700 static ClutterInputDevice *
701 clutter_device_manager_x11_get_device (ClutterDeviceManager *manager,
704 ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
706 return g_hash_table_lookup (manager_x11->devices_by_id,
707 GINT_TO_POINTER (id));
711 clutter_device_manager_x11_set_property (GObject *gobject,
716 ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject);
720 case PROP_EVENT_BASE:
721 manager_x11->xi_event_base = g_value_get_int (value);
725 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
731 clutter_device_manager_x11_class_init (ClutterDeviceManagerX11Class *klass)
733 ClutterDeviceManagerClass *manager_class;
734 GObjectClass *gobject_class;
736 obj_props[PROP_EVENT_BASE] =
737 g_param_spec_int ("event-base",
739 "The first XI event",
742 CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
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;
748 g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
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;
759 clutter_device_manager_x11_init (ClutterDeviceManagerX11 *self)
761 self->devices_by_id = g_hash_table_new (NULL, NULL);