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',