evdev: Only send the events at synchronization time.

Instead of just posting the button/key press/release events to the
server as soon as they arrive, add them to an internal queue and post
them once we receive an EV_SYN synchronization event.

The motion events are always sent first, followed by the queued events.
There will be one motion event and possibly many queued button/key
events posted every EV_SYN event.

Note that the size of the event queue (EVDEV_MAXQUEUE) is arbitrary and
you may change it. If we receive more events than the queue can handle,
those events are dropped and a warning message printed.

Tested on my Lenovo T400 using evdev for all input devices; keyboard,
touchpad, and trackpoint.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
master
Oliver McFadden 17 years ago committed by Peter Hutterer
parent 2994825665
commit 1f641d75ed
  1. 559
      src/evdev.c
  2. 18
      src/evdev.h

@ -248,22 +248,13 @@ static int wheel_down_button = 5;
static int wheel_left_button = 6; static int wheel_left_button = 6;
static int wheel_right_button = 7; static int wheel_right_button = 7;
static void
PostButtonClicks(InputInfoPtr pInfo, int button, int count)
{
int i;
for (i = 0; i < count; i++) {
xf86PostButtonEvent(pInfo->dev, 0, button, 1, 0, 0);
xf86PostButtonEvent(pInfo->dev, 0, button, 0, 0, 0);
}
}
static void static void
PostKbdEvent(InputInfoPtr pInfo, struct input_event *ev, int value) PostKbdEvent(InputInfoPtr pInfo, struct input_event *ev, int value)
{ {
int code = ev->code + MIN_KEYCODE; int code = ev->code + MIN_KEYCODE;
static char warned[KEY_CNT]; static char warned[KEY_CNT];
EventQueuePtr pQueue;
EvdevPtr pEvdev = pInfo->private;
/* Filter all repeated events from device. /* Filter all repeated events from device.
We'll do softrepeat in the server, but only since 1.6 */ We'll do softrepeat in the server, but only since 1.6 */
@ -292,7 +283,47 @@ PostKbdEvent(InputInfoPtr pInfo, struct input_event *ev, int value)
return; return;
} }
xf86PostKeyboardEvent(pInfo->dev, code, value); if (pEvdev->num_queue >= EVDEV_MAXQUEUE)
{
xf86Msg(X_NONE, "%s: dropping event due to full queue!\n", pInfo->name);
return;
}
pQueue = &pEvdev->queue[pEvdev->num_queue];
pQueue->type = EV_QUEUE_KEY;
pQueue->key = code;
pQueue->val = value;
pEvdev->num_queue++;
}
static void
PostButtonEvent(InputInfoPtr pInfo, int button, int value)
{
EventQueuePtr pQueue;
EvdevPtr pEvdev = pInfo->private;
if (pEvdev->num_queue >= EVDEV_MAXQUEUE)
{
xf86Msg(X_NONE, "%s: dropping event due to full queue!\n", pInfo->name);
return;
}
pQueue = &pEvdev->queue[pEvdev->num_queue];
pQueue->type = EV_QUEUE_BTN;
pQueue->key = button;
pQueue->val = value;
pEvdev->num_queue++;
}
static void
PostButtonClicks(InputInfoPtr pInfo, int button, int count)
{
int i;
for (i = 0; i < count; i++) {
PostButtonEvent(pInfo, button, 1);
PostButtonEvent(pInfo, button, 0);
}
} }
/** /**
@ -349,212 +380,358 @@ EvdevReopenTimer(OsTimerPtr timer, CARD32 time, pointer arg)
#define ABS_Y_VALUE 0x2 #define ABS_Y_VALUE 0x2
#define ABS_VALUE 0x4 #define ABS_VALUE 0x4
/** /**
* Take one input event and process it accordingly. * Take the valuators and process them accordingly.
*/ */
static void static void
EvdevProcessEvent(InputInfoPtr pInfo, struct input_event *ev) EvdevProcessValuators(InputInfoPtr pInfo, int v[MAX_VALUATORS], int *num_v,
int *first_v)
{
int tmp;
EvdevPtr pEvdev = pInfo->private;
*num_v = *first_v = 0;
/* convert to relative motion for touchpads */
if (pEvdev->abs && (pEvdev->flags & EVDEV_TOUCHPAD)) {
if (pEvdev->tool) { /* meaning, touch is active */
if (pEvdev->old_vals[0] != -1)
pEvdev->delta[REL_X] = pEvdev->vals[0] - pEvdev->old_vals[0];
if (pEvdev->old_vals[1] != -1)
pEvdev->delta[REL_Y] = pEvdev->vals[1] - pEvdev->old_vals[1];
if (pEvdev->abs & ABS_X_VALUE)
pEvdev->old_vals[0] = pEvdev->vals[0];
if (pEvdev->abs & ABS_Y_VALUE)
pEvdev->old_vals[1] = pEvdev->vals[1];
} else {
pEvdev->old_vals[0] = pEvdev->old_vals[1] = -1;
}
pEvdev->abs = 0;
pEvdev->rel = 1;
}
if (pEvdev->rel) {
int first = REL_CNT, last = 0;
int i;
if (pEvdev->swap_axes) {
tmp = pEvdev->delta[REL_X];
pEvdev->delta[REL_X] = pEvdev->delta[REL_Y];
pEvdev->delta[REL_Y] = tmp;
}
if (pEvdev->invert_x)
pEvdev->delta[REL_X] *= -1;
if (pEvdev->invert_y)
pEvdev->delta[REL_Y] *= -1;
for (i = 0; i < REL_CNT; i++)
{
int map = pEvdev->axis_map[i];
if (pEvdev->delta[i] && map != -1)
{
v[map] = pEvdev->delta[i];
if (map < first)
first = map;
if (map > last)
last = map;
}
}
*num_v = (last - first + 1);
*first_v = first;
}
/*
* 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.
*/
else if (pEvdev->abs && pEvdev->tool) {
memcpy(v, pEvdev->vals, sizeof(int) * pEvdev->num_vals);
if (pEvdev->flags & EVDEV_CALIBRATED)
{
v[0] = xf86ScaleAxis(v[0],
pEvdev->absinfo[ABS_X].maximum,
pEvdev->absinfo[ABS_X].minimum,
pEvdev->calibration.max_x, pEvdev->calibration.min_x);
v[1] = xf86ScaleAxis(v[1],
pEvdev->absinfo[ABS_Y].maximum,
pEvdev->absinfo[ABS_Y].minimum,
pEvdev->calibration.max_y, pEvdev->calibration.min_y);
}
if (pEvdev->swap_axes) {
int tmp = v[0];
v[0] = v[1];
v[1] = tmp;
}
if (pEvdev->invert_x)
v[0] = (pEvdev->absinfo[ABS_X].maximum - v[0] +
pEvdev->absinfo[ABS_X].minimum);
if (pEvdev->invert_y)
v[1] = (pEvdev->absinfo[ABS_Y].maximum - v[1] +
pEvdev->absinfo[ABS_Y].minimum);
*num_v = pEvdev->num_vals;
*first_v = 0;
}
}
/**
* Take a button input event and process it accordingly.
*/
static void
EvdevProcessButtonEvent(InputInfoPtr pInfo, struct input_event *ev)
{ {
static int delta[REL_CNT];
static int tmp, value;
static unsigned int abs, rel;
unsigned int button; unsigned int button;
int value;
EvdevPtr pEvdev = pInfo->private; EvdevPtr pEvdev = pInfo->private;
button = EvdevUtilButtonEventToButtonNumber(pEvdev, ev->code);
/* Get the signed value, earlier kernels had this as unsigned */ /* Get the signed value, earlier kernels had this as unsigned */
value = ev->value; value = ev->value;
switch (ev->type) { /* Handle drag lock */
case EV_REL: if (EvdevDragLockFilterEvent(pInfo, button, value))
/* Ignore EV_REL events if we never set up for them. */ return;
if (!(pEvdev->flags & EVDEV_RELATIVE_EVENTS))
break;
/* Handle mouse wheel emulation */ if (EvdevWheelEmuFilterButton(pInfo, button, value))
if (EvdevWheelEmuFilterMotion(pInfo, ev)) return;
break;
rel = 1; if (EvdevMBEmuFilterEvent(pInfo, button, value))
return;
switch (ev->code) {
case REL_WHEEL: if (button)
if (value > 0) PostButtonEvent(pInfo, button, value);
PostButtonClicks(pInfo, wheel_up_button, value); else
else if (value < 0) PostKbdEvent(pInfo, ev, value);
PostButtonClicks(pInfo, wheel_down_button, -value); }
break;
/**
case REL_DIAL: * Take the relative motion input event and process it accordingly.
case REL_HWHEEL: */
if (value > 0) static void
PostButtonClicks(pInfo, wheel_right_button, value); EvdevProcessRelativeMotionEvent(InputInfoPtr pInfo, struct input_event *ev)
else if (value < 0) {
PostButtonClicks(pInfo, wheel_left_button, -value); static int value;
break; EvdevPtr pEvdev = pInfo->private;
/* We don't post wheel events as axis motion. */ /* Get the signed value, earlier kernels had this as unsigned */
default: value = ev->value;
delta[ev->code] += value;
break; /* Ignore EV_REL events if we never set up for them. */
} if (!(pEvdev->flags & EVDEV_RELATIVE_EVENTS))
return;
/* Handle mouse wheel emulation */
if (EvdevWheelEmuFilterMotion(pInfo, ev))
return;
pEvdev->rel = 1;
switch (ev->code) {
case REL_WHEEL:
if (value > 0)
PostButtonClicks(pInfo, wheel_up_button, value);
else if (value < 0)
PostButtonClicks(pInfo, wheel_down_button, -value);
break; break;
case EV_ABS: case REL_DIAL:
/* Ignore EV_ABS events if we never set up for them. */ case REL_HWHEEL:
if (!(pEvdev->flags & EVDEV_ABSOLUTE_EVENTS)) if (value > 0)
break; PostButtonClicks(pInfo, wheel_right_button, value);
else if (value < 0)
PostButtonClicks(pInfo, wheel_left_button, -value);
break;
if (ev->code > ABS_MAX) /* We don't post wheel events as axis motion. */
break; default:
pEvdev->vals[pEvdev->axis_map[ev->code]] = value; pEvdev->delta[ev->code] += value;
if (ev->code == ABS_X)
abs |= ABS_X_VALUE;
else if (ev->code == ABS_Y)
abs |= ABS_Y_VALUE;
else
abs |= ABS_VALUE;
break; break;
}
}
case EV_KEY: /**
/* don't repeat mouse buttons */ * Take the absolute motion input event and process it accordingly.
if (ev->code >= BTN_MOUSE && ev->code < KEY_OK) */
if (value == 2) static void
break; EvdevProcessAbsoluteMotionEvent(InputInfoPtr pInfo, struct input_event *ev)
{
switch (ev->code) { static int value;
case BTN_TOUCH: EvdevPtr pEvdev = pInfo->private;
case BTN_TOOL_PEN:
case BTN_TOOL_RUBBER: /* Get the signed value, earlier kernels had this as unsigned */
case BTN_TOOL_BRUSH: value = ev->value;
case BTN_TOOL_PENCIL:
case BTN_TOOL_AIRBRUSH: /* Ignore EV_ABS events if we never set up for them. */
case BTN_TOOL_FINGER: if (!(pEvdev->flags & EVDEV_ABSOLUTE_EVENTS))
case BTN_TOOL_MOUSE: return;
case BTN_TOOL_LENS:
pEvdev->tool = value ? ev->code : 0; if (ev->code > ABS_MAX)
if (!(pEvdev->flags & EVDEV_TOUCHSCREEN)) return;
break;
/* Treat BTN_TOUCH from devices that only have BTN_TOUCH as pEvdev->vals[pEvdev->axis_map[ev->code]] = value;
* BTN_LEFT. */ if (ev->code == ABS_X)
ev->code = BTN_LEFT; pEvdev->abs |= ABS_X_VALUE;
/* Intentional fallthrough! */ else if (ev->code == ABS_Y)
pEvdev->abs |= ABS_Y_VALUE;
default: else
button = EvdevUtilButtonEventToButtonNumber(pEvdev, ev->code); pEvdev->abs |= ABS_VALUE;
}
/* Handle drag lock */
if (EvdevDragLockFilterEvent(pInfo, button, value)) /**
break; * Take the key press/release input event and process it accordingly.
*/
if (EvdevWheelEmuFilterButton(pInfo, button, value)) static void
break; EvdevProcessKeyEvent(InputInfoPtr pInfo, struct input_event *ev)
{
if (EvdevMBEmuFilterEvent(pInfo, button, value)) static int value;
break; EvdevPtr pEvdev = pInfo->private;
if (button) /* Get the signed value, earlier kernels had this as unsigned */
xf86PostButtonEvent(pInfo->dev, 0, button, value, 0, 0); value = ev->value;
else
PostKbdEvent(pInfo, ev, value); /* don't repeat mouse buttons */
break; if (ev->code >= BTN_MOUSE && ev->code < KEY_OK)
} if (value == 2)
return;
switch (ev->code) {
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;
if (!(pEvdev->flags & EVDEV_TOUCHSCREEN))
break;
/* Treat BTN_TOUCH from devices that only have BTN_TOUCH as
* BTN_LEFT. */
ev->code = BTN_LEFT;
/* Intentional fallthrough! */
default:
EvdevProcessButtonEvent(pInfo, ev);
break; break;
}
}
case EV_SYN: /**
/* convert to relative motion for touchpads */ * Post the relative motion events.
if (abs && (pEvdev->flags & EVDEV_TOUCHPAD)) { */
if (pEvdev->tool) { /* meaning, touch is active */ static void
if (pEvdev->old_vals[0] != -1) EvdevPostRelativeMotionEvents(InputInfoPtr pInfo, int *num_v, int *first_v,
delta[REL_X] = pEvdev->vals[0] - pEvdev->old_vals[0]; int v[MAX_VALUATORS])
if (pEvdev->old_vals[1] != -1) {
delta[REL_Y] = pEvdev->vals[1] - pEvdev->old_vals[1]; EvdevPtr pEvdev = pInfo->private;
if (abs & ABS_X_VALUE)
pEvdev->old_vals[0] = pEvdev->vals[0];
if (abs & ABS_Y_VALUE)
pEvdev->old_vals[1] = pEvdev->vals[1];
} else {
pEvdev->old_vals[0] = pEvdev->old_vals[1] = -1;
}
abs = 0;
rel = 1;
}
if (rel) { if (pEvdev->rel) {
int post_deltas[REL_CNT] = {0}; /* axis-mapped deltas */ xf86PostMotionEventP(pInfo->dev, FALSE, *first_v, *num_v, v + *first_v);
int first = REL_CNT, last = 0; }
int i; }
if (pEvdev->swap_axes) { /**
tmp = delta[REL_X]; * Post the absolute motion events.
delta[REL_X] = delta[REL_Y]; */
delta[REL_Y] = tmp; static void
} EvdevPostAbsoluteMotionEvents(InputInfoPtr pInfo, int *num_v, int *first_v,
if (pEvdev->invert_x) int v[MAX_VALUATORS])
delta[REL_X] *= -1; {
if (pEvdev->invert_y) EvdevPtr pEvdev = pInfo->private;
delta[REL_Y] *= -1;
for (i = 0; i < REL_CNT; i++) /*
{ * Some devices only generate valid abs coords when BTN_DIGI is
int map = pEvdev->axis_map[i]; * pressed. On wacom tablets, this means that the pen is in
if (delta[i] && map != -1) * proximity of the tablet. After the pen is removed, BTN_DIGI is
{ * released, and a (0, 0) absolute event is generated. Checking
post_deltas[map] = delta[i]; * pEvdev->digi here, lets us ignore that event. pEvdev is
if (map < first) * initialized to 1 so devices that doesn't use this scheme still
first = map; * just works.
if (map > last) */
last = map; if (pEvdev->abs && pEvdev->tool) {
} xf86PostMotionEventP(pInfo->dev, TRUE, *first_v, *num_v, v);
} }
}
xf86PostMotionEventP(pInfo->dev, FALSE, first, /**
(last - first + 1), &post_deltas[first]); * Post the queued key/button events.
} */
static void EvdevPostQueuedEvents(InputInfoPtr pInfo, int *num_v, int *first_v,
int v[MAX_VALUATORS])
{
int i;
EvdevPtr pEvdev = pInfo->private;
/* for (i = 0; i < pEvdev->num_queue; i++) {
* Some devices only generate valid abs coords when BTN_DIGI is switch (pEvdev->queue[i].type) {
* pressed. On wacom tablets, this means that the pen is in case EV_QUEUE_KEY:
* proximity of the tablet. After the pen is removed, BTN_DIGI is xf86PostKeyboardEvent(pInfo->dev, pEvdev->queue[i].key,
* released, and a (0, 0) absolute event is generated. Checking pEvdev->queue[i].val);
* pEvdev->digi here, lets us ignore that event. pEvdev is break;
* initialized to 1 so devices that doesn't use this scheme still case EV_QUEUE_BTN:
* just works. /* FIXME: Add xf86PostButtonEventP to the X server so that we may
*/ * pass the valuators on ButtonPress/Release events, too. Currently
if (abs && pEvdev->tool) { * only MotionNotify events contain the pointer position. */
int v[MAX_VALUATORS]; xf86PostButtonEvent(pInfo->dev, 0, pEvdev->queue[i].key,
pEvdev->queue[i].val, 0, 0);
memcpy(v, pEvdev->vals, sizeof(int) * pEvdev->num_vals); break;
if (pEvdev->flags & EVDEV_CALIBRATED) }
{ }
v[0] = xf86ScaleAxis(v[0], }
pEvdev->absinfo[ABS_X].maximum,
pEvdev->absinfo[ABS_X].minimum,
pEvdev->calibration.max_x, pEvdev->calibration.min_x);
v[1] = xf86ScaleAxis(v[1],
pEvdev->absinfo[ABS_Y].maximum,
pEvdev->absinfo[ABS_Y].minimum,
pEvdev->calibration.max_y, pEvdev->calibration.min_y);
}
if (pEvdev->swap_axes) { /**
int tmp = v[0]; * Take the synchronization input event and process it accordingly; the motion
v[0] = v[1]; * notify events are sent first, then any button/key press/release events.
v[1] = tmp; */
} static void
EvdevProcessSyncEvent(InputInfoPtr pInfo, struct input_event *ev)
{
int num_v = 0, first_v = 0;
int v[MAX_VALUATORS];
EvdevPtr pEvdev = pInfo->private;
if (pEvdev->invert_x) EvdevProcessValuators(pInfo, v, &num_v, &first_v);
v[0] = (pEvdev->absinfo[ABS_X].maximum - v[0] +
pEvdev->absinfo[ABS_X].minimum);
if (pEvdev->invert_y)
v[1] = (pEvdev->absinfo[ABS_Y].maximum - v[1] +
pEvdev->absinfo[ABS_Y].minimum);
xf86PostMotionEventP(pInfo->dev, TRUE, 0, pEvdev->num_vals, v); EvdevPostRelativeMotionEvents(pInfo, &num_v, &first_v, v);
} EvdevPostAbsoluteMotionEvents(pInfo, &num_v, &first_v, v);
EvdevPostQueuedEvents(pInfo, &num_v, &first_v, v);
memset(pEvdev->delta, 0, sizeof(pEvdev->delta));
memset(pEvdev->queue, 0, sizeof(pEvdev->queue));
pEvdev->num_queue = 0;
pEvdev->abs = 0;
pEvdev->rel = 0;
}
memset(delta, 0, sizeof(delta)); /**
tmp = 0; * Process the events from the device; nothing is actually posted to the server
abs = 0; * until an EV_SYN event is received.
rel = 0; */
static void
EvdevProcessEvent(InputInfoPtr pInfo, struct input_event *ev)
{
switch (ev->type) {
case EV_REL:
EvdevProcessRelativeMotionEvent(pInfo, ev);
break;
case EV_ABS:
EvdevProcessAbsoluteMotionEvent(pInfo, ev);
break;
case EV_KEY:
EvdevProcessKeyEvent(pInfo, ev);
break;
case EV_SYN:
EvdevProcessSyncEvent(pInfo, ev);
break;
} }
} }

