[PATCH evdev 2/2] Add SYN_DROPPED handling #59702
Chung-Yih Wang (王崇懿)
cywang at google.com
Wed Mar 27 03:33:21 PDT 2013
Sorry for unresponsiveness for a long time as there were some family
issues I had to handle in the past few weeks.
Please see my comments inline, In the mean time, I will work on the
test cases first.
Thanks for your great reviews!
Chung-yih
On Fri, Jan 25, 2013 at 1:14 PM, Peter Hutterer
<peter.hutterer at who-t.net> wrote:
>
> On Wed, Jan 23, 2013 at 10:19:17AM +0800, Chung-yih Wang wrote:
> > If an evdev client cannot consume evdev events in its queue fast enough, the
> > evdev kernel driver will enqueue a SYN_DROPPED event and clear the queue
> > once the client's queue is full. The result is that the X driver will be out
> > of sync with respect to the kernel driver state. The patch tries to handle the
> > SYN_DROPPED event by retrieving the kernel driver's state. Retrieving this
> > state is inherently non-atomic, since it requires a sequence of ioctls. We use
> > a simple before and after time stamping approach to deal with the race
> > condition between partially syncing state and any potentially stale events that
> > arrive during synchronization.
>
> wow, this one was good to review. nice work. It's almost correct, though
> there are a few things that need working on. First, best to rebase because
> master now has changed axis mapping, so you'll need a few changes
> there (should only matter for the MT code where you can use abs_axis_map, so
> simple search/replace).
Done, have use abs_axis_map instead.
>
>
> All this is in the signal handler, you can't use xf86IDrvMsg here, you'll
> need to use LogMessageVerbSigSafe instead.
Done.
> Brings up the next question: timpercmp() is not listed as signal-safe,
> though I highly suspect it is anyway.
timercmp() is just a macro not thread/signal related.
# define timercmp(a, b, CMP) \
(((a)->tv_sec == (b)->tv_sec) ? \
((a)->tv_usec CMP (b)->tv_usec) : \
((a)->tv_sec CMP (b)->tv_sec))
>
>
> > Signed-off-by: Chung-yih Wang <cywang at chromium.org>
> > ---
> > src/evdev.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
> > src/evdev.h | 17 +++
> > 2 files changed, 381 insertions(+), 7 deletions(-)
> >
> > diff --git a/src/evdev.c b/src/evdev.c
> > index 6564cd0..fc8a08d 100644
> > --- a/src/evdev.c
> > +++ b/src/evdev.c
> > @@ -128,6 +128,8 @@ static void EvdevSetCalibration(InputInfoPtr pInfo, int num_calibration, int cal
> > static int EvdevOpenDevice(InputInfoPtr pInfo);
> > static void EvdevCloseDevice(InputInfoPtr pInfo);
> >
> > +static int EvdevInjectEvent(InputInfoPtr pInfo, uint16_t type,
> > + uint16_t code, int32_t value);
> > static void EvdevInitAxesLabels(EvdevPtr pEvdev, int mode, int natoms, Atom *atoms);
> > static void EvdevInitOneAxisLabel(EvdevPtr pEvdev, int axis,
> > const char **labels, int label_idx, Atom *atoms);
> > @@ -135,6 +137,20 @@ static void EvdevInitButtonLabels(EvdevPtr pEvdev, int natoms, Atom *atoms);
> > static void EvdevInitProperty(DeviceIntPtr dev);
> > static int EvdevSetProperty(DeviceIntPtr dev, Atom atom,
> > XIPropertyValuePtr val, BOOL checkonly);
> > +static void EvdevSyncState(InputInfoPtr pInfo);
> > +static void EvdevGetKernelTime(struct timeval *current_time,
> > + BOOL use_monotonic);
> > +static int EvdevKeyStateSync(InputInfoPtr pInfo);
> > +static int EvdevAbsAxesSync(InputInfoPtr pInfo);
> > +static int EvdevAbsMtSlotSync(InputInfoPtr pInfo);
> > +static int EvdevInjectAbsMtAxisChangeEvent(InputInfoPtr pInfo, int slot_index,
> > + uint16_t code, int32_t value);
> > +static int EvdevCheckAbsMtAxesChange(InputInfoPtr pInfo, MTSlotInfoPtr slots,
> > + int *count_after_synreport);
> > +static int EvdevGetAllSlotVals(InputInfoPtr pInfo, MTSlotInfoPtr slots);
> > +static int EvdevAbsMtStateSync(InputInfoPtr pInfo, int *count_after_synreport);
> > +static int EvdevAbsStateSync(InputInfoPtr pInfo, int *count_after_synreport);
> > +
> > static Atom prop_product_id;
> > static Atom prop_invert;
> > static Atom prop_calibration;
> > @@ -208,6 +224,11 @@ static inline void EvdevSetBit(unsigned long *array, int bit)
> > array[bit / LONG_BITS] |= (1LL << (bit % LONG_BITS));
> > }
> >
> > +static inline void EvdevClearBit(unsigned long *array, int bit)
> > +{
> > + array[bit / LONG_BITS] &= ~(1LL << (bit % LONG_BITS));
> > +}
> > +
> > static int
> > EvdevGetMajorMinor(InputInfoPtr pInfo)
> > {
> > @@ -649,6 +670,11 @@ EvdevProcessButtonEvent(InputInfoPtr pInfo, struct input_event *ev)
> > /* Get the signed value, earlier kernels had this as unsigned */
> > value = ev->value;
> >
> > + if (ev->value)
> > + EvdevSetBit(pEvdev->key_state_bitmask, ev->code);
> > + else
> > + EvdevClearBit(pEvdev->key_state_bitmask, ev->code);
> > +
> > /* Handle drag lock */
> > if (EvdevDragLockFilterEvent(pInfo, button, value))
> > return;
> > @@ -779,6 +805,7 @@ EvdevProcessTouchEvent(InputInfoPtr pInfo, struct input_event *ev)
> > if (pEvdev->slot_state == SLOTSTATE_EMPTY)
> > pEvdev->slot_state = SLOTSTATE_UPDATE;
> > if (ev->code == ABS_MT_TRACKING_ID) {
> > + pEvdev->cached_tid[slot_index] = ev->value;
> > if (ev->value >= 0) {
> > pEvdev->slot_state = SLOTSTATE_OPEN;
> >
> > @@ -993,7 +1020,7 @@ static void EvdevPostQueuedEvents(InputInfoPtr pInfo, int num_v, int first_v,
> > * 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
> > +static BOOL
> > EvdevProcessSyncEvent(InputInfoPtr pInfo, struct input_event *ev)
> > {
> > int i;
> > @@ -1001,6 +1028,11 @@ EvdevProcessSyncEvent(InputInfoPtr pInfo, struct input_event *ev)
> > int v[MAX_VALUATORS] = {};
> > EvdevPtr pEvdev = pInfo->private;
> >
> > + if (ev->code == SYN_DROPPED) {
> > + xf86IDrvMsg(pInfo, X_INFO, "+++ SYN_DROPPED +++\n");
> > + return TRUE;
> > + }
> > +
> > EvdevProcessProximityState(pInfo);
> >
> > EvdevProcessValuators(pInfo);
> > @@ -1028,16 +1060,20 @@ EvdevProcessSyncEvent(InputInfoPtr pInfo, struct input_event *ev)
> > pEvdev->abs_queued = 0;
> > pEvdev->rel_queued = 0;
> > pEvdev->prox_queued = 0;
> > -
> > + return FALSE;
> > }
>
> this is one bit I don't like. If we're returning a BOOL, let's return true
> on success and false otherwise. Given the name of the function that seems
> more logical than this:
> if (!EvdevProcessSyncEvent())
> everything is fine
>
> >
> > /**
> > * Process the events from the device; nothing is actually posted to the server
> > - * until an EV_SYN event is received.
> > + * until an EV_SYN event is received. As the SYN_DROPPED event indicates that the
> > + * state of evdev driver will be out of sync with the event queue, additional
> > + * handling is required for processing the SYN_DROPPED event. The function returns
> > + * TRUE if a SYN_DROPPED event is received, FALSE otherwise.
> > */
> > -static void
> > +static BOOL
> > EvdevProcessEvent(InputInfoPtr pInfo, struct input_event *ev)
> > {
> > + BOOL syn_dropped = FALSE;
> > switch (ev->type) {
> > case EV_REL:
> > EvdevProcessRelativeMotionEvent(pInfo, ev);
> > @@ -1049,9 +1085,10 @@ EvdevProcessEvent(InputInfoPtr pInfo, struct input_event *ev)
> > EvdevProcessKeyEvent(pInfo, ev);
> > break;
> > case EV_SYN:
> > - EvdevProcessSyncEvent(pInfo, ev);
> > + syn_dropped = EvdevProcessSyncEvent(pInfo, ev);
> > break;
> > }
> > + return syn_dropped;
> > }
> >
> > #undef ABS_X_VALUE
> > @@ -1082,6 +1119,308 @@ EvdevFreeMasks(EvdevPtr pEvdev)
> > #endif
> > }
> >
> > +static void
> > +EvdevGetKernelTime(struct timeval *current_time, BOOL use_monotonic) {
>
> it's called use_monotonic here but is_monotonic in the struct? any reason
> you didn't use use_monotonic in the struct too? seems like the better naming
> to me.
>
> > + struct timespec now;
> > + clockid_t clockid = (use_monotonic) ? CLOCK_MONOTONIC : CLOCK_REALTIME;
> > +
> > + clock_gettime(clockid, &now);
> > + current_time->tv_sec = now.tv_sec;
> > + current_time->tv_usec = now.tv_nsec / 1000;
> > +}
> > +
> > +static int
> > +EvdevInjectEvent(InputInfoPtr pInfo, uint16_t type, uint16_t code,
> > + int32_t value) {
> > + EvdevPtr pEvdev = pInfo->private;
> > + struct input_event ev;
> > +
> > + ev.type = type;
> > + ev.code = code;
> > + ev.value = value;
> > + EvdevGetKernelTime(&ev.time, pEvdev->is_monotonic);
> > + /* Inject the event by processing it */
> > + EvdevProcessEvent(pInfo, &ev);
>
> We probably need to bump EVDEV_MAXQUEUE to avoid dropped events during sync.
I don't think we need the increase the queue size here. Since the
whole sync process could be divided into 2 steps
1. EvdevAbsAxesSync() : all EvdevInjectEvent() calls of ABS_ axes
changes will be stored in valuator first instead.
EvdevProcessEvent(EV_ABS)->EvdevProcessAbsoluteMotionEvent(). The
queue will not be used in EvdevAbsAxesSync().
2. EvdevAbsMtStateSync(): all ABS_MT_ axes changes will be stored
in its slot's valuator first. Depending on the finger state change,
each slot change may use one slot of the queue to store the slot's
valuator. This will use max. number of slots supported by a device at
most.
And we also append one SYN_REPORT event if there is any axis/slot
change to flush the queue. Therefore, I think the current
EVDEV_MAXQUEUE (32) is good enough.
> + return 1;
>
> > +}
> > +
> > +static int
> > +EvdevKeyStateSync(InputInfoPtr pInfo) {
> > + EvdevPtr pEvdev = pInfo->private;
> > + unsigned long key_state_bitmask[NLONGS(KEY_CNT)];
> > + int i, ev_count = 0;
> > + int len = sizeof(key_state_bitmask);
> > +
> > + if (ioctl(pInfo->fd, EVIOCGKEY(len), key_state_bitmask) < 0) {
> > + xf86IDrvMsg(pInfo, X_ERROR,
> > + "ioctl EVIOCGKEY failed: %s\n", strerror(errno));
> > + return !Success;
> > + }
> > + for (i = 0; i < KEY_CNT; i++) {
> > + int orig_value, current_value;
> > + if (!EvdevBitIsSet(pEvdev->key_bitmask, i))
> > + continue;
> > + orig_value = EvdevBitIsSet(pEvdev->key_state_bitmask, i);
> > + current_value = EvdevBitIsSet(key_state_bitmask, i);
> > + if (current_value == orig_value)
> > + continue;
> > + ev_count += EvdevInjectEvent(pInfo, EV_KEY, i, current_value);
> > + }
> > + return ev_count;
> > +}
> > +
> > +static int
> > +EvdevAbsAxesSync(InputInfoPtr pInfo) {
> > + EvdevPtr device = pInfo->private;
> > + struct input_absinfo absinfo;
> > + int i, ev_count = 0;
> > +
> > + /* Sync all ABS_ axes excluding ABS_MT_ axes */
> > + for (i = ABS_X; i < ABS_MAX; i++) {
> > + if (i >= ABS_MT_SLOT && i <= _ABS_MT_LAST)
> > + continue;
> > + if (!EvdevBitIsSet(device->abs_bitmask, i))
> > + continue;
> > + if (ioctl(pInfo->fd, EVIOCGABS(i), &absinfo) < 0) {
> > + xf86IDrvMsg(pInfo, X_ERROR, "ioctl EVIOCGABS(%zu) failed: %s\n",
> > + i, strerror(errno));
>
> evdev.c:1186:25: warning: format '%zu' expects argument of type 'size_t',
> but argument 4 has type 'int' [-Wformat]
>
Fixed.
>
>
> > + } else if (absinfo.value != device->absinfo[i].value) {
> > + ev_count += EvdevInjectEvent(pInfo, EV_ABS, i, absinfo.value);
>
>
> > + }
> > + }
> > + return ev_count;
> > +}
> > +
> > +static int
> > +EvdevAbsMtSlotSync(InputInfoPtr pInfo) {
> > + EvdevPtr device = pInfo->private;
> > + struct input_absinfo absinfo;
> > + int ev_count = 0;
> > +
> > + if (ioctl(pInfo->fd, EVIOCGABS(ABS_MT_SLOT), &absinfo) < 0) {
> > + xf86IDrvMsg(pInfo, X_ERROR, "ioctl EVIOCGABS(ABS_MT_SLOT) failed: %s\n",
> > + strerror(errno));
> > + return 0;
> > + }
> > + if (device->cur_slot != absinfo.value)
> > + ev_count = EvdevInjectEvent(pInfo, EV_ABS, ABS_MT_SLOT, absinfo.value);
> > + return ev_count;
> > +}
> > +
> > +static int
> > +EvdevInjectAbsMtAxisChangeEvent(InputInfoPtr pInfo, int slot_index,
> > + uint16_t code, int32_t value) {
> > + EvdevPtr device = pInfo->private;
> > + int ev_count = 0;
> > +
> > + if (device->cur_slot != slot_index)
> > + ev_count += EvdevInjectEvent(pInfo, EV_ABS, ABS_MT_SLOT, slot_index);
> > + ev_count += EvdevInjectEvent(pInfo, EV_ABS, code, value);
> > + return ev_count;
> > +}
> > +
> > +static int
> > +EvdevCheckAbsMtAxesChange(InputInfoPtr pInfo, MTSlotInfoPtr slots,
> > + int *count_after_synreport)
>
> > +{
> > + EvdevPtr device = pInfo->private;
> > + int i, j, ev_count = 0;
> > + int total_ev_count = 0;
> > +
> > + /*
> > + * There will be five conditions of a slot change after SYN_DROPPED:
> > + * a. Finger leaving, i.e., tracking id changes from a non-negative
> > + * number to -1.
> > + * b. Finger arriving, i.e., tracking id changes from -1 to a
> > + * non-negative number.
> > + * c. Finger changing, i.e., original finger leaving and new finger
> > + * arriving, tracking id changes from a non-negative number to
> > + * another one.
> > + * d. Same finger, but axes change, i.e., no tracking id changes, but some
> > + * axes values have changed.
> > + * e. Fingers arrive and leave: tracking ID was -1, and is still -1, but
> > + * some axes values have changed.
> > + * f. nothing changed
> > + *
> > + * To have X server seamless of SYN_DROPPED event, additional event
> > + * injections will be required except for conditions e and f:
> > + *
> > + * Finger leaving (a): all axes of the slot should be updated first, then
> > + * followed with tracking id change (-1).
> > + *
> > + * Finger arriving (b): new tracking id should be injected first, followed
> > + * with all axes updates.
> > + *
> > + * Finger changing (c): first, inject finger leaving with tracking id -1,
> > + * followed with new tracking id event, then update all axes data.
> > + *
> > + * Same finger, but axes change (d): all axes updates should be injected
> > + *
> > + */
> > +
> > + for (i = 0; i < num_slots(device); i++) {
> > + int curr_tid = slots[ABS_MT_TRACKING_ID - _ABS_MT_FIRST].values[i];
> > + int orig_tid = device->cached_tid[i];
> > +
> > + /* For conditions b and c, inject the tracking id change events first */
> > + if (orig_tid != curr_tid && curr_tid != -1) {
> > + /* For (c), inject the leaving event for original finger */
> > + if (orig_tid != -1) {
> > + ev_count += EvdevInjectAbsMtAxisChangeEvent(pInfo,
> > + i,
> > + ABS_MT_TRACKING_ID,
> > + -1);
> > + ev_count += EvdevInjectEvent(pInfo, EV_SYN, SYN_REPORT, 0);
> > + /* Reset the count_after_synreport after SYN_REPORT event */
> > + total_ev_count += ev_count;
> > + *count_after_synreport = ev_count = 0;
> > + }
> > + /* For (b) and (c), set the new tid before updating axes */
> > + ev_count += EvdevInjectAbsMtAxisChangeEvent(pInfo,
> > + i,
> > + ABS_MT_TRACKING_ID,
> > + curr_tid);
> > + }
> > +
> > +
> > + for (j = _ABS_MT_FIRST; j <= _ABS_MT_LAST; j++) {
> > + int axis = j - _ABS_MT_FIRST;
> > + int map, orig_value, curr_value;
> > + if ((j == ABS_MT_TRACKING_ID) ||
> > + ((map = device->axis_map[j]) == -1))
> > + continue;
> > + if (!EvdevBitIsSet(device->abs_bitmask, j))
> > + continue;
> > +
> > + orig_value = valuator_mask_get(device->last_mt_vals[i], map);
>
> last_mt_vals[i] is not guaranteed to be set. Use valuator_mask_isset or
> valuator_mask_fetch here. Not that it'd have much effect, but better to be
> correct.
last_mt_vals are used to keep last mt vals per slot, the initial
values are 0 for all axies(in EvdevAddAbsValuatorClass()). No matter
if the original value of an axis is set or not before this function,
we need to compare it with the value read from the kernel evdev
driver's slot. That's to say, with this patch, we also update initial
values read from evdev driver in EvdevOn().
> > + curr_value = slots[axis].values[i];
> > +
> > + if (orig_value == curr_value)
> > + continue;
> > +
> > + /* For condition e, internal axes values should be updated */
> > + if (orig_tid == -1 && curr_tid == -1) {
> > + valuator_mask_set(device->last_mt_vals[i], map, curr_value);
> > + continue;
> > + }
> > +
> > + /* In addition to condition d, all axes updates will be injected */
> > + ev_count += EvdevInjectAbsMtAxisChangeEvent(pInfo,
> > + i,
> > + j,
> > + curr_value);
> > + }
> > +
> > + /* For condition a, inject finger leaving event */
> > + if (orig_tid != -1 && curr_tid == -1) {
> > + ev_count += EvdevInjectAbsMtAxisChangeEvent(pInfo,
> > + i,
> > + ABS_MT_TRACKING_ID,
> > + -1);
> > + }
> > + }
> > + /* Update current slot index if it is different from cur_slot value */
> > + ev_count += EvdevAbsMtSlotSync(pInfo);
> > + *count_after_synreport += ev_count;
> > +
> > + return total_ev_count + ev_count;
> > +}
> > +
> > +static int
> > +EvdevGetAllSlotVals(InputInfoPtr pInfo, MTSlotInfoPtr slots)
> > +{
> > + EvdevPtr device = pInfo->private;
> > + int i;
> > +
> > + /* Retrieve current ABS_MT_ axes for all slots */
> > + for (i = _ABS_MT_FIRST; i <= _ABS_MT_LAST; i++) {
> > + MTSlotInfoPtr req = &slots[i - _ABS_MT_FIRST];
> > + if (!EvdevBitIsSet(device->abs_bitmask, i))
> > + continue;
> > + req->code = i;
> > + if (ioctl(pInfo->fd, EVIOCGMTSLOTS((sizeof(*req))), req) < 0) {
> > + xf86IDrvMsg(pInfo, X_ERROR,
> > + "ioctl EVIOCGMTSLOTS(req.code=%d) failed: %s\n",
> > + req->code, strerror(errno));
> > + return !Success;
> > + }
> > + }
> > +
> > + return Success;
> > +}
> > +
> > +static int
> > +EvdevAbsMtStateSync(InputInfoPtr pInfo, int *count_after_synreport) {
> > + MTSlotInfo slots[_ABS_MT_CNT];
> > + int ev_count = 0;
> > +
> > + /* Get all current slots axes, then check if there is any update required */
> > + if (EvdevGetAllSlotVals(pInfo, slots) == Success) {
> > + ev_count = EvdevCheckAbsMtAxesChange(pInfo, slots,
> > + count_after_synreport);
> > + }
> > +
> > + return ev_count;
> > +}
> > +
> > +static int
> > +EvdevAbsStateSync(InputInfoPtr pInfo, int *count_after_synreport) {
> > + EvdevPtr device = pInfo->private;
> > + int ev_count;
> > +
> > + /* Sync all ABS_ axes */
> > + ev_count = EvdevAbsAxesSync(pInfo);
> > + *count_after_synreport += ev_count;
> > +
> > + /* Sync ABS_MT_ axes for all slots if exists */
> > + if (device->num_mt_vals)
> > + ev_count += EvdevAbsMtStateSync(pInfo, count_after_synreport);
> > +
> > + return ev_count;
> > +}
> > +
> > +/**
> > + * Synchronize the current state with kernel evdev driver.
> > + */
> > +static void
> > +EvdevSyncState(InputInfoPtr pInfo)
> > +{
> > + int ev_count = 0;
> > + int ev_count_after_synreport = 0;
> > + EvdevPtr device = pInfo->private;
> > +
> > + EvdevGetKernelTime(&device->before_sync_time, device->is_monotonic);
> > +
> > + ev_count = EvdevKeyStateSync(pInfo);
> > + ev_count_after_synreport += ev_count;
> > +
> > + /*
> > + * TODO: sync all led, switch and sound states as well. We probably need
> > + * to post events out actively if the new states are different from the
> > + * cached ones.
> > + */
> > +
> > + /* sync abs and abs_mt value/limits */
> > + ev_count += EvdevAbsStateSync(pInfo, &ev_count_after_synreport);
> > +
> > + /*
> > + * Push SYN_REPORT event out if there is any event injected
> > + * during the state synchronization.
> > + */
> > + if (ev_count_after_synreport)
> > + ev_count += EvdevInjectEvent(pInfo, EV_SYN, SYN_REPORT, 0);
>
> the count_after_synreport seems unnecessary. we can just append an EV_SYN,
> if the event is empty nothing will happen in the driver (or shouldn't anyway
> :) I don't think you need to pass count_after_synreport around.
I understand your point. When I did the patch, I did not really figure
it out why I had an impression that additional SYN_REPORT may screw up
X input handling. For sure, additional SYN_REPORT shouldn't cause any
issue theoretically. I will do another round of testing later before
removing it.
>
> the rest looks good. what I do require for this though is test cases. This
> isn't something that I expect to happen frequently and reproducing it
> without proper testcases will be a major pain.
Agree, in fact, I had test cases especially for multi-touch device to
address six cases described in EvdevCheckAbsMtAxesChange(), see
http://git.chromium.org/gitweb/?p=chromiumos/third_party/autotest.git;a=tree;f=client/site_tests/platform_EvdevSynDropTest;h=a1b7d000c961537f03f98eefb1fdbf35514059ee;hb=refs/heads/master.
I will try to convert them into xorg integration test later.
>
> grab git://people.freedesktop.org/~whot/xorg-integration-tests and add them
> to test/input/evdev.cpp. that's using evemu for device descriptions and
> events, but you can force SYN_DROPPED events with PlayOne(), or really any
> other type of event. let me know if you need help with that. but really, we
> need tests for keyboard, mice, abs devices and MT devices before we can
> merge this with reasonable confidence.
Unfortunately, we are not able to inject a SYN_DROPPED event with
evemu as the kernel evdev driver will just ignore it.
evdev_write()->input_inject_event()->input_handle_event()->input_get_disposition()
In input_get_disposition(), SYN_DROPPED event will be ignored instead
of passed to device.
We could probably make another kernel patch to accept the SYN_DROPPED
event. I will read the xorg test framework first.
>
> Cheers,
> Peter
>
> > +
> > + EvdevGetKernelTime(&device->after_sync_time, device->is_monotonic);
> > +
> > + xf86IDrvMsg(pInfo, X_INFO,
> > + "Sync_State: before %ld.%ld after %ld.%ld injected events=%d\n",
> > + device->before_sync_time.tv_sec,
> > + device->before_sync_time.tv_usec,
> > + device->after_sync_time.tv_sec,
> > + device->after_sync_time.tv_usec,
> > + ev_count);
> > +}
> > +
> > /* just a magic number to reduce the number of reads */
> > #define NUM_EVENTS 16
> >
> > @@ -1090,6 +1429,7 @@ EvdevReadInput(InputInfoPtr pInfo)
> > {
> > struct input_event ev[NUM_EVENTS];
> > int i, len = sizeof(ev);
> > + BOOL sync_evdev_state = FALSE;
> >
> > while (len == sizeof(ev))
> > {
> > @@ -1120,9 +1460,23 @@ EvdevReadInput(InputInfoPtr pInfo)
> > break;
> > }
> >
> > - for (i = 0; i < len/sizeof(ev[0]); i++)
> > - EvdevProcessEvent(pInfo, &ev[i]);
> > + for (i = 0; i < len/sizeof(ev[0]); i++) {
> > + if (sync_evdev_state)
> > + break;
> > + if (timercmp(&ev[i].time, &pEvdev->before_sync_time, <)) {
> > + /* Ignore events before last sync time */
> > + continue;
> > + } else if (timercmp(&ev[i].time, &pEvdev->after_sync_time, >)) {
> > + /* Event_Process returns TRUE if SYN_DROPPED detected */
> > + sync_evdev_state = EvdevProcessEvent(pInfo, &ev[i]);
> > + } else {
> > + /* If the event occurred during sync, then sync again */
> > + sync_evdev_state = TRUE;
> > + }
> > + }
> > }
> > + if (sync_evdev_state)
> > + EvdevSyncState(pInfo);
> > }
> >
> > static void
> > @@ -1308,6 +1662,7 @@ EvdevAddAbsValuatorClass(DeviceIntPtr device)
> > }
> >
> > for (i = 0; i < num_slots(pEvdev); i++) {
> > + pEvdev->cached_tid[i] = -1;
> > pEvdev->last_mt_vals[i] = valuator_mask_new(num_mt_axes_total);
> > if (!pEvdev->last_mt_vals[i]) {
> > xf86IDrvMsg(pInfo, X_ERROR,
> > @@ -1833,6 +2188,8 @@ EvdevOn(DeviceIntPtr device)
> > Evdev3BEmuOn(pInfo);
> > pEvdev->flags |= EVDEV_INITIALIZED;
> > device->public.on = TRUE;
> > + pEvdev->slot_state = SLOTSTATE_EMPTY;
> > + EvdevSyncState(pInfo);
> >
> > return Success;
> > }
> > diff --git a/src/evdev.h b/src/evdev.h
> > index 58a3fa3..277eff5 100644
> > --- a/src/evdev.h
> > +++ b/src/evdev.h
> > @@ -101,6 +101,12 @@
> > /* Number of longs needed to hold the given number of bits */
> > #define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
> >
> > +#define _ABS_MT_FIRST ABS_MT_TOUCH_MAJOR
> > +#define _ABS_MT_LAST ABS_MT_DISTANCE
> > +#define _ABS_MT_CNT (_ABS_MT_LAST - _ABS_MT_FIRST + 1)
> > +k you need to pass count_af
> > +#define MAX_SLOT_COUNT 64
> > +
> > /* Function key mode */
> > enum fkeymode {
> > FKEYMODE_UNKNOWN = 0,
> > @@ -251,8 +257,19 @@ typedef struct {
> > EventQueueRec queue[EVDEV_MAXQUEUE];
> >
> > enum fkeymode fkeymode;
> > +
> > + /* Sync timestamps */
> > + unsigned long key_state_bitmask[NLONGS(KEY_CNT)];
> > + struct timeval before_sync_time;
> > + struct timeval after_sync_time;
> > + int32_t cached_tid[MAX_SLOT_COUNT];
> > } EvdevRec, *EvdevPtr;
> >
> > +typedef struct {
> > + uint32_t code;
> > + int32_t values[MAX_SLOT_COUNT];
> > +} MTSlotInfo, *MTSlotInfoPtr;
> > +
> > /* Event posting functions */
> > void EvdevQueueKbdEvent(InputInfoPtr pInfo, struct input_event *ev, int value);
> > void EvdevQueueButtonEvent(InputInfoPtr pInfo, int button, int value);
> > --
> > 1.7.7.3
> >
> > _______________________________________________
> > xorg-devel at lists.x.org: X.Org development
> > Archives: http://lists.x.org/archives/xorg-devel
> > Info: http://lists.x.org/mailman/listinfo/xorg-devel
> >
More information about the xorg-devel
mailing list