diff --git a/src/backends/x11/meta-clutter-backend-x11.c b/src/backends/x11/meta-clutter-backend-x11.c
index b6334de8b..d0654fa8a 100644
--- a/src/backends/x11/meta-clutter-backend-x11.c
+++ b/src/backends/x11/meta-clutter-backend-x11.c
@@ -31,6 +31,7 @@
#include "backends/x11/meta-clutter-backend-x11.h"
#include "backends/x11/meta-device-manager-x11.h"
#include "backends/x11/meta-keymap-x11.h"
+#include "backends/x11/meta-seat-x11.h"
#include "backends/x11/meta-xkb-a11y-x11.h"
#include "backends/x11/nested/meta-stage-x11-nested.h"
#include "clutter/clutter-mutter.h"
@@ -43,6 +44,7 @@ struct _MetaClutterBackendX11
ClutterBackendX11 parent;
MetaKeymapX11 *keymap;
MetaDeviceManagerX11 *device_manager;
+ MetaSeatX11 *core_seat;
};
G_DEFINE_TYPE (MetaClutterBackendX11, meta_clutter_backend_x11,
@@ -119,7 +121,6 @@ meta_clutter_backend_x11_translate_event (ClutterBackend *backend,
ClutterEvent *event)
{
MetaClutterBackendX11 *backend_x11 = META_CLUTTER_BACKEND_X11 (backend);
- MetaDeviceManagerX11 *device_manager_x11;
MetaStageX11 *stage_x11;
ClutterBackendClass *clutter_backend_class;
@@ -135,28 +136,12 @@ meta_clutter_backend_x11_translate_event (ClutterBackend *backend,
if (meta_stage_x11_translate_event (stage_x11, native, event))
return TRUE;
- device_manager_x11 = META_DEVICE_MANAGER_X11 (backend_x11->device_manager);
- if (meta_device_manager_x11_translate_event (device_manager_x11,
- native, event))
+ if (meta_seat_x11_translate_event (backend_x11->core_seat, native, event))
return TRUE;
return FALSE;
}
-static void
-on_keymap_state_change (MetaKeymapX11 *keymap_x11,
- gpointer data)
-{
- ClutterDeviceManager *device_manager = CLUTTER_DEVICE_MANAGER (data);
- ClutterKbdA11ySettings kbd_a11y_settings;
-
- /* On keymaps state change, just reapply the current settings, it'll
- * take care of enabling/disabling mousekeys based on NumLock state.
- */
- clutter_device_manager_get_kbd_a11y_settings (device_manager, &kbd_a11y_settings);
- meta_device_manager_x11_apply_kbd_a11y_settings (device_manager, &kbd_a11y_settings);
-}
-
static void
meta_clutter_backend_x11_init_events (ClutterBackend *backend)
{
@@ -175,25 +160,30 @@ meta_clutter_backend_x11_init_events (ClutterBackend *backend)
if (XIQueryVersion (clutter_x11_get_default_display (),
&major, &minor) != BadRequest)
{
+ backend_x11->core_seat =
+ meta_seat_x11_new (event_base,
+ META_VIRTUAL_CORE_POINTER_ID,
+ META_VIRTUAL_CORE_KEYBOARD_ID);
+
g_debug ("Creating XI2 device manager");
backend_x11->device_manager =
g_object_new (META_TYPE_DEVICE_MANAGER_X11,
"backend", backend_x11,
- "opcode", event_base,
+ "seat", backend_x11->core_seat,
NULL);
}
}
- if (!backend_x11->device_manager)
+ if (!backend_x11->core_seat)
g_error ("No XInput 2.3 support");
+}
- backend_x11->keymap = g_object_new (META_TYPE_KEYMAP_X11,
- "backend", backend_x11,
- NULL);
- g_signal_connect (backend_x11->keymap,
- "state-changed",
- G_CALLBACK (on_keymap_state_change),
- backend_x11->device_manager);
+static ClutterSeat *
+meta_clutter_backend_x11_get_default_seat (ClutterBackend *backend)
+{
+ MetaClutterBackendX11 *backend_x11 = META_CLUTTER_BACKEND_X11 (backend);
+
+ return CLUTTER_SEAT (backend_x11->core_seat);
}
static void
@@ -214,4 +204,5 @@ meta_clutter_backend_x11_class_init (MetaClutterBackendX11Class *klass)
clutter_backend_class->get_keymap = meta_clutter_backend_x11_get_keymap;
clutter_backend_class->translate_event = meta_clutter_backend_x11_translate_event;
clutter_backend_class->init_events = meta_clutter_backend_x11_init_events;
+ clutter_backend_class->get_default_seat = meta_clutter_backend_x11_get_default_seat;
}
diff --git a/src/backends/x11/meta-device-manager-x11.c b/src/backends/x11/meta-device-manager-x11.c
index c7d748a55..946aea5b0 100644
--- a/src/backends/x11/meta-device-manager-x11.c
+++ b/src/backends/x11/meta-device-manager-x11.c
@@ -28,6 +28,7 @@
#include "backends/x11/meta-input-device-x11.h"
#include "backends/x11/meta-input-device-tool-x11.h"
#include "backends/x11/meta-keymap-x11.h"
+#include "backends/x11/meta-seat-x11.h"
#include "backends/x11/meta-stage-x11.h"
#include "backends/x11/meta-virtual-input-device-x11.h"
#include "backends/x11/meta-xkb-a11y-x11.h"
@@ -40,54 +41,13 @@ enum
{
PROP_0,
- PROP_OPCODE,
+ PROP_SEAT,
PROP_LAST
};
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
-static const char *clutter_input_axis_atom_names[] = {
- "Abs X", /* CLUTTER_INPUT_AXIS_X */
- "Abs Y", /* CLUTTER_INPUT_AXIS_Y */
- "Abs Pressure", /* CLUTTER_INPUT_AXIS_PRESSURE */
- "Abs Tilt X", /* CLUTTER_INPUT_AXIS_XTILT */
- "Abs Tilt Y", /* CLUTTER_INPUT_AXIS_YTILT */
- "Abs Wheel", /* CLUTTER_INPUT_AXIS_WHEEL */
- "Abs Distance", /* CLUTTER_INPUT_AXIS_DISTANCE */
-};
-
-#define N_AXIS_ATOMS G_N_ELEMENTS (clutter_input_axis_atom_names)
-
-static const char *wacom_type_atoms[] = {
- "STYLUS",
- "CURSOR",
- "ERASER",
- "PAD",
- "TOUCH"
-};
-#define N_WACOM_TYPE_ATOMS G_N_ELEMENTS (wacom_type_atoms)
-
-enum
-{
- WACOM_TYPE_STYLUS,
- WACOM_TYPE_CURSOR,
- WACOM_TYPE_ERASER,
- WACOM_TYPE_PAD,
- WACOM_TYPE_TOUCH,
-};
-
-enum
-{
- PAD_AXIS_FIRST = 3, /* First axes are always x/y/pressure, ignored in pads */
- PAD_AXIS_STRIP1 = PAD_AXIS_FIRST,
- PAD_AXIS_STRIP2,
- PAD_AXIS_RING1,
- PAD_AXIS_RING2,
-};
-
-static Atom clutter_input_axis_atoms[N_AXIS_ATOMS] = { 0, };
-
G_DEFINE_TYPE (MetaDeviceManagerX11,
meta_device_manager_x11,
CLUTTER_TYPE_DEVICE_MANAGER)
@@ -115,953 +75,6 @@ meta_device_manager_x11_free_event_data (ClutterDeviceManager *device_manager,
meta_event_x11_free (event_x11);
}
-static void
-translate_valuator_class (Display *xdisplay,
- ClutterInputDevice *device,
- XIValuatorClassInfo *class)
-{
- static gboolean atoms_initialized = FALSE;
- ClutterInputAxis i, axis = CLUTTER_INPUT_AXIS_IGNORE;
-
- if (G_UNLIKELY (!atoms_initialized))
- {
- XInternAtoms (xdisplay,
- (char **) clutter_input_axis_atom_names, N_AXIS_ATOMS,
- False,
- clutter_input_axis_atoms);
-
- atoms_initialized = TRUE;
- }
-
- for (i = 0;
- i < N_AXIS_ATOMS;
- i += 1)
- {
- if (clutter_input_axis_atoms[i] == class->label)
- {
- axis = i + 1;
- break;
- }
- }
-
- _clutter_input_device_add_axis (device, axis,
- class->min,
- class->max,
- class->resolution);
-
- g_debug ("Added axis '%s' (min:%.2f, max:%.2fd, res:%d) of device %d",
- clutter_input_axis_atom_names[axis],
- class->min,
- class->max,
- class->resolution,
- device->id);
-}
-
-static void
-translate_device_classes (Display *xdisplay,
- ClutterInputDevice *device,
- XIAnyClassInfo **classes,
- int n_classes)
-{
- int i;
-
- for (i = 0; i < n_classes; i++)
- {
- XIAnyClassInfo *class_info = classes[i];
-
- switch (class_info->type)
- {
- case XIKeyClass:
- {
- XIKeyClassInfo *key_info = (XIKeyClassInfo *) class_info;
- int j;
-
- _clutter_input_device_set_n_keys (device,
- key_info->num_keycodes);
-
- for (j = 0; j < key_info->num_keycodes; j++)
- {
- clutter_input_device_set_key (device, j,
- key_info->keycodes[i],
- 0);
- }
- }
- break;
-
- case XIValuatorClass:
- translate_valuator_class (xdisplay, device,
- (XIValuatorClassInfo *) class_info);
- break;
-
- case XIScrollClass:
- {
- XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info;
- ClutterScrollDirection direction;
-
- if (scroll_info->scroll_type == XIScrollTypeVertical)
- direction = CLUTTER_SCROLL_DOWN;
- else
- direction = CLUTTER_SCROLL_RIGHT;
-
- g_debug ("Scroll valuator %d: %s, increment: %f",
- scroll_info->number,
- scroll_info->scroll_type == XIScrollTypeVertical
- ? "vertical"
- : "horizontal",
- scroll_info->increment);
-
- _clutter_input_device_add_scroll_info (device,
- scroll_info->number,
- direction,
- scroll_info->increment);
- }
- break;
-
- default:
- break;
- }
- }
-}
-
-static gboolean
-is_touch_device (XIAnyClassInfo **classes,
- int n_classes,
- ClutterInputDeviceType *device_type,
- uint32_t *n_touch_points)
-{
- int i;
-
- for (i = 0; i < n_classes; i++)
- {
- XITouchClassInfo *class = (XITouchClassInfo *) classes[i];
-
- if (class->type != XITouchClass)
- continue;
-
- if (class->num_touches > 0)
- {
- if (class->mode == XIDirectTouch)
- *device_type = CLUTTER_TOUCHSCREEN_DEVICE;
- else if (class->mode == XIDependentTouch)
- *device_type = CLUTTER_TOUCHPAD_DEVICE;
- else
- continue;
-
- *n_touch_points = class->num_touches;
-
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-static gboolean
-is_touchpad_device (XIDeviceInfo *info)
-{
- gulong nitems, bytes_after;
- uint32_t *data = NULL;
- int rc, format;
- Atom type;
- Atom prop;
-
- prop = XInternAtom (clutter_x11_get_default_display (), "libinput Tapping Enabled", True);
- if (prop == None)
- return FALSE;
-
- clutter_x11_trap_x_errors ();
- rc = XIGetProperty (clutter_x11_get_default_display (),
- info->deviceid,
- prop,
- 0, 1, False, XA_INTEGER, &type, &format, &nitems, &bytes_after,
- (guchar **) &data);
- clutter_x11_untrap_x_errors ();
-
- /* We don't care about the data */
- XFree (data);
-
- if (rc != Success || type != XA_INTEGER || format != 8 || nitems != 1)
- return FALSE;
-
- return TRUE;
-}
-
-static gboolean
-get_device_ids (XIDeviceInfo *info,
- char **vendor_id,
- char **product_id)
-{
- gulong nitems, bytes_after;
- uint32_t *data = NULL;
- int rc, format;
- Atom type;
-
- clutter_x11_trap_x_errors ();
- rc = XIGetProperty (clutter_x11_get_default_display (),
- info->deviceid,
- XInternAtom (clutter_x11_get_default_display (), "Device Product ID", False),
- 0, 2, False, XA_INTEGER, &type, &format, &nitems, &bytes_after,
- (guchar **) &data);
- clutter_x11_untrap_x_errors ();
-
- if (rc != Success || type != XA_INTEGER || format != 32 || nitems != 2)
- {
- XFree (data);
- return FALSE;
- }
-
- if (vendor_id)
- *vendor_id = g_strdup_printf ("%.4x", data[0]);
- if (product_id)
- *product_id = g_strdup_printf ("%.4x", data[1]);
-
- XFree (data);
-
- return TRUE;
-}
-
-static char *
-get_device_node_path (XIDeviceInfo *info)
-{
- gulong nitems, bytes_after;
- guchar *data;
- int rc, format;
- Atom prop, type;
- char *node_path;
-
- prop = XInternAtom (clutter_x11_get_default_display (), "Device Node", False);
- if (prop == None)
- return NULL;
-
- clutter_x11_trap_x_errors ();
-
- rc = XIGetProperty (clutter_x11_get_default_display (),
- info->deviceid, prop, 0, 1024, False,
- XA_STRING, &type, &format, &nitems, &bytes_after,
- (guchar **) &data);
-
- if (clutter_x11_untrap_x_errors ())
- return NULL;
-
- if (rc != Success || type != XA_STRING || format != 8)
- {
- XFree (data);
- return FALSE;
- }
-
- node_path = g_strdup ((char *) data);
- XFree (data);
-
- return node_path;
-}
-
-static void
-get_pad_features (XIDeviceInfo *info,
- uint32_t *n_rings,
- uint32_t *n_strips)
-{
- int i, rings = 0, strips = 0;
-
- for (i = PAD_AXIS_FIRST; i < info->num_classes; i++)
- {
- XIValuatorClassInfo *valuator = (XIValuatorClassInfo*) info->classes[i];
- int axis = valuator->number;
-
- if (valuator->type != XIValuatorClass)
- continue;
- if (valuator->max <= 1)
- continue;
-
- /* Ring/strip axes are fixed in pad devices as handled by the
- * wacom driver. Match those to detect pad features.
- */
- if (axis == PAD_AXIS_STRIP1 || axis == PAD_AXIS_STRIP2)
- strips++;
- else if (axis == PAD_AXIS_RING1 || axis == PAD_AXIS_RING2)
- rings++;
- }
-
- *n_rings = rings;
- *n_strips = strips;
-}
-
-/* The Wacom driver exports the tool type as property. Use that over
- guessing based on the device name */
-static gboolean
-guess_source_from_wacom_type (XIDeviceInfo *info,
- ClutterInputDeviceType *source_out)
-{
- gulong nitems, bytes_after;
- uint32_t *data = NULL;
- int rc, format;
- Atom type;
- Atom prop;
- Atom device_type;
- Atom types[N_WACOM_TYPE_ATOMS];
-
- prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Tool Type", True);
- if (prop == None)
- return FALSE;
-
- clutter_x11_trap_x_errors ();
- rc = XIGetProperty (clutter_x11_get_default_display (),
- info->deviceid,
- prop,
- 0, 1, False, XA_ATOM, &type, &format, &nitems, &bytes_after,
- (guchar **) &data);
- clutter_x11_untrap_x_errors ();
-
- if (rc != Success || type != XA_ATOM || format != 32 || nitems != 1)
- {
- XFree (data);
- return FALSE;
- }
-
- device_type = *data;
- XFree (data);
-
- if (device_type == 0)
- return FALSE;
-
- rc = XInternAtoms (clutter_x11_get_default_display (),
- (char **)wacom_type_atoms,
- N_WACOM_TYPE_ATOMS,
- False,
- types);
- if (rc == 0)
- return FALSE;
-
- if (device_type == types[WACOM_TYPE_STYLUS])
- {
- *source_out = CLUTTER_PEN_DEVICE;
- }
- else if (device_type == types[WACOM_TYPE_CURSOR])
- {
- *source_out = CLUTTER_CURSOR_DEVICE;
- }
- else if (device_type == types[WACOM_TYPE_ERASER])
- {
- *source_out = CLUTTER_ERASER_DEVICE;
- }
- else if (device_type == types[WACOM_TYPE_PAD])
- {
- *source_out = CLUTTER_PAD_DEVICE;
- }
- else if (device_type == types[WACOM_TYPE_TOUCH])
- {
- uint32_t num_touches = 0;
-
- if (!is_touch_device (info->classes, info->num_classes,
- source_out, &num_touches))
- *source_out = CLUTTER_TOUCHSCREEN_DEVICE;
- }
- else
- {
- return FALSE;
- }
-
- return TRUE;
-}
-
-static ClutterInputDevice *
-create_device (MetaDeviceManagerX11 *manager_xi2,
- ClutterBackend *backend,
- XIDeviceInfo *info)
-{
- ClutterInputDeviceType source, touch_source;
- ClutterInputDevice *retval;
- ClutterInputMode mode;
- gboolean is_enabled;
- uint32_t num_touches = 0, num_rings = 0, num_strips = 0;
- char *vendor_id = NULL, *product_id = NULL, *node_path = NULL;
-
- if (info->use == XIMasterKeyboard || info->use == XISlaveKeyboard)
- {
- source = CLUTTER_KEYBOARD_DEVICE;
- }
- else if (is_touchpad_device (info))
- {
- source = CLUTTER_TOUCHPAD_DEVICE;
- }
- else if (info->use == XISlavePointer &&
- is_touch_device (info->classes, info->num_classes,
- &touch_source,
- &num_touches))
- {
- source = touch_source;
- }
- else if (!guess_source_from_wacom_type (info, &source))
- {
- char *name;
-
- name = g_ascii_strdown (info->name, -1);
-
- if (strstr (name, "eraser") != NULL)
- source = CLUTTER_ERASER_DEVICE;
- else if (strstr (name, "cursor") != NULL)
- source = CLUTTER_CURSOR_DEVICE;
- else if (strstr (name, " pad") != NULL)
- source = CLUTTER_PAD_DEVICE;
- else if (strstr (name, "wacom") != NULL || strstr (name, "pen") != NULL)
- source = CLUTTER_PEN_DEVICE;
- else if (strstr (name, "touchpad") != NULL)
- source = CLUTTER_TOUCHPAD_DEVICE;
- else
- source = CLUTTER_POINTER_DEVICE;
-
- g_free (name);
- }
-
- switch (info->use)
- {
- case XIMasterKeyboard:
- case XIMasterPointer:
- mode = CLUTTER_INPUT_MODE_MASTER;
- is_enabled = TRUE;
- break;
-
- case XISlaveKeyboard:
- case XISlavePointer:
- mode = CLUTTER_INPUT_MODE_SLAVE;
- is_enabled = FALSE;
- break;
-
- case XIFloatingSlave:
- default:
- mode = CLUTTER_INPUT_MODE_FLOATING;
- is_enabled = FALSE;
- break;
- }
-
- if (info->use != XIMasterKeyboard &&
- info->use != XIMasterPointer)
- {
- get_device_ids (info, &vendor_id, &product_id);
- node_path = get_device_node_path (info);
- }
-
- if (source == CLUTTER_PAD_DEVICE)
- {
- is_enabled = TRUE;
- get_pad_features (info, &num_rings, &num_strips);
- }
-
- retval = g_object_new (META_TYPE_INPUT_DEVICE_X11,
- "name", info->name,
- "id", info->deviceid,
- "has-cursor", (info->use == XIMasterPointer),
- "device-manager", manager_xi2,
- "device-type", source,
- "device-mode", mode,
- "backend", backend,
- "enabled", is_enabled,
- "vendor-id", vendor_id,
- "product-id", product_id,
- "device-node", node_path,
- "n-rings", num_rings,
- "n-strips", num_strips,
- "n-mode-groups", MAX (num_rings, num_strips),
- NULL);
-
- translate_device_classes (clutter_x11_get_default_display (), retval,
- info->classes,
- info->num_classes);
-
-#ifdef HAVE_LIBWACOM
- if (source == CLUTTER_PAD_DEVICE)
- meta_input_device_x11_ensure_wacom_info (retval, manager_xi2->wacom_db);
-#endif
-
- g_free (vendor_id);
- g_free (product_id);
- g_free (node_path);
-
- g_debug ("Created device '%s' (id: %d, has-cursor: %s)",
- info->name,
- info->deviceid,
- info->use == XIMasterPointer ? "yes" : "no");
-
- return retval;
-}
-
-static void
-pad_passive_button_grab (ClutterInputDevice *device)
-{
- XIGrabModifiers xi_grab_mods = { XIAnyModifier, };
- XIEventMask xi_event_mask;
- int device_id, rc;
-
- device_id = clutter_input_device_get_device_id (device);
-
- xi_event_mask.deviceid = device_id;
- xi_event_mask.mask_len = XIMaskLen (XI_LASTEVENT);
- xi_event_mask.mask = g_new0 (unsigned char, xi_event_mask.mask_len);
-
- XISetMask (xi_event_mask.mask, XI_Motion);
- XISetMask (xi_event_mask.mask, XI_ButtonPress);
- XISetMask (xi_event_mask.mask, XI_ButtonRelease);
-
- clutter_x11_trap_x_errors ();
- rc = XIGrabButton (clutter_x11_get_default_display (),
- device_id, XIAnyButton,
- clutter_x11_get_root_window (), None,
- XIGrabModeSync, XIGrabModeSync,
- True, &xi_event_mask, 1, &xi_grab_mods);
- if (rc != 0)
- {
- g_warning ("Could not passively grab pad device: %s",
- clutter_input_device_get_device_name (device));
- }
- else
- {
- XIAllowEvents (clutter_x11_get_default_display (),
- device_id, XIAsyncDevice,
- CLUTTER_CURRENT_TIME);
- }
-
- clutter_x11_untrap_x_errors ();
-
- g_free (xi_event_mask.mask);
-}
-
-static ClutterInputDevice *
-add_device (MetaDeviceManagerX11 *manager_xi2,
- ClutterBackend *backend,
- XIDeviceInfo *info,
- gboolean in_construction)
-{
- ClutterInputDevice *device;
-
- device = create_device (manager_xi2, backend, info);
-
- /* we don't go through the DeviceManager::add_device() vfunc because
- * that emits the signal, and we only do it conditionally
- */
- g_hash_table_replace (manager_xi2->devices_by_id,
- GINT_TO_POINTER (info->deviceid),
- device);
-
- if (info->use == XIMasterPointer ||
- info->use == XIMasterKeyboard)
- {
- manager_xi2->master_devices =
- g_list_prepend (manager_xi2->master_devices, device);
- }
- else if (info->use == XISlavePointer ||
- info->use == XISlaveKeyboard ||
- info->use == XIFloatingSlave)
- {
- manager_xi2->slave_devices =
- g_list_prepend (manager_xi2->slave_devices, device);
- }
- else
- g_warning ("Unhandled device: %s",
- clutter_input_device_get_device_name (device));
-
- if (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE)
- pad_passive_button_grab (device);
-
- /* relationships between devices and signal emissions are not
- * necessary while we're constructing the device manager instance
- */
- if (!in_construction)
- {
- if (info->use == XISlavePointer || info->use == XISlaveKeyboard)
- {
- ClutterInputDevice *master;
-
- master = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (info->attachment));
- _clutter_input_device_set_associated_device (device, master);
- _clutter_input_device_add_slave (master, device);
- }
-
- /* blow the cache */
- g_slist_free (manager_xi2->all_devices);
- manager_xi2->all_devices = NULL;
-
- g_signal_emit_by_name (manager_xi2, "device-added", device);
- }
-
- return device;
-}
-
-static void
-remove_device (MetaDeviceManagerX11 *manager_xi2,
- int device_id)
-{
- ClutterInputDevice *device;
-
- device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (device_id));
-
- if (device != NULL)
- {
- manager_xi2->master_devices =
- g_list_remove (manager_xi2->master_devices, device);
- manager_xi2->slave_devices =
- g_list_remove (manager_xi2->slave_devices, device);
-
- /* blow the cache */
- g_slist_free (manager_xi2->all_devices);
- manager_xi2->all_devices = NULL;
-
- g_signal_emit_by_name (manager_xi2, "device-removed", device);
-
- g_object_run_dispose (G_OBJECT (device));
-
- g_hash_table_remove (manager_xi2->devices_by_id,
- GINT_TO_POINTER (device_id));
- }
-}
-
-static void
-translate_hierarchy_event (ClutterBackend *backend,
- MetaDeviceManagerX11 *manager_xi2,
- XIHierarchyEvent *ev)
-{
- int i;
-
- for (i = 0; i < ev->num_info; i++)
- {
- if (ev->info[i].flags & XIDeviceEnabled &&
- !g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (ev->info[i].deviceid)))
- {
- XIDeviceInfo *info;
- int n_devices;
-
- g_debug ("Hierarchy event: device enabled");
-
- clutter_x11_trap_x_errors ();
- info = XIQueryDevice (clutter_x11_get_default_display (),
- ev->info[i].deviceid,
- &n_devices);
- clutter_x11_untrap_x_errors ();
- if (info != NULL)
- {
- add_device (manager_xi2, backend, &info[0], FALSE);
- XIFreeDeviceInfo (info);
- }
- }
- else if (ev->info[i].flags & XIDeviceDisabled)
- {
- g_debug ("Hierarchy event: device disabled");
-
- remove_device (manager_xi2, ev->info[i].deviceid);
- }
- else if ((ev->info[i].flags & XISlaveAttached) ||
- (ev->info[i].flags & XISlaveDetached))
- {
- ClutterInputDevice *master, *slave;
- XIDeviceInfo *info;
- int n_devices;
-
- g_debug ("Hierarchy event: slave %s",
- (ev->info[i].flags & XISlaveAttached)
- ? "attached"
- : "detached");
-
- slave = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (ev->info[i].deviceid));
- master = clutter_input_device_get_associated_device (slave);
-
- /* detach the slave in both cases */
- if (master != NULL)
- {
- _clutter_input_device_remove_slave (master, slave);
- _clutter_input_device_set_associated_device (slave, NULL);
- }
-
- /* and attach the slave to the new master if needed */
- if (ev->info[i].flags & XISlaveAttached)
- {
- clutter_x11_trap_x_errors ();
- info = XIQueryDevice (clutter_x11_get_default_display (),
- ev->info[i].deviceid,
- &n_devices);
- clutter_x11_untrap_x_errors ();
- if (info != NULL)
- {
- master = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (info->attachment));
- if (master != NULL)
- {
- _clutter_input_device_set_associated_device (slave, master);
- _clutter_input_device_add_slave (master, slave);
- }
- XIFreeDeviceInfo (info);
- }
- }
- }
- }
-}
-
-static void
-meta_device_manager_x11_select_events (ClutterDeviceManager *manager,
- Window xwindow,
- XIEventMask *event_mask)
-{
- Display *xdisplay;
-
- xdisplay = clutter_x11_get_default_display ();
-
- XISelectEvents (xdisplay, xwindow, event_mask, 1);
-}
-
-static ClutterStage *
-get_event_stage (MetaDeviceManagerX11 *manager_xi2,
- XIEvent *xi_event)
-{
- Window xwindow = None;
-
- switch (xi_event->evtype)
- {
- case XI_KeyPress:
- case XI_KeyRelease:
- case XI_ButtonPress:
- case XI_ButtonRelease:
- case XI_Motion:
- case XI_TouchBegin:
- case XI_TouchUpdate:
- case XI_TouchEnd:
- {
- XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
-
- xwindow = xev->event;
- }
- break;
-
- case XI_Enter:
- case XI_Leave:
- case XI_FocusIn:
- case XI_FocusOut:
- {
- XIEnterEvent *xev = (XIEnterEvent *) xi_event;
-
- xwindow = xev->event;
- }
- break;
-
- default:
- break;
- }
-
- if (xwindow == None)
- return NULL;
-
- return meta_x11_get_stage_from_window (xwindow);
-}
-
-/*
- * print_key_sym: Translate a symbol to its printable form if any
- * @symbol: the symbol to translate
- * @buffer: the buffer where to put the translated string
- * @len: size of the buffer
- *
- * Translates @symbol into a printable representation in @buffer, if possible.
- *
- * Return value: The number of bytes of the translated string, 0 if the
- * symbol can't be printed
- *
- * Note: The code is derived from libX11's src/KeyBind.c
- * Copyright 1985, 1987, 1998 The Open Group
- *
- * Note: This code works for Latin-1 symbols. clutter_keysym_to_unicode()
- * does the work for the other keysyms.
- */
-static int
-print_keysym (uint32_t symbol,
- char *buffer,
- int len)
-{
- unsigned long high_bytes;
- unsigned char c;
-
- high_bytes = symbol >> 8;
- if (!(len &&
- ((high_bytes == 0) ||
- ((high_bytes == 0xFF) &&
- (((symbol >= CLUTTER_KEY_BackSpace) &&
- (symbol <= CLUTTER_KEY_Clear)) ||
- (symbol == CLUTTER_KEY_Return) ||
- (symbol == CLUTTER_KEY_Escape) ||
- (symbol == CLUTTER_KEY_KP_Space) ||
- (symbol == CLUTTER_KEY_KP_Tab) ||
- (symbol == CLUTTER_KEY_KP_Enter) ||
- ((symbol >= CLUTTER_KEY_KP_Multiply) &&
- (symbol <= CLUTTER_KEY_KP_9)) ||
- (symbol == CLUTTER_KEY_KP_Equal) ||
- (symbol == CLUTTER_KEY_Delete))))))
- return 0;
-
- /* if X keysym, convert to ascii by grabbing low 7 bits */
- if (symbol == CLUTTER_KEY_KP_Space)
- c = CLUTTER_KEY_space & 0x7F; /* patch encoding botch */
- else if (high_bytes == 0xFF)
- c = symbol & 0x7F;
- else
- c = symbol & 0xFF;
-
- buffer[0] = c;
- return 1;
-}
-
-static double *
-translate_axes (ClutterInputDevice *device,
- double x,
- double y,
- XIValuatorState *valuators)
-{
- uint32_t n_axes = clutter_input_device_get_n_axes (device);
- uint32_t i;
- double *retval;
- double *values;
-
- retval = g_new0 (double, n_axes);
- values = valuators->values;
-
- for (i = 0; i < valuators->mask_len * 8; i++)
- {
- ClutterInputAxis axis;
- double val;
-
- if (!XIMaskIsSet (valuators->mask, i))
- continue;
-
- axis = clutter_input_device_get_axis (device, i);
- val = *values++;
-
- switch (axis)
- {
- case CLUTTER_INPUT_AXIS_X:
- retval[i] = x;
- break;
-
- case CLUTTER_INPUT_AXIS_Y:
- retval[i] = y;
- break;
-
- default:
- _clutter_input_device_translate_axis (device, i, val, &retval[i]);
- break;
- }
- }
-
- return retval;
-}
-
-static gboolean
-translate_pad_axis (ClutterInputDevice *device,
- XIValuatorState *valuators,
- ClutterEventType *evtype,
- uint32_t *number,
- double *value)
-{
- double *values;
- int i;
-
- values = valuators->values;
-
- for (i = PAD_AXIS_FIRST; i < valuators->mask_len * 8; i++)
- {
- double val;
- uint32_t axis_number = 0;
-
- if (!XIMaskIsSet (valuators->mask, i))
- continue;
-
- val = *values++;
- if (val <= 0)
- continue;
-
- _clutter_input_device_translate_axis (device, i, val, value);
-
- if (i == PAD_AXIS_RING1 || i == PAD_AXIS_RING2)
- {
- *evtype = CLUTTER_PAD_RING;
- (*value) *= 360.0;
- }
- else if (i == PAD_AXIS_STRIP1 || i == PAD_AXIS_STRIP2)
- {
- *evtype = CLUTTER_PAD_STRIP;
- }
- else
- continue;
-
- if (i == PAD_AXIS_STRIP2 || i == PAD_AXIS_RING2)
- axis_number++;
-
- *number = axis_number;
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-translate_coords (MetaStageX11 *stage_x11,
- double event_x,
- double event_y,
- float *x_out,
- float *y_out)
-{
- ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
- ClutterActor *stage = CLUTTER_ACTOR (stage_cogl->wrapper);
- float stage_width;
- float stage_height;
-
- clutter_actor_get_size (stage, &stage_width, &stage_height);
-
- *x_out = CLAMP (event_x, 0, stage_width);
- *y_out = CLAMP (event_y, 0, stage_height);
-}
-
-static double
-scroll_valuators_changed (ClutterInputDevice *device,
- XIValuatorState *valuators,
- double *dx_p,
- double *dy_p)
-{
- gboolean retval = FALSE;
- uint32_t n_axes, n_val, i;
- double *values;
-
- n_axes = clutter_input_device_get_n_axes (device);
- values = valuators->values;
-
- *dx_p = *dy_p = 0.0;
-
- n_val = 0;
-
- for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++)
- {
- ClutterScrollDirection direction;
- double delta;
-
- if (!XIMaskIsSet (valuators->mask, i))
- continue;
-
- if (_clutter_input_device_get_scroll_delta (device, i,
- values[n_val],
- &direction,
- &delta))
- {
- retval = TRUE;
-
- if (direction == CLUTTER_SCROLL_UP ||
- direction == CLUTTER_SCROLL_DOWN)
- *dy_p = delta;
- else
- *dx_p = delta;
- }
-
- n_val += 1;
- }
-
- return retval;
-}
-
static void
meta_device_manager_x11_select_stage_events (ClutterDeviceManager *manager,
ClutterStage *stage)
@@ -1098,828 +111,6 @@ meta_device_manager_x11_select_stage_events (ClutterDeviceManager *manager,
g_free (mask);
}
-static uint
-device_get_tool_serial (ClutterInputDevice *device)
-{
- gulong nitems, bytes_after;
- uint32_t *data = NULL;
- int serial_id = 0;
- int rc, format;
- Atom type;
- Atom prop;
-
- prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Serial IDs", True);
- if (prop == None)
- return 0;
-
- clutter_x11_trap_x_errors ();
- rc = XIGetProperty (clutter_x11_get_default_display (),
- clutter_input_device_get_device_id (device),
- prop, 0, 4, FALSE, XA_INTEGER, &type, &format, &nitems, &bytes_after,
- (guchar **) &data);
- clutter_x11_untrap_x_errors ();
-
- if (rc == Success && type == XA_INTEGER && format == 32 && nitems >= 4)
- serial_id = data[3];
-
- XFree (data);
-
- return serial_id;
-}
-
-static void
-handle_property_event (MetaDeviceManagerX11 *manager_xi2,
- XIEvent *event)
-{
- XIPropertyEvent *xev = (XIPropertyEvent *) event;
- Atom serial_ids_prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Serial IDs", True);
- ClutterInputDevice *device;
-
- device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->deviceid));
- if (!device)
- return;
-
- if (xev->property == serial_ids_prop)
- {
- ClutterInputDeviceTool *tool = NULL;
- ClutterInputDeviceToolType type;
- int serial_id;
-
- serial_id = device_get_tool_serial (device);
-
- if (serial_id != 0)
- {
- tool = g_hash_table_lookup (manager_xi2->tools_by_serial,
- GUINT_TO_POINTER (serial_id));
- if (!tool)
- {
- type = clutter_input_device_get_device_type (device) == CLUTTER_ERASER_DEVICE ?
- CLUTTER_INPUT_DEVICE_TOOL_ERASER : CLUTTER_INPUT_DEVICE_TOOL_PEN;
- tool = meta_input_device_tool_x11_new (serial_id, type);
- g_hash_table_insert (manager_xi2->tools_by_serial,
- GUINT_TO_POINTER (serial_id),
- tool);
- }
- }
-
- meta_input_device_x11_update_tool (device, tool);
- g_signal_emit_by_name (manager_xi2, "tool-changed", device, tool);
- }
-}
-
-static gboolean
-translate_pad_event (ClutterEvent *event,
- XIDeviceEvent *xev,
- ClutterInputDevice *device)
-{
- double value;
- uint32_t number, mode = 0;
-
- if (!translate_pad_axis (device, &xev->valuators,
- &event->any.type,
- &number, &value))
- return FALSE;
-
- /* When touching a ring/strip a first XI_Motion event
- * is generated. Use it to reset the pad state, so
- * later events actually have a directionality.
- */
- if (xev->evtype == XI_Motion)
- value = -1;
-
-#ifdef HAVE_LIBWACOM
- mode = meta_input_device_x11_get_pad_group_mode (device, number);
-#endif
-
- if (event->any.type == CLUTTER_PAD_RING)
- {
- event->pad_ring.ring_number = number;
- event->pad_ring.angle = value;
- event->pad_ring.mode = mode;
- }
- else
- {
- event->pad_strip.strip_number = number;
- event->pad_strip.value = value;
- event->pad_strip.mode = mode;
- }
-
- event->any.time = xev->time;
- clutter_event_set_device (event, device);
- clutter_event_set_source_device (event, device);
-
- g_debug ("%s: win:0x%x, device:%d '%s', time:%d "
- "(value:%f)",
- event->any.type == CLUTTER_PAD_RING
- ? "pad ring "
- : "pad strip",
- (unsigned int) xev->event,
- device->id,
- device->device_name,
- event->any.time, value);
-
- return TRUE;
-}
-
-static void
-handle_raw_event (MetaDeviceManagerX11 *manager_xi2,
- XEvent *xevent)
-{
- ClutterInputDevice *device;
- XGenericEventCookie *cookie;
- XIEvent *xi_event;
- XIRawEvent *xev;
- float x,y;
-
- cookie = &xevent->xcookie;
- xi_event = (XIEvent *) cookie->data;
- xev = (XIRawEvent *) xi_event;
-
- device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->deviceid));
- if (device == NULL)
- return;
-
- if (!_clutter_is_input_pointer_a11y_enabled (device))
- return;
-
- switch (cookie->evtype)
- {
- case XI_RawMotion:
- g_debug ("raw motion: device:%d '%s'",
- device->id,
- device->device_name);
-
- /* We don't get actual pointer location with raw events, and we cannot
- * rely on `clutter_input_device_get_coords()` either because of
- * unreparented toplevels (like all client-side decoration windows),
- * so we need to explicitely query the pointer here...
- */
- if (meta_input_device_x11_get_pointer_location (device, &x, &y))
- _clutter_input_pointer_a11y_on_motion_event (device, x, y);
- break;
- case XI_RawButtonPress:
- case XI_RawButtonRelease:
- g_debug ("raw button %s: device:%d '%s' button %i",
- cookie->evtype == XI_RawButtonPress
- ? "press "
- : "release",
- device->id,
- device->device_name,
- xev->detail);
- _clutter_input_pointer_a11y_on_button_event (device,
- xev->detail,
- (cookie->evtype == XI_RawButtonPress));
- break;
- }
-}
-
-static ClutterInputDevice *
-get_source_device_checked (MetaDeviceManagerX11 *manager_xi2,
- XIDeviceEvent *xev)
-{
- ClutterInputDevice *source_device;
-
- source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->sourceid));
-
- if (!source_device)
- g_warning ("Impossible to get the source device with id %d for event of "
- "type %d", xev->sourceid, xev->evtype);
-
- return source_device;
-}
-
-gboolean
-meta_device_manager_x11_translate_event (MetaDeviceManagerX11 *manager_xi2,
- XEvent *xevent,
- ClutterEvent *event)
-{
- gboolean retval = FALSE;
- ClutterBackend *backend = clutter_get_default_backend ();
- MetaStageX11 *stage_x11 = NULL;
- ClutterStage *stage = NULL;
- ClutterInputDevice *device, *source_device;
- XGenericEventCookie *cookie;
- XIEvent *xi_event;
-
- cookie = &xevent->xcookie;
-
- if (cookie->type != GenericEvent ||
- cookie->extension != manager_xi2->opcode)
- return FALSE;
-
- xi_event = (XIEvent *) cookie->data;
-
- if (!xi_event)
- return FALSE;
-
- if (cookie->evtype == XI_RawMotion ||
- cookie->evtype == XI_RawButtonPress ||
- cookie->evtype == XI_RawButtonRelease)
- {
- handle_raw_event (manager_xi2, xevent);
- return FALSE;
- }
-
- if (!(xi_event->evtype == XI_HierarchyChanged ||
- xi_event->evtype == XI_DeviceChanged ||
- xi_event->evtype == XI_PropertyEvent))
- {
- stage = get_event_stage (manager_xi2, xi_event);
- if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage))
- return FALSE;
- else
- stage_x11 = META_STAGE_X11 (_clutter_stage_get_window (stage));
- }
-
- event->any.stage = stage;
-
- switch (xi_event->evtype)
- {
- case XI_HierarchyChanged:
- {
- XIHierarchyEvent *xev = (XIHierarchyEvent *) xi_event;
-
- translate_hierarchy_event (backend, manager_xi2, xev);
- }
- retval = FALSE;
- break;
-
- case XI_DeviceChanged:
- {
- XIDeviceChangedEvent *xev = (XIDeviceChangedEvent *) xi_event;
-
- device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->deviceid));
- source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->sourceid));
- if (device)
- {
- _clutter_input_device_reset_axes (device);
- translate_device_classes (clutter_x11_get_default_display (),
- device,
- xev->classes,
- xev->num_classes);
- }
-
- if (source_device)
- _clutter_input_device_reset_scroll_info (source_device);
- }
- retval = FALSE;
- break;
-
- case XI_KeyPress:
- case XI_KeyRelease:
- {
- XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
- MetaKeymapX11 *keymap_x11 = META_KEYMAP_X11 (clutter_backend_get_keymap (backend));
- MetaEventX11 *event_x11;
- char buffer[7] = { 0, };
- gunichar n;
-
- source_device = get_source_device_checked (manager_xi2, xev);
- if (!source_device)
- return FALSE;
-
- event->key.type = event->type = (xev->evtype == XI_KeyPress)
- ? CLUTTER_KEY_PRESS
- : CLUTTER_KEY_RELEASE;
-
- if (xev->evtype == XI_KeyPress && xev->flags & XIKeyRepeat)
- clutter_event_set_flags (event, CLUTTER_EVENT_FLAG_REPEATED);
-
- event->key.time = xev->time;
- event->key.stage = stage;
- meta_input_device_x11_translate_state (event, &xev->mods, &xev->buttons, &xev->group);
- event->key.hardware_keycode = xev->detail;
-
- /* keyval is the key ignoring all modifiers ('1' vs. '!') */
- event->key.keyval =
- meta_keymap_x11_translate_key_state (keymap_x11,
- event->key.hardware_keycode,
- &event->key.modifier_state,
- NULL);
-
- /* KeyEvents have platform specific data associated to them */
- event_x11 = meta_event_x11_new ();
- _clutter_event_set_platform_data (event, event_x11);
-
- event_x11->key_group =
- meta_keymap_x11_get_key_group (keymap_x11,
- event->key.modifier_state);
- event_x11->key_is_modifier =
- meta_keymap_x11_get_is_modifier (keymap_x11,
- event->key.hardware_keycode);
- event_x11->num_lock_set =
- clutter_keymap_get_num_lock_state (CLUTTER_KEYMAP (keymap_x11));
- event_x11->caps_lock_set =
- clutter_keymap_get_caps_lock_state (CLUTTER_KEYMAP (keymap_x11));
-
- clutter_event_set_source_device (event, source_device);
-
- device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->deviceid));
- clutter_event_set_device (event, device);
-
- /* XXX keep this in sync with the evdev device manager */
- n = print_keysym (event->key.keyval, buffer, sizeof (buffer));
- if (n == 0)
- {
- /* not printable */
- event->key.unicode_value = (gunichar) '\0';
- }
- else
- {
- event->key.unicode_value = g_utf8_get_char_validated (buffer, n);
- if (event->key.unicode_value == -1 ||
- event->key.unicode_value == -2)
- event->key.unicode_value = (gunichar) '\0';
- }
-
- g_debug ("%s: win:0x%x device:%d source:%d, key: %12s (%d)",
- event->any.type == CLUTTER_KEY_PRESS
- ? "key press "
- : "key release",
- (unsigned int) stage_x11->xwin,
- xev->deviceid,
- xev->sourceid,
- event->key.keyval ? buffer : "(none)",
- event->key.keyval);
-
- if (xi_event->evtype == XI_KeyPress)
- meta_stage_x11_set_user_time (stage_x11, event->key.time);
-
- retval = TRUE;
- }
- break;
-
- case XI_ButtonPress:
- case XI_ButtonRelease:
- {
- XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
-
- source_device = get_source_device_checked (manager_xi2, xev);
- if (!source_device)
- return FALSE;
-
- device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->deviceid));
-
- /* Set the stage for core events coming out of nowhere (see bug #684509) */
- if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER &&
- clutter_input_device_get_pointer_stage (device) == NULL &&
- stage != NULL)
- _clutter_input_device_set_stage (device, stage);
-
- if (clutter_input_device_get_device_type (source_device) == CLUTTER_PAD_DEVICE)
- {
- /* We got these events because of the passive button grab */
- XIAllowEvents (clutter_x11_get_default_display (),
- xev->sourceid,
- XIAsyncDevice,
- xev->time);
-
- event->any.stage = stage;
-
- if (xev->detail >= 4 && xev->detail <= 7)
- {
- retval = FALSE;
-
- if (xi_event->evtype == XI_ButtonPress &&
- translate_pad_event (event, xev, source_device))
- retval = TRUE;
-
- break;
- }
-
- event->any.type =
- (xi_event->evtype == XI_ButtonPress) ? CLUTTER_PAD_BUTTON_PRESS
- : CLUTTER_PAD_BUTTON_RELEASE;
- event->any.time = xev->time;
-
- /* The 4-7 button range is taken as non-existent on pad devices,
- * let the buttons above that take over this range.
- */
- if (xev->detail > 7)
- xev->detail -= 4;
-
- /* Pad buttons are 0-indexed */
- event->pad_button.button = xev->detail - 1;
-#ifdef HAVE_LIBWACOM
- meta_input_device_x11_update_pad_state (device,
- event->pad_button.button,
- (xi_event->evtype == XI_ButtonPress),
- &event->pad_button.group,
- &event->pad_button.mode);
-#endif
- clutter_event_set_device (event, device);
- clutter_event_set_source_device (event, source_device);
-
- g_debug ("%s: win:0x%x, device:%d '%s', time:%d "
- "(button:%d)",
- event->any.type == CLUTTER_BUTTON_PRESS
- ? "pad button press "
- : "pad button release",
- (unsigned int) stage_x11->xwin,
- device->id,
- device->device_name,
- event->any.time,
- event->pad_button.button);
- retval = TRUE;
- break;
- }
-
- switch (xev->detail)
- {
- case 4:
- case 5:
- case 6:
- case 7:
- /* we only generate Scroll events on ButtonPress */
- if (xi_event->evtype == XI_ButtonRelease)
- return FALSE;
-
- event->scroll.type = event->type = CLUTTER_SCROLL;
-
- if (xev->detail == 4)
- event->scroll.direction = CLUTTER_SCROLL_UP;
- else if (xev->detail == 5)
- event->scroll.direction = CLUTTER_SCROLL_DOWN;
- else if (xev->detail == 6)
- event->scroll.direction = CLUTTER_SCROLL_LEFT;
- else
- event->scroll.direction = CLUTTER_SCROLL_RIGHT;
-
- event->scroll.stage = stage;
-
- event->scroll.time = xev->time;
- translate_coords (stage_x11, xev->event_x, xev->event_y, &event->scroll.x, &event->scroll.y);
- meta_input_device_x11_translate_state (event,
- &xev->mods,
- &xev->buttons,
- &xev->group);
-
- clutter_event_set_source_device (event, source_device);
- clutter_event_set_device (event, device);
-
- event->scroll.axes = translate_axes (event->scroll.device,
- event->scroll.x,
- event->scroll.y,
- &xev->valuators);
- g_debug ("scroll: win:0x%x, device:%d '%s', time:%d "
- "(direction:%s, "
- "x:%.2f, y:%.2f, "
- "emulated:%s)",
- (unsigned int) stage_x11->xwin,
- device->id,
- device->device_name,
- event->any.time,
- event->scroll.direction == CLUTTER_SCROLL_UP ? "up" :
- event->scroll.direction == CLUTTER_SCROLL_DOWN ? "down" :
- event->scroll.direction == CLUTTER_SCROLL_LEFT ? "left" :
- event->scroll.direction == CLUTTER_SCROLL_RIGHT ? "right" :
- "invalid",
- event->scroll.x,
- event->scroll.y,
- (xev->flags & XIPointerEmulated) ? "yes" : "no");
- break;
-
- default:
- event->button.type = event->type =
- (xi_event->evtype == XI_ButtonPress) ? CLUTTER_BUTTON_PRESS
- : CLUTTER_BUTTON_RELEASE;
-
- event->button.stage = stage;
-
- event->button.time = xev->time;
- translate_coords (stage_x11, xev->event_x, xev->event_y, &event->button.x, &event->button.y);
- event->button.button = xev->detail;
- meta_input_device_x11_translate_state (event,
- &xev->mods,
- &xev->buttons,
- &xev->group);
-
- clutter_event_set_source_device (event, source_device);
- clutter_event_set_device (event, device);
- clutter_event_set_device_tool (event,
- meta_input_device_x11_get_current_tool (source_device));
-
- event->button.axes = translate_axes (event->button.device,
- event->button.x,
- event->button.y,
- &xev->valuators);
- g_debug ("%s: win:0x%x, device:%d '%s', time:%d "
- "(button:%d, "
- "x:%.2f, y:%.2f, "
- "axes:%s, "
- "emulated:%s)",
- event->any.type == CLUTTER_BUTTON_PRESS
- ? "button press "
- : "button release",
- (unsigned int) stage_x11->xwin,
- device->id,
- device->device_name,
- event->any.time,
- event->button.button,
- event->button.x,
- event->button.y,
- event->button.axes != NULL ? "yes" : "no",
- (xev->flags & XIPointerEmulated) ? "yes" : "no");
- break;
- }
-
- if (device->stage != NULL)
- _clutter_input_device_set_stage (source_device, device->stage);
-
- if (xev->flags & XIPointerEmulated)
- _clutter_event_set_pointer_emulated (event, TRUE);
-
- if (xi_event->evtype == XI_ButtonPress)
- meta_stage_x11_set_user_time (stage_x11, event->button.time);
-
- retval = TRUE;
- }
- break;
-
- case XI_Motion:
- {
- XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
- double delta_x, delta_y;
-
- source_device = get_source_device_checked (manager_xi2, xev);
- if (!source_device)
- return FALSE;
-
- device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->deviceid));
-
- if (clutter_input_device_get_device_type (source_device) == CLUTTER_PAD_DEVICE)
- {
- event->any.stage = stage;
-
- if (translate_pad_event (event, xev, source_device))
- retval = TRUE;
- break;
- }
-
- /* Set the stage for core events coming out of nowhere (see bug #684509) */
- if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER &&
- clutter_input_device_get_pointer_stage (device) == NULL &&
- stage != NULL)
- _clutter_input_device_set_stage (device, stage);
-
- if (scroll_valuators_changed (source_device,
- &xev->valuators,
- &delta_x, &delta_y))
- {
- event->scroll.type = event->type = CLUTTER_SCROLL;
- event->scroll.direction = CLUTTER_SCROLL_SMOOTH;
-
- event->scroll.stage = stage;
- event->scroll.time = xev->time;
- translate_coords (stage_x11, xev->event_x, xev->event_y, &event->scroll.x, &event->scroll.y);
- meta_input_device_x11_translate_state (event,
- &xev->mods,
- &xev->buttons,
- &xev->group);
-
- clutter_event_set_scroll_delta (event, delta_x, delta_y);
- clutter_event_set_source_device (event, source_device);
- clutter_event_set_device (event, device);
-
- g_debug ("smooth scroll: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, delta:%f, %f)",
- (unsigned int) stage_x11->xwin,
- event->scroll.device->id,
- event->scroll.device->device_name,
- event->scroll.x,
- event->scroll.y,
- delta_x, delta_y);
-
- retval = TRUE;
- break;
- }
-
- event->motion.type = event->type = CLUTTER_MOTION;
-
- event->motion.stage = stage;
-
- event->motion.time = xev->time;
- translate_coords (stage_x11, xev->event_x, xev->event_y, &event->motion.x, &event->motion.y);
- meta_input_device_x11_translate_state (event,
- &xev->mods,
- &xev->buttons,
- &xev->group);
-
- clutter_event_set_source_device (event, source_device);
- clutter_event_set_device (event, device);
- clutter_event_set_device_tool (event,
- meta_input_device_x11_get_current_tool (source_device));
-
- event->motion.axes = translate_axes (event->motion.device,
- event->motion.x,
- event->motion.y,
- &xev->valuators);
-
- if (device->stage != NULL)
- _clutter_input_device_set_stage (source_device, device->stage);
-
- if (xev->flags & XIPointerEmulated)
- _clutter_event_set_pointer_emulated (event, TRUE);
-
- g_debug ("motion: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, axes:%s)",
- (unsigned int) stage_x11->xwin,
- event->motion.device->id,
- event->motion.device->device_name,
- event->motion.x,
- event->motion.y,
- event->motion.axes != NULL ? "yes" : "no");
-
- retval = TRUE;
- }
- break;
-
- case XI_TouchBegin:
- {
- XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
- device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->deviceid));
- if (!_clutter_input_device_get_stage (device))
- _clutter_input_device_set_stage (device, stage);
- }
- /* Fall through */
- case XI_TouchEnd:
- {
- XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
-
- source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->sourceid));
-
- if (xi_event->evtype == XI_TouchBegin)
- event->touch.type = event->type = CLUTTER_TOUCH_BEGIN;
- else
- event->touch.type = event->type = CLUTTER_TOUCH_END;
-
- event->touch.stage = stage;
- event->touch.time = xev->time;
- translate_coords (stage_x11, xev->event_x, xev->event_y, &event->touch.x, &event->touch.y);
- meta_input_device_x11_translate_state (event,
- &xev->mods,
- &xev->buttons,
- &xev->group);
-
- clutter_event_set_source_device (event, source_device);
-
- device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->deviceid));
- clutter_event_set_device (event, device);
-
- event->touch.axes = translate_axes (event->touch.device,
- event->motion.x,
- event->motion.y,
- &xev->valuators);
-
- if (xi_event->evtype == XI_TouchBegin)
- {
- event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;
-
- meta_stage_x11_set_user_time (stage_x11, event->touch.time);
- }
-
- event->touch.sequence = GUINT_TO_POINTER (xev->detail);
-
- if (xev->flags & XITouchEmulatingPointer)
- _clutter_event_set_pointer_emulated (event, TRUE);
-
- g_debug ("touch %s: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f, axes:%s)",
- event->type == CLUTTER_TOUCH_BEGIN ? "begin" : "end",
- (unsigned int) stage_x11->xwin,
- event->touch.device->id,
- event->touch.device->device_name,
- GPOINTER_TO_UINT (event->touch.sequence),
- event->touch.x,
- event->touch.y,
- event->touch.axes != NULL ? "yes" : "no");
-
- retval = TRUE;
- }
- break;
-
- case XI_TouchUpdate:
- {
- XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
-
- source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->sourceid));
-
- event->touch.type = event->type = CLUTTER_TOUCH_UPDATE;
- event->touch.stage = stage;
- event->touch.time = xev->time;
- event->touch.sequence = GUINT_TO_POINTER (xev->detail);
- translate_coords (stage_x11, xev->event_x, xev->event_y, &event->touch.x, &event->touch.y);
-
- clutter_event_set_source_device (event, source_device);
-
- device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->deviceid));
- clutter_event_set_device (event, device);
-
- event->touch.axes = translate_axes (event->touch.device,
- event->motion.x,
- event->motion.y,
- &xev->valuators);
-
- meta_input_device_x11_translate_state (event,
- &xev->mods,
- &xev->buttons,
- &xev->group);
- event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;
-
- if (xev->flags & XITouchEmulatingPointer)
- _clutter_event_set_pointer_emulated (event, TRUE);
-
- g_debug ("touch update: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f, axes:%s)",
- (unsigned int) stage_x11->xwin,
- event->touch.device->id,
- event->touch.device->device_name,
- GPOINTER_TO_UINT (event->touch.sequence),
- event->touch.x,
- event->touch.y,
- event->touch.axes != NULL ? "yes" : "no");
-
- retval = TRUE;
- }
- break;
-
- case XI_Enter:
- case XI_Leave:
- {
- XIEnterEvent *xev = (XIEnterEvent *) xi_event;
-
- device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->deviceid));
-
- source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (xev->sourceid));
-
- if (xi_event->evtype == XI_Enter)
- {
- event->crossing.type = event->type = CLUTTER_ENTER;
-
- event->crossing.stage = stage;
- event->crossing.source = CLUTTER_ACTOR (stage);
- event->crossing.related = NULL;
-
- event->crossing.time = xev->time;
- translate_coords (stage_x11, xev->event_x, xev->event_y, &event->crossing.x, &event->crossing.y);
-
- _clutter_input_device_set_stage (device, stage);
- }
- else
- {
- if (device->stage == NULL)
- {
- g_debug ("Discarding Leave for ButtonRelease "
- "event off-stage");
- retval = FALSE;
- break;
- }
-
- event->crossing.type = event->type = CLUTTER_LEAVE;
-
- event->crossing.stage = stage;
- event->crossing.source = CLUTTER_ACTOR (stage);
- event->crossing.related = NULL;
-
- event->crossing.time = xev->time;
- translate_coords (stage_x11, xev->event_x, xev->event_y, &event->crossing.x, &event->crossing.y);
-
- _clutter_input_device_set_stage (device, NULL);
- }
-
- _clutter_input_device_reset_scroll_info (source_device);
-
- clutter_event_set_device (event, device);
- clutter_event_set_source_device (event, source_device);
-
- retval = TRUE;
- }
- break;
-
- case XI_FocusIn:
- case XI_FocusOut:
- retval = FALSE;
- break;
- case XI_PropertyEvent:
- handle_property_event (manager_xi2, xi_event);
- retval = FALSE;
- break;
- }
-
- return retval;
-}
-
static void
meta_device_manager_x11_add_device (ClutterDeviceManager *manager,
ClutterInputDevice *device)
@@ -1938,17 +129,28 @@ static const GSList *
meta_device_manager_x11_get_devices (ClutterDeviceManager *manager)
{
MetaDeviceManagerX11 *manager_xi2 = META_DEVICE_MANAGER_X11 (manager);
+ ClutterBackend *backend = clutter_get_default_backend ();
+ ClutterSeat *seat = clutter_backend_get_default_seat (backend);
GSList *all_devices = NULL;
- GList *l;
+ GList *l, *devices;
if (manager_xi2->all_devices != NULL)
return manager_xi2->all_devices;
- for (l = manager_xi2->master_devices; l != NULL; l = l->next)
- all_devices = g_slist_prepend (all_devices, l->data);
+ all_devices = g_slist_prepend (all_devices,
+ clutter_seat_get_pointer (seat));
+ all_devices = g_slist_prepend (all_devices,
+ clutter_seat_get_keyboard (seat));
- for (l = manager_xi2->slave_devices; l != NULL; l = l->next)
+ devices = clutter_seat_list_devices (seat, clutter_seat_get_pointer (seat));
+ for (l = devices; l; l = l->next)
all_devices = g_slist_prepend (all_devices, l->data);
+ g_list_free (devices);
+
+ devices = clutter_seat_list_devices (seat, clutter_seat_get_keyboard (seat));
+ for (l = devices; l; l = l->next)
+ all_devices = g_slist_prepend (all_devices, l->data);
+ g_list_free (devices);
manager_xi2->all_devices = g_slist_reverse (all_devices);
@@ -1959,40 +161,26 @@ static ClutterInputDevice *
meta_device_manager_x11_get_device (ClutterDeviceManager *manager,
gint id)
{
- MetaDeviceManagerX11 *manager_xi2 = META_DEVICE_MANAGER_X11 (manager);
+ ClutterBackend *backend = clutter_get_default_backend ();
+ ClutterSeat *seat = clutter_backend_get_default_seat (backend);
- return g_hash_table_lookup (manager_xi2->devices_by_id,
- GINT_TO_POINTER (id));
+ return meta_seat_x11_lookup_device_id (META_SEAT_X11 (seat), id);
}
static ClutterInputDevice *
meta_device_manager_x11_get_core_device (ClutterDeviceManager *manager,
ClutterInputDeviceType device_type)
{
- MetaDeviceManagerX11 *manager_xi2 = META_DEVICE_MANAGER_X11 (manager);
- ClutterInputDevice *pointer = NULL;
- GList *l;
-
- for (l = manager_xi2->master_devices; l != NULL ; l = l->next)
- {
- ClutterInputDevice *device = l->data;
- if (clutter_input_device_get_device_type (device) == CLUTTER_POINTER_DEVICE)
- {
- pointer = device;
- break;
- }
- }
-
- if (pointer == NULL)
- return NULL;
+ ClutterBackend *backend = clutter_get_default_backend ();
+ ClutterSeat *seat = clutter_backend_get_default_seat (backend);
switch (device_type)
{
case CLUTTER_POINTER_DEVICE:
- return pointer;
+ return clutter_seat_get_pointer (seat);;
case CLUTTER_KEYBOARD_DEVICE:
- return clutter_input_device_get_associated_device (pointer);
+ return clutter_seat_get_keyboard (seat);;
default:
break;
@@ -2002,112 +190,44 @@ meta_device_manager_x11_get_core_device (ClutterDeviceManager *manager,
}
static void
-relate_masters (gpointer key,
- gpointer value,
- gpointer data)
+on_device_added (ClutterSeat *seat,
+ ClutterInputDevice *parent,
+ ClutterInputDevice *device,
+ ClutterDeviceManager *manager)
{
- MetaDeviceManagerX11 *manager_xi2 = data;
- ClutterInputDevice *device, *relative;
-
- device = g_hash_table_lookup (manager_xi2->devices_by_id, key);
- relative = g_hash_table_lookup (manager_xi2->devices_by_id, value);
-
- _clutter_input_device_set_associated_device (device, relative);
- _clutter_input_device_set_associated_device (relative, device);
+ g_signal_emit_by_name (manager, "device-added", device);
}
static void
-relate_slaves (gpointer key,
- gpointer value,
- gpointer data)
+on_device_removed (ClutterSeat *seat,
+ ClutterInputDevice *parent,
+ ClutterInputDevice *device,
+ ClutterDeviceManager *manager)
{
- MetaDeviceManagerX11 *manager_xi2 = data;
- ClutterInputDevice *master, *slave;
+ g_signal_emit_by_name (manager, "device-removed", device);
+}
- slave = g_hash_table_lookup (manager_xi2->devices_by_id, key);
- master = g_hash_table_lookup (manager_xi2->devices_by_id, value);
-
- _clutter_input_device_set_associated_device (slave, master);
- _clutter_input_device_add_slave (master, slave);
+static void
+on_tool_changed (ClutterSeat *seat,
+ ClutterInputDevice *device,
+ ClutterInputDeviceTool *tool,
+ ClutterDeviceManager *manager)
+{
+ g_signal_emit_by_name (manager, "tool-changed", device, tool);
}
static void
meta_device_manager_x11_constructed (GObject *object)
{
- MetaDeviceManagerX11 *manager_xi2 = META_DEVICE_MANAGER_X11 (object);
ClutterDeviceManager *manager = CLUTTER_DEVICE_MANAGER (object);
- ClutterBackend *backend = clutter_get_default_backend ();
- GHashTable *masters, *slaves;
- XIDeviceInfo *info;
- XIEventMask event_mask;
- unsigned char mask[(XI_LASTEVENT + 7) / 8] = { 0, };
- int n_devices, i;
+ MetaDeviceManagerX11 *manager_xi2 = META_DEVICE_MANAGER_X11 (object);
- masters = g_hash_table_new (NULL, NULL);
- slaves = g_hash_table_new (NULL, NULL);
-
- info = XIQueryDevice (clutter_x11_get_default_display (),
- XIAllDevices, &n_devices);
-
- for (i = 0; i < n_devices; i++)
- {
- XIDeviceInfo *xi_device = &info[i];
-
- if (!xi_device->enabled)
- continue;
-
- add_device (manager_xi2, backend, xi_device, TRUE);
-
- if (xi_device->use == XIMasterPointer ||
- xi_device->use == XIMasterKeyboard)
- {
- g_hash_table_insert (masters,
- GINT_TO_POINTER (xi_device->deviceid),
- GINT_TO_POINTER (xi_device->attachment));
- }
- else if (xi_device->use == XISlavePointer ||
- xi_device->use == XISlaveKeyboard)
- {
- g_hash_table_insert (slaves,
- GINT_TO_POINTER (xi_device->deviceid),
- GINT_TO_POINTER (xi_device->attachment));
- }
- }
-
- XIFreeDeviceInfo (info);
-
- g_hash_table_foreach (masters, relate_masters, manager_xi2);
- g_hash_table_destroy (masters);
-
- g_hash_table_foreach (slaves, relate_slaves, manager_xi2);
- g_hash_table_destroy (slaves);
-
- XISetMask (mask, XI_HierarchyChanged);
- XISetMask (mask, XI_DeviceChanged);
- XISetMask (mask, XI_PropertyEvent);
-
- event_mask.deviceid = XIAllDevices;
- event_mask.mask_len = sizeof (mask);
- event_mask.mask = mask;
-
- meta_device_manager_x11_select_events (manager,
- clutter_x11_get_root_window (),
- &event_mask);
-
- memset(mask, 0, sizeof (mask));
- XISetMask (mask, XI_RawMotion);
- XISetMask (mask, XI_RawButtonPress);
- XISetMask (mask, XI_RawButtonRelease);
-
- event_mask.deviceid = XIAllMasterDevices;
- event_mask.mask_len = sizeof (mask);
- event_mask.mask = mask;
-
- meta_device_manager_x11_select_events (manager,
- clutter_x11_get_root_window (),
- &event_mask);
-
- XSync (clutter_x11_get_default_display (), False);
+ g_signal_connect (manager_xi2->seat, "device-added",
+ G_CALLBACK (on_device_added), manager_xi2);
+ g_signal_connect (manager_xi2->seat, "device-added",
+ G_CALLBACK (on_device_removed), manager_xi2);
+ g_signal_connect (manager_xi2->seat, "tool-changed",
+ G_CALLBACK (on_tool_changed), manager_xi2);
meta_device_manager_x11_a11y_init (manager);
@@ -2125,8 +245,8 @@ meta_device_manager_x11_set_property (GObject *object,
switch (prop_id)
{
- case PROP_OPCODE:
- manager_xi2->opcode = g_value_get_int (value);
+ case PROP_SEAT:
+ manager_xi2->seat = g_value_get_object (value);
break;
default:
@@ -2158,13 +278,12 @@ meta_device_manager_x11_class_init (MetaDeviceManagerX11Class *klass)
ClutterDeviceManagerClass *manager_class;
GObjectClass *gobject_class;
- obj_props[PROP_OPCODE] =
- g_param_spec_int ("opcode",
- "Opcode",
- "The XI2 opcode",
- -1, G_MAXINT,
- -1,
- G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
+ obj_props[PROP_SEAT] =
+ g_param_spec_object ("seat",
+ "Seat",
+ "Seat",
+ CLUTTER_TYPE_SEAT,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->constructed = meta_device_manager_x11_constructed;
@@ -2189,13 +308,4 @@ meta_device_manager_x11_class_init (MetaDeviceManagerX11Class *klass)
static void
meta_device_manager_x11_init (MetaDeviceManagerX11 *self)
{
- self->devices_by_id = g_hash_table_new_full (NULL, NULL,
- NULL,
- (GDestroyNotify) g_object_unref);
- self->tools_by_serial = g_hash_table_new_full (NULL, NULL, NULL,
- (GDestroyNotify) g_object_unref);
-
-#ifdef HAVE_LIBWACOM
- self->wacom_db = libwacom_database_new ();
-#endif
}
diff --git a/src/backends/x11/meta-device-manager-x11.h b/src/backends/x11/meta-device-manager-x11.h
index 4bf762cdd..90d811526 100644
--- a/src/backends/x11/meta-device-manager-x11.h
+++ b/src/backends/x11/meta-device-manager-x11.h
@@ -36,19 +36,8 @@ struct _MetaDeviceManagerX11
{
ClutterDeviceManager parent_instance;
- GHashTable *devices_by_id;
- GHashTable *tools_by_serial;
-
GSList *all_devices;
-
- GList *master_devices;
- GList *slave_devices;
-
- int opcode;
-
-#ifdef HAVE_LIBWACOM
- WacomDeviceDatabase *wacom_db;
-#endif
+ ClutterSeat *seat;
};
gboolean meta_device_manager_x11_translate_event (MetaDeviceManagerX11 *manager_xi2,
diff --git a/src/backends/x11/meta-seat-x11.c b/src/backends/x11/meta-seat-x11.c
new file mode 100644
index 000000000..72fcacd93
--- /dev/null
+++ b/src/backends/x11/meta-seat-x11.c
@@ -0,0 +1,2137 @@
+/*
+ * Copyright (C) 2019 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ * Author: Carlos Garnacho
+ */
+#include "config.h"
+
+#include
+
+#include "backends/x11/meta-event-x11.h"
+#include "backends/x11/meta-input-device-tool-x11.h"
+#include "backends/x11/meta-input-device-x11.h"
+#include "backends/x11/meta-keymap-x11.h"
+#include "backends/x11/meta-stage-x11.h"
+#include "clutter/clutter-mutter.h"
+#include "clutter/x11/clutter-x11.h"
+#include "meta-seat-x11.h"
+
+enum
+{
+ PROP_0,
+ PROP_OPCODE,
+ PROP_POINTER_ID,
+ PROP_KEYBOARD_ID,
+ N_PROPS
+};
+
+struct _MetaSeatX11
+{
+ ClutterSeat parent_instance;
+ ClutterInputDevice *core_pointer;
+ ClutterInputDevice *core_keyboard;
+ GList *devices;
+ GHashTable *devices_by_id;
+ GHashTable *tools_by_serial;
+
+ int pointer_id;
+ int keyboard_id;
+ int opcode;
+
+#ifdef HAVE_LIBWACOM
+ WacomDeviceDatabase *wacom_db;
+#endif
+};
+
+static GParamSpec *props[N_PROPS] = { 0 };
+
+G_DEFINE_TYPE (MetaSeatX11, meta_seat_x11, CLUTTER_TYPE_SEAT)
+
+static const char *clutter_input_axis_atom_names[] = {
+ "Abs X", /* CLUTTER_INPUT_AXIS_X */
+ "Abs Y", /* CLUTTER_INPUT_AXIS_Y */
+ "Abs Pressure", /* CLUTTER_INPUT_AXIS_PRESSURE */
+ "Abs Tilt X", /* CLUTTER_INPUT_AXIS_XTILT */
+ "Abs Tilt Y", /* CLUTTER_INPUT_AXIS_YTILT */
+ "Abs Wheel", /* CLUTTER_INPUT_AXIS_WHEEL */
+ "Abs Distance", /* CLUTTER_INPUT_AXIS_DISTANCE */
+};
+
+static const char *wacom_type_atoms[] = {
+ "STYLUS",
+ "CURSOR",
+ "ERASER",
+ "PAD",
+ "TOUCH"
+};
+#define N_WACOM_TYPE_ATOMS G_N_ELEMENTS (wacom_type_atoms)
+
+enum
+{
+ WACOM_TYPE_STYLUS,
+ WACOM_TYPE_CURSOR,
+ WACOM_TYPE_ERASER,
+ WACOM_TYPE_PAD,
+ WACOM_TYPE_TOUCH,
+};
+
+enum
+{
+ PAD_AXIS_FIRST = 3, /* First axes are always x/y/pressure, ignored in pads */
+ PAD_AXIS_STRIP1 = PAD_AXIS_FIRST,
+ PAD_AXIS_STRIP2,
+ PAD_AXIS_RING1,
+ PAD_AXIS_RING2,
+};
+
+#define N_AXIS_ATOMS G_N_ELEMENTS (clutter_input_axis_atom_names)
+
+static Atom clutter_input_axis_atoms[N_AXIS_ATOMS] = { 0, };
+
+static void
+translate_valuator_class (Display *xdisplay,
+ ClutterInputDevice *device,
+ XIValuatorClassInfo *class)
+{
+ static gboolean atoms_initialized = FALSE;
+ ClutterInputAxis i, axis = CLUTTER_INPUT_AXIS_IGNORE;
+
+ if (G_UNLIKELY (!atoms_initialized))
+ {
+ XInternAtoms (xdisplay,
+ (char **) clutter_input_axis_atom_names, N_AXIS_ATOMS,
+ False,
+ clutter_input_axis_atoms);
+
+ atoms_initialized = TRUE;
+ }
+
+ for (i = 0;
+ i < N_AXIS_ATOMS;
+ i += 1)
+ {
+ if (clutter_input_axis_atoms[i] == class->label)
+ {
+ axis = i + 1;
+ break;
+ }
+ }
+
+ _clutter_input_device_add_axis (device, axis,
+ class->min,
+ class->max,
+ class->resolution);
+
+ g_debug ("Added axis '%s' (min:%.2f, max:%.2fd, res:%d) of device %d",
+ clutter_input_axis_atom_names[axis],
+ class->min,
+ class->max,
+ class->resolution,
+ device->id);
+}
+
+static void
+translate_device_classes (Display *xdisplay,
+ ClutterInputDevice *device,
+ XIAnyClassInfo **classes,
+ int n_classes)
+{
+ int i;
+
+ for (i = 0; i < n_classes; i++)
+ {
+ XIAnyClassInfo *class_info = classes[i];
+
+ switch (class_info->type)
+ {
+ case XIKeyClass:
+ {
+ XIKeyClassInfo *key_info = (XIKeyClassInfo *) class_info;
+ int j;
+
+ _clutter_input_device_set_n_keys (device,
+ key_info->num_keycodes);
+
+ for (j = 0; j < key_info->num_keycodes; j++)
+ {
+ clutter_input_device_set_key (device, j,
+ key_info->keycodes[i],
+ 0);
+ }
+ }
+ break;
+
+ case XIValuatorClass:
+ translate_valuator_class (xdisplay, device,
+ (XIValuatorClassInfo *) class_info);
+ break;
+
+ case XIScrollClass:
+ {
+ XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info;
+ ClutterScrollDirection direction;
+
+ if (scroll_info->scroll_type == XIScrollTypeVertical)
+ direction = CLUTTER_SCROLL_DOWN;
+ else
+ direction = CLUTTER_SCROLL_RIGHT;
+
+ g_debug ("Scroll valuator %d: %s, increment: %f",
+ scroll_info->number,
+ scroll_info->scroll_type == XIScrollTypeVertical
+ ? "vertical"
+ : "horizontal",
+ scroll_info->increment);
+
+ _clutter_input_device_add_scroll_info (device,
+ scroll_info->number,
+ direction,
+ scroll_info->increment);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static gboolean
+is_touch_device (XIAnyClassInfo **classes,
+ int n_classes,
+ ClutterInputDeviceType *device_type,
+ uint32_t *n_touch_points)
+{
+ int i;
+
+ for (i = 0; i < n_classes; i++)
+ {
+ XITouchClassInfo *class = (XITouchClassInfo *) classes[i];
+
+ if (class->type != XITouchClass)
+ continue;
+
+ if (class->num_touches > 0)
+ {
+ if (class->mode == XIDirectTouch)
+ *device_type = CLUTTER_TOUCHSCREEN_DEVICE;
+ else if (class->mode == XIDependentTouch)
+ *device_type = CLUTTER_TOUCHPAD_DEVICE;
+ else
+ continue;
+
+ *n_touch_points = class->num_touches;
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+is_touchpad_device (XIDeviceInfo *info)
+{
+ gulong nitems, bytes_after;
+ uint32_t *data = NULL;
+ int rc, format;
+ Atom type;
+ Atom prop;
+
+ prop = XInternAtom (clutter_x11_get_default_display (), "libinput Tapping Enabled", True);
+ if (prop == None)
+ return FALSE;
+
+ clutter_x11_trap_x_errors ();
+ rc = XIGetProperty (clutter_x11_get_default_display (),
+ info->deviceid,
+ prop,
+ 0, 1, False, XA_INTEGER, &type, &format, &nitems, &bytes_after,
+ (guchar **) &data);
+ clutter_x11_untrap_x_errors ();
+
+ /* We don't care about the data */
+ XFree (data);
+
+ if (rc != Success || type != XA_INTEGER || format != 8 || nitems != 1)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+get_device_ids (XIDeviceInfo *info,
+ char **vendor_id,
+ char **product_id)
+{
+ gulong nitems, bytes_after;
+ uint32_t *data = NULL;
+ int rc, format;
+ Atom type;
+
+ clutter_x11_trap_x_errors ();
+ rc = XIGetProperty (clutter_x11_get_default_display (),
+ info->deviceid,
+ XInternAtom (clutter_x11_get_default_display (), "Device Product ID", False),
+ 0, 2, False, XA_INTEGER, &type, &format, &nitems, &bytes_after,
+ (guchar **) &data);
+ clutter_x11_untrap_x_errors ();
+
+ if (rc != Success || type != XA_INTEGER || format != 32 || nitems != 2)
+ {
+ XFree (data);
+ return FALSE;
+ }
+
+ if (vendor_id)
+ *vendor_id = g_strdup_printf ("%.4x", data[0]);
+ if (product_id)
+ *product_id = g_strdup_printf ("%.4x", data[1]);
+
+ XFree (data);
+
+ return TRUE;
+}
+
+static char *
+get_device_node_path (XIDeviceInfo *info)
+{
+ gulong nitems, bytes_after;
+ guchar *data;
+ int rc, format;
+ Atom prop, type;
+ char *node_path;
+
+ prop = XInternAtom (clutter_x11_get_default_display (), "Device Node", False);
+ if (prop == None)
+ return NULL;
+
+ clutter_x11_trap_x_errors ();
+
+ rc = XIGetProperty (clutter_x11_get_default_display (),
+ info->deviceid, prop, 0, 1024, False,
+ XA_STRING, &type, &format, &nitems, &bytes_after,
+ (guchar **) &data);
+
+ if (clutter_x11_untrap_x_errors ())
+ return NULL;
+
+ if (rc != Success || type != XA_STRING || format != 8)
+ {
+ XFree (data);
+ return FALSE;
+ }
+
+ node_path = g_strdup ((char *) data);
+ XFree (data);
+
+ return node_path;
+}
+
+static void
+get_pad_features (XIDeviceInfo *info,
+ uint32_t *n_rings,
+ uint32_t *n_strips)
+{
+ int i, rings = 0, strips = 0;
+
+ for (i = PAD_AXIS_FIRST; i < info->num_classes; i++)
+ {
+ XIValuatorClassInfo *valuator = (XIValuatorClassInfo*) info->classes[i];
+ int axis = valuator->number;
+
+ if (valuator->type != XIValuatorClass)
+ continue;
+ if (valuator->max <= 1)
+ continue;
+
+ /* Ring/strip axes are fixed in pad devices as handled by the
+ * wacom driver. Match those to detect pad features.
+ */
+ if (axis == PAD_AXIS_STRIP1 || axis == PAD_AXIS_STRIP2)
+ strips++;
+ else if (axis == PAD_AXIS_RING1 || axis == PAD_AXIS_RING2)
+ rings++;
+ }
+
+ *n_rings = rings;
+ *n_strips = strips;
+}
+
+/* The Wacom driver exports the tool type as property. Use that over
+ guessing based on the device name */
+static gboolean
+guess_source_from_wacom_type (XIDeviceInfo *info,
+ ClutterInputDeviceType *source_out)
+{
+ gulong nitems, bytes_after;
+ uint32_t *data = NULL;
+ int rc, format;
+ Atom type;
+ Atom prop;
+ Atom device_type;
+ Atom types[N_WACOM_TYPE_ATOMS];
+
+ prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Tool Type", True);
+ if (prop == None)
+ return FALSE;
+
+ clutter_x11_trap_x_errors ();
+ rc = XIGetProperty (clutter_x11_get_default_display (),
+ info->deviceid,
+ prop,
+ 0, 1, False, XA_ATOM, &type, &format, &nitems, &bytes_after,
+ (guchar **) &data);
+ clutter_x11_untrap_x_errors ();
+
+ if (rc != Success || type != XA_ATOM || format != 32 || nitems != 1)
+ {
+ XFree (data);
+ return FALSE;
+ }
+
+ device_type = *data;
+ XFree (data);
+
+ if (device_type == 0)
+ return FALSE;
+
+ rc = XInternAtoms (clutter_x11_get_default_display (),
+ (char **)wacom_type_atoms,
+ N_WACOM_TYPE_ATOMS,
+ False,
+ types);
+ if (rc == 0)
+ return FALSE;
+
+ if (device_type == types[WACOM_TYPE_STYLUS])
+ {
+ *source_out = CLUTTER_PEN_DEVICE;
+ }
+ else if (device_type == types[WACOM_TYPE_CURSOR])
+ {
+ *source_out = CLUTTER_CURSOR_DEVICE;
+ }
+ else if (device_type == types[WACOM_TYPE_ERASER])
+ {
+ *source_out = CLUTTER_ERASER_DEVICE;
+ }
+ else if (device_type == types[WACOM_TYPE_PAD])
+ {
+ *source_out = CLUTTER_PAD_DEVICE;
+ }
+ else if (device_type == types[WACOM_TYPE_TOUCH])
+ {
+ uint32_t num_touches = 0;
+
+ if (!is_touch_device (info->classes, info->num_classes,
+ source_out, &num_touches))
+ *source_out = CLUTTER_TOUCHSCREEN_DEVICE;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static ClutterInputDevice *
+create_device (MetaSeatX11 *seat_x11,
+ ClutterBackend *backend,
+ XIDeviceInfo *info)
+{
+ ClutterInputDeviceType source, touch_source;
+ ClutterInputDevice *retval;
+ ClutterInputMode mode;
+ gboolean is_enabled;
+ uint32_t num_touches = 0, num_rings = 0, num_strips = 0;
+ char *vendor_id = NULL, *product_id = NULL, *node_path = NULL;
+
+ if (info->use == XIMasterKeyboard || info->use == XISlaveKeyboard)
+ {
+ source = CLUTTER_KEYBOARD_DEVICE;
+ }
+ else if (is_touchpad_device (info))
+ {
+ source = CLUTTER_TOUCHPAD_DEVICE;
+ }
+ else if (info->use == XISlavePointer &&
+ is_touch_device (info->classes, info->num_classes,
+ &touch_source,
+ &num_touches))
+ {
+ source = touch_source;
+ }
+ else if (!guess_source_from_wacom_type (info, &source))
+ {
+ char *name;
+
+ name = g_ascii_strdown (info->name, -1);
+
+ if (strstr (name, "eraser") != NULL)
+ source = CLUTTER_ERASER_DEVICE;
+ else if (strstr (name, "cursor") != NULL)
+ source = CLUTTER_CURSOR_DEVICE;
+ else if (strstr (name, " pad") != NULL)
+ source = CLUTTER_PAD_DEVICE;
+ else if (strstr (name, "wacom") != NULL || strstr (name, "pen") != NULL)
+ source = CLUTTER_PEN_DEVICE;
+ else if (strstr (name, "touchpad") != NULL)
+ source = CLUTTER_TOUCHPAD_DEVICE;
+ else
+ source = CLUTTER_POINTER_DEVICE;
+
+ g_free (name);
+ }
+
+ switch (info->use)
+ {
+ case XIMasterKeyboard:
+ case XIMasterPointer:
+ mode = CLUTTER_INPUT_MODE_MASTER;
+ is_enabled = TRUE;
+ break;
+
+ case XISlaveKeyboard:
+ case XISlavePointer:
+ mode = CLUTTER_INPUT_MODE_SLAVE;
+ is_enabled = FALSE;
+ break;
+
+ case XIFloatingSlave:
+ default:
+ mode = CLUTTER_INPUT_MODE_FLOATING;
+ is_enabled = FALSE;
+ break;
+ }
+
+ if (info->use != XIMasterKeyboard &&
+ info->use != XIMasterPointer)
+ {
+ get_device_ids (info, &vendor_id, &product_id);
+ node_path = get_device_node_path (info);
+ }
+
+ if (source == CLUTTER_PAD_DEVICE)
+ {
+ is_enabled = TRUE;
+ get_pad_features (info, &num_rings, &num_strips);
+ }
+
+ retval = g_object_new (META_TYPE_INPUT_DEVICE_X11,
+ "name", info->name,
+ "id", info->deviceid,
+ "has-cursor", (info->use == XIMasterPointer),
+ "device-type", source,
+ "device-mode", mode,
+ "backend", backend,
+ "enabled", is_enabled,
+ "vendor-id", vendor_id,
+ "product-id", product_id,
+ "device-node", node_path,
+ "n-rings", num_rings,
+ "n-strips", num_strips,
+ "n-mode-groups", MAX (num_rings, num_strips),
+ "seat", seat_x11,
+ NULL);
+
+ translate_device_classes (clutter_x11_get_default_display (), retval,
+ info->classes,
+ info->num_classes);
+
+#ifdef HAVE_LIBWACOM
+ if (source == CLUTTER_PAD_DEVICE)
+ meta_input_device_x11_ensure_wacom_info (retval, seat_x11->wacom_db);
+#endif
+
+ g_free (vendor_id);
+ g_free (product_id);
+ g_free (node_path);
+
+ g_debug ("Created device '%s' (id: %d, has-cursor: %s)",
+ info->name,
+ info->deviceid,
+ info->use == XIMasterPointer ? "yes" : "no");
+
+ return retval;
+}
+
+static void
+pad_passive_button_grab (ClutterInputDevice *device)
+{
+ XIGrabModifiers xi_grab_mods = { XIAnyModifier, };
+ XIEventMask xi_event_mask;
+ int device_id, rc;
+
+ device_id = clutter_input_device_get_device_id (device);
+
+ xi_event_mask.deviceid = device_id;
+ xi_event_mask.mask_len = XIMaskLen (XI_LASTEVENT);
+ xi_event_mask.mask = g_new0 (unsigned char, xi_event_mask.mask_len);
+
+ XISetMask (xi_event_mask.mask, XI_Motion);
+ XISetMask (xi_event_mask.mask, XI_ButtonPress);
+ XISetMask (xi_event_mask.mask, XI_ButtonRelease);
+
+ clutter_x11_trap_x_errors ();
+ rc = XIGrabButton (clutter_x11_get_default_display (),
+ device_id, XIAnyButton,
+ clutter_x11_get_root_window (), None,
+ XIGrabModeSync, XIGrabModeSync,
+ True, &xi_event_mask, 1, &xi_grab_mods);
+ if (rc != 0)
+ {
+ g_warning ("Could not passively grab pad device: %s",
+ clutter_input_device_get_device_name (device));
+ }
+ else
+ {
+ XIAllowEvents (clutter_x11_get_default_display (),
+ device_id, XIAsyncDevice,
+ CLUTTER_CURRENT_TIME);
+ }
+
+ clutter_x11_untrap_x_errors ();
+
+ g_free (xi_event_mask.mask);
+}
+
+static ClutterInputDevice *
+add_device (MetaSeatX11 *seat_x11,
+ ClutterBackend *backend,
+ XIDeviceInfo *info,
+ gboolean in_construction)
+{
+ ClutterInputDevice *device;
+
+ device = create_device (seat_x11, backend, info);
+
+ g_hash_table_replace (seat_x11->devices_by_id,
+ GINT_TO_POINTER (info->deviceid),
+ device);
+
+ if (info->use == XIMasterPointer &&
+ info->deviceid == seat_x11->pointer_id)
+ {
+ seat_x11->core_pointer = device;
+ }
+ else if (info->use == XIMasterKeyboard &&
+ info->deviceid == seat_x11->keyboard_id)
+ {
+ seat_x11->core_keyboard = device;
+ }
+ else if ((info->use == XISlavePointer &&
+ info->attachment == seat_x11->pointer_id) ||
+ (info->use == XISlaveKeyboard &&
+ info->attachment == seat_x11->keyboard_id))
+ {
+ seat_x11->devices = g_list_prepend (seat_x11->devices, device);
+ }
+ else
+ {
+ g_warning ("Unhandled device: %s",
+ clutter_input_device_get_device_name (device));
+ }
+
+ if (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE)
+ pad_passive_button_grab (device);
+
+ /* relationships between devices and signal emissions are not
+ * necessary while we're constructing the device manager instance
+ */
+ if (!in_construction)
+ {
+ if (info->use == XISlavePointer || info->use == XISlaveKeyboard)
+ {
+ ClutterInputDevice *master;
+
+ master = g_hash_table_lookup (seat_x11->devices_by_id,
+ GINT_TO_POINTER (info->attachment));
+ _clutter_input_device_set_associated_device (device, master);
+ _clutter_input_device_add_slave (master, device);
+
+ g_signal_emit_by_name (seat_x11, "device-added", device);
+ }
+ }
+
+ return device;
+}
+
+static void
+remove_device (MetaSeatX11 *seat_x11,
+ int device_id)
+{
+ ClutterInputDevice *device;
+
+ device = g_hash_table_lookup (seat_x11->devices_by_id,
+ GINT_TO_POINTER (device_id));
+
+ if (device != NULL)
+ {
+ if (seat_x11->core_pointer == device)
+ {
+ seat_x11->core_pointer = NULL;
+ }
+ else if (seat_x11->core_keyboard == device)
+ {
+ seat_x11->core_keyboard = NULL;
+ }
+ else
+ {
+ seat_x11->devices = g_list_remove (seat_x11->devices, device);
+ g_signal_emit_by_name (seat_x11, "device-removed", device);
+ g_hash_table_remove (seat_x11->devices_by_id,
+ GINT_TO_POINTER (device_id));
+ }
+
+ g_object_run_dispose (G_OBJECT (device));
+ g_object_unref (device);
+ }
+}
+
+static void
+relate_masters (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ MetaSeatX11 *seat_x11 = data;
+ ClutterInputDevice *device, *relative;
+
+ device = g_hash_table_lookup (seat_x11->devices_by_id, key);
+ relative = g_hash_table_lookup (seat_x11->devices_by_id, value);
+
+ _clutter_input_device_set_associated_device (device, relative);
+ _clutter_input_device_set_associated_device (relative, device);
+}
+
+static void
+relate_slaves (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ MetaSeatX11 *seat_x11 = data;
+ ClutterInputDevice *master, *slave;
+
+ slave = g_hash_table_lookup (seat_x11->devices_by_id, key);
+ master = g_hash_table_lookup (seat_x11->devices_by_id, value);
+
+ _clutter_input_device_set_associated_device (slave, master);
+ _clutter_input_device_add_slave (master, slave);
+}
+
+static uint
+device_get_tool_serial (ClutterInputDevice *device)
+{
+ gulong nitems, bytes_after;
+ uint32_t *data = NULL;
+ int serial_id = 0;
+ int rc, format;
+ Atom type;
+ Atom prop;
+
+ prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Serial IDs", True);
+ if (prop == None)
+ return 0;
+
+ clutter_x11_trap_x_errors ();
+ rc = XIGetProperty (clutter_x11_get_default_display (),
+ clutter_input_device_get_device_id (device),
+ prop, 0, 4, FALSE, XA_INTEGER, &type, &format, &nitems, &bytes_after,
+ (guchar **) &data);
+ clutter_x11_untrap_x_errors ();
+
+ if (rc == Success && type == XA_INTEGER && format == 32 && nitems >= 4)
+ serial_id = data[3];
+
+ XFree (data);
+
+ return serial_id;
+}
+
+static void
+translate_hierarchy_event (ClutterBackend *backend,
+ MetaSeatX11 *seat_x11,
+ XIHierarchyEvent *ev)
+{
+ int i;
+
+ for (i = 0; i < ev->num_info; i++)
+ {
+ if (ev->info[i].flags & XIDeviceEnabled &&
+ !g_hash_table_lookup (seat_x11->devices_by_id,
+ GINT_TO_POINTER (ev->info[i].deviceid)))
+ {
+ XIDeviceInfo *info;
+ int n_devices;
+
+ g_debug ("Hierarchy event: device enabled");
+
+ clutter_x11_trap_x_errors ();
+ info = XIQueryDevice (clutter_x11_get_default_display (),
+ ev->info[i].deviceid,
+ &n_devices);
+ clutter_x11_untrap_x_errors ();
+ if (info != NULL)
+ {
+ add_device (seat_x11, backend, &info[0], FALSE);
+ XIFreeDeviceInfo (info);
+ }
+ }
+ else if (ev->info[i].flags & XIDeviceDisabled)
+ {
+ g_debug ("Hierarchy event: device disabled");
+
+ remove_device (seat_x11, ev->info[i].deviceid);
+ }
+ else if ((ev->info[i].flags & XISlaveAttached) ||
+ (ev->info[i].flags & XISlaveDetached))
+ {
+ ClutterInputDevice *master, *slave;
+ XIDeviceInfo *info;
+ int n_devices;
+
+ g_debug ("Hierarchy event: slave %s",
+ (ev->info[i].flags & XISlaveAttached)
+ ? "attached"
+ : "detached");
+
+ slave = g_hash_table_lookup (seat_x11->devices_by_id,
+ GINT_TO_POINTER (ev->info[i].deviceid));
+ master = clutter_input_device_get_associated_device (slave);
+
+ /* detach the slave in both cases */
+ if (master != NULL)
+ {
+ _clutter_input_device_remove_slave (master, slave);
+ _clutter_input_device_set_associated_device (slave, NULL);
+ }
+
+ /* and attach the slave to the new master if needed */
+ if (ev->info[i].flags & XISlaveAttached)
+ {
+ clutter_x11_trap_x_errors ();
+ info = XIQueryDevice (clutter_x11_get_default_display (),
+ ev->info[i].deviceid,
+ &n_devices);
+ clutter_x11_untrap_x_errors ();
+ if (info != NULL)
+ {
+ master = g_hash_table_lookup (seat_x11->devices_by_id,
+ GINT_TO_POINTER (info->attachment));
+ if (master != NULL)
+ {
+ _clutter_input_device_set_associated_device (slave, master);
+ _clutter_input_device_add_slave (master, slave);
+ }
+ XIFreeDeviceInfo (info);
+ }
+ }
+ }
+ }
+}
+
+static void
+translate_property_event (MetaSeatX11 *seat_x11,
+ XIEvent *event)
+{
+ XIPropertyEvent *xev = (XIPropertyEvent *) event;
+ Atom serial_ids_prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Serial IDs", True);
+ ClutterInputDevice *device;
+
+ device = g_hash_table_lookup (seat_x11->devices_by_id,
+ GINT_TO_POINTER (xev->deviceid));
+ if (!device)
+ return;
+
+ if (xev->property == serial_ids_prop)
+ {
+ ClutterInputDeviceTool *tool = NULL;
+ ClutterInputDeviceToolType type;
+ int serial_id;
+
+ serial_id = device_get_tool_serial (device);
+
+ if (serial_id != 0)
+ {
+ tool = g_hash_table_lookup (seat_x11->tools_by_serial,
+ GUINT_TO_POINTER (serial_id));
+ if (!tool)
+ {
+ type = clutter_input_device_get_device_type (device) == CLUTTER_ERASER_DEVICE ?
+ CLUTTER_INPUT_DEVICE_TOOL_ERASER : CLUTTER_INPUT_DEVICE_TOOL_PEN;
+ tool = meta_input_device_tool_x11_new (serial_id, type);
+ g_hash_table_insert (seat_x11->tools_by_serial,
+ GUINT_TO_POINTER (serial_id),
+ tool);
+ }
+ }
+
+ meta_input_device_x11_update_tool (device, tool);
+ g_signal_emit_by_name (seat_x11, "tool-changed", device, tool);
+ }
+}
+
+static void
+translate_raw_event (MetaSeatX11 *seat_x11,
+ XEvent *xevent)
+{
+ ClutterInputDevice *device;
+ XGenericEventCookie *cookie;
+ XIEvent *xi_event;
+ XIRawEvent *xev;
+ float x,y;
+
+ cookie = &xevent->xcookie;
+ xi_event = (XIEvent *) cookie->data;
+ xev = (XIRawEvent *) xi_event;
+
+ device = g_hash_table_lookup (seat_x11->devices_by_id,
+ GINT_TO_POINTER (xev->deviceid));
+ if (device == NULL)
+ return;
+
+ if (!_clutter_is_input_pointer_a11y_enabled (device))
+ return;
+
+ switch (cookie->evtype)
+ {
+ case XI_RawMotion:
+ g_debug ("raw motion: device:%d '%s'",
+ device->id,
+ device->device_name);
+
+ /* We don't get actual pointer location with raw events, and we cannot
+ * rely on `clutter_input_device_get_coords()` either because of
+ * unreparented toplevels (like all client-side decoration windows),
+ * so we need to explicitely query the pointer here...
+ */
+ if (meta_input_device_x11_get_pointer_location (device, &x, &y))
+ _clutter_input_pointer_a11y_on_motion_event (device, x, y);
+ break;
+ case XI_RawButtonPress:
+ case XI_RawButtonRelease:
+ g_debug ("raw button %s: device:%d '%s' button %i",
+ cookie->evtype == XI_RawButtonPress
+ ? "press "
+ : "release",
+ device->id,
+ device->device_name,
+ xev->detail);
+ _clutter_input_pointer_a11y_on_button_event (device,
+ xev->detail,
+ (cookie->evtype == XI_RawButtonPress));
+ break;
+ }
+}
+
+static gboolean
+translate_pad_axis (ClutterInputDevice *device,
+ XIValuatorState *valuators,
+ ClutterEventType *evtype,
+ uint32_t *number,
+ double *value)
+{
+ double *values;
+ int i;
+
+ values = valuators->values;
+
+ for (i = PAD_AXIS_FIRST; i < valuators->mask_len * 8; i++)
+ {
+ double val;
+ uint32_t axis_number = 0;
+
+ if (!XIMaskIsSet (valuators->mask, i))
+ continue;
+
+ val = *values++;
+ if (val <= 0)
+ continue;
+
+ _clutter_input_device_translate_axis (device, i, val, value);
+
+ if (i == PAD_AXIS_RING1 || i == PAD_AXIS_RING2)
+ {
+ *evtype = CLUTTER_PAD_RING;
+ (*value) *= 360.0;
+ }
+ else if (i == PAD_AXIS_STRIP1 || i == PAD_AXIS_STRIP2)
+ {
+ *evtype = CLUTTER_PAD_STRIP;
+ }
+ else
+ continue;
+
+ if (i == PAD_AXIS_STRIP2 || i == PAD_AXIS_RING2)
+ axis_number++;
+
+ *number = axis_number;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+translate_pad_event (ClutterEvent *event,
+ XIDeviceEvent *xev,
+ ClutterInputDevice *device)
+{
+ double value;
+ uint32_t number, mode = 0;
+
+ if (!translate_pad_axis (device, &xev->valuators,
+ &event->any.type,
+ &number, &value))
+ return FALSE;
+
+ /* When touching a ring/strip a first XI_Motion event
+ * is generated. Use it to reset the pad state, so
+ * later events actually have a directionality.
+ */
+ if (xev->evtype == XI_Motion)
+ value = -1;
+
+#ifdef HAVE_LIBWACOM
+ mode = meta_input_device_x11_get_pad_group_mode (device, number);
+#endif
+
+ if (event->any.type == CLUTTER_PAD_RING)
+ {
+ event->pad_ring.ring_number = number;
+ event->pad_ring.angle = value;
+ event->pad_ring.mode = mode;
+ }
+ else
+ {
+ event->pad_strip.strip_number = number;
+ event->pad_strip.value = value;
+ event->pad_strip.mode = mode;
+ }
+
+ event->any.time = xev->time;
+ clutter_event_set_device (event, device);
+ clutter_event_set_source_device (event, device);
+
+ g_debug ("%s: win:0x%x, device:%d '%s', time:%d "
+ "(value:%f)",
+ event->any.type == CLUTTER_PAD_RING
+ ? "pad ring "
+ : "pad strip",
+ (unsigned int) xev->event,
+ device->id,
+ device->device_name,
+ event->any.time, value);
+
+ return TRUE;
+}
+
+static ClutterStage *
+get_event_stage (MetaSeatX11 *seat_x11,
+ XIEvent *xi_event)
+{
+ Window xwindow = None;
+
+ switch (xi_event->evtype)
+ {
+ case XI_KeyPress:
+ case XI_KeyRelease:
+ case XI_ButtonPress:
+ case XI_ButtonRelease:
+ case XI_Motion:
+ case XI_TouchBegin:
+ case XI_TouchUpdate:
+ case XI_TouchEnd:
+ {
+ XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+
+ xwindow = xev->event;
+ }
+ break;
+
+ case XI_Enter:
+ case XI_Leave:
+ case XI_FocusIn:
+ case XI_FocusOut:
+ {
+ XIEnterEvent *xev = (XIEnterEvent *) xi_event;
+
+ xwindow = xev->event;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (xwindow == None)
+ return NULL;
+
+ return meta_x11_get_stage_from_window (xwindow);
+}
+
+/*
+ * print_key_sym: Translate a symbol to its printable form if any
+ * @symbol: the symbol to translate
+ * @buffer: the buffer where to put the translated string
+ * @len: size of the buffer
+ *
+ * Translates @symbol into a printable representation in @buffer, if possible.
+ *
+ * Return value: The number of bytes of the translated string, 0 if the
+ * symbol can't be printed
+ *
+ * Note: The code is derived from libX11's src/KeyBind.c
+ * Copyright 1985, 1987, 1998 The Open Group
+ *
+ * Note: This code works for Latin-1 symbols. clutter_keysym_to_unicode()
+ * does the work for the other keysyms.
+ */
+static int
+print_keysym (uint32_t symbol,
+ char *buffer,
+ int len)
+{
+ unsigned long high_bytes;
+ unsigned char c;
+
+ high_bytes = symbol >> 8;
+ if (!(len &&
+ ((high_bytes == 0) ||
+ ((high_bytes == 0xFF) &&
+ (((symbol >= CLUTTER_KEY_BackSpace) &&
+ (symbol <= CLUTTER_KEY_Clear)) ||
+ (symbol == CLUTTER_KEY_Return) ||
+ (symbol == CLUTTER_KEY_Escape) ||
+ (symbol == CLUTTER_KEY_KP_Space) ||
+ (symbol == CLUTTER_KEY_KP_Tab) ||
+ (symbol == CLUTTER_KEY_KP_Enter) ||
+ ((symbol >= CLUTTER_KEY_KP_Multiply) &&
+ (symbol <= CLUTTER_KEY_KP_9)) ||
+ (symbol == CLUTTER_KEY_KP_Equal) ||
+ (symbol == CLUTTER_KEY_Delete))))))
+ return 0;
+
+ /* if X keysym, convert to ascii by grabbing low 7 bits */
+ if (symbol == CLUTTER_KEY_KP_Space)
+ c = CLUTTER_KEY_space & 0x7F; /* patch encoding botch */
+ else if (high_bytes == 0xFF)
+ c = symbol & 0x7F;
+ else
+ c = symbol & 0xFF;
+
+ buffer[0] = c;
+ return 1;
+}
+
+static double *
+translate_axes (ClutterInputDevice *device,
+ double x,
+ double y,
+ XIValuatorState *valuators)
+{
+ uint32_t n_axes = clutter_input_device_get_n_axes (device);
+ uint32_t i;
+ double *retval;
+ double *values;
+
+ retval = g_new0 (double, n_axes);
+ values = valuators->values;
+
+ for (i = 0; i < valuators->mask_len * 8; i++)
+ {
+ ClutterInputAxis axis;
+ double val;
+
+ if (!XIMaskIsSet (valuators->mask, i))
+ continue;
+
+ axis = clutter_input_device_get_axis (device, i);
+ val = *values++;
+
+ switch (axis)
+ {
+ case CLUTTER_INPUT_AXIS_X:
+ retval[i] = x;
+ break;
+
+ case CLUTTER_INPUT_AXIS_Y:
+ retval[i] = y;
+ break;
+
+ default:
+ _clutter_input_device_translate_axis (device, i, val, &retval[i]);
+ break;
+ }
+ }
+
+ return retval;
+}
+
+static double
+scroll_valuators_changed (ClutterInputDevice *device,
+ XIValuatorState *valuators,
+ double *dx_p,
+ double *dy_p)
+{
+ gboolean retval = FALSE;
+ uint32_t n_axes, n_val, i;
+ double *values;
+
+ n_axes = clutter_input_device_get_n_axes (device);
+ values = valuators->values;
+
+ *dx_p = *dy_p = 0.0;
+
+ n_val = 0;
+
+ for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++)
+ {
+ ClutterScrollDirection direction;
+ double delta;
+
+ if (!XIMaskIsSet (valuators->mask, i))
+ continue;
+
+ if (_clutter_input_device_get_scroll_delta (device, i,
+ values[n_val],
+ &direction,
+ &delta))
+ {
+ retval = TRUE;
+
+ if (direction == CLUTTER_SCROLL_UP ||
+ direction == CLUTTER_SCROLL_DOWN)
+ *dy_p = delta;
+ else
+ *dx_p = delta;
+ }
+
+ n_val += 1;
+ }
+
+ return retval;
+}
+
+static void
+translate_coords (MetaStageX11 *stage_x11,
+ double event_x,
+ double event_y,
+ float *x_out,
+ float *y_out)
+{
+ ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
+ ClutterActor *stage = CLUTTER_ACTOR (stage_cogl->wrapper);
+ float stage_width;
+ float stage_height;
+
+ clutter_actor_get_size (stage, &stage_width, &stage_height);
+
+ *x_out = CLAMP (event_x, 0, stage_width);
+ *y_out = CLAMP (event_y, 0, stage_height);
+}
+
+static void
+meta_seat_x11_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MetaSeatX11 *seat_x11 = META_SEAT_X11 (object);
+
+ switch (prop_id)
+ {
+ case PROP_OPCODE:
+ seat_x11->opcode = g_value_get_int (value);
+ break;
+ case PROP_POINTER_ID:
+ seat_x11->pointer_id = g_value_get_int (value);
+ break;
+ case PROP_KEYBOARD_ID:
+ seat_x11->keyboard_id = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+meta_seat_x11_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MetaSeatX11 *seat_x11 = META_SEAT_X11 (object);
+
+ switch (prop_id)
+ {
+ case PROP_OPCODE:
+ g_value_set_int (value, seat_x11->opcode);
+ break;
+ case PROP_POINTER_ID:
+ g_value_set_int (value, seat_x11->pointer_id);
+ break;
+ case PROP_KEYBOARD_ID:
+ g_value_set_int (value, seat_x11->keyboard_id);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+meta_seat_x11_constructed (GObject *object)
+{
+ MetaSeatX11 *seat_x11 = META_SEAT_X11 (object);
+ ClutterBackend *backend = clutter_get_default_backend ();
+ GHashTable *masters, *slaves;
+ XIDeviceInfo *info;
+ XIEventMask event_mask;
+ unsigned char mask[XIMaskLen(XI_LASTEVENT)] = { 0, };
+ int n_devices, i;
+ Display *xdisplay;
+
+ xdisplay = clutter_x11_get_default_display ();
+ masters = g_hash_table_new (NULL, NULL);
+ slaves = g_hash_table_new (NULL, NULL);
+
+ info = XIQueryDevice (clutter_x11_get_default_display (),
+ XIAllDevices, &n_devices);
+
+ for (i = 0; i < n_devices; i++)
+ {
+ XIDeviceInfo *xi_device = &info[i];
+
+ if (!xi_device->enabled)
+ continue;
+
+ add_device (seat_x11, backend, xi_device, TRUE);
+
+ if (xi_device->use == XIMasterPointer ||
+ xi_device->use == XIMasterKeyboard)
+ {
+ g_hash_table_insert (masters,
+ GINT_TO_POINTER (xi_device->deviceid),
+ GINT_TO_POINTER (xi_device->attachment));
+ }
+ else if (xi_device->use == XISlavePointer ||
+ xi_device->use == XISlaveKeyboard)
+ {
+ g_hash_table_insert (slaves,
+ GINT_TO_POINTER (xi_device->deviceid),
+ GINT_TO_POINTER (xi_device->attachment));
+ }
+ }
+
+ XIFreeDeviceInfo (info);
+
+ g_hash_table_foreach (masters, relate_masters, seat_x11);
+ g_hash_table_destroy (masters);
+
+ g_hash_table_foreach (slaves, relate_slaves, seat_x11);
+ g_hash_table_destroy (slaves);
+
+ XISetMask (mask, XI_HierarchyChanged);
+ XISetMask (mask, XI_DeviceChanged);
+ XISetMask (mask, XI_PropertyEvent);
+
+ event_mask.deviceid = XIAllDevices;
+ event_mask.mask_len = sizeof (mask);
+ event_mask.mask = mask;
+
+ XISelectEvents (xdisplay, clutter_x11_get_root_window (),
+ &event_mask, 1);
+
+ memset(mask, 0, sizeof (mask));
+ XISetMask (mask, XI_RawMotion);
+ XISetMask (mask, XI_RawButtonPress);
+ XISetMask (mask, XI_RawButtonRelease);
+
+ event_mask.deviceid = XIAllMasterDevices;
+ event_mask.mask_len = sizeof (mask);
+ event_mask.mask = mask;
+
+ XISelectEvents (xdisplay, clutter_x11_get_root_window (),
+ &event_mask, 1);
+
+ XSync (xdisplay, False);
+
+ if (G_OBJECT_CLASS (meta_seat_x11_parent_class)->constructed)
+ G_OBJECT_CLASS (meta_seat_x11_parent_class)->constructed (object);
+}
+
+static void
+meta_seat_x11_finalize (GObject *object)
+{
+ MetaSeatX11 *seat_x11 = META_SEAT_X11 (object);
+
+ g_hash_table_unref (seat_x11->devices_by_id);
+ g_hash_table_unref (seat_x11->tools_by_serial);
+ g_list_free (seat_x11->devices);
+
+#ifdef HAVE_LIBWACOM
+ libwacom_database_destroy (seat_x11->wacom_db);
+#endif
+
+ G_OBJECT_CLASS (meta_seat_x11_parent_class)->finalize (object);
+}
+
+static ClutterInputDevice *
+meta_seat_x11_get_pointer (ClutterSeat *seat)
+{
+ MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
+
+ return seat_x11->core_pointer;
+}
+
+static ClutterInputDevice *
+meta_seat_x11_get_keyboard (ClutterSeat *seat)
+{
+ MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
+
+ return seat_x11->core_keyboard;
+}
+
+static GList *
+meta_seat_x11_list_devices (ClutterSeat *seat)
+{
+ MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
+ GList *retval = NULL, *l;
+
+ for (l = seat_x11->devices; l; l = l->next)
+ retval = g_list_prepend (retval, l->data);
+
+ return retval;
+}
+
+static void
+meta_seat_x11_class_init (MetaSeatX11Class *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ClutterSeatClass *seat_class = CLUTTER_SEAT_CLASS (klass);
+
+ object_class->set_property = meta_seat_x11_set_property;
+ object_class->get_property = meta_seat_x11_get_property;
+ object_class->constructed = meta_seat_x11_constructed;
+ object_class->finalize = meta_seat_x11_finalize;
+
+ seat_class->get_pointer = meta_seat_x11_get_pointer;
+ seat_class->get_keyboard = meta_seat_x11_get_keyboard;
+ seat_class->list_devices = meta_seat_x11_list_devices;
+
+ props[PROP_OPCODE] =
+ g_param_spec_int ("opcode",
+ "Opcode",
+ "Opcode",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY);
+ props[PROP_POINTER_ID] =
+ g_param_spec_int ("pointer-id",
+ "Pointer ID",
+ "Pointer ID",
+ 2, G_MAXINT, 2,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY);
+ props[PROP_KEYBOARD_ID] =
+ g_param_spec_int ("keyboard-id",
+ "Keyboard ID",
+ "Keyboard ID",
+ 2, G_MAXINT, 2,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+meta_seat_x11_init (MetaSeatX11 *seat)
+{
+ seat->devices_by_id = g_hash_table_new_full (NULL, NULL,
+ NULL,
+ (GDestroyNotify) g_object_unref);
+ seat->tools_by_serial = g_hash_table_new_full (NULL, NULL, NULL,
+ (GDestroyNotify) g_object_unref);
+
+#ifdef HAVE_LIBWACOM
+ seat->wacom_db = libwacom_database_new ();
+#endif
+}
+
+MetaSeatX11 *
+meta_seat_x11_new (int opcode,
+ int master_pointer,
+ int master_keyboard)
+{
+ return g_object_new (META_TYPE_SEAT_X11,
+ "opcode", opcode,
+ "pointer-id", master_pointer,
+ "keyboard-id", master_keyboard,
+ NULL);
+}
+
+static ClutterInputDevice *
+get_source_device_checked (MetaSeatX11 *seat,
+ XIDeviceEvent *xev)
+{
+ ClutterInputDevice *source_device;
+
+ source_device = g_hash_table_lookup (seat->devices_by_id,
+ GINT_TO_POINTER (xev->sourceid));
+
+ if (!source_device)
+ g_warning ("Impossible to get the source device with id %d for event of "
+ "type %d", xev->sourceid, xev->evtype);
+
+ return source_device;
+}
+
+gboolean
+meta_seat_x11_translate_event (MetaSeatX11 *seat,
+ XEvent *xevent,
+ ClutterEvent *event)
+{
+ gboolean retval = FALSE;
+ ClutterBackend *backend = clutter_get_default_backend ();
+ ClutterStage *stage = NULL;
+ MetaStageX11 *stage_x11 = NULL;
+ ClutterInputDevice *device, *source_device;
+ XGenericEventCookie *cookie;
+ XIEvent *xi_event;
+
+ cookie = &xevent->xcookie;
+
+ if (cookie->type != GenericEvent ||
+ cookie->extension != seat->opcode)
+ return FALSE;
+
+ xi_event = (XIEvent *) cookie->data;
+
+ if (!xi_event)
+ return FALSE;
+
+ if (cookie->evtype == XI_RawMotion ||
+ cookie->evtype == XI_RawButtonPress ||
+ cookie->evtype == XI_RawButtonRelease)
+ {
+ translate_raw_event (seat, xevent);
+ return FALSE;
+ }
+
+ if (!(xi_event->evtype == XI_HierarchyChanged ||
+ xi_event->evtype == XI_DeviceChanged ||
+ xi_event->evtype == XI_PropertyEvent))
+ {
+ stage = get_event_stage (seat, xi_event);
+ if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage))
+ return FALSE;
+ else
+ stage_x11 = META_STAGE_X11 (_clutter_stage_get_window (stage));
+ }
+
+ event->any.stage = stage;
+
+ switch (xi_event->evtype)
+ {
+ case XI_HierarchyChanged:
+ {
+ XIHierarchyEvent *xev = (XIHierarchyEvent *) xi_event;
+
+ translate_hierarchy_event (backend, seat, xev);
+ }
+ retval = FALSE;
+ break;
+
+ case XI_DeviceChanged:
+ {
+ XIDeviceChangedEvent *xev = (XIDeviceChangedEvent *) xi_event;
+
+ device = g_hash_table_lookup (seat->devices_by_id,
+ GINT_TO_POINTER (xev->deviceid));
+ source_device = g_hash_table_lookup (seat->devices_by_id,
+ GINT_TO_POINTER (xev->sourceid));
+ if (device)
+ {
+ _clutter_input_device_reset_axes (device);
+ translate_device_classes (clutter_x11_get_default_display (),
+ device,
+ xev->classes,
+ xev->num_classes);
+ }
+
+ if (source_device)
+ _clutter_input_device_reset_scroll_info (source_device);
+ }
+ retval = FALSE;
+ break;
+ case XI_KeyPress:
+ case XI_KeyRelease:
+ {
+ XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+ MetaKeymapX11 *keymap_x11 = META_KEYMAP_X11 (clutter_backend_get_keymap (backend));
+ MetaEventX11 *event_x11;
+ char buffer[7] = { 0, };
+ gunichar n;
+
+ source_device = get_source_device_checked (seat, xev);
+ if (!source_device)
+ return FALSE;
+
+ event->key.type = event->type = (xev->evtype == XI_KeyPress)
+ ? CLUTTER_KEY_PRESS
+ : CLUTTER_KEY_RELEASE;
+
+ if (xev->evtype == XI_KeyPress && xev->flags & XIKeyRepeat)
+ clutter_event_set_flags (event, CLUTTER_EVENT_FLAG_REPEATED);
+
+ event->key.time = xev->time;
+ event->key.stage = stage;
+ meta_input_device_x11_translate_state (event, &xev->mods, &xev->buttons, &xev->group);
+ event->key.hardware_keycode = xev->detail;
+
+ /* keyval is the key ignoring all modifiers ('1' vs. '!') */
+ event->key.keyval =
+ meta_keymap_x11_translate_key_state (keymap_x11,
+ event->key.hardware_keycode,
+ &event->key.modifier_state,
+ NULL);
+
+ /* KeyEvents have platform specific data associated to them */
+ event_x11 = meta_event_x11_new ();
+ _clutter_event_set_platform_data (event, event_x11);
+
+ event_x11->key_group =
+ meta_keymap_x11_get_key_group (keymap_x11,
+ event->key.modifier_state);
+ event_x11->key_is_modifier =
+ meta_keymap_x11_get_is_modifier (keymap_x11,
+ event->key.hardware_keycode);
+ event_x11->num_lock_set =
+ clutter_keymap_get_num_lock_state (CLUTTER_KEYMAP (keymap_x11));
+ event_x11->caps_lock_set =
+ clutter_keymap_get_caps_lock_state (CLUTTER_KEYMAP (keymap_x11));
+
+ clutter_event_set_source_device (event, source_device);
+
+ device = g_hash_table_lookup (seat->devices_by_id,
+ GINT_TO_POINTER (xev->deviceid));
+ clutter_event_set_device (event, device);
+
+ /* XXX keep this in sync with the evdev device manager */
+ n = print_keysym (event->key.keyval, buffer, sizeof (buffer));
+ if (n == 0)
+ {
+ /* not printable */
+ event->key.unicode_value = (gunichar) '\0';
+ }
+ else
+ {
+ event->key.unicode_value = g_utf8_get_char_validated (buffer, n);
+ if (event->key.unicode_value == -1 ||
+ event->key.unicode_value == -2)
+ event->key.unicode_value = (gunichar) '\0';
+ }
+
+ g_debug ("%s: win:0x%x device:%d source:%d, key: %12s (%d)",
+ event->any.type == CLUTTER_KEY_PRESS
+ ? "key press "
+ : "key release",
+ (unsigned int) stage_x11->xwin,
+ xev->deviceid,
+ xev->sourceid,
+ event->key.keyval ? buffer : "(none)",
+ event->key.keyval);
+
+ if (xi_event->evtype == XI_KeyPress)
+ meta_stage_x11_set_user_time (stage_x11, event->key.time);
+
+ retval = TRUE;
+ }
+ break;
+
+ case XI_ButtonPress:
+ case XI_ButtonRelease:
+ {
+ XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+
+ source_device = get_source_device_checked (seat, xev);
+ if (!source_device)
+ return FALSE;
+
+ device = g_hash_table_lookup (seat->devices_by_id,
+ GINT_TO_POINTER (xev->deviceid));
+
+ /* Set the stage for core events coming out of nowhere (see bug #684509) */
+ if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER &&
+ clutter_input_device_get_pointer_stage (device) == NULL &&
+ stage != NULL)
+ _clutter_input_device_set_stage (device, stage);
+
+ if (clutter_input_device_get_device_type (source_device) == CLUTTER_PAD_DEVICE)
+ {
+ /* We got these events because of the passive button grab */
+ XIAllowEvents (clutter_x11_get_default_display (),
+ xev->sourceid,
+ XIAsyncDevice,
+ xev->time);
+
+ event->any.stage = stage;
+
+ if (xev->detail >= 4 && xev->detail <= 7)
+ {
+ retval = FALSE;
+
+ if (xi_event->evtype == XI_ButtonPress &&
+ translate_pad_event (event, xev, source_device))
+ retval = TRUE;
+
+ break;
+ }
+
+ event->any.type =
+ (xi_event->evtype == XI_ButtonPress) ? CLUTTER_PAD_BUTTON_PRESS
+ : CLUTTER_PAD_BUTTON_RELEASE;
+ event->any.time = xev->time;
+
+ /* The 4-7 button range is taken as non-existent on pad devices,
+ * let the buttons above that take over this range.
+ */
+ if (xev->detail > 7)
+ xev->detail -= 4;
+
+ /* Pad buttons are 0-indexed */
+ event->pad_button.button = xev->detail - 1;
+#ifdef HAVE_LIBWACOM
+ meta_input_device_x11_update_pad_state (device,
+ event->pad_button.button,
+ (xi_event->evtype == XI_ButtonPress),
+ &event->pad_button.group,
+ &event->pad_button.mode);
+#endif
+ clutter_event_set_device (event, device);
+ clutter_event_set_source_device (event, source_device);
+
+ g_debug ("%s: win:0x%x, device:%d '%s', time:%d "
+ "(button:%d)",
+ event->any.type == CLUTTER_BUTTON_PRESS
+ ? "pad button press "
+ : "pad button release",
+ (unsigned int) stage_x11->xwin,
+ device->id,
+ device->device_name,
+ event->any.time,
+ event->pad_button.button);
+ retval = TRUE;
+ break;
+ }
+
+ switch (xev->detail)
+ {
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ /* we only generate Scroll events on ButtonPress */
+ if (xi_event->evtype == XI_ButtonRelease)
+ return FALSE;
+
+ event->scroll.type = event->type = CLUTTER_SCROLL;
+
+ if (xev->detail == 4)
+ event->scroll.direction = CLUTTER_SCROLL_UP;
+ else if (xev->detail == 5)
+ event->scroll.direction = CLUTTER_SCROLL_DOWN;
+ else if (xev->detail == 6)
+ event->scroll.direction = CLUTTER_SCROLL_LEFT;
+ else
+ event->scroll.direction = CLUTTER_SCROLL_RIGHT;
+
+ event->scroll.stage = stage;
+
+ event->scroll.time = xev->time;
+ translate_coords (stage_x11, xev->event_x, xev->event_y, &event->scroll.x, &event->scroll.y);
+ meta_input_device_x11_translate_state (event,
+ &xev->mods,
+ &xev->buttons,
+ &xev->group);
+
+ clutter_event_set_source_device (event, source_device);
+ clutter_event_set_device (event, device);
+
+ event->scroll.axes = translate_axes (event->scroll.device,
+ event->scroll.x,
+ event->scroll.y,
+ &xev->valuators);
+ g_debug ("scroll: win:0x%x, device:%d '%s', time:%d "
+ "(direction:%s, "
+ "x:%.2f, y:%.2f, "
+ "emulated:%s)",
+ (unsigned int) stage_x11->xwin,
+ device->id,
+ device->device_name,
+ event->any.time,
+ event->scroll.direction == CLUTTER_SCROLL_UP ? "up" :
+ event->scroll.direction == CLUTTER_SCROLL_DOWN ? "down" :
+ event->scroll.direction == CLUTTER_SCROLL_LEFT ? "left" :
+ event->scroll.direction == CLUTTER_SCROLL_RIGHT ? "right" :
+ "invalid",
+ event->scroll.x,
+ event->scroll.y,
+ (xev->flags & XIPointerEmulated) ? "yes" : "no");
+ break;
+
+ default:
+ event->button.type = event->type =
+ (xi_event->evtype == XI_ButtonPress) ? CLUTTER_BUTTON_PRESS
+ : CLUTTER_BUTTON_RELEASE;
+
+ event->button.stage = stage;
+
+ event->button.time = xev->time;
+ translate_coords (stage_x11, xev->event_x, xev->event_y, &event->button.x, &event->button.y);
+ event->button.button = xev->detail;
+ meta_input_device_x11_translate_state (event,
+ &xev->mods,
+ &xev->buttons,
+ &xev->group);
+
+ clutter_event_set_source_device (event, source_device);
+ clutter_event_set_device (event, device);
+ clutter_event_set_device_tool (event,
+ meta_input_device_x11_get_current_tool (source_device));
+
+ event->button.axes = translate_axes (event->button.device,
+ event->button.x,
+ event->button.y,
+ &xev->valuators);
+ g_debug ("%s: win:0x%x, device:%d '%s', time:%d "
+ "(button:%d, "
+ "x:%.2f, y:%.2f, "
+ "axes:%s, "
+ "emulated:%s)",
+ event->any.type == CLUTTER_BUTTON_PRESS
+ ? "button press "
+ : "button release",
+ (unsigned int) stage_x11->xwin,
+ device->id,
+ device->device_name,
+ event->any.time,
+ event->button.button,
+ event->button.x,
+ event->button.y,
+ event->button.axes != NULL ? "yes" : "no",
+ (xev->flags & XIPointerEmulated) ? "yes" : "no");
+ break;
+ }
+
+ if (device->stage != NULL)
+ _clutter_input_device_set_stage (source_device, device->stage);
+
+ if (xev->flags & XIPointerEmulated)
+ _clutter_event_set_pointer_emulated (event, TRUE);
+
+ if (xi_event->evtype == XI_ButtonPress)
+ meta_stage_x11_set_user_time (stage_x11, event->button.time);
+
+ retval = TRUE;
+ }
+ break;
+
+ case XI_Motion:
+ {
+ XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+ double delta_x, delta_y;
+
+ source_device = get_source_device_checked (seat, xev);
+ if (!source_device)
+ return FALSE;
+
+ device = g_hash_table_lookup (seat->devices_by_id,
+ GINT_TO_POINTER (xev->deviceid));
+
+ if (clutter_input_device_get_device_type (source_device) == CLUTTER_PAD_DEVICE)
+ {
+ event->any.stage = stage;
+
+ if (translate_pad_event (event, xev, source_device))
+ retval = TRUE;
+ break;
+ }
+
+ /* Set the stage for core events coming out of nowhere (see bug #684509) */
+ if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER &&
+ clutter_input_device_get_pointer_stage (device) == NULL &&
+ stage != NULL)
+ _clutter_input_device_set_stage (device, stage);
+
+ if (scroll_valuators_changed (source_device,
+ &xev->valuators,
+ &delta_x, &delta_y))
+ {
+ event->scroll.type = event->type = CLUTTER_SCROLL;
+ event->scroll.direction = CLUTTER_SCROLL_SMOOTH;
+
+ event->scroll.stage = stage;
+ event->scroll.time = xev->time;
+ translate_coords (stage_x11, xev->event_x, xev->event_y, &event->scroll.x, &event->scroll.y);
+ meta_input_device_x11_translate_state (event,
+ &xev->mods,
+ &xev->buttons,
+ &xev->group);
+
+ clutter_event_set_scroll_delta (event, delta_x, delta_y);
+ clutter_event_set_source_device (event, source_device);
+ clutter_event_set_device (event, device);
+
+ g_debug ("smooth scroll: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, delta:%f, %f)",
+ (unsigned int) stage_x11->xwin,
+ event->scroll.device->id,
+ event->scroll.device->device_name,
+ event->scroll.x,
+ event->scroll.y,
+ delta_x, delta_y);
+
+ retval = TRUE;
+ break;
+ }
+
+ event->motion.type = event->type = CLUTTER_MOTION;
+
+ event->motion.stage = stage;
+
+ event->motion.time = xev->time;
+ translate_coords (stage_x11, xev->event_x, xev->event_y, &event->motion.x, &event->motion.y);
+ meta_input_device_x11_translate_state (event,
+ &xev->mods,
+ &xev->buttons,
+ &xev->group);
+
+ clutter_event_set_source_device (event, source_device);
+ clutter_event_set_device (event, device);
+ clutter_event_set_device_tool (event,
+ meta_input_device_x11_get_current_tool (source_device));
+
+ event->motion.axes = translate_axes (event->motion.device,
+ event->motion.x,
+ event->motion.y,
+ &xev->valuators);
+
+ if (device->stage != NULL)
+ _clutter_input_device_set_stage (source_device, device->stage);
+
+ if (xev->flags & XIPointerEmulated)
+ _clutter_event_set_pointer_emulated (event, TRUE);
+
+ g_debug ("motion: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, axes:%s)",
+ (unsigned int) stage_x11->xwin,
+ event->motion.device->id,
+ event->motion.device->device_name,
+ event->motion.x,
+ event->motion.y,
+ event->motion.axes != NULL ? "yes" : "no");
+
+ retval = TRUE;
+ }
+ break;
+
+ case XI_TouchBegin:
+ {
+ XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+ device = g_hash_table_lookup (seat->devices_by_id,
+ GINT_TO_POINTER (xev->deviceid));
+ if (!_clutter_input_device_get_stage (device))
+ _clutter_input_device_set_stage (device, stage);
+ }
+ /* Fall through */
+ case XI_TouchEnd:
+ {
+ XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+
+ source_device = g_hash_table_lookup (seat->devices_by_id,
+ GINT_TO_POINTER (xev->sourceid));
+
+ if (xi_event->evtype == XI_TouchBegin)
+ event->touch.type = event->type = CLUTTER_TOUCH_BEGIN;
+ else
+ event->touch.type = event->type = CLUTTER_TOUCH_END;
+
+ event->touch.stage = stage;
+ event->touch.time = xev->time;
+ translate_coords (stage_x11, xev->event_x, xev->event_y, &event->touch.x, &event->touch.y);
+ meta_input_device_x11_translate_state (event,
+ &xev->mods,
+ &xev->buttons,
+ &xev->group);
+
+ clutter_event_set_source_device (event, source_device);
+
+ device = g_hash_table_lookup (seat->devices_by_id,
+ GINT_TO_POINTER (xev->deviceid));
+ clutter_event_set_device (event, device);
+
+ event->touch.axes = translate_axes (event->touch.device,
+ event->motion.x,
+ event->motion.y,
+ &xev->valuators);
+
+ if (xi_event->evtype == XI_TouchBegin)
+ {
+ event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;
+
+ meta_stage_x11_set_user_time (stage_x11, event->touch.time);
+ }
+
+ event->touch.sequence = GUINT_TO_POINTER (xev->detail);
+
+ if (xev->flags & XITouchEmulatingPointer)
+ _clutter_event_set_pointer_emulated (event, TRUE);
+
+ g_debug ("touch %s: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f, axes:%s)",
+ event->type == CLUTTER_TOUCH_BEGIN ? "begin" : "end",
+ (unsigned int) stage_x11->xwin,
+ event->touch.device->id,
+ event->touch.device->device_name,
+ GPOINTER_TO_UINT (event->touch.sequence),
+ event->touch.x,
+ event->touch.y,
+ event->touch.axes != NULL ? "yes" : "no");
+
+ retval = TRUE;
+ }
+ break;
+
+ case XI_TouchUpdate:
+ {
+ XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+
+ source_device = g_hash_table_lookup (seat->devices_by_id,
+ GINT_TO_POINTER (xev->sourceid));
+
+ event->touch.type = event->type = CLUTTER_TOUCH_UPDATE;
+ event->touch.stage = stage;
+ event->touch.time = xev->time;
+ event->touch.sequence = GUINT_TO_POINTER (xev->detail);
+ translate_coords (stage_x11, xev->event_x, xev->event_y, &event->touch.x, &event->touch.y);
+
+ clutter_event_set_source_device (event, source_device);
+
+ device = g_hash_table_lookup (seat->devices_by_id,
+ GINT_TO_POINTER (xev->deviceid));
+ clutter_event_set_device (event, device);
+
+ event->touch.axes = translate_axes (event->touch.device,
+ event->motion.x,
+ event->motion.y,
+ &xev->valuators);
+
+ meta_input_device_x11_translate_state (event,
+ &xev->mods,
+ &xev->buttons,
+ &xev->group);
+ event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;
+
+ if (xev->flags & XITouchEmulatingPointer)
+ _clutter_event_set_pointer_emulated (event, TRUE);
+
+ g_debug ("touch update: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f, axes:%s)",
+ (unsigned int) stage_x11->xwin,
+ event->touch.device->id,
+ event->touch.device->device_name,
+ GPOINTER_TO_UINT (event->touch.sequence),
+ event->touch.x,
+ event->touch.y,
+ event->touch.axes != NULL ? "yes" : "no");
+
+ retval = TRUE;
+ }
+ break;
+
+ case XI_Enter:
+ case XI_Leave:
+ {
+ XIEnterEvent *xev = (XIEnterEvent *) xi_event;
+
+ device = g_hash_table_lookup (seat->devices_by_id,
+ GINT_TO_POINTER (xev->deviceid));
+
+ source_device = g_hash_table_lookup (seat->devices_by_id,
+ GINT_TO_POINTER (xev->sourceid));
+
+ if (xi_event->evtype == XI_Enter)
+ {
+ event->crossing.type = event->type = CLUTTER_ENTER;
+
+ event->crossing.stage = stage;
+ event->crossing.source = CLUTTER_ACTOR (stage);
+ event->crossing.related = NULL;
+
+ event->crossing.time = xev->time;
+ translate_coords (stage_x11, xev->event_x, xev->event_y, &event->crossing.x, &event->crossing.y);
+
+ _clutter_input_device_set_stage (device, stage);
+ }
+ else
+ {
+ if (device->stage == NULL)
+ {
+ g_debug ("Discarding Leave for ButtonRelease "
+ "event off-stage");
+ retval = FALSE;
+ break;
+ }
+
+ event->crossing.type = event->type = CLUTTER_LEAVE;
+
+ event->crossing.stage = stage;
+ event->crossing.source = CLUTTER_ACTOR (stage);
+ event->crossing.related = NULL;
+
+ event->crossing.time = xev->time;
+ translate_coords (stage_x11, xev->event_x, xev->event_y, &event->crossing.x, &event->crossing.y);
+
+ _clutter_input_device_set_stage (device, NULL);
+ }
+
+ _clutter_input_device_reset_scroll_info (source_device);
+
+ clutter_event_set_device (event, device);
+ clutter_event_set_source_device (event, source_device);
+
+ retval = TRUE;
+ }
+ break;
+
+ case XI_FocusIn:
+ case XI_FocusOut:
+ retval = FALSE;
+ break;
+ case XI_PropertyEvent:
+ translate_property_event (seat, xi_event);
+ retval = FALSE;
+ break;
+ }
+
+ return retval;
+}
+
+ClutterInputDevice *
+meta_seat_x11_lookup_device_id (MetaSeatX11 *seat_x11,
+ int device_id)
+{
+ return g_hash_table_lookup (seat_x11->devices_by_id,
+ GINT_TO_POINTER (device_id));
+}
diff --git a/src/backends/x11/meta-seat-x11.h b/src/backends/x11/meta-seat-x11.h
new file mode 100644
index 000000000..ff7d1000d
--- /dev/null
+++ b/src/backends/x11/meta-seat-x11.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ * Author: Carlos Garnacho
+ */
+#ifndef META_SEAT_X11_H
+#define META_SEAT_X11_H
+
+#include "clutter/clutter.h"
+
+G_BEGIN_DECLS
+
+#define META_TYPE_SEAT_X11 meta_seat_x11_get_type ()
+G_DECLARE_FINAL_TYPE (MetaSeatX11, meta_seat_x11, META, SEAT_X11, ClutterSeat)
+
+MetaSeatX11 * meta_seat_x11_new (int opcode,
+ int master_pointer,
+ int master_keyboard);
+gboolean meta_seat_x11_translate_event (MetaSeatX11 *seat,
+ XEvent *xevent,
+ ClutterEvent *event);
+ClutterInputDevice * meta_seat_x11_lookup_device_id (MetaSeatX11 *seat_x11,
+ int device_id);
+
+G_END_DECLS
+
+#endif /* META_SEAT_X11_H */
diff --git a/src/meson.build b/src/meson.build
index 1f9bab95f..ad0cfd7e4 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -256,6 +256,8 @@ mutter_sources = [
'backends/x11/meta-input-device-tool-x11.h',
'backends/x11/meta-input-settings-x11.c',
'backends/x11/meta-input-settings-x11.h',
+ 'backends/x11/meta-seat-x11.c',
+ 'backends/x11/meta-seat-x11.h',
'backends/x11/meta-keymap-x11.c',
'backends/x11/meta-keymap-x11.h',
'backends/x11/meta-monitor-manager-xrandr.c',