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_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
PostKbdEvent(InputInfoPtr pInfo, struct input_event *ev, int value)
{
int code = ev->code + MIN_KEYCODE;
static char warned[KEY_CNT];
EventQueuePtr pQueue;
EvdevPtr pEvdev = pInfo->private;
/* Filter all repeated events from device.
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;
}
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_VALUE 0x4
/**
* Take one input event and process it accordingly.
* Take the valuators and process them accordingly.
*/
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;
int value;
EvdevPtr pEvdev = pInfo->private;
button = EvdevUtilButtonEventToButtonNumber(pEvdev, ev->code);
/* Get the signed value, earlier kernels had this as unsigned */
value = ev->value;
switch (ev->type) {
case EV_REL:
/* Ignore EV_REL events if we never set up for them. */
if (!(pEvdev->flags & EVDEV_RELATIVE_EVENTS))
break;
/* Handle drag lock */
if (EvdevDragLockFilterEvent(pInfo, button, value))
return;
/* Handle mouse wheel emulation */
if (EvdevWheelEmuFilterMotion(pInfo, ev))
break;
if (EvdevWheelEmuFilterButton(pInfo, button, value))
return;
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;
case REL_DIAL:
case REL_HWHEEL:
if (value > 0)
PostButtonClicks(pInfo, wheel_right_button, value);
else if (value < 0)
PostButtonClicks(pInfo, wheel_left_button, -value);
break;
/* We don't post wheel events as axis motion. */
default:
delta[ev->code] += value;
break;
}
if (EvdevMBEmuFilterEvent(pInfo, button, value))
return;
if (button)
PostButtonEvent(pInfo, button, value);
else
PostKbdEvent(pInfo, ev, value);
}
/**
* Take the relative motion input event and process it accordingly.
*/
static void
EvdevProcessRelativeMotionEvent(InputInfoPtr pInfo, struct input_event *ev)
{
static int value;
EvdevPtr pEvdev = pInfo->private;
/* Get the signed value, earlier kernels had this as unsigned */
value = ev->value;
/* 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;
case EV_ABS:
/* Ignore EV_ABS events if we never set up for them. */
if (!(pEvdev->flags & EVDEV_ABSOLUTE_EVENTS))
break;
case REL_DIAL:
case REL_HWHEEL:
if (value > 0)
PostButtonClicks(pInfo, wheel_right_button, value);
else if (value < 0)
PostButtonClicks(pInfo, wheel_left_button, -value);
break;
if (ev->code > ABS_MAX)
break;
pEvdev->vals[pEvdev->axis_map[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;
/* We don't post wheel events as axis motion. */
default:
pEvdev->delta[ev->code] += value;
break;
}
}
case EV_KEY:
/* don't repeat mouse buttons */
if (ev->code >= BTN_MOUSE && ev->code < KEY_OK)
if (value == 2)
break;
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:
button = EvdevUtilButtonEventToButtonNumber(pEvdev, ev->code);
/* Handle drag lock */
if (EvdevDragLockFilterEvent(pInfo, button, value))
break;
if (EvdevWheelEmuFilterButton(pInfo, button, value))
break;
if (EvdevMBEmuFilterEvent(pInfo, button, value))
break;
if (button)
xf86PostButtonEvent(pInfo->dev, 0, button, value, 0, 0);
else
PostKbdEvent(pInfo, ev, value);
break;
}
/**
* Take the absolute motion input event and process it accordingly.
*/
static void
EvdevProcessAbsoluteMotionEvent(InputInfoPtr pInfo, struct input_event *ev)
{
static int value;
EvdevPtr pEvdev = pInfo->private;
/* Get the signed value, earlier kernels had this as unsigned */
value = ev->value;
/* Ignore EV_ABS events if we never set up for them. */
if (!(pEvdev->flags & EVDEV_ABSOLUTE_EVENTS))
return;
if (ev->code > ABS_MAX)
return;
pEvdev->vals[pEvdev->axis_map[ev->code]] = value;
if (ev->code == ABS_X)
pEvdev->abs |= ABS_X_VALUE;
else if (ev->code == ABS_Y)
pEvdev->abs |= ABS_Y_VALUE;
else
pEvdev->abs |= ABS_VALUE;
}
/**
* Take the key press/release input event and process it accordingly.
*/
static void
EvdevProcessKeyEvent(InputInfoPtr pInfo, struct input_event *ev)
{
static int value;
EvdevPtr pEvdev = pInfo->private;
/* Get the signed value, earlier kernels had this as unsigned */
value = ev->value;
/* don't repeat mouse buttons */
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;
}
}
case EV_SYN:
/* convert to relative motion for touchpads */
if (abs && (pEvdev->flags & EVDEV_TOUCHPAD)) {
if (pEvdev->tool) { /* meaning, touch is active */
if (pEvdev->old_vals[0] != -1)
delta[REL_X] = pEvdev->vals[0] - pEvdev->old_vals[0];
if (pEvdev->old_vals[1] != -1)
delta[REL_Y] = pEvdev->vals[1] - pEvdev->old_vals[1];
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;
}
/**
* Post the relative motion events.
*/
static void
EvdevPostRelativeMotionEvents(InputInfoPtr pInfo, int *num_v, int *first_v,
int v[MAX_VALUATORS])
{
EvdevPtr pEvdev = pInfo->private;
if (rel) {
int post_deltas[REL_CNT] = {0}; /* axis-mapped deltas */
int first = REL_CNT, last = 0;
int i;
if (pEvdev->rel) {
xf86PostMotionEventP(pInfo->dev, FALSE, *first_v, *num_v, v + *first_v);
}
}
if (pEvdev->swap_axes) {
tmp = delta[REL_X];
delta[REL_X] = delta[REL_Y];
delta[REL_Y] = tmp;
}
if (pEvdev->invert_x)
delta[REL_X] *= -1;
if (pEvdev->invert_y)
delta[REL_Y] *= -1;
/**
* Post the absolute motion events.
*/
static void
EvdevPostAbsoluteMotionEvents(InputInfoPtr pInfo, int *num_v, int *first_v,
int v[MAX_VALUATORS])
{
EvdevPtr pEvdev = pInfo->private;
for (i = 0; i < REL_CNT; i++)
{
int map = pEvdev->axis_map[i];
if (delta[i] && map != -1)
{
post_deltas[map] = delta[i];
if (map < first)
first = map;
if (map > last)
last = map;
}
}
/*
* 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 (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;
/*
* 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) {
int v[MAX_VALUATORS];
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);
}
for (i = 0; i < pEvdev->num_queue; i++) {
switch (pEvdev->queue[i].type) {
case EV_QUEUE_KEY:
xf86PostKeyboardEvent(pInfo->dev, pEvdev->queue[i].key,
pEvdev->queue[i].val);
break;
case EV_QUEUE_BTN:
/* FIXME: Add xf86PostButtonEventP to the X server so that we may
* pass the valuators on ButtonPress/Release events, too. Currently
* only MotionNotify events contain the pointer position. */
xf86PostButtonEvent(pInfo->dev, 0, pEvdev->queue[i].key,
pEvdev->queue[i].val, 0, 0);
break;
}
}
}
if (pEvdev->swap_axes) {
int tmp = v[0];
v[0] = v[1];
v[1] = tmp;
}
/**
* Take the synchronization input event and process it accordingly; the motion
* notify events are sent first, then any button/key press/release events.
*/
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)
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);
EvdevProcessValuators(pInfo, v, &num_v, &first_v);
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;
abs = 0;
rel = 0;
/**
* Process the events from the device; nothing is actually posted to the server
* until an EV_SYN event is received.
*/
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
#define EVDEV_MAXBUTTONS 32
#define EVDEV_MAXQUEUE 32
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
#define HAVE_PROPERTIES 1
@ -87,6 +88,16 @@ typedef struct {
int traveled_distance;
} 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 {
const char *device;
int grabDevice; /* grab the event device? */
@ -103,6 +114,9 @@ typedef struct {
BOOL invert_x;
BOOL invert_y;
int delta[REL_CNT];
unsigned int abs, rel;
/* XKB stuff has to be per-device rather than per-driver */
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 5
XkbComponentNamesRec xkbnames;
@ -159,6 +173,10 @@ typedef struct {
/* minor/major number */
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;
unsigned int EvdevUtilButtonEventToButtonNumber(EvdevPtr pEvdev, int code);

Loading…
Cancel
Save