aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Jackson <ajax@redhat.com>2008-03-08 19:54:44 -0500
committerAdam Jackson <ajax@redhat.com>2008-03-08 19:54:44 -0500
commit6271494faa4c45f4fa10509f72e0515f2cef36c6 (patch)
treea496a2d6d151af6e89ad9e6a33e82ec61347be1d
parentNuke the keyboard bell code. (diff)
downloadxf86-input-evdev-6271494faa4c45f4fa10509f72e0515f2cef36c6.tar.gz
xf86-input-evdev-6271494faa4c45f4fa10509f72e0515f2cef36c6.tar.bz2
xf86-input-evdev-6271494faa4c45f4fa10509f72e0515f2cef36c6.zip
Add absolute coordinate event support.
There are two major classes here, touchscreens and touchpads. Touchpads are logically more like relative devices, in that your initial touch should not warp the cursor. So attempt to detect touchpads (via the existence of BTN_TOUCH) and translate absolute events from those devices to relative motion.
-rw-r--r--src/evdev.c213
1 files changed, 186 insertions, 27 deletions
diff --git a/src/evdev.c b/src/evdev.c
index 6dd6c8d..b671f1c 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -69,6 +69,13 @@
#define ArrayLength(a) (sizeof(a) / (sizeof((a)[0])))
+/* evdev flags */
+#define EVDEV_KEYBOARD_EVENTS (1 << 0)
+#define EVDEV_BUTTON_EVENTS (1 << 1)
+#define EVDEV_RELATIVE_EVENTS (1 << 2)
+#define EVDEV_ABSOLUTE_EVENTS (1 << 3)
+#define EVDEV_TOUCHPAD (1 << 4)
+
#define MIN_KEYCODE 8
#define GLYPHS_PER_KEY 2
#define AltMask Mod1Mask
@@ -85,9 +92,14 @@
typedef struct {
int kernel24;
- int noXkb;
+ int screen;
+ int min_x, min_y, max_x, max_y;
+ int abs_x, abs_y, old_x, old_y;
+ int flags;
+ int tool;
/* XKB stuff has to be per-device rather than per-driver */
+ int noXkb;
#ifdef XKB
char *xkb_rules;
char *xkb_model;
@@ -178,9 +190,13 @@ EvdevReadInput(InputInfoPtr pInfo)
struct input_event ev;
int len, value;
int dx, dy;
+ unsigned int abs;
+ Bool do_touchpad_motion = FALSE;
+ EvdevPtr pEvdev = pInfo->private;
dx = 0;
dy = 0;
+ abs = 0;
while (xf86WaitForInput (pInfo->fd, 0) > 0) {
len = read(pInfo->fd, &ev, sizeof ev);
@@ -195,7 +211,7 @@ EvdevReadInput(InputInfoPtr pInfo)
value = ev.value;
switch (ev.type) {
- case EV_REL:
+ case EV_REL:
switch (ev.code) {
case REL_X:
dx += value;
@@ -221,8 +237,18 @@ EvdevReadInput(InputInfoPtr pInfo)
}
break;
- case EV_ABS:
- 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;
+ }
+ break;
case EV_KEY:
switch (ev.code) {
@@ -242,11 +268,24 @@ EvdevReadInput(InputInfoPtr pInfo)
value, 0, 0);
break;
+ case BTN_TOUCH:
+ case BTN_TOOL_PEN:
+ case BTN_TOOL_RUBBER:
+ case BTN_TOOL_BRUSH:
+ case BTN_TOOL_PENCIL:
+ case BTN_TOOL_AIRBRUSH:
+ case BTN_TOOL_FINGER:
+ case BTN_TOOL_MOUSE:
+ case BTN_TOOL_LENS:
+ pEvdev->tool = value ? ev.code : 0;
+ break;
+
default:
if (ev.code > BTN_TASK && ev.code < KEY_OK)
break;
PostKbdEvent(pInfo, &ev, value);
+ break;
}
break;
@@ -255,8 +294,37 @@ EvdevReadInput(InputInfoPtr pInfo)
}
}
+ /* convert to relative motion for touchpads */
+ if (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_x != -1)
+ dy = pEvdev->abs_y - pEvdev->old_y;
+ pEvdev->old_x = pEvdev->abs_x;
+ pEvdev->old_y = pEvdev->abs_y;
+ } else {
+ pEvdev->old_x = pEvdev->old_y = -1;
+ }
+ }
+
if (dx != 0 || dy != 0)
- xf86PostMotionEvent(pInfo->dev, 0, 0, 2, dx, dy);
+ xf86PostMotionEvent(pInfo->dev, FALSE, 0, 2, dx, dy);
+
+ /*
+ * Some devices only generate valid abs coords when BTN_DIGI is
+ * pressed. On wacom tablets, this means that the pen is in
+ * proximity of the tablet. After the pen is removed, BTN_DIGI is
+ * released, and a (0, 0) absolute event is generated. Checking
+ * 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) {
+ xf86PostMotionEvent(pInfo->dev, TRUE, 0, 2,
+ pEvdev->abs_x, pEvdev->abs_y);
+ }
}
#define TestBit(bit, array) (array[(bit) / 8] & (1 << ((bit) % 8)))
@@ -513,6 +581,56 @@ EvdevAddKeyClass(DeviceIntPtr device)
}
static int
+EvdevAddAbsClass(DeviceIntPtr device)
+{
+ InputInfoPtr pInfo;
+ EvdevPtr pEvdev;
+ struct input_absinfo absinfo_x, absinfo_y;
+
+ 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 (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 (!InitValuatorClassDeviceStruct(device, 2, GetMotionHistory,
+ 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);
+ xf86MotionHistoryAllocate(pInfo);
+
+ if (!InitPtrFeedbackClassDeviceStruct(device, EvdevPtrCtrlProc))
+ return !Success;
+
+ pInfo->flags |= XI86_POINTER_CAPABLE;
+
+ return Success;
+}
+
+static int
EvdevAddRelClass(DeviceIntPtr device)
{
InputInfoPtr pInfo;
@@ -520,7 +638,7 @@ EvdevAddRelClass(DeviceIntPtr device)
pInfo = device->public.devicePrivate;
if (!InitValuatorClassDeviceStruct(device, 2, GetMotionHistory,
- GetMotionHistorySize(), 0))
+ GetMotionHistorySize(), Relative))
return !Success;
/* X valuator */
@@ -535,6 +653,8 @@ EvdevAddRelClass(DeviceIntPtr device)
if (!InitPtrFeedbackClassDeviceStruct(device, EvdevPtrCtrlProc))
return !Success;
+ pInfo->flags |= XI86_POINTER_CAPABLE;
+
xf86MotionHistoryAllocate(pInfo);
return Success;
@@ -562,8 +682,6 @@ EvdevAddButtonClass(DeviceIntPtr device)
if (!InitButtonClassDeviceStruct(device, ArrayLength(map), map))
return !Success;
- pInfo->flags |= XI86_POINTER_CAPABLE | XI86_SEND_DRAG_EVENTS;
-
return Success;
}
@@ -571,20 +689,21 @@ static int
EvdevInit(DeviceIntPtr device)
{
InputInfoPtr pInfo;
+ EvdevPtr pEvdev;
pInfo = device->public.devicePrivate;
+ pEvdev = pInfo->private;
- /* FIXME: This doesn't add buttons for keyboards with
- * scrollwheels. */
+ /* FIXME: This doesn't add buttons for keyboards with scrollwheels. */
- if (pInfo->flags & XI86_KEYBOARD_CAPABLE) {
+ if (pEvdev->flags & EVDEV_KEYBOARD_EVENTS)
EvdevAddKeyClass(device);
- }
-
- if (pInfo->flags & XI86_POINTER_CAPABLE) {
+ if (pEvdev->flags & EVDEV_BUTTON_EVENTS)
EvdevAddButtonClass(device);
+ if (pEvdev->flags & EVDEV_RELATIVE_EVENTS)
EvdevAddRelClass(device);
- }
+ if (pEvdev->flags & EVDEV_ABSOLUTE_EVENTS)
+ EvdevAddAbsClass(device);
return Success;
}
@@ -631,13 +750,24 @@ static Bool
EvdevConvert(InputInfoPtr pInfo, int first, int num, int v0, int v1, int v2,
int v3, int v4, int v5, int *x, int *y)
{
- if (first == 0 && num == 2) {
- *x = v0;
- *y = v1;
- return TRUE;
+ EvdevPtr pEvdev = pInfo->private;
+ int screenWidth = screenInfo.screens[pEvdev->screen]->width;
+ int screenHeight = screenInfo.screens[pEvdev->screen]->height;
+
+ if (first != 0 || num != 2)
+ return FALSE;
+
+ /* on absolute touchpads, don't warp on initial touch */
+ if (pEvdev->flags & EVDEV_TOUCHPAD) {
+ *x = v0;
+ *y = v0;
+ return TRUE;
}
- else
- return FALSE;
+
+ *x = (v0 - pEvdev->min_x) * screenWidth / (pEvdev->max_x - pEvdev->min_x);
+ *y = (v1 - pEvdev->min_y) * screenHeight / (pEvdev->max_y - pEvdev->min_y);
+
+ return TRUE;
}
static int
@@ -645,6 +775,7 @@ EvdevProbe(InputInfoPtr pInfo)
{
char key_bitmask[(KEY_MAX + 7) / 8];
char rel_bitmask[(REL_MAX + 7) / 8];
+ char abs_bitmask[(ABS_MAX + 7) / 8];
int i, has_axes, has_buttons, has_keys;
EvdevPtr pEvdev = pInfo->private;
@@ -662,6 +793,12 @@ EvdevProbe(InputInfoPtr pInfo)
}
if (ioctl(pInfo->fd,
+ EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) < 0) {
+ xf86Msg(X_ERROR, "ioctl EVIOCGBIT failed: %s\n", strerror(errno));
+ return 1;
+ }
+
+ if (ioctl(pInfo->fd,
EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) < 0) {
xf86Msg(X_ERROR, "ioctl EVIOCGBIT failed: %s\n", strerror(errno));
return 1;
@@ -673,12 +810,24 @@ EvdevProbe(InputInfoPtr pInfo)
if (TestBit(REL_X, rel_bitmask) && TestBit(REL_Y, rel_bitmask)) {
xf86Msg(X_INFO, "%s: Found x and y relative axes\n", pInfo->name);
+ pEvdev->flags |= EVDEV_RELATIVE_EVENTS;
has_axes = TRUE;
}
+ if (TestBit(ABS_X, abs_bitmask) && TestBit(REL_Y, abs_bitmask)) {
+ xf86Msg(X_INFO, "%s: Found x and y absolute axes\n", pInfo->name);
+ pEvdev->flags |= EVDEV_ABSOLUTE_EVENTS;
+ if (TestBit(BTN_TOUCH, key_bitmask)) {
+ xf86Msg(X_INFO, "%s: Found absolute touchpad\n", pInfo->name);
+ pEvdev->flags |= EVDEV_TOUCHPAD;
+ pEvdev->old_x = pEvdev->old_y = -1;
+ }
+ has_axes = TRUE;
+ }
if (TestBit(BTN_LEFT, key_bitmask)) {
xf86Msg(X_INFO, "%s: Found mouse buttons\n", pInfo->name);
+ pEvdev->flags |= EVDEV_BUTTON_EVENTS;
has_buttons = TRUE;
}
@@ -688,6 +837,7 @@ EvdevProbe(InputInfoPtr pInfo)
if (i < BTN_MISC) {
xf86Msg(X_INFO, "%s: Found keys\n", pInfo->name);
+ pEvdev->flags |= EVDEV_KEYBOARD_EVENTS;
has_keys = TRUE;
}
@@ -758,13 +908,20 @@ EvdevPreInit(InputDriverPtr drv, IDevPtr dev, int flags)
xf86CollectInputOptions(pInfo, evdevDefaults, NULL);
xf86ProcessCommonOptions(pInfo, pInfo->options);
+ pEvdev->screen = xf86SetIntOption(pInfo->options, "ScreenNumber", 0);
+ /*
+ * We initialize pEvdev->tool to 1 so that device that doesn't use
+ * proximity will still report events.
+ */
+ pEvdev->tool = 1;
+
device = xf86CheckStrOption(dev->commonOptions, "Path", NULL);
if (!device)
device = xf86CheckStrOption(dev->commonOptions, "Device", NULL);
if (!device) {
xf86Msg(X_ERROR, "%s: No device specified.\n", pInfo->name);
- xfree(pEvdev);
- return pInfo;
+ xf86DeleteInput(pInfo, 0);
+ return NULL;
}
xf86Msg(deviceFrom, "%s: Device: \"%s\"\n", pInfo->name, device);
@@ -775,15 +932,17 @@ EvdevPreInit(InputDriverPtr drv, IDevPtr dev, int flags)
if (pInfo->fd < 0) {
xf86Msg(X_ERROR, "Unable to open evdev device \"%s\".\n", device);
- xfree(pEvdev);
- return pInfo;
+ xf86DeleteInput(pInfo, 0);
+ return NULL;
}
pEvdev->noXkb = noXkbExtension;
/* parse the XKB options during kbd setup */
- if (EvdevProbe(pInfo))
- xfree(pEvdev);
+ if (EvdevProbe(pInfo)) {
+ xf86DeleteInput(pInfo, 0);
+ return NULL;
+ }
return pInfo;
}