/* 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, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "gdkx11devicemanager-xi2.h" #include "gdkx11device-xi2.h" #include "gdkdevicemanagerprivate-core.h" #include "gdkdeviceprivate.h" #include "gdkdisplayprivate.h" #include "gdkeventtranslator.h" #include "gdkprivate-x11.h" #include "gdkintl.h" #include "gdkkeysyms.h" #ifdef XINPUT_2 #include #include #include #include #endif /* XINPUT_2 */ struct _GdkX11DeviceManagerXI2 { GdkX11DeviceManagerCore parent_object; GHashTable *id_table; GList *devices; gint opcode; }; struct _GdkX11DeviceManagerXI2Class { GdkDeviceManagerClass 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)) #ifdef XINPUT_2 #define HAS_FOCUS(toplevel) ((toplevel)->has_focus || (toplevel)->has_pointer_focus) 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 GList * gdk_x11_device_manager_xi2_list_devices (GdkDeviceManager *device_manager, GdkDeviceType type); static GdkDevice * gdk_x11_device_manager_xi2_get_client_pointer (GdkDeviceManager *device_manager); static gboolean gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator, GdkDisplay *display, GdkEvent *event, XEvent *xevent); static GdkEventMask gdk_x11_device_manager_xi2_get_handled_events (GdkEventTranslator *translator); static void gdk_x11_device_manager_xi2_select_window_events (GdkEventTranslator *translator, Window window, GdkEventMask event_mask); static GdkWindow * gdk_x11_device_manager_xi2_get_window (GdkEventTranslator *translator, XEvent *xevent); enum { PROP_0, PROP_OPCODE }; static void gdk_x11_device_manager_xi2_class_init (GdkX11DeviceManagerXI2Class *klass) { GdkDeviceManagerClass *device_manager_class = GDK_DEVICE_MANAGER_CLASS (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; device_manager_class->list_devices = gdk_x11_device_manager_xi2_list_devices; device_manager_class->get_client_pointer = gdk_x11_device_manager_xi2_get_client_pointer; 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)); } 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 (GdkDeviceManager *device_manager, Window xwindow, XIEventMask *event_mask) { GdkDisplay *display; Display *xdisplay; display = gdk_device_manager_get_display (device_manager); xdisplay = GDK_DISPLAY_XDISPLAY (display); XISelectEvents (xdisplay, xwindow, event_mask, 1); } static void translate_valuator_class (GdkDisplay *display, GdkDevice *device, XIValuatorClassInfo *info, gint n_valuator) { 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] == info->label) { use = i; break; } } if (info->label != None) label = gdk_x11_xatom_to_atom_for_display (display, info->label); else label = GDK_NONE; _gdk_device_add_axis (device, label, use, info->min, info->max, info->resolution); } static void translate_device_classes (GdkDisplay *display, GdkDevice *device, XIAnyClassInfo **classes, guint n_classes) { gint i, n_valuator = 0; 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 i; _gdk_device_set_keys (device, key_info->num_keycodes); for (i = 0; i < key_info->num_keycodes; i++) gdk_device_set_key (device, i, key_info->keycodes[i], 0); } break; case XIValuatorClass: translate_valuator_class (display, device, (XIValuatorClassInfo *) class_info, n_valuator); n_valuator++; break; default: /* Ignore */ break; } } g_object_thaw_notify (G_OBJECT (device)); } static GdkDevice * create_device (GdkDeviceManager *device_manager, GdkDisplay *display, XIDeviceInfo *dev) { GdkInputSource input_source; GdkDeviceType type; GdkDevice *device; GdkInputMode mode; if (dev->use == XIMasterKeyboard || dev->use == XISlaveKeyboard) input_source = GDK_SOURCE_KEYBOARD; 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, "wacom") || strstr (tmp_name, "pen")) input_source = GDK_SOURCE_PEN; 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; } 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-manager", device_manager, "device-id", dev->deviceid, NULL); translate_device_classes (display, device, dev->classes, dev->num_classes); return device; } static GdkDevice * add_device (GdkX11DeviceManagerXI2 *device_manager, XIDeviceInfo *dev, gboolean emit_signal) { GdkDisplay *display; GdkDevice *device; display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (device_manager)); device = create_device (GDK_DEVICE_MANAGER (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; /* 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); } g_signal_emit_by_name (device_manager, "device-added", device); } return 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) { device_manager->devices = g_list_remove (device_manager->devices, device); g_signal_emit_by_name (device_manager, "device-removed", 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); } static void relate_slaves (gpointer key, gpointer value, gpointer user_data) { GdkX11DeviceManagerXI2 *device_manager; GdkDevice *slave, *master; 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); } static void gdk_x11_device_manager_xi2_constructed (GObject *object) { GdkX11DeviceManagerXI2 *device_manager; GdkDisplay *display; GdkScreen *screen; GHashTable *masters, *slaves; Display *xdisplay; XIDeviceInfo *info, *dev; int ndevices, i; XIEventMask event_mask; unsigned char mask[2] = { 0 }; device_manager = GDK_X11_DEVICE_MANAGER_XI2 (object); display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (object)); xdisplay = GDK_DISPLAY_XDISPLAY (display); 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]; 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 */ screen = gdk_display_get_default_screen (display); XISetMask (mask, XI_HierarchyChanged); XISetMask (mask, XI_DeviceChanged); event_mask.deviceid = XIAllDevices; event_mask.mask_len = sizeof (mask); event_mask.mask = mask; _gdk_x11_device_manager_xi2_select_events (GDK_DEVICE_MANAGER (object), GDK_WINDOW_XID (gdk_screen_get_root_window (screen)), &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 GList * gdk_x11_device_manager_xi2_list_devices (GdkDeviceManager *device_manager, GdkDeviceType type) { GdkX11DeviceManagerXI2 *device_manager_xi2; GList *cur, *list = NULL; device_manager_xi2 = GDK_X11_DEVICE_MANAGER_XI2 (device_manager); for (cur = device_manager_xi2->devices; cur; cur = cur->next) { GdkDevice *dev = cur->data; if (type == gdk_device_get_device_type (dev)) list = g_list_prepend (list, dev); } return list; } static GdkDevice * gdk_x11_device_manager_xi2_get_client_pointer (GdkDeviceManager *device_manager) { GdkX11DeviceManagerXI2 *device_manager_xi2; GdkDisplay *display; int device_id; device_manager_xi2 = (GdkX11DeviceManagerXI2 *) device_manager; display = gdk_device_manager_get_display (device_manager); XIGetClientPointer (GDK_DISPLAY_XDISPLAY (display), None, &device_id); return g_hash_table_lookup (device_manager_xi2->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; 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; 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_window_events = gdk_x11_device_manager_xi2_select_window_events; iface->get_window = gdk_x11_device_manager_xi2_get_window; } static void handle_hierarchy_changed (GdkX11DeviceManagerXI2 *device_manager, XIHierarchyEvent *ev) { GdkDisplay *display; Display *xdisplay; XIDeviceInfo *info; int ndevices; gint i; display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (device_manager)); xdisplay = GDK_DISPLAY_XDISPLAY (display); for (i = 0; i < ev->num_info; i++) { if (ev->info[i].flags & XIDeviceEnabled) { info = XIQueryDevice (xdisplay, ev->info[i].deviceid, &ndevices); 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; slave = g_hash_table_lookup (device_manager->id_table, GINT_TO_POINTER (ev->info[i].deviceid)); /* 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); g_signal_emit_by_name (device_manager, "device-changed", master); } /* Add new master if it's an attachment event */ if (ev->info[i].flags & XISlaveAttached) { info = XIQueryDevice (xdisplay, ev->info[i].deviceid, &ndevices); master = g_hash_table_lookup (device_manager->id_table, GINT_TO_POINTER (info->attachment)); _gdk_device_set_associated_device (slave, master); _gdk_device_add_slave (master, slave); g_signal_emit_by_name (device_manager, "device-changed", master); } g_signal_emit_by_name (device_manager, "device-changed", slave); } } } static void handle_device_changed (GdkX11DeviceManagerXI2 *device_manager, XIDeviceChangedEvent *ev) { GdkDisplay *display; GdkDevice *device; display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (device_manager)); device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (ev->deviceid)); _gdk_device_reset_axes (device); translate_device_classes (display, device, ev->classes, ev->num_classes); g_signal_emit_by_name (G_OBJECT (device), "changed"); } static GdkCrossingMode translate_crossing_mode (gint mode) { switch (mode) { case NotifyNormal: return GDK_CROSSING_NORMAL; case NotifyGrab: return GDK_CROSSING_GRAB; case NotifyUngrab: return GDK_CROSSING_UNGRAB; default: g_assert_not_reached (); } } 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 (); } } static gboolean set_screen_from_root (GdkDisplay *display, GdkEvent *event, Window xrootwin) { GdkScreen *screen; screen = _gdk_x11_display_screen_for_xrootwin (display, xrootwin); if (screen) { gdk_event_set_screen (event, screen); return TRUE; } return FALSE; } static void set_user_time (GdkEvent *event) { GdkWindow *window; guint32 time; window = gdk_window_get_toplevel (event->any.window); g_return_if_fail (GDK_IS_WINDOW (window)); 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_window_set_user_time (window, time); } static void generate_focus_event (GdkWindow *window, GdkDevice *device, GdkDevice *source_device, gboolean in) { GdkEvent *event; event = gdk_event_new (GDK_FOCUS_CHANGE); event->focus_change.window = g_object_ref (window); event->focus_change.send_event = FALSE; event->focus_change.in = in; gdk_event_set_device (event, device); gdk_event_set_source_device (event, source_device); gdk_event_put (event); gdk_event_free (event); } static void handle_focus_change (GdkWindow *window, GdkDevice *device, GdkDevice *source_device, gint detail, gint mode, gboolean in) { GdkToplevelX11 *toplevel; gboolean had_focus; toplevel = _gdk_x11_window_get_toplevel (window); if (!toplevel) return; had_focus = HAS_FOCUS (toplevel); switch (detail) { case NotifyAncestor: case NotifyVirtual: /* When the focus moves from an ancestor of the window to * the window or a descendent of the window, *and* the * pointer is inside the window, then we were previously * receiving keystroke events in the has_pointer_focus * case and are now receiving them in the * has_focus_window case. */ if (toplevel->has_pointer && mode != NotifyGrab && mode != NotifyUngrab) toplevel->has_pointer_focus = (in) ? FALSE : TRUE; /* fall through */ case NotifyNonlinear: case NotifyNonlinearVirtual: if (mode != NotifyGrab && mode != NotifyUngrab) toplevel->has_focus_window = (in) ? TRUE : FALSE; /* We pretend that the focus moves to the grab * window, so we pay attention to NotifyGrab * NotifyUngrab, and ignore NotifyWhileGrabbed */ if (mode != NotifyWhileGrabbed) toplevel->has_focus = (in) ? TRUE : FALSE; break; case NotifyPointer: /* The X server sends NotifyPointer/NotifyGrab, * but the pointer focus is ignored while a * grab is in effect */ if (mode != NotifyGrab && mode != NotifyUngrab) toplevel->has_pointer_focus = (in) ? TRUE :FALSE; break; case NotifyInferior: case NotifyPointerRoot: case NotifyDetailNone: break; } if (HAS_FOCUS (toplevel) != had_focus) generate_focus_event (window, device, source_device, (in) ? TRUE : FALSE); } static gdouble * translate_axes (GdkDevice *device, gdouble x, gdouble y, GdkWindow *window, XIValuatorState *valuators) { guint n_axes, i; gdouble *axes; gdouble *vals; g_object_get (device, "n-axes", &n_axes, NULL); axes = g_new0 (gdouble, n_axes); vals = valuators->values; for (i = 0; i < valuators->mask_len * 8; i++) { GdkAxisUse use; gdouble val; if (!XIMaskIsSet (valuators->mask, i)) continue; use = gdk_device_get_axis_use (device, i); val = *vals++; switch (use) { case GDK_AXIS_X: case GDK_AXIS_Y: if (gdk_device_get_mode (device) == GDK_MODE_WINDOW) _gdk_device_translate_window_coord (device, window, 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; } } return axes; } static gboolean is_parent_of (GdkWindow *parent, GdkWindow *child) { GdkWindow *w; w = child; while (w != NULL) { if (w == parent) return TRUE; w = gdk_window_get_parent (w); } return FALSE; } static GdkWindow * get_event_window (GdkEventTranslator *translator, XIEvent *ev) { GdkDisplay *display; GdkWindow *window = NULL; display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (translator)); switch (ev->evtype) { case XI_KeyPress: case XI_KeyRelease: case XI_ButtonPress: case XI_ButtonRelease: case XI_Motion: { XIDeviceEvent *xev = (XIDeviceEvent *) ev; window = gdk_x11_window_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->window, window) || !info->owner_events)) { /* Report key event against grab window */ window = info->window; } } } break; case XI_Enter: case XI_Leave: case XI_FocusIn: case XI_FocusOut: { XIEnterEvent *xev = (XIEnterEvent *) ev; window = gdk_x11_window_lookup_for_display (display, xev->event); } break; } return window; } static gboolean gdk_x11_device_manager_xi2_translate_core_event (GdkEventTranslator *translator, GdkDisplay *display, GdkEvent *event, XEvent *xevent) { GdkEventTranslatorIface *parent_iface; gboolean keyboard = FALSE; GdkDevice *device; if (xevent->type == KeyPress && xevent->xkey.keycode == 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. * * 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 * window 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 ((GdkDeviceManager *)translator); if (keyboard) device = gdk_device_get_associated_device (device); gdk_event_set_device (event, device); return TRUE; } static gboolean gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator, GdkDisplay *display, GdkEvent *event, XEvent *xevent) { GdkX11DeviceManagerXI2 *device_manager; XGenericEventCookie *cookie; gboolean return_val = TRUE; GdkWindow *window; 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; window = get_event_window (translator, ev); if (window && GDK_WINDOW_DESTROYED (window)) return FALSE; 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_KeyPress: case XI_KeyRelease: { XIDeviceEvent *xev = (XIDeviceEvent *) ev; GdkKeymap *keymap = gdk_keymap_get_for_display (display); GdkModifierType consumed, state; GdkDevice *device, *source_device; event->key.type = xev->evtype == XI_KeyPress ? GDK_KEY_PRESS : GDK_KEY_RELEASE; event->key.window = window; event->key.time = xev->time; event->key.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); event->key.group = _gdk_x11_get_group_for_state (display, event->key.state); event->key.hardware_keycode = 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; _gdk_x11_event_translate_keyboard_string (&event->key); 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; GdkDevice *source_device; switch (xev->detail) { case 4: case 5: case 6: case 7: /* Button presses of button 4-7 are scroll events */ if (ev->evtype == XI_ButtonPress) { event->scroll.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->scroll.window = window; event->scroll.time = xev->time; event->scroll.x = (gdouble) xev->event_x; event->scroll.y = (gdouble) xev->event_y; event->scroll.x_root = (gdouble) xev->root_x; event->scroll.y_root = (gdouble) xev->root_y; event->scroll.device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->deviceid)); 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); break; } /* Button presses of button 4-7 are scroll events, so ignore the release */ else if (ev->evtype == XI_ButtonRelease) { return_val = FALSE; break; } /* else (XI_ButtonRelease) fall thru */ default: event->button.type = (ev->evtype == XI_ButtonPress) ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE; event->button.window = window; event->button.time = xev->time; event->button.x = (gdouble) xev->event_x; event->button.y = (gdouble) xev->event_y; event->button.x_root = (gdouble) xev->root_x; event->button.y_root = (gdouble) xev->root_y; event->button.device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->deviceid)); source_device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->sourceid)); gdk_event_set_source_device (event, source_device); event->button.axes = translate_axes (event->button.device, event->button.x, event->button.y, event->button.window, &xev->valuators); if (gdk_device_get_mode (event->button.device) == GDK_MODE_WINDOW) { GdkDevice *device = event->button.device; /* 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; if (!set_screen_from_root (display, event, xev->root)) { return_val = FALSE; break; } if (ev->evtype == XI_ButtonPress) set_user_time (event); break; } case XI_Motion: { XIDeviceEvent *xev = (XIDeviceEvent *) ev; GdkDevice *source_device; event->motion.type = GDK_MOTION_NOTIFY; event->motion.window = window; event->motion.time = xev->time; event->motion.x = (gdouble) xev->event_x; event->motion.y = (gdouble) xev->event_y; event->motion.x_root = (gdouble) xev->root_x; event->motion.y_root = (gdouble) xev->root_y; event->motion.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_event_set_source_device (event, source_device); event->motion.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); /* There doesn't seem to be motion hints in XI */ event->motion.is_hint = FALSE; event->motion.axes = translate_axes (event->motion.device, event->motion.x, event->motion.y, event->motion.window, &xev->valuators); if (gdk_device_get_mode (event->motion.device) == GDK_MODE_WINDOW) { GdkDevice *device = event->motion.device; /* 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; case XI_Enter: case XI_Leave: { XIEnterEvent *xev = (XIEnterEvent *) ev; GdkDevice *device, *source_device; event->crossing.type = (ev->evtype == XI_Enter) ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY; event->crossing.x = (gdouble) xev->event_x; event->crossing.y = (gdouble) xev->event_y; event->crossing.x_root = (gdouble) xev->root_x; event->crossing.y_root = (gdouble) xev->root_y; event->crossing.time = xev->time; event->crossing.focus = xev->focus; event->crossing.window = window; event->crossing.subwindow = gdk_x11_window_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); 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: { XIEnterEvent *xev = (XIEnterEvent *) ev; GdkDevice *device, *source_device; 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)); handle_focus_change (window, device, source_device, xev->detail, xev->mode, (ev->evtype == XI_FocusIn) ? TRUE : FALSE); return_val = FALSE; } default: return_val = FALSE; break; } event->any.send_event = cookie->send_event; if (return_val) { if (event->any.window) g_object_ref (event->any.window); if (((event->any.type == GDK_ENTER_NOTIFY) || (event->any.type == GDK_LEAVE_NOTIFY)) && (event->crossing.subwindow != NULL)) g_object_ref (event->crossing.subwindow); } else { /* Mark this event as having no resources to be freed */ event->any.window = 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_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_FOCUS_CHANGE_MASK); } static void gdk_x11_device_manager_xi2_select_window_events (GdkEventTranslator *translator, Window window, GdkEventMask evmask) { GdkDeviceManager *device_manager; XIEventMask event_mask; device_manager = GDK_DEVICE_MANAGER (translator); event_mask.deviceid = XIAllMasterDevices; event_mask.mask = _gdk_x11_device_xi2_translate_event_mask (evmask, &event_mask.mask_len); _gdk_x11_device_manager_xi2_select_events (device_manager, window, &event_mask); g_free (event_mask.mask); } static GdkWindow * gdk_x11_device_manager_xi2_get_window (GdkEventTranslator *translator, XEvent *xevent) { GdkX11DeviceManagerXI2 *device_manager; XIEvent *ev; device_manager = (GdkX11DeviceManagerXI2 *) translator; if (xevent->type != GenericEvent || xevent->xcookie.extension != device_manager->opcode) return NULL; ev = (XIEvent *) xevent->xcookie.data; return get_event_window (translator, ev); } #else /* XINPUT_2 */ static void gdk_x11_device_manager_xi2_class_init (GdkX11DeviceManagerXI2Class *klass) { } static void gdk_x11_device_manager_xi2_init (GdkX11DeviceManagerXI2 *device_manager) { } static void gdk_x11_device_manager_xi2_event_translator_init (GdkEventTranslatorIface *iface) { } #endif /* XINPUT_2 */