[RFC][PATCH] General axis valuator support
Matt Helsley
matt.helsley at gmail.com
Sun Jan 11 19:37:49 PST 2009
Currently X, Y, and PRESSURE are special case axes in xf86-input-evdev.
This patch supports axes in a more general way. There are a few open
questions and TODOs:
TODO:
Add REL axes. Since we currently limit evdev devices to
exclusively REL or ABS axes we can reuse the same vals
buffer in the device structure.
In theory there can be more than MAX_VALUATORS axes. Often there
will be less than MAX_VALUATORS axes. This either fails
to fully support a device or wastes a space. Should
probably switch to dynamically-allocated values but this
means we need an UnInit function.
Needs testing. Currently I've only got devices that
support up to three axes. A nice Wacom tablet that
supports tilt might be able to test more axes with
some small testing-specific kernel/X driver hacks.
Any other ideas for testing?
I'd like to move EvdevCacheCompare() above EvdevProbe() -- it
means we can get rid of all but the GRAB ioctls() from
EvdevProbe().
Put back the button-as-ABS_PRESSURE test.
QUESTIONS:
Is this the only code that needs to test and count bits or can
we make these pieces common somehow?
There are still locations that special-case X and Y:
1. TOUCHPAD dx, dy calculation
2. Axis-swapping (would require an axis-mapping)
3. Axis-inversion
4. Calibration
The swap, scale, inversion, and calibration transforms look like
they are done in the wrong order; we wind up using Y's
min and max to invert X if we've swapped the axes. I'd
think we'd want to swap later if not last...
Should there be a switch_mode function someday?
Should there be a set_valuators function?
Is the maximum number of axes MAX_VALUATORS or MAX_VALUATORS/6
or can there be more? Digging through some of the core
X bits left me unclear on these points...
Some of these questions and TODOs could be left until later patches in
order to simplify review. It would be nice to know which of the TODOs
folks would prefer to see in separate patches. For example, perhaps
converting the REL axes should be a separate patch.
Signed-off-by: Matt Helsley <matt.helsley at gmail.com>
---
src/evdev.c | 189
+++++++++++++++++++++++++++++-------------------------------
src/evdev.h | 9 +-
2 files changed, 100 insertions(+), 98 deletions(-)
Index: xf86-input-evdev/src/evdev.c
===================================================================
--- xf86-input-evdev.orig/src/evdev.c
+++ xf86-input-evdev/src/evdev.c
@@ -55,10 +55,35 @@
#ifndef MAXDEVICES
#include <inputstr.h> /* for MAX_DEVICES */
#define MAXDEVICES MAX_DEVICES
#endif
+#define TestBit(bit, array) (array[(bit) / LONG_BITS]) & (1L << ((bit)
% LONG_BITS))
+
+static size_t _CountBits(int x)
+{
+ x = x - ((x >> 1) & 0x55555555); /* 01010101... */
+ x = (x & 0x33333333) + ((x >> 2) & 0x33333333); /* 00110011... */
+ x = x + (x >> 4) & 0x0f0f0f0f;/* 0000111100001111... */
+ return (x * 0x01010101) >> 24;
+}
+
+static size_t CountBits(unsigned long *array, size_t nlongs)
+{
+ unsigned int i;
+ size_t count;
+
+ for (i = 0; i < nlongs; i++) {
+ unsigned long x = array[i];
+
+ count += _CountBits(x);
+ if (sizeof(long) > sizeof(int))
+ count += _CountBits(x >> (sizeof(int)*8));
+ }
+ return count;
+}
+
/* 2.4 compatibility */
#ifndef EVIOCGRAB
#define EVIOCGRAB _IOW('E', 0x90, int)
#endif
@@ -375,24 +400,14 @@ EvdevReadInput(InputInfoPtr pInfo)
break;
}
break;
case EV_ABS:
- switch (ev.code) {
- case ABS_X:
- pEvdev->abs_x = value;
- abs = 1;
- break;
- case ABS_Y:
- pEvdev->abs_y = value;
- abs = 1;
- break;
- case ABS_PRESSURE:
- pEvdev->abs_p = value;
- abs = 1;
- break;
- }
+ if (ev.code > ABS_MAX)
+ break;
+ pEvdev->vals[pEvdev->axis_map[ev.code]] = value;
+ abs = 1;
break;
case EV_KEY:
/* don't repeat mouse buttons */
if (ev.code >= BTN_MOUSE && ev.code < KEY_OK)
@@ -442,21 +457,21 @@ EvdevReadInput(InputInfoPtr pInfo)
break;
}
}
/* convert to relative motion for touchpads */
- if (pEvdev->flags & EVDEV_TOUCHPAD) {
+ if (abs && (pEvdev->flags & EVDEV_TOUCHPAD)) {
abs = 0;
if (pEvdev->tool) { /* meaning, touch is active */
- if (pEvdev->old_x != -1)
- dx = pEvdev->abs_x - pEvdev->old_x;
- if (pEvdev->old_y != -1)
- dy = pEvdev->abs_y - pEvdev->old_y;
- pEvdev->old_x = pEvdev->abs_x;
- pEvdev->old_y = pEvdev->abs_y;
+ if (pEvdev->old_vals[0] != -1)
+ dx = pEvdev->vals[0] - pEvdev->old_vals[0];
+ if (pEvdev->old_vals[1] != -1)
+ dy = pEvdev->vals[1] - pEvdev->old_vals[1];
+ pEvdev->old_vals[0] = pEvdev->vals[0];
+ pEvdev->old_vals[1] = pEvdev->vals[1];
} else {
- pEvdev->old_x = pEvdev->old_y = -1;
+ pEvdev->old_vals[0] = pEvdev->old_vals[1] = -1;
}
}
if (dx != 0 || dy != 0) {
if (pEvdev->swap_axes) {
@@ -479,32 +494,39 @@ EvdevReadInput(InputInfoPtr pInfo)
* pEvdev->digi here, lets us ignore that event. pEvdev is
* initialized to 1 so devices that doesn't use this scheme still
* just works.
*/
if (abs && pEvdev->tool) {
- int v[3];
- v[0] = (pEvdev->swap_axes) ? pEvdev->abs_y : pEvdev->abs_x;
- v[1] = (pEvdev->swap_axes) ? pEvdev->abs_x : pEvdev->abs_y;
- v[2] = pEvdev->abs_p;
+ int v[MAX_VALUATORS];
+
+ memcpy(v, pEvdev->vals, sizeof(int)*pEvdev->num_vals);
+ if (pEvdev->swap_axes) {
+ v[0] ^= v[1];
+ v[1] ^= v[0];
+ v[0] ^= v[1];
+ }
if (pEvdev->flags & EVDEV_CALIBRATED)
{
v[0] = xf86ScaleAxis(v[0],
- pEvdev->max_x, pEvdev->min_x,
+ pEvdev->absinfo[ABS_X].maximum,
+ pEvdev->absinfo[ABS_X].minimum,
pEvdev->calibration.max_x,
pEvdev->calibration.min_x);
v[1] = xf86ScaleAxis(v[1],
- pEvdev->max_y, pEvdev->min_y,
+ pEvdev->absinfo[ABS_Y].maximum,
+ pEvdev->absinfo[ABS_Y].minimum,
pEvdev->calibration.max_y,
pEvdev->calibration.min_y);
}
if (pEvdev->invert_x)
- v[0] = pEvdev->max_x - (v[0] - pEvdev->min_x);
+ v[0] = (pEvdev->absinfo[ABS_X].maximum - v[0] +
+ pEvdev->absinfo[ABS_X].minimum);
if (pEvdev->invert_y)
- v[1] = pEvdev->max_y - (v[1] - pEvdev->min_y);
+ v[1] = (pEvdev->absinfo[ABS_Y].maximum - v[1] +
+ pEvdev->absinfo[ABS_Y].minimum);
- xf86PostMotionEventP(pInfo->dev, TRUE, 0,
- 2 + (pEvdev->has_pressure ? 1 : 0), v);
+ xf86PostMotionEventP(pInfo->dev, TRUE, 0, pEvdev->num_vals, v);
}
}
#define TestBit(bit, array) (array[(bit) / LONG_BITS]) & (1L << ((bit)
% LONG_BITS))
@@ -884,76 +906,64 @@ EvdevAddKeyClass(DeviceIntPtr device)
static int
EvdevAddAbsClass(DeviceIntPtr device)
{
InputInfoPtr pInfo;
EvdevPtr pEvdev;
- struct input_absinfo absinfo_x, absinfo_y, absinfo_p;
- int num_valuators = 2;
+ int num_axes, i, axis;
pInfo = device->public.devicePrivate;
pEvdev = pInfo->private;
- if (ioctl(pInfo->fd,
- EVIOCGABS(ABS_X), &absinfo_x) < 0) {
- xf86Msg(X_ERROR, "ioctl EVIOCGABS failed: %s\n", strerror(errno));
- return !Success;
- }
+ if (!(pEvdev->bitmask & EV_ABS))
+ return !Success;
+ num_axes = CountBits(pEvdev->abs_bitmask, NLONGS(ABS_MAX));
+ if (num_axes < 1)
+ return !Success;
+ pEvdev->num_vals = num_axes;
+ memset(pEvdev->vals, 0, num_axes*sizeof(int));
+ memset(pEvdev->old_vals, -1, num_axes*sizeof(int));
- if (ioctl(pInfo->fd,
- EVIOCGABS(ABS_Y), &absinfo_y) < 0) {
- xf86Msg(X_ERROR, "ioctl EVIOCGABS failed: %s\n", strerror(errno));
- return !Success;
- }
-
- pEvdev->min_x = absinfo_x.minimum;
- pEvdev->max_x = absinfo_x.maximum;
- pEvdev->min_y = absinfo_y.minimum;
- pEvdev->max_y = absinfo_y.maximum;
-
- if (pEvdev->has_pressure &&
- ioctl(pInfo->fd, EVIOCGABS(ABS_PRESSURE), &absinfo_p) < 0) {
- xf86Msg(X_ERROR, "ioctl EVIOCGABS ABS_PRESSURE failed: %s\n",
- strerror(errno));
- return !Success;
- }
-
- if (pEvdev->has_pressure) {
- num_valuators++;
- pEvdev->max_p = absinfo_p.maximum;
- pEvdev->min_p = absinfo_p.minimum;
- }
-
-
- if (!InitValuatorClassDeviceStruct(device, num_valuators,
+ if (!InitValuatorClassDeviceStruct(device, num_axes,
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3
GetMotionHistory,
#endif
GetMotionHistorySize(),
Absolute))
return !Success;
-
- /* X valuator */
- xf86InitValuatorAxisStruct(device, 0, pEvdev->min_x, pEvdev->max_x,
- 10000, 0, 10000);
- xf86InitValuatorDefaults(device, 0);
-
- /* Y valuator */
- xf86InitValuatorAxisStruct(device, 1, pEvdev->min_y, pEvdev->max_y,
- 10000, 0, 10000);
- xf86InitValuatorDefaults(device, 1);
-
- if (pEvdev->has_pressure) {
- xf86InitValuatorAxisStruct(device, 2, pEvdev->min_p,
pEvdev->max_p,
+ for (axis = ABS_X; axis <= ABS_MAX; axis++) {
+ pEvdev->axis_map[axis] = -1;
+ if (!TestBit(axis, pEvdev->abs_bitmask))
+ continue;
+ pEvdev->axis_map[axis] = i;
+ xf86InitValuatorAxisStruct(device, i,
+ pEvdev->absinfo[axis].minimum,
+ pEvdev->absinfo[axis].maximum,
10000, 0, 10000);
- xf86InitValuatorDefaults(device, 2);
+ xf86InitValuatorDefaults(device, i);
+ pEvdev->old_vals[i] = -1;
+ i++;
}
xf86MotionHistoryAllocate(pInfo);
if (!InitPtrFeedbackClassDeviceStruct(device, EvdevPtrCtrlProc))
return !Success;
- pInfo->flags |= XI86_POINTER_CAPABLE;
+ if ((TestBit(ABS_X, pEvdev->abs_bitmask) &&
+ TestBit(ABS_Y, pEvdev->abs_bitmask)) ||
+ (TestBit(ABS_RX, pEvdev->abs_bitmask) &&
+ TestBit(ABS_RY, pEvdev->abs_bitmask)) ||
+ (TestBit(ABS_HAT0X, pEvdev->abs_bitmask) &&
+ TestBit(ABS_HAT0Y, pEvdev->abs_bitmask)) ||
+ (TestBit(ABS_HAT1X, pEvdev->abs_bitmask) &&
+ TestBit(ABS_HAT1Y, pEvdev->abs_bitmask)) ||
+ (TestBit(ABS_HAT2X, pEvdev->abs_bitmask) &&
+ TestBit(ABS_HAT2Y, pEvdev->abs_bitmask)) ||
+ (TestBit(ABS_HAT3X, pEvdev->abs_bitmask) &&
+ TestBit(ABS_HAT3Y, pEvdev->abs_bitmask)) ||
+ (TestBit(ABS_TILT_X, pEvdev->abs_bitmask) &&
+ TestBit(ABS_TILT_Y, pEvdev->abs_bitmask)))
+ pInfo->flags |= XI86_POINTER_CAPABLE;
return Success;
}
static int
@@ -1393,35 +1403,25 @@ EvdevProbe(InputInfoPtr pInfo)
}
if (TestBit(ABS_X, abs_bitmask) && TestBit(ABS_Y, abs_bitmask)) {
xf86Msg(X_INFO, "%s: Found x and y absolute axes\n",
pInfo->name);
pEvdev->flags |= EVDEV_ABSOLUTE_EVENTS;
- if (!pEvdev->has_pressure && TestBit(BTN_TOUCH, key_bitmask)) {
+ if (!TestBit(ABS_PRESSURE, pEvdev->abs_bitmask) &&
+ TestBit(BTN_TOUCH, key_bitmask)) {
if (num_buttons) {
xf86Msg(X_INFO, "%s: Found absolute touchpad\n",
pInfo->name);
pEvdev->flags |= EVDEV_TOUCHPAD;
- pEvdev->old_x = pEvdev->old_y = -1;
+ memset(pEvdev->old_vals, -1, sizeof(int)*pEvdev->num_vals);
} else {
xf86Msg(X_INFO, "%s: Found absolute touchscreen\n",
pInfo->name);
pEvdev->flags |= EVDEV_TOUCHSCREEN;
pEvdev->flags |= EVDEV_BUTTON_EVENTS;
}
}
has_axes = TRUE;
}
- if (TestBit(ABS_PRESSURE, abs_bitmask)) {
- struct input_absinfo absinfo_p;
-
- /* More than two pressure levels indicate it's not a button */
- if (ioctl(pInfo->fd,
- EVIOCGABS(ABS_PRESSURE), &absinfo_p) == 0) {
- if ((absinfo_p.maximum - absinfo_p.minimum) > 1)
- pEvdev->has_pressure = TRUE;
- }
- }
-
for (i = 0; i < BTN_MISC; i++)
if (TestBit(i, key_bitmask))
break;
if (i < BTN_MISC) {
@@ -1431,11 +1431,11 @@ EvdevProbe(InputInfoPtr pInfo)
}
if (has_axes && num_buttons) {
pInfo->flags |= XI86_POINTER_CAPABLE | XI86_SEND_DRAG_EVENTS |
XI86_CONFIGURED;
- if (pEvdev->has_pressure) {
+ if (TestBit(ABS_PRESSURE, pEvdev->abs_bitmask)) {
xf86Msg(X_INFO, "%s: Configuring as tablet\n", pInfo->name);
pInfo->type_name = XI_TABLET;
} else {
xf86Msg(X_INFO, "%s: Configuring as mouse\n", pInfo->name);
pInfo->type_name = XI_MOUSE;
@@ -1517,11 +1517,10 @@ EvdevPreInit(InputDriverPtr drv, IDevPtr
/*
* We initialize pEvdev->tool to 1 so that device that doesn't use
* proximity will still report events.
*/
pEvdev->tool = 1;
- pEvdev->has_pressure = FALSE;
device = xf86CheckStrOption(dev->commonOptions, "Device", NULL);
if (!device) {
xf86Msg(X_ERROR, "%s: No device specified.\n", pInfo->name);
xf86DeleteInput(pInfo, 0);
Index: xf86-input-evdev/src/evdev.h
===================================================================
--- xf86-input-evdev.orig/src/evdev.h
+++ xf86-input-evdev/src/evdev.h
@@ -76,19 +76,22 @@ typedef struct {
typedef struct {
const char *device;
int grabDevice; /* grab the event device? */
int screen;
- int min_x, min_y, max_x, max_y, min_p, max_p;
- int abs_x, abs_y, abs_p, old_x, old_y;
+
+ int num_vals;
+ char axis_map[max(ABS_CNT, REL_CNT)]; /* Map evdev <axis> to index
*/
+ int old_vals[MAX_VALUATORS]; /* Translate absolute inputs to
relative */
+ int vals[MAX_VALUATORS];
+
int flags;
int tool;
int buttons; /* number of buttons */
BOOL swap_axes;
BOOL invert_x;
BOOL invert_y;
- BOOL has_pressure;
/* XKB stuff has to be per-device rather than per-driver */
int noXkb;
#ifdef XKB
char *xkb_rules;
More information about the xorg
mailing list