diff options
Diffstat (limited to 'gdk/x11/gdkdevicemanager-xi.c')
-rw-r--r-- | gdk/x11/gdkdevicemanager-xi.c | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/gdk/x11/gdkdevicemanager-xi.c b/gdk/x11/gdkdevicemanager-xi.c new file mode 100644 index 0000000000..c1c3390525 --- /dev/null +++ b/gdk/x11/gdkdevicemanager-xi.c @@ -0,0 +1,650 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org> + * + * 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 "gdkdevicemanager-xi.h" +#include "gdkeventtranslator.h" +#include "gdkdevice-xi.h" +#include "gdkintl.h" +#include "gdkx.h" + +#include <X11/extensions/XInput.h> + + +struct _GdkDeviceManagerXIPrivate +{ + GHashTable *id_table; + gint event_base; + GList *devices; + gboolean ignore_core_events; +}; + +static void gdk_device_manager_xi_constructed (GObject *object); +static void gdk_device_manager_xi_finalize (GObject *object); +static void gdk_device_manager_xi_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gdk_device_manager_xi_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void gdk_device_manager_xi_event_translator_init (GdkEventTranslatorIface *iface); +static gboolean gdk_device_manager_xi_translate_event (GdkEventTranslator *translator, + GdkDisplay *display, + GdkEvent *event, + XEvent *xevent); +static GList * gdk_device_manager_xi_list_devices (GdkDeviceManager *device_manager, + GdkDeviceType type); + + +G_DEFINE_TYPE_WITH_CODE (GdkDeviceManagerXI, gdk_device_manager_xi, GDK_TYPE_DEVICE_MANAGER_CORE, + G_IMPLEMENT_INTERFACE (GDK_TYPE_EVENT_TRANSLATOR, + gdk_device_manager_xi_event_translator_init)) + +enum { + PROP_0, + PROP_EVENT_BASE +}; + +static void +gdk_device_manager_xi_class_init (GdkDeviceManagerXIClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkDeviceManagerClass *device_manager_class = GDK_DEVICE_MANAGER_CLASS (klass); + + object_class->constructed = gdk_device_manager_xi_constructed; + object_class->finalize = gdk_device_manager_xi_finalize; + object_class->set_property = gdk_device_manager_xi_set_property; + object_class->get_property = gdk_device_manager_xi_get_property; + + device_manager_class->list_devices = gdk_device_manager_xi_list_devices; + + g_object_class_install_property (object_class, + PROP_EVENT_BASE, + g_param_spec_int ("event-base", + P_("Event base"), + P_("Event base for XInput events"), + 0, G_MAXINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_type_class_add_private (object_class, sizeof (GdkDeviceManagerXIPrivate)); +} + +static GdkFilterReturn +window_input_info_filter (GdkXEvent *xevent, + GdkEvent *event, + gpointer user_data) +{ + GdkDeviceManager *device_manager; + GdkDisplay *display; + GdkWindow *window; + XEvent *xev; + + device_manager = user_data; + xev = (XEvent *) xevent; + + display = gdk_device_manager_get_display (device_manager); + window = gdk_window_lookup_for_display (display, xev->xany.window); + + if (window && xev->type == ConfigureNotify) + gdk_device_xi_update_window_info (window); + + return GDK_FILTER_CONTINUE; +} + +static void +gdk_device_manager_xi_init (GdkDeviceManagerXI *device_manager) +{ + GdkDeviceManagerXIPrivate *priv; + + device_manager->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (device_manager, + GDK_TYPE_DEVICE_MANAGER_XI, + GdkDeviceManagerXIPrivate); + + priv->id_table = g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) g_object_unref); + + gdk_window_add_filter (NULL, window_input_info_filter, device_manager); +} + +static void +translate_class_info (GdkDevice *device, + XDeviceInfo *info) +{ + GdkDeviceXI *device_xi; + XAnyClassPtr class; + gint i, j; + + device_xi = GDK_DEVICE_XI (device); + class = info->inputclassinfo; + + for (i = 0; i < info->num_classes; i++) + { + switch (class->class) { + case ButtonClass: + break; + case KeyClass: + { + XKeyInfo *xki = (XKeyInfo *)class; + guint num_keys; + + num_keys = xki->max_keycode - xki->min_keycode + 1; + _gdk_device_set_keys (device, num_keys); + + device_xi->min_keycode = xki->min_keycode; + + break; + } + case ValuatorClass: + { + XValuatorInfo *xvi = (XValuatorInfo *)class; + + for (j = 0; j < xvi->num_axes; j++) + { + GdkAxisUse use; + + switch (j) + { + case 0: + use = GDK_AXIS_X; + break; + case 1: + use = GDK_AXIS_Y; + break; + case 2: + use = GDK_AXIS_PRESSURE; + break; + case 3: + use = GDK_AXIS_XTILT; + break; + case 4: + use = GDK_AXIS_YTILT; + break; + case 5: + use = GDK_AXIS_WHEEL; + break; + default: + use = GDK_AXIS_IGNORE; + } + + _gdk_device_add_axis (device, + GDK_NONE, + use, + xvi->axes[j].min_value, + xvi->axes[j].max_value, + xvi->axes[j].resolution); + } + + break; + } + } + + class = (XAnyClassPtr) (((char *) class) + class->length); + } +} + +static GdkDevice * +create_device (GdkDeviceManager *device_manager, + GdkDisplay *display, + XDeviceInfo *info) +{ + GdkInputSource input_source; + GdkDevice *device; + + if (info->use != IsXExtensionPointer && + info->use != IsXExtensionKeyboard) + return NULL; + + if (info->use == IsXExtensionKeyboard) + input_source = GDK_SOURCE_KEYBOARD; + else + { + gchar *tmp_name; + + tmp_name = g_ascii_strdown (info->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); + } + + device = g_object_new (GDK_TYPE_DEVICE_XI, + "name", info->name, + "type", GDK_DEVICE_TYPE_FLOATING, + "input-source", input_source, + "input-mode", GDK_MODE_DISABLED, + "has-cursor", FALSE, + "display", display, + "device-manager", device_manager, + "device-id", info->id, + NULL); + translate_class_info (device, info); + + return device; +} + +static void +gdk_device_manager_xi_constructed (GObject *object) +{ + GdkDeviceManagerXIPrivate *priv; + XDeviceInfo *devices; + gint i, num_devices; + GdkDisplay *display; + + priv = GDK_DEVICE_MANAGER_XI (object)->priv; + display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (object)); + devices = XListInputDevices(GDK_DISPLAY_XDISPLAY (display), &num_devices); + + for(i = 0; i < num_devices; i++) + { + GdkDevice *device; + + device = create_device (GDK_DEVICE_MANAGER (object), + display, &devices[i]); + if (device) + { + priv->devices = g_list_prepend (priv->devices, device); + g_hash_table_insert (priv->id_table, + GINT_TO_POINTER (devices[i].id), + device); + } + } + + XFreeDeviceList(devices); + + gdk_x11_register_standard_event_type (display, + priv->event_base, + 15 /* Number of events */); + + if (G_OBJECT_CLASS (gdk_device_manager_xi_parent_class)->constructed) + G_OBJECT_CLASS (gdk_device_manager_xi_parent_class)->constructed (object); +} + +static void +gdk_device_manager_xi_finalize (GObject *object) +{ + GdkDeviceManagerXIPrivate *priv; + + priv = GDK_DEVICE_MANAGER_XI (object)->priv; + + g_list_foreach (priv->devices, (GFunc) g_object_unref, NULL); + g_list_free (priv->devices); + + g_hash_table_destroy (priv->id_table); + + gdk_window_remove_filter (NULL, window_input_info_filter, object); + + G_OBJECT_CLASS (gdk_device_manager_xi_parent_class)->finalize (object); +} + +static void +gdk_device_manager_xi_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdkDeviceManagerXIPrivate *priv; + + priv = GDK_DEVICE_MANAGER_XI (object)->priv; + + switch (prop_id) + { + case PROP_EVENT_BASE: + priv->event_base = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdk_device_manager_xi_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdkDeviceManagerXIPrivate *priv; + + priv = GDK_DEVICE_MANAGER_XI (object)->priv; + + switch (prop_id) + { + case PROP_EVENT_BASE: + g_value_set_int (value, priv->event_base); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdk_device_manager_xi_event_translator_init (GdkEventTranslatorIface *iface) +{ + iface->translate_event = gdk_device_manager_xi_translate_event; +} + +/* combine the state of the core device and the device state + * into one - for now we do this in a simple-minded manner - + * we just take the keyboard portion of the core device and + * the button portion (all of?) the device state. + * Any button remapping should go on here. + */ +static guint +translate_state (guint state, guint device_state) +{ + return device_state | (state & 0xFF); +} + +static GdkDevice * +lookup_device (GdkDeviceManagerXI *device_manager, + XEvent *xevent) +{ + GdkDeviceManagerXIPrivate *priv; + guint32 device_id; + + priv = GDK_DEVICE_MANAGER_XI (device_manager)->priv; + + /* This is a sort of a hack, as there isn't any XDeviceAnyEvent - + but it's potentially faster than scanning through the types of + every device. If we were deceived, then it won't match any of + the types for the device anyways */ + device_id = ((XDeviceButtonEvent *)xevent)->deviceid; + + return g_hash_table_lookup (priv->id_table, GINT_TO_POINTER (device_id)); +} + +static gboolean +gdk_device_manager_xi_translate_event (GdkEventTranslator *translator, + GdkDisplay *display, + GdkEvent *event, + XEvent *xevent) +{ + GdkDeviceManagerXIPrivate *priv; + GdkDeviceManagerXI *device_manager; + GdkEventTranslatorIface *parent_iface; + GdkDeviceXI *device_xi; + GdkDevice *device; + GdkWindow *window; + + parent_iface = g_type_interface_peek_parent (GDK_EVENT_TRANSLATOR_GET_IFACE (translator)); + device_manager = GDK_DEVICE_MANAGER_XI (translator); + priv = device_manager->priv; + + if (!priv->ignore_core_events && + parent_iface->translate_event (translator, display, event, xevent)) + return TRUE; + + device = lookup_device (device_manager, xevent); + device_xi = GDK_DEVICE_XI (device); + + if (!device) + return FALSE; + + window = gdk_window_lookup_for_display (display, xevent->xany.window); + + if (!window) + return FALSE; + + if ((xevent->type == device_xi->button_press_type) || + (xevent->type == device_xi->button_release_type)) + { + XDeviceButtonEvent *xdbe = (XDeviceButtonEvent *) xevent; + + event->button.type = (xdbe->type == device_xi->button_press_type) ? + GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE; + + event->button.device = device; + event->button.window = g_object_ref (window); + event->button.time = xdbe->time; + + event->button.x_root = (gdouble) xdbe->x_root; + event->button.y_root = (gdouble) xdbe->y_root; + + event->button.axes = g_new0 (gdouble, device->num_axes); + gdk_device_xi_update_axes (device, xdbe->axes_count, + xdbe->first_axis, xdbe->axis_data); + gdk_device_xi_translate_axes (device, window, + device_xi->axis_data, + event->button.axes, + &event->button.x, + &event->button.y); + + event->button.state = translate_state (xdbe->state, xdbe->device_state); + event->button.button = xdbe->button; + + if (event->button.type == GDK_BUTTON_PRESS) + _gdk_event_button_generate (gdk_drawable_get_display (event->button.window), + event); + + GDK_NOTE (EVENTS, + g_print ("button %s:\t\twindow: %ld device: %ld x,y: %f %f button: %d\n", + (event->button.type == GDK_BUTTON_PRESS) ? "press" : "release", + xdbe->window, + xdbe->deviceid, + event->button.x, event->button.y, + xdbe->button)); + + /* Update the timestamp of the latest user interaction, if the event has + * a valid timestamp. + */ + if (gdk_event_get_time (event) != GDK_CURRENT_TIME) + gdk_x11_window_set_user_time (gdk_window_get_toplevel (window), + gdk_event_get_time (event)); + return TRUE; + } + + if ((xevent->type == device_xi->key_press_type) || + (xevent->type == device_xi->key_release_type)) + { + XDeviceKeyEvent *xdke = (XDeviceKeyEvent *) xevent; + + GDK_NOTE (EVENTS, + g_print ("device key %s:\twindow: %ld device: %ld keycode: %d\n", + (event->key.type == GDK_KEY_PRESS) ? "press" : "release", + xdke->window, + xdke->deviceid, + xdke->keycode)); + + if (xdke->keycode < device_xi->min_keycode || + xdke->keycode >= device_xi->min_keycode + device->num_keys) + { + g_warning ("Invalid device key code received"); + return FALSE; + } + + event->key.keyval = device->keys[xdke->keycode - device_xi->min_keycode].keyval; + + if (event->key.keyval == 0) + { + GDK_NOTE (EVENTS, + g_print ("\t\ttranslation - NONE\n")); + + return FALSE; + } + + event->key.type = (xdke->type == device_xi->key_press_type) ? + GDK_KEY_PRESS : GDK_KEY_RELEASE; + + event->key.window = g_object_ref (window); + event->key.time = xdke->time; + + event->key.state = translate_state (xdke->state, xdke->device_state) + | device->keys[xdke->keycode - device_xi->min_keycode].modifiers; + + /* Add a string translation for the key event */ + if ((event->key.keyval >= 0x20) && (event->key.keyval <= 0xFF)) + { + event->key.length = 1; + event->key.string = g_new (gchar, 2); + event->key.string[0] = (gchar) event->key.keyval; + event->key.string[1] = 0; + } + else + { + event->key.length = 0; + event->key.string = g_new0 (gchar, 1); + } + + GDK_NOTE (EVENTS, + g_print ("\t\ttranslation - keyval: %d modifiers: %#x\n", + event->key.keyval, + event->key.state)); + + /* Update the timestamp of the latest user interaction, if the event has + * a valid timestamp. + */ + if (gdk_event_get_time (event) != GDK_CURRENT_TIME) + gdk_x11_window_set_user_time (gdk_window_get_toplevel (window), + gdk_event_get_time (event)); + return TRUE; + } + + if (xevent->type == device_xi->motion_notify_type) + { + XDeviceMotionEvent *xdme = (XDeviceMotionEvent *) xevent; + + event->motion.device = device; + + if (device_xi->in_proximity) + priv->ignore_core_events = TRUE; + + event->motion.x_root = (gdouble) xdme->x_root; + event->motion.y_root = (gdouble) xdme->y_root; + + event->motion.axes = g_new0 (gdouble, device->num_axes); + gdk_device_xi_update_axes (device, xdme->axes_count, + xdme->first_axis, xdme->axis_data); + gdk_device_xi_translate_axes (device, window, + device_xi->axis_data, + event->motion.axes, + &event->motion.x, + &event->motion.y); + + event->motion.type = GDK_MOTION_NOTIFY; + event->motion.window = g_object_ref (window); + event->motion.time = xdme->time; + event->motion.state = translate_state (xdme->state, + xdme->device_state); + event->motion.is_hint = xdme->is_hint; + + GDK_NOTE (EVENTS, + g_print ("motion notify:\t\twindow: %ld device: %ld x,y: %f %f state %#4x hint: %s\n", + xdme->window, + xdme->deviceid, + event->motion.x, event->motion.y, + event->motion.state, + (xdme->is_hint) ? "true" : "false")); + + + /* Update the timestamp of the latest user interaction, if the event has + * a valid timestamp. + */ + if (gdk_event_get_time (event) != GDK_CURRENT_TIME) + gdk_x11_window_set_user_time (gdk_window_get_toplevel (window), + gdk_event_get_time (event)); + return TRUE; + } + + if (xevent->type == device_xi->proximity_in_type || + xevent->type == device_xi->proximity_out_type) + { + XProximityNotifyEvent *xpne = (XProximityNotifyEvent *) xevent; + + if (xevent->type == device_xi->proximity_in_type) + { + event->proximity.type = GDK_PROXIMITY_IN; + device_xi->in_proximity = TRUE; + priv->ignore_core_events = TRUE; + } + else + { + event->proximity.type = GDK_PROXIMITY_OUT; + device_xi->in_proximity = FALSE; + priv->ignore_core_events = FALSE; + } + + event->proximity.device = device; + event->proximity.window = g_object_ref (window); + event->proximity.time = xpne->time; + + /* Update the timestamp of the latest user interaction, if the event has + * a valid timestamp. + */ + if (gdk_event_get_time (event) != GDK_CURRENT_TIME) + gdk_x11_window_set_user_time (gdk_window_get_toplevel (window), + gdk_event_get_time (event)); + return TRUE; + } + + if (xevent->type == device_xi->state_notify_type) + { + XDeviceStateNotifyEvent *xdse = (XDeviceStateNotifyEvent *) xevent; + XInputClass *input_class = (XInputClass *) xdse->data; + int i; + + for (i = 0; i < xdse->num_classes; i++) + { + if (input_class->class == ValuatorClass) + gdk_device_xi_update_axes (device, device->num_axes, 0, + ((XValuatorState *)input_class)->valuators); + + input_class = (XInputClass *)(((char *)input_class)+input_class->length); + } + + GDK_NOTE (EVENTS, + g_print ("device state notify:\t\twindow: %ld device: %ld\n", + xdse->window, + xdse->deviceid)); + + return FALSE; + } + + return FALSE; +} + +static GList * +gdk_device_manager_xi_list_devices (GdkDeviceManager *device_manager, + GdkDeviceType type) +{ + GdkDeviceManagerXIPrivate *priv; + + priv = GDK_DEVICE_MANAGER_XI (device_manager)->priv; + + if (type == GDK_DEVICE_TYPE_MASTER) + return GDK_DEVICE_MANAGER_CLASS (gdk_device_manager_xi_parent_class)->list_devices (device_manager, type); + else if (type == GDK_DEVICE_TYPE_FLOATING) + { + return g_list_copy (priv->devices); + } + else + return NULL; +} |