[PATCH evdev 2/2] Add SYN_DROPPED handling #59702

Peter Hutterer peter.hutterer at who-t.net
Thu Jan 24 21:14:31 PST 2013


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).

All this is in the signal handler, you can't use xf86IDrvMsg here, you'll
need to use LogMessageVerbSigSafe instead.
Brings up the next question: timpercmp() is not listed as signal-safe,
though I highly suspect it is anyway.


> 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.

> +    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]


> +        } 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.

> +            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.

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.

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.

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)
> +
> +#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