4 * An OpenGL based 'interactive canvas' library.
6 * Copyright © 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>
28 #include "clutter-device-manager-xi2.h"
30 #include "clutter-backend-x11.h"
31 #include "clutter-input-device-xi2.h"
32 #include "clutter-stage-x11.h"
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"
42 #include <X11/extensions/XInput2.h>
53 static GParamSpec *obj_props[PROP_LAST] = { NULL, };
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 */
64 #define N_AXIS_ATOMS G_N_ELEMENTS (clutter_input_axis_atom_names)
66 static Atom clutter_input_axis_atoms[N_AXIS_ATOMS] = { 0, };
68 static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface);
70 #define clutter_device_manager_xi2_get_type _clutter_device_manager_xi2_get_type
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));
79 translate_valuator_class (Display *xdisplay,
80 ClutterInputDevice *device,
81 XIValuatorClassInfo *class)
83 static gboolean atoms_initialized = FALSE;
84 ClutterInputAxis i, axis = CLUTTER_INPUT_AXIS_IGNORE;
86 if (G_UNLIKELY (!atoms_initialized))
88 XInternAtoms (xdisplay,
89 (char **) clutter_input_axis_atom_names, N_AXIS_ATOMS,
91 clutter_input_axis_atoms);
93 atoms_initialized = TRUE;
96 for (i = CLUTTER_INPUT_AXIS_IGNORE;
97 i <= CLUTTER_INPUT_AXIS_WHEEL;
100 if (clutter_input_axis_atoms[i] == class->label)
107 _clutter_input_device_add_axis (device, axis,
112 CLUTTER_NOTE (BACKEND,
113 "Added axis '%s' (min:%.2f, max:%.2fd, res:%d) of device %d",
114 clutter_input_axis_atom_names[axis],
122 translate_device_classes (Display *xdisplay,
123 ClutterInputDevice *device,
124 XIAnyClassInfo **classes,
129 for (i = 0; i < n_classes; i++)
131 XIAnyClassInfo *class_info = classes[i];
133 switch (class_info->type)
137 XIKeyClassInfo *key_info = (XIKeyClassInfo *) class_info;
140 _clutter_input_device_set_n_keys (device,
141 key_info->num_keycodes);
143 for (j = 0; j < key_info->num_keycodes; j++)
145 clutter_input_device_set_key (device, j,
146 key_info->keycodes[i],
152 case XIValuatorClass:
153 translate_valuator_class (xdisplay, device,
154 (XIValuatorClassInfo *) class_info);
157 #ifdef HAVE_XINPUT_2_2
160 XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info;
161 ClutterScrollDirection direction;
163 if (scroll_info->scroll_type == XIScrollTypeVertical)
164 direction = CLUTTER_SCROLL_DOWN;
166 direction = CLUTTER_SCROLL_RIGHT;
168 CLUTTER_NOTE (BACKEND, "Scroll valuator %d: %s, increment: %f",
170 scroll_info->scroll_type == XIScrollTypeVertical
173 scroll_info->increment);
175 _clutter_input_device_add_scroll_info (device,
178 scroll_info->increment);
181 #endif /* HAVE_XINPUT_2_2 */
190 is_touch_device (XIAnyClassInfo **classes,
192 ClutterInputDeviceType *device_type,
193 guint *n_touch_points)
195 #ifdef HAVE_XINPUT_2_2
198 for (i = 0; i < n_classes; i++)
200 XITouchClassInfo *class = (XITouchClassInfo *) classes[i];
202 if (class->type != XITouchClass)
205 if (class->num_touches > 0)
207 if (class->mode == XIDirectTouch)
208 *device_type = CLUTTER_TOUCHSCREEN_DEVICE;
209 else if (class->mode == XIDependentTouch)
210 *device_type = CLUTTER_TOUCHPAD_DEVICE;
214 *n_touch_points = class->num_touches;
224 static ClutterInputDevice *
225 create_device (ClutterDeviceManagerXI2 *manager_xi2,
226 ClutterBackendX11 *backend_x11,
229 ClutterInputDeviceType source, touch_source;
230 ClutterInputDevice *retval;
231 ClutterInputMode mode;
233 guint num_touches = 0;
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,
242 source = touch_source;
248 name = g_ascii_strdown (info->name, -1);
250 if (strstr (name, "eraser") != NULL)
251 source = CLUTTER_ERASER_DEVICE;
252 else if (strstr (name, "cursor") != NULL)
253 source = CLUTTER_CURSOR_DEVICE;
254 else if (strstr (name, "wacom") != NULL || strstr (name, "pen") != NULL)
255 source = CLUTTER_PEN_DEVICE;
257 source = CLUTTER_POINTER_DEVICE;
264 case XIMasterKeyboard:
265 case XIMasterPointer:
266 mode = CLUTTER_INPUT_MODE_MASTER;
270 case XISlaveKeyboard:
272 mode = CLUTTER_INPUT_MODE_SLAVE;
276 case XIFloatingSlave:
278 mode = CLUTTER_INPUT_MODE_FLOATING;
283 retval = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_XI2,
285 "id", info->deviceid,
286 "has-cursor", (info->use == XIMasterPointer),
287 "device-manager", manager_xi2,
288 "device-type", source,
290 "backend", backend_x11,
291 "enabled", is_enabled,
294 translate_device_classes (backend_x11->xdpy, retval,
298 CLUTTER_NOTE (BACKEND, "Created device '%s' (id: %d, has-cursor: %s)",
301 info->use == XIMasterPointer ? "yes" : "no");
306 static ClutterInputDevice *
307 add_device (ClutterDeviceManagerXI2 *manager_xi2,
308 ClutterBackendX11 *backend_x11,
310 gboolean in_construction)
312 ClutterInputDevice *device;
314 device = create_device (manager_xi2, backend_x11, info);
316 /* we don't go through the DeviceManager::add_device() vfunc because
317 * that emits the signal, and we only do it conditionally
319 g_hash_table_replace (manager_xi2->devices_by_id,
320 GINT_TO_POINTER (info->deviceid),
321 g_object_ref (device));
323 if (info->use == XIMasterPointer ||
324 info->use == XIMasterKeyboard)
326 manager_xi2->master_devices =
327 g_list_prepend (manager_xi2->master_devices, device);
329 else if (info->use == XISlavePointer ||
330 info->use == XISlaveKeyboard ||
331 info->use == XIFloatingSlave)
333 manager_xi2->slave_devices =
334 g_list_prepend (manager_xi2->slave_devices, device);
337 g_warning ("Unhandled device: %s",
338 clutter_input_device_get_device_name (device));
340 /* relationships between devices and signal emissions are not
341 * necessary while we're constructing the device manager instance
343 if (!in_construction)
345 if (info->use == XISlavePointer || info->use == XISlaveKeyboard)
347 ClutterInputDevice *master;
349 master = g_hash_table_lookup (manager_xi2->devices_by_id,
350 GINT_TO_POINTER (info->attachment));
351 _clutter_input_device_set_associated_device (device, master);
352 _clutter_input_device_add_slave (master, device);
356 g_slist_free (manager_xi2->all_devices);
357 manager_xi2->all_devices = NULL;
359 g_signal_emit_by_name (manager_xi2, "device-added", device);
366 remove_device (ClutterDeviceManagerXI2 *manager_xi2,
369 ClutterInputDevice *device;
371 device = g_hash_table_lookup (manager_xi2->devices_by_id,
372 GINT_TO_POINTER (device_id));
376 manager_xi2->master_devices =
377 g_list_remove (manager_xi2->master_devices, device);
378 manager_xi2->slave_devices =
379 g_list_remove (manager_xi2->slave_devices, device);
382 g_slist_free (manager_xi2->all_devices);
383 manager_xi2->all_devices = NULL;
385 g_signal_emit_by_name (manager_xi2, "device-removed", device);
387 g_object_run_dispose (G_OBJECT (device));
389 g_hash_table_remove (manager_xi2->devices_by_id,
390 GINT_TO_POINTER (device_id));
395 translate_hierarchy_event (ClutterBackendX11 *backend_x11,
396 ClutterDeviceManagerXI2 *manager_xi2,
397 XIHierarchyEvent *ev)
401 for (i = 0; i < ev->num_info; i++)
403 if (ev->info[i].flags & XIDeviceEnabled)
408 CLUTTER_NOTE (EVENT, "Hierarchy event: device enabled");
410 info = XIQueryDevice (backend_x11->xdpy,
411 ev->info[i].deviceid,
413 add_device (manager_xi2, backend_x11, &info[0], FALSE);
415 else if (ev->info[i].flags & XIDeviceDisabled)
417 CLUTTER_NOTE (EVENT, "Hierarchy event: device disabled");
419 remove_device (manager_xi2, ev->info[i].deviceid);
421 else if ((ev->info[i].flags & XISlaveAttached) ||
422 (ev->info[i].flags & XISlaveDetached))
424 ClutterInputDevice *master, *slave;
427 gboolean send_changed = FALSE;
429 CLUTTER_NOTE (EVENT, "Hierarchy event: slave %s",
430 (ev->info[i].flags & XISlaveAttached)
434 slave = g_hash_table_lookup (manager_xi2->devices_by_id,
435 GINT_TO_POINTER (ev->info[i].deviceid));
436 master = clutter_input_device_get_associated_device (slave);
438 /* detach the slave in both cases */
441 _clutter_input_device_remove_slave (master, slave);
442 _clutter_input_device_set_associated_device (slave, NULL);
447 /* and attach the slave to the new master if needed */
448 if (ev->info[i].flags & XISlaveAttached)
450 info = XIQueryDevice (backend_x11->xdpy,
451 ev->info[i].deviceid,
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);
459 XIFreeDeviceInfo (info);
464 ClutterStage *stage = _clutter_input_device_get_stage (master);
466 _clutter_stage_x11_events_device_changed (CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage)),
468 CLUTTER_DEVICE_MANAGER (manager_xi2));
475 clutter_device_manager_xi2_select_events (ClutterDeviceManager *manager,
477 XIEventMask *event_mask)
481 xdisplay = clutter_x11_get_default_display ();
483 XISelectEvents (xdisplay, xwindow, event_mask, 1);
486 static ClutterStage *
487 get_event_stage (ClutterEventTranslator *translator,
490 Window xwindow = None;
492 switch (xi_event->evtype)
497 case XI_ButtonRelease:
499 #ifdef HAVE_XINPUT_2_2
503 #endif /* HAVE_XINPUT_2_2 */
505 XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
507 xwindow = xev->event;
516 XIEnterEvent *xev = (XIEnterEvent *) xi_event;
518 xwindow = xev->event;
529 return clutter_x11_get_stage_from_window (xwindow);
533 * print_key_sym: Translate a symbol to its printable form if any
534 * @symbol: the symbol to translate
535 * @buffer: the buffer where to put the translated string
536 * @len: size of the buffer
538 * Translates @symbol into a printable representation in @buffer, if possible.
540 * Return value: The number of bytes of the translated string, 0 if the
541 * symbol can't be printed
543 * Note: The code is derived from libX11's src/KeyBind.c
544 * Copyright 1985, 1987, 1998 The Open Group
546 * Note: This code works for Latin-1 symbols. clutter_keysym_to_unicode()
547 * does the work for the other keysyms.
550 print_keysym (uint32_t symbol,
554 unsigned long high_bytes;
557 high_bytes = symbol >> 8;
559 ((high_bytes == 0) ||
560 ((high_bytes == 0xFF) &&
561 (((symbol >= CLUTTER_KEY_BackSpace) &&
562 (symbol <= CLUTTER_KEY_Clear)) ||
563 (symbol == CLUTTER_KEY_Return) ||
564 (symbol == CLUTTER_KEY_Escape) ||
565 (symbol == CLUTTER_KEY_KP_Space) ||
566 (symbol == CLUTTER_KEY_KP_Tab) ||
567 (symbol == CLUTTER_KEY_KP_Enter) ||
568 ((symbol >= CLUTTER_KEY_KP_Multiply) &&
569 (symbol <= CLUTTER_KEY_KP_9)) ||
570 (symbol == CLUTTER_KEY_KP_Equal) ||
571 (symbol == CLUTTER_KEY_Delete))))))
574 /* if X keysym, convert to ascii by grabbing low 7 bits */
575 if (symbol == CLUTTER_KEY_KP_Space)
576 c = CLUTTER_KEY_space & 0x7F; /* patch encoding botch */
577 else if (high_bytes == 0xFF)
587 translate_axes (ClutterInputDevice *device,
590 ClutterStageX11 *stage_x11,
591 XIValuatorState *valuators)
593 guint n_axes = clutter_input_device_get_n_axes (device);
598 retval = g_new0 (gdouble, n_axes);
599 values = valuators->values;
601 for (i = 0; i < valuators->mask_len * 8; i++)
603 ClutterInputAxis axis;
606 if (!XIMaskIsSet (valuators->mask, i))
609 axis = clutter_input_device_get_axis (device, i);
614 case CLUTTER_INPUT_AXIS_X:
618 case CLUTTER_INPUT_AXIS_Y:
623 _clutter_input_device_translate_axis (device, i, val, &retval[i]);
632 scroll_valuators_changed (ClutterInputDevice *device,
633 XIValuatorState *valuators,
637 gboolean retval = FALSE;
638 guint n_axes, n_val, i;
641 n_axes = clutter_input_device_get_n_axes (device);
642 values = valuators->values;
648 for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++)
650 ClutterScrollDirection direction;
653 if (!XIMaskIsSet (valuators->mask, i))
656 if (_clutter_input_device_get_scroll_delta (device, i,
663 if (direction == CLUTTER_SCROLL_UP ||
664 direction == CLUTTER_SCROLL_DOWN)
676 static ClutterTranslateReturn
677 clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator,
681 ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (translator);
682 ClutterTranslateReturn retval = CLUTTER_TRANSLATE_CONTINUE;
683 ClutterBackendX11 *backend_x11;
684 ClutterStageX11 *stage_x11 = NULL;
685 ClutterStage *stage = NULL;
686 ClutterInputDevice *device, *source_device;
687 XGenericEventCookie *cookie;
691 backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ());
695 cookie = &xevent->xcookie;
697 if (cookie->type != GenericEvent ||
698 cookie->extension != manager_xi2->opcode)
699 return CLUTTER_TRANSLATE_CONTINUE;
701 xi_event = (XIEvent *) cookie->data;
704 return CLUTTER_TRANSLATE_REMOVE;
706 if (!(xi_event->evtype == XI_HierarchyChanged ||
707 xi_event->evtype == XI_DeviceChanged))
709 stage = get_event_stage (translator, xi_event);
710 if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage))
711 return CLUTTER_TRANSLATE_CONTINUE;
713 stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage));
716 event->any.stage = stage;
718 switch (xi_event->evtype)
720 case XI_HierarchyChanged:
722 XIHierarchyEvent *xev = (XIHierarchyEvent *) xi_event;
724 translate_hierarchy_event (backend_x11, manager_xi2, xev);
726 retval = CLUTTER_TRANSLATE_REMOVE;
729 case XI_DeviceChanged:
731 XIDeviceChangedEvent *xev = (XIDeviceChangedEvent *) xi_event;
733 device = g_hash_table_lookup (manager_xi2->devices_by_id,
734 GINT_TO_POINTER (xev->deviceid));
735 _clutter_input_device_reset_axes (device);
736 _clutter_input_device_reset_scroll_info (device);
737 translate_device_classes (backend_x11->xdpy,
742 retval = CLUTTER_TRANSLATE_REMOVE;
748 XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
749 ClutterEventX11 *event_x11;
750 char buffer[7] = { 0, };
753 event->key.type = event->type = (xev->evtype == XI_KeyPress)
755 : CLUTTER_KEY_RELEASE;
757 event->key.time = xev->time;
758 event->key.stage = stage;
759 event->key.modifier_state =
760 _clutter_input_device_xi2_translate_state (&xev->mods, &xev->buttons);
761 event->key.hardware_keycode = xev->detail;
763 /* keyval is the key ignoring all modifiers ('1' vs. '!') */
765 _clutter_keymap_x11_translate_key_state (backend_x11->keymap,
766 event->key.hardware_keycode,
767 event->key.modifier_state,
770 /* KeyEvents have platform specific data associated to them */
771 event_x11 = _clutter_event_x11_new ();
772 _clutter_event_set_platform_data (event, event_x11);
774 event_x11->key_group =
775 _clutter_keymap_x11_get_key_group (backend_x11->keymap,
776 event->key.modifier_state);
777 event_x11->key_is_modifier =
778 _clutter_keymap_x11_get_is_modifier (backend_x11->keymap,
779 event->key.hardware_keycode);
780 event_x11->num_lock_set =
781 _clutter_keymap_x11_get_num_lock_state (backend_x11->keymap);
782 event_x11->caps_lock_set =
783 _clutter_keymap_x11_get_caps_lock_state (backend_x11->keymap);
785 source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
786 GINT_TO_POINTER (xev->sourceid));
787 clutter_event_set_source_device (event, source_device);
789 device = g_hash_table_lookup (manager_xi2->devices_by_id,
790 GINT_TO_POINTER (xev->deviceid));
791 clutter_event_set_device (event, device);
793 /* XXX keep this in sync with the evdev device manager */
794 n = print_keysym (event->key.keyval, buffer, sizeof (buffer));
798 event->key.unicode_value = (gunichar) '\0';
802 event->key.unicode_value = g_utf8_get_char_validated (buffer, n);
803 if (event->key.unicode_value == -1 ||
804 event->key.unicode_value == -2)
805 event->key.unicode_value = (gunichar) '\0';
809 "%s: win:0x%x device:%d source:%d, key: %12s (%d)",
810 event->any.type == CLUTTER_KEY_PRESS
813 (unsigned int) stage_x11->xwin,
816 event->key.keyval ? buffer : "(none)",
819 if (xi_event->evtype == XI_KeyPress)
820 _clutter_stage_x11_set_user_time (stage_x11, event->key.time);
822 retval = CLUTTER_TRANSLATE_QUEUE;
827 case XI_ButtonRelease:
829 XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
837 event->scroll.type = event->type = CLUTTER_SCROLL;
839 if (xev->detail == 4)
840 event->scroll.direction = CLUTTER_SCROLL_UP;
841 else if (xev->detail == 5)
842 event->scroll.direction = CLUTTER_SCROLL_DOWN;
843 else if (xev->detail == 6)
844 event->scroll.direction = CLUTTER_SCROLL_LEFT;
846 event->scroll.direction = CLUTTER_SCROLL_RIGHT;
848 event->scroll.stage = stage;
850 event->scroll.time = xev->time;
851 event->scroll.x = xev->event_x;
852 event->scroll.y = xev->event_y;
853 event->scroll.modifier_state =
854 _clutter_input_device_xi2_translate_state (&xev->mods,
857 source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
858 GINT_TO_POINTER (xev->sourceid));
859 clutter_event_set_source_device (event, source_device);
861 device = g_hash_table_lookup (manager_xi2->devices_by_id,
862 GINT_TO_POINTER (xev->deviceid));
863 clutter_event_set_device (event, device);
865 event->scroll.axes = translate_axes (event->scroll.device,
871 #ifdef HAVE_XINPUT_2_2
872 if (xev->flags & XIPointerEmulated)
873 _clutter_event_set_pointer_emulated (event, TRUE);
874 #endif /* HAVE_XINPUT_2_2 */
878 event->button.type = event->type =
879 (xi_event->evtype == XI_ButtonPress) ? CLUTTER_BUTTON_PRESS
880 : CLUTTER_BUTTON_RELEASE;
882 event->button.stage = stage;
884 event->button.time = xev->time;
885 event->button.x = xev->event_x;
886 event->button.y = xev->event_y;
887 event->button.button = xev->detail;
888 event->button.modifier_state =
889 _clutter_input_device_xi2_translate_state (&xev->mods,
892 source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
893 GINT_TO_POINTER (xev->sourceid));
894 clutter_event_set_source_device (event, source_device);
896 device = g_hash_table_lookup (manager_xi2->devices_by_id,
897 GINT_TO_POINTER (xev->deviceid));
898 clutter_event_set_device (event, device);
900 event->button.axes = translate_axes (event->button.device,
908 if (source_device != NULL && device->stage != NULL)
909 _clutter_input_device_set_stage (source_device, device->stage);
912 "%s: win:0x%x, device:%s (button:%d, x:%.2f, y:%.2f, axes:%s)",
913 event->any.type == CLUTTER_BUTTON_PRESS
916 (unsigned int) stage_x11->xwin,
917 event->button.device->device_name,
918 event->button.button,
921 event->button.axes != NULL ? "yes" : "no");
923 #ifdef HAVE_XINPUT_2_2
924 if (xev->flags & XIPointerEmulated)
925 _clutter_event_set_pointer_emulated (event, TRUE);
926 #endif /* HAVE_XINPUT_2_2 */
928 if (xi_event->evtype == XI_ButtonPress)
929 _clutter_stage_x11_set_user_time (stage_x11, event->button.time);
931 retval = CLUTTER_TRANSLATE_QUEUE;
937 XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
938 gdouble delta_x, delta_y;
940 source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
941 GINT_TO_POINTER (xev->sourceid));
943 if (scroll_valuators_changed (source_device,
947 event->scroll.type = event->type = CLUTTER_SCROLL;
948 event->scroll.direction = CLUTTER_SCROLL_SMOOTH;
950 event->scroll.stage = stage;
951 event->scroll.time = xev->time;
952 event->scroll.x = xev->event_x;
953 event->scroll.y = xev->event_y;
954 event->scroll.modifier_state =
955 _clutter_input_device_xi2_translate_state (&xev->mods,
958 clutter_event_set_scroll_delta (event, delta_x, delta_y);
959 clutter_event_set_source_device (event, source_device);
961 device = g_hash_table_lookup (manager_xi2->devices_by_id,
962 GINT_TO_POINTER (xev->deviceid));
963 clutter_event_set_device (event, device);
966 "smooth scroll: win:0x%x device:%s (x:%.2f, y:%.2f, delta:%f, %f)",
967 (unsigned int) stage_x11->xwin,
968 event->scroll.device->device_name,
973 retval = CLUTTER_TRANSLATE_QUEUE;
977 event->motion.type = event->type = CLUTTER_MOTION;
979 event->motion.stage = stage;
981 event->motion.time = xev->time;
982 event->motion.x = xev->event_x;
983 event->motion.y = xev->event_y;
984 event->motion.modifier_state =
985 _clutter_input_device_xi2_translate_state (&xev->mods,
988 clutter_event_set_source_device (event, source_device);
990 device = g_hash_table_lookup (manager_xi2->devices_by_id,
991 GINT_TO_POINTER (xev->deviceid));
992 clutter_event_set_device (event, device);
994 event->motion.axes = translate_axes (event->motion.device,
1000 if (source_device != NULL && device->stage != NULL)
1001 _clutter_input_device_set_stage (source_device, device->stage);
1003 #ifdef HAVE_XINPUT_2_2
1004 if (xev->flags & XIPointerEmulated)
1005 _clutter_event_set_pointer_emulated (event, TRUE);
1006 #endif /* HAVE_XINPUT_2_2 */
1008 CLUTTER_NOTE (EVENT, "motion: win:0x%x device:%s (x:%.2f, y:%.2f, axes:%s)",
1009 (unsigned int) stage_x11->xwin,
1010 event->motion.device->device_name,
1013 event->motion.axes != NULL ? "yes" : "no");
1015 retval = CLUTTER_TRANSLATE_QUEUE;
1019 #ifdef HAVE_XINPUT_2_2
1023 XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
1025 source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
1026 GINT_TO_POINTER (xev->sourceid));
1028 if (xi_event->evtype == XI_TouchBegin)
1029 event->touch.type = event->type = CLUTTER_TOUCH_BEGIN;
1031 event->touch.type = event->type = CLUTTER_TOUCH_END;
1033 event->touch.stage = stage;
1034 event->touch.time = xev->time;
1035 event->touch.x = xev->event_x;
1036 event->touch.y = xev->event_y;
1037 event->touch.modifier_state =
1038 _clutter_input_device_xi2_translate_state (&xev->mods,
1041 clutter_event_set_source_device (event, source_device);
1043 device = g_hash_table_lookup (manager_xi2->devices_by_id,
1044 GINT_TO_POINTER (xev->deviceid));
1045 clutter_event_set_device (event, device);
1047 event->touch.axes = translate_axes (event->motion.device,
1053 if (source_device != NULL && device->stage != NULL)
1054 _clutter_input_device_set_stage (source_device, device->stage);
1056 if (xi_event->evtype == XI_TouchBegin)
1058 event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;
1060 _clutter_stage_x11_set_user_time (stage_x11, event->touch.time);
1063 event->touch.sequence = GUINT_TO_POINTER (xev->detail);
1065 if (xev->flags & XITouchEmulatingPointer)
1066 _clutter_event_set_pointer_emulated (event, TRUE);
1068 CLUTTER_NOTE (EVENT, "touch %s: win:0x%x device:%s (x:%.2f, y:%.2f, axes:%s)",
1069 event->type == CLUTTER_TOUCH_BEGIN ? "begin" : "end",
1070 (unsigned int) stage_x11->xwin,
1071 event->touch.device->device_name,
1074 event->touch.axes != NULL ? "yes" : "no");
1076 retval = CLUTTER_TRANSLATE_QUEUE;
1080 case XI_TouchUpdate:
1082 XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
1084 source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
1085 GINT_TO_POINTER (xev->sourceid));
1087 event->touch.type = event->type = CLUTTER_TOUCH_UPDATE;
1088 event->touch.stage = stage;
1089 event->touch.time = xev->time;
1090 event->touch.sequence = GUINT_TO_POINTER (xev->detail);
1091 event->touch.x = xev->event_x;
1092 event->touch.y = xev->event_y;
1094 clutter_event_set_source_device (event, source_device);
1096 device = g_hash_table_lookup (manager_xi2->devices_by_id,
1097 GINT_TO_POINTER (xev->deviceid));
1098 clutter_event_set_device (event, device);
1100 event->touch.axes = translate_axes (event->motion.device,
1106 if (source_device != NULL && device->stage != NULL)
1107 _clutter_input_device_set_stage (source_device, device->stage);
1109 event->touch.modifier_state =
1110 _clutter_input_device_xi2_translate_state (&xev->mods,
1112 event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;
1114 if (xev->flags & XITouchEmulatingPointer)
1115 _clutter_event_set_pointer_emulated (event, TRUE);
1117 CLUTTER_NOTE (EVENT, "touch update: win:0x%x device:%s (x:%.2f, y:%.2f, axes:%s)",
1118 (unsigned int) stage_x11->xwin,
1119 event->touch.device->device_name,
1122 event->touch.axes != NULL ? "yes" : "no");
1124 retval = CLUTTER_TRANSLATE_QUEUE;
1127 #endif /* HAVE_XINPUT_2_2 */
1132 XIEnterEvent *xev = (XIEnterEvent *) xi_event;
1134 device = g_hash_table_lookup (manager_xi2->devices_by_id,
1135 GINT_TO_POINTER (xev->deviceid));
1137 source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
1138 GINT_TO_POINTER (xev->sourceid));
1140 if (xi_event->evtype == XI_Enter)
1142 event->crossing.type = event->type = CLUTTER_ENTER;
1144 event->crossing.stage = stage;
1145 event->crossing.source = CLUTTER_ACTOR (stage);
1146 event->crossing.related = NULL;
1148 event->crossing.time = xev->time;
1149 event->crossing.x = xev->event_x;
1150 event->crossing.y = xev->event_y;
1152 _clutter_stage_add_device (stage, device);
1156 if (device->stage == NULL)
1158 CLUTTER_NOTE (EVENT,
1159 "Discarding Leave for ButtonRelease "
1162 retval = CLUTTER_TRANSLATE_REMOVE;
1166 event->crossing.type = event->type = CLUTTER_LEAVE;
1168 event->crossing.stage = stage;
1169 event->crossing.source = CLUTTER_ACTOR (stage);
1170 event->crossing.related = NULL;
1172 event->crossing.time = xev->time;
1173 event->crossing.x = xev->event_x;
1174 event->crossing.y = xev->event_y;
1176 _clutter_stage_remove_device (stage, device);
1179 _clutter_input_device_reset_scroll_info (source_device);
1181 clutter_event_set_device (event, device);
1182 clutter_event_set_source_device (event, source_device);
1184 retval = CLUTTER_TRANSLATE_QUEUE;
1190 retval = CLUTTER_TRANSLATE_CONTINUE;
1198 clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
1200 iface->translate_event = clutter_device_manager_xi2_translate_event;
1204 clutter_device_manager_xi2_add_device (ClutterDeviceManager *manager,
1205 ClutterInputDevice *device)
1211 clutter_device_manager_xi2_remove_device (ClutterDeviceManager *manager,
1212 ClutterInputDevice *device)
1217 static const GSList *
1218 clutter_device_manager_xi2_get_devices (ClutterDeviceManager *manager)
1220 ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager);
1221 GSList *all_devices = NULL;
1224 if (manager_xi2->all_devices != NULL)
1225 return manager_xi2->all_devices;
1227 for (l = manager_xi2->master_devices; l != NULL; l = l->next)
1228 all_devices = g_slist_prepend (all_devices, l->data);
1230 for (l = manager_xi2->slave_devices; l != NULL; l = l->next)
1231 all_devices = g_slist_prepend (all_devices, l->data);
1233 manager_xi2->all_devices = g_slist_reverse (all_devices);
1235 return manager_xi2->all_devices;
1238 static ClutterInputDevice *
1239 clutter_device_manager_xi2_get_device (ClutterDeviceManager *manager,
1242 ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager);
1244 return g_hash_table_lookup (manager_xi2->devices_by_id,
1245 GINT_TO_POINTER (id));
1248 static ClutterInputDevice *
1249 clutter_device_manager_xi2_get_core_device (ClutterDeviceManager *manager,
1250 ClutterInputDeviceType device_type)
1252 ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager);
1253 ClutterBackendX11 *backend_x11;
1254 ClutterInputDevice *device;
1258 CLUTTER_BACKEND_X11 (_clutter_device_manager_get_backend (manager));
1260 XIGetClientPointer (backend_x11->xdpy, None, &device_id);
1262 device = g_hash_table_lookup (manager_xi2->devices_by_id,
1263 GINT_TO_POINTER (device_id));
1265 switch (device_type)
1267 case CLUTTER_POINTER_DEVICE:
1270 case CLUTTER_KEYBOARD_DEVICE:
1271 return clutter_input_device_get_associated_device (device);
1281 relate_masters (gpointer key,
1285 ClutterDeviceManagerXI2 *manager_xi2 = data;
1286 ClutterInputDevice *device, *relative;
1288 device = g_hash_table_lookup (manager_xi2->devices_by_id, key);
1289 relative = g_hash_table_lookup (manager_xi2->devices_by_id, value);
1291 _clutter_input_device_set_associated_device (device, relative);
1292 _clutter_input_device_set_associated_device (relative, device);
1296 relate_slaves (gpointer key,
1300 ClutterDeviceManagerXI2 *manager_xi2 = data;
1301 ClutterInputDevice *master, *slave;
1303 master = g_hash_table_lookup (manager_xi2->devices_by_id, key);
1304 slave = g_hash_table_lookup (manager_xi2->devices_by_id, value);
1306 _clutter_input_device_set_associated_device (slave, master);
1307 _clutter_input_device_add_slave (master, slave);
1311 clutter_device_manager_xi2_constructed (GObject *gobject)
1313 ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (gobject);
1314 ClutterDeviceManager *manager = CLUTTER_DEVICE_MANAGER (gobject);
1315 ClutterBackendX11 *backend_x11;
1316 GHashTable *masters, *slaves;
1318 XIEventMask event_mask;
1319 unsigned char mask[2] = { 0, };
1323 CLUTTER_BACKEND_X11 (_clutter_device_manager_get_backend (manager));
1325 masters = g_hash_table_new (NULL, NULL);
1326 slaves = g_hash_table_new (NULL, NULL);
1328 info = XIQueryDevice (backend_x11->xdpy, XIAllDevices, &n_devices);
1330 for (i = 0; i < n_devices; i++)
1332 XIDeviceInfo *xi_device = &info[i];
1334 add_device (manager_xi2, backend_x11, xi_device, TRUE);
1336 if (xi_device->use == XIMasterPointer ||
1337 xi_device->use == XIMasterKeyboard)
1339 g_hash_table_insert (masters,
1340 GINT_TO_POINTER (xi_device->deviceid),
1341 GINT_TO_POINTER (xi_device->attachment));
1343 else if (xi_device->use == XISlavePointer ||
1344 xi_device->use == XISlaveKeyboard)
1346 g_hash_table_insert (slaves,
1347 GINT_TO_POINTER (xi_device->deviceid),
1348 GINT_TO_POINTER (xi_device->attachment));
1352 XIFreeDeviceInfo (info);
1354 g_hash_table_foreach (masters, relate_masters, manager_xi2);
1355 g_hash_table_destroy (masters);
1357 g_hash_table_foreach (slaves, relate_slaves, manager_xi2);
1358 g_hash_table_destroy (slaves);
1360 XISetMask (mask, XI_HierarchyChanged);
1361 XISetMask (mask, XI_DeviceChanged);
1363 event_mask.deviceid = XIAllDevices;
1364 event_mask.mask_len = sizeof (mask);
1365 event_mask.mask = mask;
1367 clutter_device_manager_xi2_select_events (manager,
1368 clutter_x11_get_root_window (),
1371 if (G_OBJECT_CLASS (clutter_device_manager_xi2_parent_class)->constructed)
1372 G_OBJECT_CLASS (clutter_device_manager_xi2_parent_class)->constructed (gobject);
1376 clutter_device_manager_xi2_set_property (GObject *gobject,
1378 const GValue *value,
1381 ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (gobject);
1386 manager_xi2->opcode = g_value_get_int (value);
1390 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1396 clutter_device_manager_xi2_class_init (ClutterDeviceManagerXI2Class *klass)
1398 ClutterDeviceManagerClass *manager_class;
1399 GObjectClass *gobject_class;
1401 obj_props[PROP_OPCODE] =
1402 g_param_spec_int ("opcode",
1407 CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
1409 gobject_class = G_OBJECT_CLASS (klass);
1410 gobject_class->constructed = clutter_device_manager_xi2_constructed;
1411 gobject_class->set_property = clutter_device_manager_xi2_set_property;
1413 g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
1415 manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass);
1416 manager_class->add_device = clutter_device_manager_xi2_add_device;
1417 manager_class->remove_device = clutter_device_manager_xi2_remove_device;
1418 manager_class->get_devices = clutter_device_manager_xi2_get_devices;
1419 manager_class->get_core_device = clutter_device_manager_xi2_get_core_device;
1420 manager_class->get_device = clutter_device_manager_xi2_get_device;
1424 clutter_device_manager_xi2_init (ClutterDeviceManagerXI2 *self)
1426 self->devices_by_id = g_hash_table_new_full (NULL, NULL,
1428 (GDestroyNotify) g_object_unref);