gtk/gdk/x11/gdkdevicemanager-xi.c
Benjamin Otte de9a082ddb x11: Make headers identical no matter if we run with or without XI2
Previously we weren't installing the device headers when compiling
without XINPUT support. But we would include them from gdkx.h, so
essentially the build was broken.

With this patch the types will exist but not do anything.
2011-02-12 01:27:40 +01:00

688 lines
22 KiB
C

/* 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 "gdkx11devicemanager-xi.h"
#include "gdkdevicemanagerprivate-core.h"
#include "gdkdeviceprivate-xi.h"
#include "gdkdevicemanagerprivate.h"
#include "gdkeventtranslator.h"
#include "gdkintl.h"
#include "gdkprivate-x11.h"
#ifdef XINPUT_XFREE
#include <X11/extensions/XInput.h>
#endif /* XINPUT_XFREE */
struct _GdkX11DeviceManagerXI
{
GdkX11DeviceManagerCore parent_object;
GHashTable *id_table;
gint event_base;
GList *devices;
gboolean ignore_core_events;
};
struct _GdkX11DeviceManagerXIClass
{
GdkX11DeviceManagerCoreClass parent_class;
};
static void gdk_x11_device_manager_xi_event_translator_init (GdkEventTranslatorIface *iface);
G_DEFINE_TYPE_WITH_CODE (GdkX11DeviceManagerXI, gdk_x11_device_manager_xi, GDK_TYPE_X11_DEVICE_MANAGER_CORE,
G_IMPLEMENT_INTERFACE (GDK_TYPE_EVENT_TRANSLATOR,
gdk_x11_device_manager_xi_event_translator_init))
#ifdef XINPUT_XFREE
static void gdk_x11_device_manager_xi_constructed (GObject *object);
static void gdk_x11_device_manager_xi_dispose (GObject *object);
static void gdk_x11_device_manager_xi_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gdk_x11_device_manager_xi_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static gboolean gdk_x11_device_manager_xi_translate_event (GdkEventTranslator *translator,
GdkDisplay *display,
GdkEvent *event,
XEvent *xevent);
static GList * gdk_x11_device_manager_xi_list_devices (GdkDeviceManager *device_manager,
GdkDeviceType type);
enum {
PROP_0,
PROP_EVENT_BASE
};
static void
gdk_x11_device_manager_xi_class_init (GdkX11DeviceManagerXIClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GdkDeviceManagerClass *device_manager_class = GDK_DEVICE_MANAGER_CLASS (klass);
object_class->constructed = gdk_x11_device_manager_xi_constructed;
object_class->dispose = gdk_x11_device_manager_xi_dispose;
object_class->set_property = gdk_x11_device_manager_xi_set_property;
object_class->get_property = gdk_x11_device_manager_xi_get_property;
device_manager_class->list_devices = gdk_x11_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));
}
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_x11_window_lookup_for_display (display, xev->xany.window);
if (window && xev->type == ConfigureNotify)
_gdk_x11_device_xi_update_window_info (window);
return GDK_FILTER_CONTINUE;
}
static void
gdk_x11_device_manager_xi_init (GdkX11DeviceManagerXI *device_manager)
{
device_manager->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)
{
GdkX11DeviceXI *device_xi;
XAnyClassPtr class;
gint i, j;
device_xi = GDK_X11_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);
}
}
/* old versions of XI.h don't define these */
#ifndef IsXExtensionKeyboard
#define IsXExtensionKeyboard 3
#define IsXExtensionPointer 4
#endif
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_X11_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_x11_device_manager_xi_constructed (GObject *object)
{
GdkX11DeviceManagerXI *device_manager;
XDeviceInfo *devices;
gint i, num_devices;
GdkDisplay *display;
device_manager = GDK_X11_DEVICE_MANAGER_XI (object);
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)
{
device_manager->devices = g_list_prepend (device_manager->devices, device);
g_hash_table_insert (device_manager->id_table,
GINT_TO_POINTER (devices[i].id),
g_object_ref (device));
}
}
XFreeDeviceList (devices);
gdk_x11_register_standard_event_type (display,
device_manager->event_base,
15 /* Number of events */);
if (G_OBJECT_CLASS (gdk_x11_device_manager_xi_parent_class)->constructed)
G_OBJECT_CLASS (gdk_x11_device_manager_xi_parent_class)->constructed (object);
}
static void
gdk_x11_device_manager_xi_dispose (GObject *object)
{
GdkX11DeviceManagerXI *device_manager;
device_manager = GDK_X11_DEVICE_MANAGER_XI (object);
g_list_foreach (device_manager->devices, (GFunc) g_object_unref, NULL);
g_list_free (device_manager->devices);
device_manager->devices = NULL;
if (device_manager->id_table != NULL)
{
g_hash_table_destroy (device_manager->id_table);
device_manager->id_table = NULL;
}
gdk_window_remove_filter (NULL, window_input_info_filter, object);
G_OBJECT_CLASS (gdk_x11_device_manager_xi_parent_class)->dispose (object);
}
static void
gdk_x11_device_manager_xi_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GdkX11DeviceManagerXI *device_manager;
device_manager = GDK_X11_DEVICE_MANAGER_XI (object);
switch (prop_id)
{
case PROP_EVENT_BASE:
device_manager->event_base = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gdk_x11_device_manager_xi_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GdkX11DeviceManagerXI *device_manager;
device_manager = GDK_X11_DEVICE_MANAGER_XI (object);
switch (prop_id)
{
case PROP_EVENT_BASE:
g_value_set_int (value, device_manager->event_base);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gdk_x11_device_manager_xi_event_translator_init (GdkEventTranslatorIface *iface)
{
iface->translate_event = gdk_x11_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 (GdkX11DeviceManagerXI *manager,
XEvent *xevent)
{
GdkX11DeviceManagerXI *device_manager;
guint32 device_id;
device_manager = GDK_X11_DEVICE_MANAGER_XI (manager);
/* 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 (device_manager->id_table, GINT_TO_POINTER (device_id));
}
static gboolean
gdk_x11_device_manager_xi_translate_event (GdkEventTranslator *translator,
GdkDisplay *display,
GdkEvent *event,
XEvent *xevent)
{
GdkX11DeviceManagerXI *device_manager;
GdkEventTranslatorIface *parent_iface;
GdkX11DeviceXI *device_xi;
GdkDevice *device;
GdkWindow *window;
parent_iface = g_type_interface_peek_parent (GDK_EVENT_TRANSLATOR_GET_IFACE (translator));
device_manager = GDK_X11_DEVICE_MANAGER_XI (translator);
if (!device_manager->ignore_core_events &&
parent_iface->translate_event (translator, display, event, xevent))
return TRUE;
device = lookup_device (device_manager, xevent);
device_xi = GDK_X11_DEVICE_XI (device);
if (!device)
return FALSE;
window = gdk_x11_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, gdk_device_get_n_axes (device));
_gdk_x11_device_xi_update_axes (device, xdbe->axes_count,
xdbe->first_axis, xdbe->axis_data);
_gdk_x11_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_window_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 + gdk_device_get_n_keys (device))
{
g_warning ("Invalid device key code received");
return FALSE;
}
gdk_device_get_key (device, xdke->keycode - device_xi->min_keycode,
&event->key.keyval,
&event->key.state);
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);
/* 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)
device_manager->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, gdk_device_get_n_axes (device));
_gdk_x11_device_xi_update_axes (device, xdme->axes_count,
xdme->first_axis, xdme->axis_data);
_gdk_x11_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;
device_manager->ignore_core_events = TRUE;
}
else
{
event->proximity.type = GDK_PROXIMITY_OUT;
device_xi->in_proximity = FALSE;
device_manager->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_x11_device_xi_update_axes (device, gdk_device_get_n_axes (device), 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_x11_device_manager_xi_list_devices (GdkDeviceManager *manager,
GdkDeviceType type)
{
GdkX11DeviceManagerXI *device_manager;
device_manager = GDK_X11_DEVICE_MANAGER_XI (manager);
if (type == GDK_DEVICE_TYPE_MASTER)
return GDK_DEVICE_MANAGER_CLASS (gdk_x11_device_manager_xi_parent_class)->list_devices (manager, type);
else if (type == GDK_DEVICE_TYPE_FLOATING)
{
return g_list_copy (device_manager->devices);
}
else
return NULL;
}
#else /* XINPUT_XFREE */
static void
gdk_x11_device_manager_xi_class_init (GdkX11DeviceManagerXIClass *klass)
{
}
static void
gdk_x11_device_manager_xi_init (GdkX11DeviceManagerXI *device_manager)
{
}
static void
gdk_x11_device_manager_xi_event_translator_init (GdkEventTranslatorIface *iface)
{
}
#endif /* XINPUT_XFREE */