From 1f641d75edba7394201c1c53938215bae696791b Mon Sep 17 00:00:00 2001 From: Oliver McFadden Date: Thu, 23 Jul 2009 13:19:49 +0300 Subject: 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 --- src/evdev.c | 559 +++++++++++++++++++++++++++++++++++++++--------------------- src/evdev.h | 18 ++ 2 files changed, 386 insertions(+), 191 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 0387a14..e4e6d72 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -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; } } diff --git a/src/evdev.h b/src/evdev.h index 5b95369..23ee003 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -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); -- cgit v1.2.3