diff -urN xf86-input-evdev-2.9.2/include/evdev-properties.h xf86-input-evdev-2.9.2.new/include/evdev-properties.h --- a/include/evdev-properties.h 2015-03-10 20:49:19.000000000 -0700 +++ b/include/evdev-properties.h 2016-02-14 16:01:53.189968403 -0800 @@ -90,5 +90,7 @@ /* Smooth scroll */ /* INT32, 3 values (vertical, horizontal, dial) */ #define EVDEV_PROP_SCROLL_DISTANCE "Evdev Scrolling Distance" +/* CARD32 */ +#define EVDEV_PROP_DEBOUNCE_DELAY "Evdev Debounce Delay" #endif diff -urN xf86-input-evdev-2.9.2/src/Makefile.am xf86-input-evdev-2.9.2.new/src/Makefile.am --- a/src/Makefile.am 2015-03-10 20:49:19.000000000 -0700 +++ b/src/Makefile.am 2016-02-14 16:12:04.164008950 -0800 @@ -39,6 +39,7 @@ emuThird.c \ emuWheel.c \ draglock.c \ + debounce.c \ apple.c \ axis_labels.h diff -urN xf86-input-evdev-2.9.2/src/debounce.c xf86-input-evdev-2.9.2.new/src/debounce.c --- a/dev/null 1969-12-31 16:00:00.000000000 -0800 +++ b/src/debounce.c 2016-02-14 16:10:23.803349586 -0800 @@ -0,0 +1,198 @@ +/* + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of the authors + * not be used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. The authors make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "evdev.h" + +#include +#include +#include +#include + +#include + +/* how many milliseconds to wait for buttons to settle */ +static Atom prop_dbdelay; + +struct ButtonTime { + unsigned int button; + Time time; +}; + +static int +EvdevDBCompareButtonTimes(const void *bt1, const void *bt2) +{ + return ((const struct ButtonTime *) bt1)->time - + ((const struct ButtonTime *) bt2)->time; +} + +/** + * Timer callback for debouncing buttons. + */ +static CARD32 +EvdevDBTimer(OsTimerPtr timer, CARD32 time, pointer arg) +{ + InputInfoPtr pInfo = arg; + EvdevPtr pEvdev = pInfo->private; + struct debounce *db = &pEvdev->debounce; + struct ButtonTime release[EVDEV_MAXBUTTONS]; + size_t nRelease = 0; + int sigstate = xf86BlockSIGIO(); + + if (db->bouncing) { + BOOL bouncing = FALSE; + Time next_expiry = ~0; + for (size_t button = 0; button < EVDEV_MAXBUTTONS; ++button) { + if (db->state[button].bouncing) { + Time expiry = db->state[button].expiry; + if (expiry <= time) { + db->state[button].pressed = FALSE; + db->state[button].bouncing = FALSE; + release[nRelease].button = button; + release[nRelease].time = expiry; + ++nRelease; + } + else if (expiry < next_expiry) { + bouncing = TRUE; + next_expiry = expiry; + } + } + } + if (nRelease > 0) { + if (nRelease > 1) { + qsort(release, nRelease, sizeof *release, + EvdevDBCompareButtonTimes); + } + for (size_t i = 0; i < nRelease; ++i) { + EvdevPostButtonEvent(pInfo, release[i].button, BUTTON_RELEASE); + } + } + if (bouncing) { + db->timer = TimerSet(db->timer, 0, next_expiry - GetTimeInMillis(), + EvdevDBTimer, pInfo); + } + else { + db->bouncing = FALSE; + } + } + + xf86UnblockSIGIO(sigstate); + return 0; +} + +/** + * Debounce button states. + * + * @return TRUE iff event was swallowed by debouncing. + */ +BOOL +EvdevDBFilterEvent(InputInfoPtr pInfo, unsigned int button, BOOL pressed) +{ + EvdevPtr pEvdev = pInfo->private; + struct debounce *db = &pEvdev->debounce; + + if (db->delay == 0) { + return FALSE; + } + + if (pressed) { + if (db->state[button].pressed) { + db->state[button].bouncing = FALSE; + return TRUE; + } + db->state[button].pressed = TRUE; + return FALSE; + } + + if (db->state[button].pressed) { + db->state[button].bouncing = TRUE; + db->state[button].expiry = GetTimeInMillis() + db->delay; + if (!db->bouncing) { + db->bouncing = TRUE; + db->timer = TimerSet(db->timer, 0, db->delay, EvdevDBTimer, pInfo); + } + } + + return TRUE; +} + +void +EvdevDBPreInit(InputInfoPtr pInfo) +{ + EvdevPtr pEvdev = pInfo->private; + struct debounce *db = &pEvdev->debounce; + + db->delay = xf86SetIntOption(pInfo->options, "DebounceDelay", 0); + db->timer = TimerSet(NULL, 0, 0, NULL, NULL); +} + +void +EvdevDBFinalize(InputInfoPtr pInfo) +{ + EvdevPtr pEvdev = pInfo->private; + struct debounce *db = &pEvdev->debounce; + + TimerFree(db->timer), db->timer = NULL; +} + +static int +EvdevDBSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val, + BOOL checkonly) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + EvdevPtr pEvdev = pInfo->private; + struct debounce *db = &pEvdev->debounce; + + if (atom == prop_dbdelay) { + if (val->format != 32 || val->size != 1 || val->type != XA_INTEGER) { + return BadMatch; + } + if (!checkonly) { + db->delay = *((CARD32 *) val->data); + } + } + + return Success; +} + +void +EvdevDBInitProperty(DeviceIntPtr dev) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + EvdevPtr pEvdev = pInfo->private; + struct debounce *db = &pEvdev->debounce; + + if (dev->button) { + prop_dbdelay = MakeAtom(EVDEV_PROP_DEBOUNCE_DELAY, + strlen(EVDEV_PROP_DEBOUNCE_DELAY), TRUE); + if (XIChangeDeviceProperty(dev, prop_dbdelay, XA_INTEGER, 32, + PropModeReplace, 1, &db->delay, FALSE) + != Success) { + return; + } + XISetDevicePropertyDeletable(dev, prop_dbdelay, FALSE); + XIRegisterPropertyHandler(dev, EvdevDBSetProperty, NULL, NULL); + } +} diff -urN xf86-input-evdev-2.9.2/src/evdev.c xf86-input-evdev-2.9.2.new/src/evdev.c --- a/src/evdev.c 2015-03-26 18:35:50.000000000 -0700 +++ b/src/evdev.c 2016-02-14 16:29:46.631020314 -0800 @@ -618,6 +618,10 @@ if (EvdevMBEmuFilterEvent(pInfo, button, value)) return; + + if (EvdevDBFilterEvent(pInfo, button, value)) + return; + if (button) EvdevQueueButtonEvent(pInfo, button, value); @@ -1957,6 +1961,7 @@ Evdev3BEmuInitProperty(device); EvdevWheelEmuInitProperty(device); EvdevDragLockInitProperty(device); + EvdevDBInitProperty(device); EvdevAppleInitProperty(device); return Success; @@ -2014,6 +2019,7 @@ { EvdevMBEmuFinalize(pInfo); Evdev3BEmuFinalize(pInfo); + EvdevDBFinalize(pInfo); } if (pInfo->fd != -1) { @@ -2665,6 +2671,7 @@ Evdev3BEmuPreInit(pInfo); EvdevWheelEmuPreInit(pInfo); EvdevDragLockPreInit(pInfo); + EvdevDBPreInit(pInfo); } return Success; diff -urN xf86-input-evdev-2.9.2/src/evdev.h xf86-input-evdev-2.9.2.new/src/evdev.h --- a/src/evdev.h 2015-03-26 18:35:50.000000000 -0700 +++ b/src/evdev.h 2016-02-14 16:35:32.983319999 -0800 @@ -224,6 +224,17 @@ Time expires; /* time of expiry */ Time timeout; } emulateWheel; + /* Button debouncing */ + struct debounce { + Time delay; + OsTimerPtr timer; + BOOL bouncing; + struct { + BOOL pressed; + BOOL bouncing; + Time expiry; + } state[EVDEV_MAXBUTTONS]; + } debounce; struct { int vert_delta; int horiz_delta; @@ -296,6 +307,12 @@ void EvdevDragLockPreInit(InputInfoPtr pInfo); BOOL EvdevDragLockFilterEvent(InputInfoPtr pInfo, unsigned int button, int value); +/* Button debouncing */ +BOOL EvdevDBFilterEvent(InputInfoPtr pInfo, unsigned int button, BOOL pressed); +void EvdevDBPreInit(InputInfoPtr pInfo); +void EvdevDBFinalize(InputInfoPtr pInfo); +void EvdevDBInitProperty(DeviceIntPtr dev); + void EvdevMBEmuInitProperty(DeviceIntPtr); void Evdev3BEmuInitProperty(DeviceIntPtr); void EvdevWheelEmuInitProperty(DeviceIntPtr);