diff --git a/src/Makefile.am b/src/Makefile.am index c1c303cbb..6abfecd4f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -170,6 +170,8 @@ libmutter_la_SOURCES = \ if HAVE_XINPUT2 libmutter_la_SOURCES += \ + core/device-map-xi2.c \ + core/device-map-xi2.h \ core/devices-xi2.c \ core/devices-xi2.h endif diff --git a/src/core/device-map-xi2.c b/src/core/device-map-xi2.c new file mode 100644 index 000000000..10690df1f --- /dev/null +++ b/src/core/device-map-xi2.c @@ -0,0 +1,265 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* Input device map, XInput2 implementation */ + +/* + * Copyright (C) 2011 Carlos Garnacho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" +#include "device-map-xi2.h" +#include +#include "devices-xi2.h" + +#define XINPUT2_VERSION_MAJOR 2 +#define XINPUT2_VERSION_MINOR 0 + +G_DEFINE_TYPE (MetaDeviceMapXI2, meta_device_map_xi2, META_TYPE_DEVICE_MAP) + +static gboolean +meta_device_map_xi2_grab_key (MetaDeviceMap *device_map, + Window xwindow, + guint keycode, + guint modifiers, + gboolean sync) +{ + XIGrabModifiers mods = { modifiers, 0 }; + MetaDisplay *display; + XIEventMask mask; + gint retval; + + display = meta_device_map_get_display (device_map); + + mask.deviceid = XIAllMasterDevices; + mask.mask = meta_device_xi2_translate_event_mask (KeyPressMask | + KeyReleaseMask, + &mask.mask_len); + + retval = XIGrabKeycode (display->xdisplay, + XIAllMasterDevices, + keycode, xwindow, + (sync) ? GrabModeSync : GrabModeAsync, + GrabModeAsync, /* Never care about the other device */ + True, &mask, 1, &mods); + + return (retval == Success); +} + +static void +meta_device_map_xi2_ungrab_key (MetaDeviceMap *device_map, + Window xwindow, + guint keycode, + guint modifiers) +{ + XIGrabModifiers mods = { modifiers, 0 }; + MetaDisplay *display; + + display = meta_device_map_get_display (device_map); + XIUngrabKeycode (display->xdisplay, + XIAllMasterDevices, + keycode, xwindow, + 1, &mods); +} + +static gboolean +meta_device_map_xi2_grab_button (MetaDeviceMap *device_map, + Window xwindow, + guint n_button, + guint modifiers, + guint evmask, + gboolean sync) +{ + XIGrabModifiers mods = { modifiers, 0 }; + XIEventMask mask; + MetaDisplay *display; + int retval; + + display = meta_device_map_get_display (device_map); + + mask.deviceid = XIAllMasterDevices; + mask.mask = meta_device_xi2_translate_event_mask (evmask, &mask.mask_len); + + retval = XIGrabButton (display->xdisplay, + XIAllMasterDevices, + n_button, xwindow, None, + (sync) ? GrabModeSync : GrabModeAsync, + GrabModeAsync, /* Never care about the other device */ + False, &mask, 1, &mods); + + return (retval == Success); +} + +static void +meta_device_map_xi2_ungrab_button (MetaDeviceMap *device_map, + Window xwindow, + guint n_button, + guint modifiers) +{ + XIGrabModifiers mods = { modifiers, 0 }; + MetaDisplay *display; + + display = meta_device_map_get_display (device_map); + XIUngrabButton (display->xdisplay, + XIAllMasterDevices, + n_button, xwindow, 1, &mods); +} + +static void +add_device_from_info (MetaDeviceMap *device_map, + gint use, + gint device_id) +{ + MetaDevice *device; + MetaDisplay *display; + + display = meta_device_map_get_display (device_map); + + if (use == XIMasterPointer) + device = meta_device_pointer_xi2_new (display, device_id); + else if (use == XIMasterKeyboard) + device = meta_device_keyboard_xi2_new (display, device_id); + + if (device) + { + meta_device_map_add_device (device_map, device); + g_object_unref (device); + } +} + +static void +pair_devices (gpointer key, + gpointer value, + gpointer user_data) +{ + MetaDevice *device1, *device2; + MetaDeviceMap *device_map; + + device_map = user_data; + device1 = meta_device_map_lookup (device_map, GPOINTER_TO_INT (key)); + device2 = meta_device_map_lookup (device_map, GPOINTER_TO_INT (value)); + + meta_device_pair_devices (device1, device2); +} + +static void +meta_device_map_xi2_constructed (GObject *object) +{ + MetaDeviceMap *device_map = META_DEVICE_MAP (object); + MetaDisplay *display; + XIDeviceInfo *info; + GHashTable *pairs; + int n_devices, i; + + display = meta_device_map_get_display (device_map); + + /* We're only interested in master devices, + * detached slave devices are left for applications + * to handle. + */ + info = XIQueryDevice (display->xdisplay, XIAllMasterDevices, &n_devices); + pairs = g_hash_table_new (NULL, NULL); + + for (i = 0; i < n_devices; i++) + { + add_device_from_info (device_map, info[i].use, info[i].deviceid); + g_hash_table_insert (pairs, + GINT_TO_POINTER (info[i].deviceid), + GINT_TO_POINTER (info[i].attachment)); + } + + g_hash_table_foreach (pairs, pair_devices, device_map); + g_hash_table_destroy (pairs); + + XIFreeDeviceInfo (info); +} + +static void +meta_device_map_xi2_class_init (MetaDeviceMapXI2Class *klass) +{ + MetaDeviceMapClass *device_map_class = META_DEVICE_MAP_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = meta_device_map_xi2_constructed; + + device_map_class->grab_key = meta_device_map_xi2_grab_key; + device_map_class->ungrab_key = meta_device_map_xi2_ungrab_key; + device_map_class->grab_button = meta_device_map_xi2_grab_button; + device_map_class->ungrab_button = meta_device_map_xi2_ungrab_button; +} + +static void +meta_device_map_xi2_init (MetaDeviceMapXI2 *device_map) +{ +} + +gboolean +meta_device_map_xi2_handle_hierarchy_event (MetaDeviceMapXI2 *device_map, + XEvent *ev) +{ + MetaDisplay *display; + + display = meta_device_map_get_display (META_DEVICE_MAP (device_map)); + + if (ev->type == GenericEvent && + ev->xcookie.extension == display->xinput2_opcode) + { + XIHierarchyEvent *xev; + GHashTable *pairs; + gint i; + + g_assert (display->have_xinput2 == TRUE); + + xev = (XIHierarchyEvent *) ev->xcookie.data; + + if (xev->evtype != XI_HierarchyChanged) + return FALSE; + + pairs = g_hash_table_new (NULL, NULL); + + for (i = 0; i < xev->num_info; i++) + { + if (xev->info[i].flags & XIMasterAdded) + { + add_device_from_info (META_DEVICE_MAP (device_map), + xev->info[i].use, + xev->info[i].deviceid); + g_hash_table_insert (pairs, + GINT_TO_POINTER (xev->info[i].deviceid), + GINT_TO_POINTER (xev->info[i].attachment)); + } + else if (xev->info[i].flags & XIMasterRemoved) + { + MetaDevice *device; + + device = meta_device_map_lookup (META_DEVICE_MAP (device_map), + xev->info[i].deviceid); + + if (device) + meta_device_map_remove_device (META_DEVICE_MAP (device_map), + device); + } + } + + g_hash_table_foreach (pairs, pair_devices, device_map); + g_hash_table_destroy (pairs); + + return TRUE; + } + + return FALSE; +} diff --git a/src/core/device-map-xi2.h b/src/core/device-map-xi2.h new file mode 100644 index 000000000..be3830a3f --- /dev/null +++ b/src/core/device-map-xi2.h @@ -0,0 +1,59 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/** + * \file device-map-xi2.h device map for XInput2 devices + * + * Input devices. + * This file contains the XInput2 implementation of the device map + */ + +/* + * Copyright (C) 2011 Carlos Garnacho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef META_DEVICE_MAP_XI2_H +#define META_DEVICE_MAP_XI2_H + +typedef struct _MetaDeviceMapXI2 MetaDeviceMapXI2; +typedef struct _MetaDeviceMapXI2Class MetaDeviceMapXI2Class; + +#include "device-map.h" + +#define META_TYPE_DEVICE_MAP_XI2 (meta_device_map_xi2_get_type ()) +#define META_DEVICE_MAP_XI2(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_DEVICE_MAP_XI2, MetaDeviceMapXI2)) +#define META_DEVICE_MAP_XI2_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_DEVICE_MAP_XI2, MetaDeviceMapXI2Class)) +#define META_IS_DEVICE_MAP_XI2(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_DEVICE_MAP_XI2)) +#define META_IS_DEVICE_MAP_XI2_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_DEVICE_MAP_XI2)) +#define META_DEVICE_MAP_XI2_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_DEVICE_MAP_XI2, MetaDeviceMapXI2Class)) + +struct _MetaDeviceMapXI2 +{ + MetaDeviceMap parent_instance; +}; + +struct _MetaDeviceMapXI2Class +{ + MetaDeviceMapClass parent_class; +}; + +GType meta_device_map_xi2_get_type (void) G_GNUC_CONST; + +gboolean meta_device_map_xi2_handle_hierarchy_event (MetaDeviceMapXI2 *device_map, + XEvent *ev); + +#endif /* META_DEVICE_MAP_XI2_H */ diff --git a/src/core/device-map.c b/src/core/device-map.c index 1f28ed407..df0f0da74 100644 --- a/src/core/device-map.c +++ b/src/core/device-map.c @@ -25,6 +25,14 @@ #include "device-map.h" #include "device-map-core.h" +#ifdef HAVE_XINPUT2 +#include +#include "device-map-xi2.h" + +#define XINPUT2_VERSION_MAJOR 2 +#define XINPUT2_VERSION_MINOR 0 +#endif + G_DEFINE_TYPE (MetaDeviceMap, meta_device_map, G_TYPE_OBJECT) typedef struct MetaDeviceMapPrivate MetaDeviceMapPrivate; @@ -192,11 +200,52 @@ meta_device_map_remove_device (MetaDeviceMap *device_map, } } + +#ifdef HAVE_XINPUT2 + +static gboolean +initialize_xinput (MetaDisplay *display) +{ + int major, minor, opcode; + int unused; + + if (!XQueryExtension (display->xdisplay, + "XInputExtension", + &opcode, &unused, &unused)) + return FALSE; + + major = XINPUT2_VERSION_MAJOR; + minor = XINPUT2_VERSION_MINOR; + + XIQueryVersion (display->xdisplay, &major, &minor); + + if (major == XINPUT2_VERSION_MAJOR && + minor == XINPUT2_VERSION_MINOR) + { + display->have_xinput2 = TRUE; + display->xinput2_opcode = opcode; + + return TRUE; + } + + return FALSE; +} + +#endif /* HAVE_XINPUT2 */ + MetaDeviceMap * meta_device_map_new (MetaDisplay *display, gboolean force_core) { - return g_object_new (META_TYPE_DEVICE_MAP_CORE, + GType type = META_TYPE_DEVICE_MAP_CORE; + +#ifdef HAVE_XINPUT2 + if (!force_core && + initialize_xinput (display)) + type = META_TYPE_DEVICE_MAP_XI2; +#endif + + return g_object_new (type, "display", display, NULL); } diff --git a/src/core/display.c b/src/core/display.c index 12263d215..81349292b 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -71,6 +71,10 @@ #ifdef HAVE_XCURSOR #include #endif +#ifdef HAVE_XINPUT2 +#include +#include "device-map-xi2.h" +#endif #include #include #include @@ -1725,6 +1729,17 @@ event_callback (XEvent *event, } #endif /* HAVE_SHAPE */ +#ifdef HAVE_XINPUT2 + if (display->have_xinput2 && + meta_device_map_xi2_handle_hierarchy_event (META_DEVICE_MAP_XI2 (display->device_map), + event)) + { + /* Let GDK Handle the event too for its own device accounting */ + filter_out_event = FALSE; + bypass_compositor = FALSE; + } + else +#endif if (meta_input_event_get_type (display, event, &evtype)) { Window xwindow = meta_input_event_get_window (display, event);