@ -54,6 +54,7 @@
#endif #endif
#define EVDEV_MAXBUTTONS 32 #define EVDEV_MAXBUTTONS 32
#define EVDEV_MAXQUEUE 32
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
#define HAVE_PROPERTIES 1 #define HAVE_PROPERTIES 1
@ -87,6 +88,16 @@ typedef struct {
int traveled_distance; int traveled_distance;
} WheelAxis, *WheelAxisPtr; } WheelAxis, *WheelAxisPtr;
/* Event queue used to defer keyboard/button events until EV_SYN time. */
typedef struct {
enum {
EV_QUEUE_KEY, /* xf86PostKeyboardEvent() */
EV_QUEUE_BTN, /* xf86PostButtonEvent() */
} type;
int key; /* May be either a key code or button number. */
int val; /* State of the key/button; pressed or released. */
} EventQueueRec, *EventQueuePtr;
typedef struct { typedef struct {
const char *device; const char *device;
int grabDevice; /* grab the event device? */ int grabDevice; /* grab the event device? */
@ -103,6 +114,9 @@ typedef struct {
BOOL invert_x; BOOL invert_x;
BOOL invert_y; BOOL invert_y;
int delta[REL_CNT];
unsigned int abs, rel;
/* XKB stuff has to be per-device rather than per-driver */ /* XKB stuff has to be per-device rather than per-driver */
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 5 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 5
XkbComponentNamesRec xkbnames; XkbComponentNamesRec xkbnames;
@ -159,6 +173,10 @@ typedef struct {
/* minor/major number */ /* minor/major number */
dev_t min_maj; dev_t min_maj;
/* Event queue used to defer keyboard/button events until EV_SYN time. */
int num_queue;
EventQueueRec queue[EVDEV_MAXQUEUE];
} EvdevRec, *EvdevPtr; } EvdevRec, *EvdevPtr;
unsigned int EvdevUtilButtonEventToButtonNumber(EvdevPtr pEvdev, int code); unsigned int EvdevUtilButtonEventToButtonNumber(EvdevPtr pEvdev, int code);

Loading…
Cancel
Save