/* GDK - The GIMP Drawing Kit * Copyright (C) 2009 Carlos Garnacho * * 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 . */ #include "config.h" #include "gdkx11devicemanager-xi2.h" #include "gdkx11device-xi2.h" #include "gdkx11devicemanager-core.h" #include "gdkdevicemanagerprivate-core.h" #include "gdkdeviceprivate.h" #include "gdkdevicetoolprivate.h" #include "gdkdisplayprivate.h" #include "gdkeventtranslator.h" #include "gdkprivate-x11.h" #include "gdkdisplay-x11.h" #include "gdkintl.h" #include "gdkkeysyms.h" #include "gdkinternals.h" #include "gdkseatdefaultprivate.h" #include #include #include #include #include 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, }; struct _GdkX11DeviceManagerXI2 { GdkX11DeviceManagerCore parent_object; GHashTable *id_table; GList *devices; gint opcode; gint major; gint minor; }; struct _GdkX11DeviceManagerXI2Class { GdkX11DeviceManagerCoreClass parent_class; }; static void gdk_x11_device_manager_xi2_event_translator_init (GdkEventTranslatorIface *iface); G_DEFINE_TYPE_WITH_CODE (GdkX11DeviceManagerXI2, gdk_x11_device_manager_xi2, GDK_TYPE_X11_DEVICE_MANAGER_CORE, G_IMPLEMENT_INTERFACE (GDK_TYPE_EVENT_TRANSLATOR, gdk_x11_device_manager_xi2_event_translator_init)) static void gdk_x11_device_manager_xi2_constructed (GObject *object); static void gdk_x11_device_manager_xi2_dispose (GObject *object); static void gdk_x11_device_manager_xi2_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void gdk_x11_device_manager_xi2_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static GdkDevice * gdk_x11_device_manager_xi2_get_client_pointer (GdkX11DeviceManagerXI2 *device_manager); static gboolean gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator, GdkDisplay *display, GdkEvent *event, const XEvent *xevent); static GdkEventMask gdk_x11_device_manager_xi2_get_handled_events (GdkEventTranslator *translator); static void gdk_x11_device_manager_xi2_select_surface_events (GdkEventTranslator *translator, Window window, GdkEventMask event_mask); static GdkSurface * gdk_x11_device_manager_xi2_get_surface (GdkEventTranslator *translator, const XEvent *xevent); enum { PROP_0, PROP_OPCODE, PROP_MAJOR, PROP_MINOR }; static void gdk_x11_device_manager_xi2_class_init (GdkX11DeviceManagerXI2Class *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = gdk_x11_device_manager_xi2_constructed; object_class->dispose = gdk_x11_device_manager_xi2_dispose; object_class->set_property = gdk_x11_device_manager_xi2_set_property; object_class->get_property = gdk_x11_device_manager_xi2_get_property; g_object_class_install_property (object_class, PROP_OPCODE, g_param_spec_int ("opcode", P_("Opcode"), P_("Opcode for XInput2 requests"), 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_MAJOR, g_param_spec_int ("major", P_("Major"), P_("Major version number"), 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_MINOR, g_param_spec_int ("minor", P_("Minor"), P_("Minor version number"), 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } static void gdk_x11_device_manager_xi2_init (GdkX11DeviceManagerXI2 *device_manager) { device_manager->id_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_object_unref); } static void _gdk_x11_device_manager_xi2_select_events (GdkX11DeviceManagerXI2 *device_manager, Window xwindow, XIEventMask *event_mask) { GdkDisplay *display; Display *xdisplay; display = GDK_X11_DEVICE_MANAGER_CORE (device_manager)->display; xdisplay = GDK_DISPLAY_XDISPLAY (display); XISelectEvents (xdisplay, xwindow, event_mask, 1); } static void translate_valuator_class (GdkDisplay *display, GdkDevice *device, Atom valuator_label, gdouble min, gdouble max, gdouble resolution) { static gboolean initialized = FALSE; static Atom label_atoms [GDK_AXIS_LAST] = { 0 }; GdkAxisUse use = GDK_AXIS_IGNORE; GdkAtom label; gint i; if (!initialized) { label_atoms [GDK_AXIS_X] = gdk_x11_get_xatom_by_name_for_display (display, "Abs X"); label_atoms [GDK_AXIS_Y] = gdk_x11_get_xatom_by_name_for_display (display, "Abs Y"); label_atoms [GDK_AXIS_PRESSURE] = gdk_x11_get_xatom_by_name_for_display (display, "Abs Pressure"); label_atoms [GDK_AXIS_XTILT] = gdk_x11_get_xatom_by_name_for_display (display, "Abs Tilt X"); label_atoms [GDK_AXIS_YTILT] = gdk_x11_get_xatom_by_name_for_display (display, "Abs Tilt Y"); label_atoms [GDK_AXIS_WHEEL] = gdk_x11_get_xatom_by_name_for_display (display, "Abs Wheel"); initialized = TRUE; } for (i = GDK_AXIS_IGNORE; i < GDK_AXIS_LAST; i++) { if (label_atoms[i] == valuator_label) { use = i; break; } } if (valuator_label != None) label = gdk_x11_xatom_to_atom_for_display (display, valuator_label); else label = NULL; _gdk_device_add_axis (device, label, use, min, max, resolution); GDK_DISPLAY_NOTE (display, INPUT, g_message ("\n\taxis: %s %s", (const char *)label, use == GDK_AXIS_IGNORE ? "(ignored)" : "(used)")); } static void translate_device_classes (GdkDisplay *display, GdkDevice *device, XIAnyClassInfo **classes, guint n_classes) { gint i; g_object_freeze_notify (G_OBJECT (device)); for (i = 0; i < n_classes; i++) { XIAnyClassInfo *class_info = classes[i]; switch (class_info->type) { case XIKeyClass: { XIKeyClassInfo *key_info = (XIKeyClassInfo *) class_info; gint j; _gdk_device_set_keys (device, key_info->num_keycodes); for (j = 0; j < key_info->num_keycodes; j++) gdk_device_set_key (device, j, key_info->keycodes[j], 0); } break; case XIValuatorClass: { XIValuatorClassInfo *valuator_info = (XIValuatorClassInfo *) class_info; translate_valuator_class (display, device, valuator_info->label, valuator_info->min, valuator_info->max, valuator_info->resolution); } break; #ifdef XINPUT_2_2 case XIScrollClass: { XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info; GdkScrollDirection direction; if (scroll_info->scroll_type == XIScrollTypeVertical) direction = GDK_SCROLL_DOWN; else direction = GDK_SCROLL_RIGHT; GDK_DISPLAY_NOTE (display, INPUT, g_message ("\n\tscroll valuator %d: %s, increment %f", scroll_info->number, scroll_info->scroll_type == XIScrollTypeVertical ? "vertical" : "horizontal", scroll_info->increment)); _gdk_x11_device_xi2_add_scroll_valuator (GDK_X11_DEVICE_XI2 (device), scroll_info->number, direction, scroll_info->increment); } #endif /* XINPUT_2_2 */ default: /* Ignore */ break; } } g_object_thaw_notify (G_OBJECT (device)); } static gboolean is_touch_device (XIAnyClassInfo **classes, guint n_classes, GdkInputSource *device_type, gint *num_touches) { #ifdef XINPUT_2_2 guint 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 = GDK_SOURCE_TOUCHSCREEN; else if (class->mode == XIDependentTouch) *device_type = GDK_SOURCE_TOUCHPAD; else continue; *num_touches = class->num_touches; return TRUE; } } #endif return FALSE; } static gboolean has_abs_axes (GdkDisplay *display, XIAnyClassInfo **classes, guint n_classes) { gboolean has_x = FALSE, has_y = FALSE; Atom abs_x, abs_y; guint i; abs_x = gdk_x11_get_xatom_by_name_for_display (display, "Abs X"); abs_y = gdk_x11_get_xatom_by_name_for_display (display, "Abs Y"); for (i = 0; i < n_classes; i++) { XIValuatorClassInfo *class = (XIValuatorClassInfo *) classes[i]; if (class->type != XIValuatorClass) continue; if (class->mode != XIModeAbsolute) continue; if (class->label == abs_x) has_x = TRUE; else if (class->label == abs_y) has_y = TRUE; if (has_x && has_y) break; } return (has_x && has_y); } static gboolean get_device_ids (GdkDisplay *display, XIDeviceInfo *info, gchar **vendor_id, gchar **product_id) { gulong nitems, bytes_after; guint32 *data; int rc, format; Atom prop, type; gdk_x11_display_error_trap_push (display); prop = XInternAtom (GDK_DISPLAY_XDISPLAY (display), "Device Product ID", True); if (prop == None) { gdk_x11_display_error_trap_pop_ignored (display); return 0; } rc = XIGetProperty (GDK_DISPLAY_XDISPLAY (display), info->deviceid, prop, 0, 2, False, XA_INTEGER, &type, &format, &nitems, &bytes_after, (guchar **) &data); gdk_x11_display_error_trap_pop_ignored (display); if (rc != Success || type != XA_INTEGER || format != 32 || nitems != 2) 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 gboolean has_bool_prop (GdkDisplay *display, XIDeviceInfo *info, const char *prop_name) { gulong nitems, bytes_after; guint32 *data; int rc, format; Atom type; gdk_x11_display_error_trap_push (display); rc = XIGetProperty (GDK_DISPLAY_XDISPLAY (display), info->deviceid, gdk_x11_get_xatom_by_name_for_display (display, prop_name), 0, 1, False, XA_INTEGER, &type, &format, &nitems, &bytes_after, (guchar **) &data); gdk_x11_display_error_trap_pop_ignored (display); if (rc != Success || type != XA_INTEGER || format != 8 || nitems != 1) return FALSE; XFree (data); return TRUE; } static gboolean is_touchpad_device (GdkDisplay *display, XIDeviceInfo *info) { /* * Touchpads are heuristically recognized via XI properties that the various * Xorg drivers expose: * libinput: libinput Tapping Enabled * synaptics: Synaptics Off * cmt: Raw Touch Passthrough */ return has_bool_prop (display, info, "libinput Tapping Enabled") || has_bool_prop (display, info, "Synaptics Off") || has_bool_prop (display, info, "Raw Touch Passthrough"); } static GdkDevice * create_device (GdkX11DeviceManagerXI2 *device_manager, GdkDisplay *display, XIDeviceInfo *dev) { GdkInputSource input_source; GdkInputSource touch_source; GdkDeviceType type; GdkDevice *device; GdkInputMode mode; gint num_touches = 0; gchar *vendor_id = NULL, *product_id = NULL; if (dev->use == XIMasterKeyboard || dev->use == XISlaveKeyboard) input_source = GDK_SOURCE_KEYBOARD; else if (is_touchpad_device (display, dev)) input_source = GDK_SOURCE_TOUCHPAD; else if (dev->use == XISlavePointer && is_touch_device (dev->classes, dev->num_classes, &touch_source, &num_touches)) input_source = touch_source; else { gchar *tmp_name; tmp_name = g_ascii_strdown (dev->name, -1); if (strstr (tmp_name, "eraser")) input_source = GDK_SOURCE_ERASER; else if (strstr (tmp_name, "cursor")) input_source = GDK_SOURCE_CURSOR; else if (strstr (tmp_name, " pad")) input_source = GDK_SOURCE_TABLET_PAD; else if (strstr (tmp_name, "wacom") || strstr (tmp_name, "pen")) input_source = GDK_SOURCE_PEN; else if (!strstr (tmp_name, "mouse") && !strstr (tmp_name, "pointer") && !strstr (tmp_name, "qemu usb tablet") && !strstr (tmp_name, "spice vdagent tablet") && !strstr (tmp_name, "virtualbox usb tablet") && has_abs_axes (display, dev->classes, dev->num_classes)) input_source = GDK_SOURCE_TOUCHSCREEN; else if (strstr (tmp_name, "trackpoint") || strstr (tmp_name, "dualpoint stick")) input_source = GDK_SOURCE_TRACKPOINT; else input_source = GDK_SOURCE_MOUSE; g_free (tmp_name); } switch (dev->use) { case XIMasterKeyboard: case XIMasterPointer: type = GDK_DEVICE_TYPE_MASTER; mode = GDK_MODE_SCREEN; break; case XISlaveKeyboard: case XISlavePointer: type = GDK_DEVICE_TYPE_SLAVE; mode = GDK_MODE_DISABLED; break; case XIFloatingSlave: default: type = GDK_DEVICE_TYPE_FLOATING; mode = GDK_MODE_DISABLED; break; } GDK_DISPLAY_NOTE (display, INPUT, ({ const gchar *type_names[] = { "master", "slave", "floating" }; const gchar *source_names[] = { "mouse", "pen", "eraser", "cursor", "keyboard", "direct touch", "indirect touch", "trackpoint", "pad" }; const gchar *mode_names[] = { "disabled", "screen", "window" }; g_message ("input device:\n\tname: %s\n\ttype: %s\n\tsource: %s\n\tmode: %s\n\thas cursor: %d\n\ttouches: %d", dev->name, type_names[type], source_names[input_source], mode_names[mode], dev->use == XIMasterPointer, num_touches); })); if (dev->use != XIMasterKeyboard && dev->use != XIMasterPointer) get_device_ids (display, dev, &vendor_id, &product_id); device = g_object_new (GDK_TYPE_X11_DEVICE_XI2, "name", dev->name, "type", type, "input-source", input_source, "input-mode", mode, "has-cursor", (dev->use == XIMasterPointer), "display", display, "device-id", dev->deviceid, "vendor-id", vendor_id, "product-id", product_id, "num-touches", num_touches, NULL); translate_device_classes (display, device, dev->classes, dev->num_classes); g_free (vendor_id); g_free (product_id); return device; } static void ensure_seat_for_device_pair (GdkX11DeviceManagerXI2 *device_manager, GdkDevice *device1, GdkDevice *device2) { GdkDevice *pointer, *keyboard; GdkDisplay *display; GdkSeat *seat; display = GDK_X11_DEVICE_MANAGER_CORE (device_manager)->display; seat = gdk_device_get_seat (device1); if (!seat) { if (gdk_device_get_source (device1) == GDK_SOURCE_KEYBOARD) { keyboard = device1; pointer = device2; } else { pointer = device1; keyboard = device2; } seat = gdk_seat_default_new_for_master_pair (pointer, keyboard); gdk_display_add_seat (display, seat); g_object_unref (seat); } } static GdkDevice * add_device (GdkX11DeviceManagerXI2 *device_manager, XIDeviceInfo *dev, gboolean emit_signal) { GdkDisplay *display; GdkDevice *device; display = GDK_X11_DEVICE_MANAGER_CORE (device_manager)->display; device = create_device (device_manager, display, dev); g_hash_table_replace (device_manager->id_table, GINT_TO_POINTER (dev->deviceid), g_object_ref (device)); device_manager->devices = g_list_append (device_manager->devices, device); if (emit_signal) { if (dev->use == XISlavePointer || dev->use == XISlaveKeyboard) { GdkDevice *master; GdkSeat *seat; /* The device manager is already constructed, then * keep the hierarchy coherent for the added device. */ master = g_hash_table_lookup (device_manager->id_table, GINT_TO_POINTER (dev->attachment)); _gdk_device_set_associated_device (device, master); _gdk_device_add_slave (master, device); seat = gdk_device_get_seat (master); gdk_seat_default_add_slave (GDK_SEAT_DEFAULT (seat), device); } else if (dev->use == XIMasterPointer || dev->use == XIMasterKeyboard) { GdkDevice *relative; relative = g_hash_table_lookup (device_manager->id_table, GINT_TO_POINTER (dev->attachment)); if (relative) { _gdk_device_set_associated_device (device, relative); _gdk_device_set_associated_device (relative, device); ensure_seat_for_device_pair (device_manager, device, relative); } } } return device; } static void detach_from_seat (GdkDevice *device) { GdkSeat *seat = gdk_device_get_seat (device); if (!seat) return; if (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER) gdk_display_remove_seat (gdk_device_get_display (device), seat); else if (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_SLAVE) gdk_seat_default_remove_slave (GDK_SEAT_DEFAULT (seat), device); } static void remove_device (GdkX11DeviceManagerXI2 *device_manager, gint device_id) { GdkDevice *device; device = g_hash_table_lookup (device_manager->id_table, GINT_TO_POINTER (device_id)); if (device) { detach_from_seat (device); device_manager->devices = g_list_remove (device_manager->devices, device); g_object_run_dispose (G_OBJECT (device)); g_hash_table_remove (device_manager->id_table, GINT_TO_POINTER (device_id)); } } static void relate_masters (gpointer key, gpointer value, gpointer user_data) { GdkX11DeviceManagerXI2 *device_manager; GdkDevice *device, *relative; device_manager = user_data; device = g_hash_table_lookup (device_manager->id_table, key); relative = g_hash_table_lookup (device_manager->id_table, value); _gdk_device_set_associated_device (device, relative); _gdk_device_set_associated_device (relative, device); ensure_seat_for_device_pair (device_manager, device, relative); } static void relate_slaves (gpointer key, gpointer value, gpointer user_data) { GdkX11DeviceManagerXI2 *device_manager; GdkDevice *slave, *master; GdkSeat *seat; device_manager = user_data; slave = g_hash_table_lookup (device_manager->id_table, key); master = g_hash_table_lookup (device_manager->id_table, value); _gdk_device_set_associated_device (slave, master); _gdk_device_add_slave (master, slave); seat = gdk_device_get_seat (master); gdk_seat_default_add_slave (GDK_SEAT_DEFAULT (seat), slave); } static void gdk_x11_device_manager_xi2_constructed (GObject *object) { GdkX11DeviceManagerXI2 *device_manager; GdkDisplay *display; GHashTable *masters, *slaves; Display *xdisplay; XIDeviceInfo *info, *dev; int ndevices, i; XIEventMask event_mask; unsigned char mask[2] = { 0 }; G_OBJECT_CLASS (gdk_x11_device_manager_xi2_parent_class)->constructed (object); device_manager = GDK_X11_DEVICE_MANAGER_XI2 (object); display = GDK_X11_DEVICE_MANAGER_CORE (device_manager)->display; xdisplay = GDK_DISPLAY_XDISPLAY (display); g_assert (device_manager->major == 2); masters = g_hash_table_new (NULL, NULL); slaves = g_hash_table_new (NULL, NULL); info = XIQueryDevice (xdisplay, XIAllDevices, &ndevices); /* Initialize devices list */ for (i = 0; i < ndevices; i++) { dev = &info[i]; if (!dev->enabled) continue; add_device (device_manager, dev, FALSE); if (dev->use == XIMasterPointer || dev->use == XIMasterKeyboard) { g_hash_table_insert (masters, GINT_TO_POINTER (dev->deviceid), GINT_TO_POINTER (dev->attachment)); } else if (dev->use == XISlavePointer || dev->use == XISlaveKeyboard) { g_hash_table_insert (slaves, GINT_TO_POINTER (dev->deviceid), GINT_TO_POINTER (dev->attachment)); } } XIFreeDeviceInfo (info); /* Stablish relationships between devices */ g_hash_table_foreach (masters, relate_masters, object); g_hash_table_destroy (masters); g_hash_table_foreach (slaves, relate_slaves, object); g_hash_table_destroy (slaves); /* Connect to hierarchy change events */ 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; _gdk_x11_device_manager_xi2_select_events (device_manager, GDK_DISPLAY_XROOTWIN (display), &event_mask); } static void gdk_x11_device_manager_xi2_dispose (GObject *object) { GdkX11DeviceManagerXI2 *device_manager; device_manager = GDK_X11_DEVICE_MANAGER_XI2 (object); g_list_free_full (device_manager->devices, g_object_unref); device_manager->devices = NULL; if (device_manager->id_table) { g_hash_table_destroy (device_manager->id_table); device_manager->id_table = NULL; } G_OBJECT_CLASS (gdk_x11_device_manager_xi2_parent_class)->dispose (object); } static GdkDevice * gdk_x11_device_manager_xi2_get_client_pointer (GdkX11DeviceManagerXI2 *device_manager) { GdkDisplay *display; int device_id; display = GDK_X11_DEVICE_MANAGER_CORE (device_manager)->display; XIGetClientPointer (GDK_DISPLAY_XDISPLAY (display), None, &device_id); return g_hash_table_lookup (device_manager->id_table, GINT_TO_POINTER (device_id)); } static void gdk_x11_device_manager_xi2_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GdkX11DeviceManagerXI2 *device_manager; device_manager = GDK_X11_DEVICE_MANAGER_XI2 (object); switch (prop_id) { case PROP_OPCODE: device_manager->opcode = g_value_get_int (value); break; case PROP_MAJOR: device_manager->major = g_value_get_int (value); break; case PROP_MINOR: device_manager->minor = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gdk_x11_device_manager_xi2_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GdkX11DeviceManagerXI2 *device_manager; device_manager = GDK_X11_DEVICE_MANAGER_XI2 (object); switch (prop_id) { case PROP_OPCODE: g_value_set_int (value, device_manager->opcode); break; case PROP_MAJOR: g_value_set_int (value, device_manager->major); break; case PROP_MINOR: g_value_set_int (value, device_manager->minor); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gdk_x11_device_manager_xi2_event_translator_init (GdkEventTranslatorIface *iface) { iface->translate_event = gdk_x11_device_manager_xi2_translate_event; iface->get_handled_events = gdk_x11_device_manager_xi2_get_handled_events; iface->select_surface_events = gdk_x11_device_manager_xi2_select_surface_events; iface->get_surface = gdk_x11_device_manager_xi2_get_surface; } static void handle_hierarchy_changed (GdkX11DeviceManagerXI2 *device_manager, XIHierarchyEvent *ev) { GdkDisplay *display; Display *xdisplay; XIDeviceInfo *info; int ndevices; gint i; display = GDK_X11_DEVICE_MANAGER_CORE (device_manager)->display; xdisplay = GDK_DISPLAY_XDISPLAY (display); for (i = 0; i < ev->num_info; i++) { if (ev->info[i].flags & XIDeviceEnabled) { gdk_x11_display_error_trap_push (display); info = XIQueryDevice (xdisplay, ev->info[i].deviceid, &ndevices); gdk_x11_display_error_trap_pop_ignored (display); if (info) { add_device (device_manager, &info[0], TRUE); XIFreeDeviceInfo (info); } } else if (ev->info[i].flags & XIDeviceDisabled) remove_device (device_manager, ev->info[i].deviceid); else if (ev->info[i].flags & XISlaveAttached || ev->info[i].flags & XISlaveDetached) { GdkDevice *master, *slave; GdkSeat *seat; slave = g_hash_table_lookup (device_manager->id_table, GINT_TO_POINTER (ev->info[i].deviceid)); if (!slave) continue; /* Remove old master info */ master = gdk_device_get_associated_device (slave); if (master) { _gdk_device_remove_slave (master, slave); _gdk_device_set_associated_device (slave, NULL); seat = gdk_device_get_seat (master); gdk_seat_default_remove_slave (GDK_SEAT_DEFAULT (seat), slave); } /* Add new master if it's an attachment event */ if (ev->info[i].flags & XISlaveAttached) { gdk_x11_display_error_trap_push (display); info = XIQueryDevice (xdisplay, ev->info[i].deviceid, &ndevices); gdk_x11_display_error_trap_pop_ignored (display); if (info) { master = g_hash_table_lookup (device_manager->id_table, GINT_TO_POINTER (info->attachment)); XIFreeDeviceInfo (info); } if (master) { _gdk_device_set_associated_device (slave, master); _gdk_device_add_slave (master, slave); seat = gdk_device_get_seat (master); gdk_seat_default_add_slave (GDK_SEAT_DEFAULT (seat), slave); } } } } } static void handle_device_changed (GdkX11DeviceManagerXI2 *device_manager, XIDeviceChangedEvent *ev) { GdkDisplay *display; GdkDevice *device, *source_device; display = GDK_X11_DEVICE_MANAGER_CORE (device_manager)->display; device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (ev->deviceid)); source_device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (ev->sourceid)); if (device) { _gdk_device_reset_axes (device); _gdk_device_xi2_unset_scroll_valuators ((GdkX11DeviceXI2 *) device); gdk_x11_device_xi2_store_axes (GDK_X11_DEVICE_XI2 (device), NULL, 0); translate_device_classes (display, device, ev->classes, ev->num_classes); g_signal_emit_by_name (G_OBJECT (device), "changed"); } if (source_device) _gdk_device_xi2_reset_scroll_valuators (GDK_X11_DEVICE_XI2 (source_device)); } static gboolean device_get_tool_serial_and_id (GdkDevice *device, guint *serial_id, guint *id) { GdkDisplay *display; gulong nitems, bytes_after; guint32 *data; int rc, format; Atom type; display = gdk_device_get_display (device); gdk_x11_display_error_trap_push (display); rc = XIGetProperty (GDK_DISPLAY_XDISPLAY (display), gdk_x11_device_get_id (device), gdk_x11_get_xatom_by_name_for_display (display, "Wacom Serial IDs"), 0, 5, False, XA_INTEGER, &type, &format, &nitems, &bytes_after, (guchar **) &data); gdk_x11_display_error_trap_pop_ignored (display); if (rc != Success) return FALSE; if (type == XA_INTEGER && format == 32) { if (nitems >= 4) *serial_id = data[3]; if (nitems >= 5) *id = data[4]; } XFree (data); return TRUE; } static GdkDeviceToolType device_get_tool_type (GdkDevice *device) { GdkDisplay *display; gulong nitems, bytes_after; guint32 *data; int rc, format; Atom type; Atom device_type; Atom types[N_WACOM_TYPE_ATOMS]; GdkDeviceToolType tool_type = GDK_DEVICE_TOOL_TYPE_UNKNOWN; display = gdk_device_get_display (device); gdk_x11_display_error_trap_push (display); rc = XIGetProperty (GDK_DISPLAY_XDISPLAY (display), gdk_x11_device_get_id (device), gdk_x11_get_xatom_by_name_for_display (display, "Wacom Tool Type"), 0, 1, False, XA_ATOM, &type, &format, &nitems, &bytes_after, (guchar **) &data); gdk_x11_display_error_trap_pop_ignored (display); if (rc != Success) return GDK_DEVICE_TOOL_TYPE_UNKNOWN; if (type != XA_ATOM || format != 32 || nitems != 1) { XFree (data); return GDK_DEVICE_TOOL_TYPE_UNKNOWN; } device_type = *data; XFree (data); if (device_type == 0) return GDK_DEVICE_TOOL_TYPE_UNKNOWN; gdk_x11_display_error_trap_push (display); rc = XInternAtoms (GDK_DISPLAY_XDISPLAY (display), (char **) wacom_type_atoms, N_WACOM_TYPE_ATOMS, False, types); gdk_x11_display_error_trap_pop_ignored (display); if (rc == 0) return GDK_DEVICE_TOOL_TYPE_UNKNOWN; if (device_type == types[WACOM_TYPE_STYLUS]) tool_type = GDK_DEVICE_TOOL_TYPE_PEN; else if (device_type == types[WACOM_TYPE_CURSOR]) tool_type = GDK_DEVICE_TOOL_TYPE_MOUSE; else if (device_type == types[WACOM_TYPE_ERASER]) tool_type = GDK_DEVICE_TOOL_TYPE_ERASER; else if (device_type == types[WACOM_TYPE_TOUCH]) tool_type = GDK_DEVICE_TOOL_TYPE_UNKNOWN; return tool_type; } static void handle_property_change (GdkX11DeviceManagerXI2 *device_manager, XIPropertyEvent *ev) { GdkDevice *device; device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (ev->deviceid)); if (device != NULL && ev->property == gdk_x11_get_xatom_by_name_for_display (gdk_device_get_display (device), "Wacom Serial IDs")) { GdkDeviceTool *tool = NULL; guint serial_id = 0, tool_id = 0; GdkSeat *seat; if (ev->what != XIPropertyDeleted && device_get_tool_serial_and_id (device, &serial_id, &tool_id)) { seat = gdk_device_get_seat (device); tool = gdk_seat_get_tool (seat, serial_id, tool_id); if (!tool && serial_id > 0) { GdkDeviceToolType tool_type; tool_type = device_get_tool_type (device); if (tool_type != GDK_DEVICE_TOOL_TYPE_UNKNOWN) { tool = gdk_device_tool_new (serial_id, tool_id, tool_type, 0); gdk_seat_default_add_tool (GDK_SEAT_DEFAULT (seat), tool); } } } gdk_device_update_tool (device, tool); } } static GdkCrossingMode translate_crossing_mode (gint mode) { switch (mode) { case XINotifyNormal: return GDK_CROSSING_NORMAL; case XINotifyGrab: case XINotifyPassiveGrab: return GDK_CROSSING_GRAB; case XINotifyUngrab: case XINotifyPassiveUngrab: return GDK_CROSSING_UNGRAB; case XINotifyWhileGrabbed: /* Fall through, unexpected in pointer crossing events */ default: g_assert_not_reached (); return GDK_CROSSING_NORMAL; } } static GdkNotifyType translate_notify_type (gint detail) { switch (detail) { case NotifyInferior: return GDK_NOTIFY_INFERIOR; case NotifyAncestor: return GDK_NOTIFY_ANCESTOR; case NotifyVirtual: return GDK_NOTIFY_VIRTUAL; case NotifyNonlinear: return GDK_NOTIFY_NONLINEAR; case NotifyNonlinearVirtual: return GDK_NOTIFY_NONLINEAR_VIRTUAL; default: g_assert_not_reached (); return GDK_NOTIFY_UNKNOWN; } } static void set_user_time (GdkEvent *event) { GdkSurface *surface; guint32 time; surface = gdk_surface_get_toplevel (event->any.surface); g_return_if_fail (GDK_IS_SURFACE (surface)); time = gdk_event_get_time (event); /* If an event doesn't have a valid timestamp, we shouldn't use it * to update the latest user interaction time. */ if (time != GDK_CURRENT_TIME) gdk_x11_surface_set_user_time (surface, time); } static gdouble * translate_axes (GdkDevice *device, gdouble x, gdouble y, GdkSurface *surface, XIValuatorState *valuators) { guint n_axes, i; gdouble *axes; gdouble *vals; n_axes = gdk_device_get_n_axes (device); axes = g_new0 (gdouble, n_axes); vals = valuators->values; for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++) { GdkAxisUse use; gdouble val; if (!XIMaskIsSet (valuators->mask, i)) { axes[i] = gdk_x11_device_xi2_get_last_axis_value (GDK_X11_DEVICE_XI2 (device), i); continue; } use = gdk_device_get_axis_use (device, i); val = *vals++; switch ((guint) use) { case GDK_AXIS_X: case GDK_AXIS_Y: if (gdk_device_get_mode (device) == GDK_MODE_SURFACE) _gdk_device_translate_surface_coord (device, surface, i, val, &axes[i]); else { if (use == GDK_AXIS_X) axes[i] = x; else axes[i] = y; } break; default: _gdk_device_translate_axis (device, i, val, &axes[i]); break; } } gdk_x11_device_xi2_store_axes (GDK_X11_DEVICE_XI2 (device), axes, n_axes); return axes; } static gboolean is_parent_of (GdkSurface *parent, GdkSurface *child) { GdkSurface *w; w = child; while (w != NULL) { if (w == parent) return TRUE; w = gdk_surface_get_parent (w); } return FALSE; } static gboolean get_event_surface (GdkEventTranslator *translator, XIEvent *ev, GdkSurface **surface_p) { GdkDisplay *display; GdkSurface *surface = NULL; gboolean should_have_window = TRUE; display = GDK_X11_DEVICE_MANAGER_CORE (translator)->display; switch (ev->evtype) { case XI_KeyPress: case XI_KeyRelease: case XI_ButtonPress: case XI_ButtonRelease: case XI_Motion: #ifdef XINPUT_2_2 case XI_TouchUpdate: case XI_TouchBegin: case XI_TouchEnd: #endif /* XINPUT_2_2 */ { XIDeviceEvent *xev = (XIDeviceEvent *) ev; surface = gdk_x11_surface_lookup_for_display (display, xev->event); /* Apply keyboard grabs to non-native windows */ if (ev->evtype == XI_KeyPress || ev->evtype == XI_KeyRelease) { GdkDeviceGrabInfo *info; GdkDevice *device; gulong serial; device = g_hash_table_lookup (GDK_X11_DEVICE_MANAGER_XI2 (translator)->id_table, GUINT_TO_POINTER (((XIDeviceEvent *) ev)->deviceid)); serial = _gdk_display_get_next_serial (display); info = _gdk_display_has_device_grab (display, device, serial); if (info && (!is_parent_of (info->surface, surface) || !info->owner_events)) { /* Report key event against grab surface */ surface = info->surface; } } } break; case XI_Enter: case XI_Leave: case XI_FocusIn: case XI_FocusOut: { XIEnterEvent *xev = (XIEnterEvent *) ev; surface = gdk_x11_surface_lookup_for_display (display, xev->event); } break; default: should_have_window = FALSE; break; } *surface_p = surface; if (should_have_window && !surface) return FALSE; return TRUE; } static gboolean gdk_x11_device_manager_xi2_translate_core_event (GdkEventTranslator *translator, GdkDisplay *display, GdkEvent *event, const XEvent *xevent) { GdkEventTranslatorIface *parent_iface; gboolean keyboard = FALSE; GdkDevice *device; if ((xevent->type == KeyPress || xevent->type == KeyRelease) && (xevent->xkey.keycode == 0 || xevent->xkey.serial == 0)) { /* The X input methods (when triggered via XFilterEvent) * generate a core key press event with keycode 0 to signal the * end of a key sequence. We use the core translate_event * implementation to translate this event. * * Other less educated IM modules like to filter every keypress, * only to have these replaced by their own homegrown events, * these events oddly have serial=0, so we try to catch these. * * This is just a bandaid fix to keep xim working with a single * keyboard until XFilterEvent learns about XI2. */ keyboard = TRUE; } else if (xevent->xany.send_event) { /* If another process sends us core events, process them; we * assume that it won't send us redundant core and XI2 events. * (At the moment, it's not possible to send XI2 events anyway. * In the future, an app that was trying to decide whether to * send core or XI2 events could look at the event mask on the * surface to see which kind we are listening to.) */ switch (xevent->type) { case KeyPress: case KeyRelease: case FocusIn: case FocusOut: keyboard = TRUE; break; case ButtonPress: case ButtonRelease: case MotionNotify: case EnterNotify: case LeaveNotify: break; default: return FALSE; } } else return FALSE; parent_iface = g_type_interface_peek_parent (GDK_EVENT_TRANSLATOR_GET_IFACE (translator)); if (!parent_iface->translate_event (translator, display, event, xevent)) return FALSE; /* The core device manager sets a core device on the event. * We need to override that with an XI2 device, since we are * using XI2. */ device = gdk_x11_device_manager_xi2_get_client_pointer ((GdkX11DeviceManagerXI2 *)translator); if (keyboard) device = gdk_device_get_associated_device (device); gdk_event_set_device (event, device); return TRUE; } static gboolean scroll_valuators_changed (GdkX11DeviceXI2 *device, XIValuatorState *valuators, gdouble *dx, gdouble *dy) { gboolean has_scroll_valuators = FALSE; GdkScrollDirection direction; guint n_axes, i, n_val; gdouble *vals; n_axes = gdk_device_get_n_axes (GDK_DEVICE (device)); vals = valuators->values; *dx = *dy = 0; n_val = 0; for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++) { gdouble delta; if (!XIMaskIsSet (valuators->mask, i)) continue; if (_gdk_x11_device_xi2_get_scroll_delta (device, i, vals[n_val], &direction, &delta)) { has_scroll_valuators = TRUE; if (direction == GDK_SCROLL_UP || direction == GDK_SCROLL_DOWN) *dy = delta; else *dx = delta; } n_val++; } return has_scroll_valuators; } static gboolean gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator, GdkDisplay *display, GdkEvent *event, const XEvent *xevent) { GdkX11DeviceManagerXI2 *device_manager; const XGenericEventCookie *cookie; GdkDevice *device, *source_device; gboolean return_val = TRUE; GdkSurface *surface; GdkSurfaceImplX11 *impl; int scale; XIEvent *ev; device_manager = (GdkX11DeviceManagerXI2 *) translator; cookie = &xevent->xcookie; if (xevent->type != GenericEvent) return gdk_x11_device_manager_xi2_translate_core_event (translator, display, event, xevent); else if (cookie->extension != device_manager->opcode) return FALSE; ev = (XIEvent *) cookie->data; if (!ev) return FALSE; if (!get_event_surface (translator, ev, &surface)) return FALSE; if (surface && GDK_SURFACE_DESTROYED (surface)) return FALSE; scale = 1; if (surface) { impl = GDK_SURFACE_IMPL_X11 (surface->impl); scale = impl->surface_scale; } if (ev->evtype == XI_Motion || ev->evtype == XI_ButtonRelease) { if (_gdk_x11_moveresize_handle_event (xevent)) return FALSE; } switch (ev->evtype) { case XI_HierarchyChanged: handle_hierarchy_changed (device_manager, (XIHierarchyEvent *) ev); return_val = FALSE; break; case XI_DeviceChanged: handle_device_changed (device_manager, (XIDeviceChangedEvent *) ev); return_val = FALSE; break; case XI_PropertyEvent: handle_property_change (device_manager, (XIPropertyEvent *) ev); return_val = FALSE; break; case XI_KeyPress: case XI_KeyRelease: { XIDeviceEvent *xev = (XIDeviceEvent *) ev; GdkKeymap *keymap = gdk_display_get_keymap (display); GdkModifierType consumed, state; GDK_DISPLAY_NOTE (display, EVENTS, g_message ("key %s:\twindow %ld\n" "\tdevice:%u\n" "\tsource device:%u\n" "\tkey number: %u\n", (ev->evtype == XI_KeyPress) ? "press" : "release", xev->event, xev->deviceid, xev->sourceid, xev->detail)); event->any.type = xev->evtype == XI_KeyPress ? GDK_KEY_PRESS : GDK_KEY_RELEASE; event->any.surface = surface; event->key.time = xev->time; event->key.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); event->key.group = xev->group.effective; event->key.hardware_keycode = xev->detail; gdk_event_set_scancode (event, xev->detail); event->key.is_modifier = gdk_x11_keymap_key_is_modifier (keymap, event->key.hardware_keycode); device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->deviceid)); gdk_event_set_device (event, device); source_device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->sourceid)); gdk_event_set_source_device (event, source_device); event->key.keyval = GDK_KEY_VoidSymbol; gdk_keymap_translate_keyboard_state (keymap, event->key.hardware_keycode, event->key.state, event->key.group, &event->key.keyval, NULL, NULL, &consumed); state = event->key.state & ~consumed; _gdk_x11_keymap_add_virt_mods (keymap, &state); event->key.state |= state; if (ev->evtype == XI_KeyPress) set_user_time (event); /* FIXME: emulate autorepeat on key * release? XI2 seems attached to Xkb. */ } break; case XI_ButtonPress: case XI_ButtonRelease: { XIDeviceEvent *xev = (XIDeviceEvent *) ev; GDK_DISPLAY_NOTE (display, EVENTS, g_message ("button %s:\twindow %ld\n" "\tdevice:%u\n" "\tsource device:%u\n" "\tbutton number: %u\n" "\tx,y: %.2f %.2f", (ev->evtype == XI_ButtonPress) ? "press" : "release", xev->event, xev->deviceid, xev->sourceid, xev->detail, xev->event_x, xev->event_y)); #ifdef XINPUT_2_2 if (xev->flags & XIPointerEmulated) return FALSE; #endif if (ev->evtype == XI_ButtonRelease && (xev->detail >= 4 && xev->detail <= 7)) return FALSE; else if (ev->evtype == XI_ButtonPress && (xev->detail >= 4 && xev->detail <= 7)) { /* Button presses of button 4-7 are scroll events */ event->any.type = GDK_SCROLL; if (xev->detail == 4) event->scroll.direction = GDK_SCROLL_UP; else if (xev->detail == 5) event->scroll.direction = GDK_SCROLL_DOWN; else if (xev->detail == 6) event->scroll.direction = GDK_SCROLL_LEFT; else event->scroll.direction = GDK_SCROLL_RIGHT; event->any.surface = surface; event->scroll.time = xev->time; event->scroll.x = (gdouble) xev->event_x / scale; event->scroll.y = (gdouble) xev->event_y / scale; event->scroll.x_root = (gdouble) xev->root_x / scale; event->scroll.y_root = (gdouble) xev->root_y / scale; event->scroll.delta_x = 0; event->scroll.delta_y = 0; device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->deviceid)); gdk_event_set_device (event, device); source_device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->sourceid)); gdk_event_set_source_device (event, source_device); event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); } else { event->any.type = (ev->evtype == XI_ButtonPress) ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE; event->any.surface = surface; event->button.time = xev->time; event->button.x = (gdouble) xev->event_x / scale; event->button.y = (gdouble) xev->event_y / scale; event->button.x_root = (gdouble) xev->root_x / scale; event->button.y_root = (gdouble) xev->root_y / scale; device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->deviceid)); gdk_event_set_device (event, device); source_device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->sourceid)); gdk_event_set_source_device (event, source_device); gdk_event_set_device_tool (event, source_device->last_tool); event->button.axes = translate_axes (device, event->button.x, event->button.y, event->any.surface, &xev->valuators); if (gdk_device_get_mode (device) == GDK_MODE_SURFACE) { /* Update event coordinates from axes */ gdk_device_get_axis (device, event->button.axes, GDK_AXIS_X, &event->button.x); gdk_device_get_axis (device, event->button.axes, GDK_AXIS_Y, &event->button.y); } event->button.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); event->button.button = xev->detail; } if (return_val == FALSE) break; gdk_event_set_display (event, display); if (ev->evtype == XI_ButtonPress) set_user_time (event); break; } case XI_Motion: { XIDeviceEvent *xev = (XIDeviceEvent *) ev; gdouble delta_x, delta_y; #ifdef XINPUT_2_2 if (xev->flags & XIPointerEmulated) return FALSE; #endif source_device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->sourceid)); device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->deviceid)); /* When scrolling, X might send events twice here; once with both the * device and the source device set to the physical device, and once * with the device set to the master device. * Since we are only interested in the latter, and * scroll_valuators_changed() updates the valuator cache for the * source device, we need to explicitly ignore the first event in * order to get the correct delta for the second. */ if (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_SLAVE && scroll_valuators_changed (GDK_X11_DEVICE_XI2 (source_device), &xev->valuators, &delta_x, &delta_y)) { event->any.type = GDK_SCROLL; event->scroll.direction = GDK_SCROLL_SMOOTH; if (delta_x == 0.0 && delta_y == 0.0) event->scroll.is_stop = TRUE; GDK_DISPLAY_NOTE (display, EVENTS, g_message ("smooth scroll: \n\tdevice: %u\n\tsource device: %u\n\twindow %ld\n\tdeltas: %f %f", xev->deviceid, xev->sourceid, xev->event, delta_x, delta_y)); event->any.surface = surface; event->scroll.time = xev->time; event->scroll.x = (gdouble) xev->event_x / scale; event->scroll.y = (gdouble) xev->event_y / scale; event->scroll.x_root = (gdouble) xev->root_x / scale; event->scroll.y_root = (gdouble) xev->root_y / scale; event->scroll.delta_x = delta_x; event->scroll.delta_y = delta_y; gdk_event_set_device (event, device); gdk_event_set_source_device (event, source_device); event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); break; } event->any.type = GDK_MOTION_NOTIFY; event->any.surface = surface; event->motion.time = xev->time; event->motion.x = (gdouble) xev->event_x / scale; event->motion.y = (gdouble) xev->event_y / scale; event->motion.x_root = (gdouble) xev->root_x / scale; event->motion.y_root = (gdouble) xev->root_y / scale; gdk_event_set_device (event, device); gdk_event_set_source_device (event, source_device); gdk_event_set_device_tool (event, source_device->last_tool); event->motion.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); event->motion.axes = translate_axes (device, event->motion.x, event->motion.y, event->any.surface, &xev->valuators); if (gdk_device_get_mode (device) == GDK_MODE_SURFACE) { /* Update event coordinates from axes */ gdk_device_get_axis (device, event->motion.axes, GDK_AXIS_X, &event->motion.x); gdk_device_get_axis (device, event->motion.axes, GDK_AXIS_Y, &event->motion.y); } } break; #ifdef XINPUT_2_2 case XI_TouchBegin: case XI_TouchEnd: { XIDeviceEvent *xev = (XIDeviceEvent *) ev; GDK_DISPLAY_NOTE (display, EVENTS, g_message ("touch %s:\twindow %ld\n\ttouch id: %u\n\tpointer emulating: %s", ev->evtype == XI_TouchBegin ? "begin" : "end", xev->event, xev->detail, xev->flags & XITouchEmulatingPointer ? "true" : "false")); if (ev->evtype == XI_TouchBegin) event->any.type = GDK_TOUCH_BEGIN; else if (ev->evtype == XI_TouchEnd) event->any.type = GDK_TOUCH_END; event->any.surface = surface; event->touch.time = xev->time; event->touch.x = (gdouble) xev->event_x / scale; event->touch.y = (gdouble) xev->event_y / scale; event->touch.x_root = (gdouble) xev->root_x / scale; event->touch.y_root = (gdouble) xev->root_y / scale; device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->deviceid)); gdk_event_set_device (event, device); source_device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->sourceid)); gdk_event_set_source_device (event, source_device); event->touch.axes = translate_axes (device, event->touch.x, event->touch.y, event->any.surface, &xev->valuators); if (gdk_device_get_mode (device) == GDK_MODE_SURFACE) { /* Update event coordinates from axes */ gdk_device_get_axis (device, event->touch.axes, GDK_AXIS_X, &event->touch.x); gdk_device_get_axis (device, event->touch.axes, GDK_AXIS_Y, &event->touch.y); } event->touch.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); if (ev->evtype == XI_TouchBegin) event->touch.state |= GDK_BUTTON1_MASK; event->touch.sequence = GUINT_TO_POINTER (xev->detail); if (xev->flags & XITouchEmulatingPointer) { event->touch.emulating_pointer = TRUE; gdk_event_set_pointer_emulated (event, TRUE); } if (return_val == FALSE) break; gdk_event_set_display (event, display); if (ev->evtype == XI_TouchBegin) set_user_time (event); } break; case XI_TouchUpdate: { XIDeviceEvent *xev = (XIDeviceEvent *) ev; GDK_DISPLAY_NOTE (display, EVENTS, g_message ("touch update:\twindow %ld\n\ttouch id: %u\n\tpointer emulating: %s", xev->event, xev->detail, xev->flags & XITouchEmulatingPointer ? "true" : "false")); event->any.surface = surface; event->touch.sequence = GUINT_TO_POINTER (xev->detail); event->any.type = GDK_TOUCH_UPDATE; event->touch.time = xev->time; event->touch.x = (gdouble) xev->event_x / scale; event->touch.y = (gdouble) xev->event_y / scale; event->touch.x_root = (gdouble) xev->root_x / scale; event->touch.y_root = (gdouble) xev->root_y / scale; device = g_hash_table_lookup (device_manager->id_table, GINT_TO_POINTER (xev->deviceid)); gdk_event_set_device (event, device); source_device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->sourceid)); gdk_event_set_source_device (event, source_device); event->touch.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); event->touch.state |= GDK_BUTTON1_MASK; if (xev->flags & XITouchEmulatingPointer) { event->touch.emulating_pointer = TRUE; gdk_event_set_pointer_emulated (event, TRUE); } event->touch.axes = translate_axes (device, event->touch.x, event->touch.y, event->any.surface, &xev->valuators); if (gdk_device_get_mode (device) == GDK_MODE_SURFACE) { /* Update event coordinates from axes */ gdk_device_get_axis (device, event->touch.axes, GDK_AXIS_X, &event->touch.x); gdk_device_get_axis (device, event->touch.axes, GDK_AXIS_Y, &event->touch.y); } } break; #endif /* XINPUT_2_2 */ case XI_Enter: case XI_Leave: { XIEnterEvent *xev = (XIEnterEvent *) ev; GDK_DISPLAY_NOTE (display, EVENTS, g_message ("%s notify:\twindow %ld\n\tsubwindow:%ld\n" "\tdevice: %u\n\tsource device: %u\n" "\tnotify type: %u\n\tcrossing mode: %u", (ev->evtype == XI_Enter) ? "enter" : "leave", xev->event, xev->child, xev->deviceid, xev->sourceid, xev->detail, xev->mode)); event->any.type = (ev->evtype == XI_Enter) ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY; event->crossing.x = (gdouble) xev->event_x / scale; event->crossing.y = (gdouble) xev->event_y / scale; event->crossing.x_root = (gdouble) xev->root_x / scale; event->crossing.y_root = (gdouble) xev->root_y / scale; event->crossing.time = xev->time; event->crossing.focus = xev->focus; event->any.surface = surface; event->crossing.child_surface = gdk_x11_surface_lookup_for_display (display, xev->child); device = g_hash_table_lookup (device_manager->id_table, GINT_TO_POINTER (xev->deviceid)); gdk_event_set_device (event, device); source_device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->sourceid)); gdk_event_set_source_device (event, source_device); if (ev->evtype == XI_Enter && xev->detail != XINotifyInferior && xev->mode != XINotifyPassiveUngrab && gdk_surface_get_surface_type (surface) == GDK_SURFACE_TOPLEVEL) { if (gdk_device_get_device_type (source_device) != GDK_DEVICE_TYPE_MASTER) _gdk_device_xi2_reset_scroll_valuators (GDK_X11_DEVICE_XI2 (source_device)); else { GList *slaves, *l; slaves = gdk_device_list_slave_devices (source_device); for (l = slaves; l; l = l->next) _gdk_device_xi2_reset_scroll_valuators (GDK_X11_DEVICE_XI2 (l->data)); g_list_free (slaves); } } event->crossing.mode = translate_crossing_mode (xev->mode); event->crossing.detail = translate_notify_type (xev->detail); event->crossing.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); } break; case XI_FocusIn: case XI_FocusOut: { if (surface) { XIEnterEvent *xev = (XIEnterEvent *) ev; device = g_hash_table_lookup (device_manager->id_table, GINT_TO_POINTER (xev->deviceid)); source_device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->sourceid)); _gdk_device_manager_core_handle_focus (surface, xev->event, device, source_device, (ev->evtype == XI_FocusIn) ? TRUE : FALSE, xev->detail, xev->mode); } return_val = FALSE; } break; default: return_val = FALSE; break; } event->any.send_event = cookie->send_event; if (return_val) { if (event->any.surface) g_object_ref (event->any.surface); if (((event->any.type == GDK_ENTER_NOTIFY) || (event->any.type == GDK_LEAVE_NOTIFY)) && (event->crossing.child_surface != NULL)) g_object_ref (event->crossing.child_surface); } else { /* Mark this event as having no resources to be freed */ event->any.surface = NULL; event->any.type = GDK_NOTHING; } return return_val; } static GdkEventMask gdk_x11_device_manager_xi2_get_handled_events (GdkEventTranslator *translator) { return (GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_FOCUS_CHANGE_MASK | GDK_TOUCH_MASK); } static void gdk_x11_device_manager_xi2_select_surface_events (GdkEventTranslator *translator, Window window, GdkEventMask evmask) { XIEventMask event_mask; event_mask.deviceid = XIAllMasterDevices; event_mask.mask = _gdk_x11_device_xi2_translate_event_mask (GDK_X11_DEVICE_MANAGER_XI2 (translator), evmask, &event_mask.mask_len); _gdk_x11_device_manager_xi2_select_events (GDK_X11_DEVICE_MANAGER_XI2 (translator), window, &event_mask); g_free (event_mask.mask); } static GdkSurface * gdk_x11_device_manager_xi2_get_surface (GdkEventTranslator *translator, const XEvent *xevent) { GdkX11DeviceManagerXI2 *device_manager; XIEvent *ev; GdkSurface *surface = NULL; device_manager = (GdkX11DeviceManagerXI2 *) translator; if (xevent->type != GenericEvent || xevent->xcookie.extension != device_manager->opcode) return NULL; ev = (XIEvent *) xevent->xcookie.data; if (!ev) return NULL; get_event_surface (translator, ev, &surface); return surface; } GdkDevice * _gdk_x11_device_manager_xi2_lookup (GdkX11DeviceManagerXI2 *device_manager_xi2, gint device_id) { return g_hash_table_lookup (device_manager_xi2->id_table, GINT_TO_POINTER (device_id)); }