From: Egbert Eich Date: Mon Apr 18 23:26:25 2016 +0200 Subject: [PATCH 2/2]Add delay between button press and release to kiosk mode. Patch-mainline: to be upstreamed References: FATE#320263 Signed-off-by: Egbert Eich This allows to notice the visual feedback of a UI on a button press. Signed-off-by: Egbert Eich --- include/evdev-properties.h | 1 + man/evdev.man | 15 ++++ src/evdev.c | 4 + src/evdev.h | 11 ++- src/kioskTouch.c | 201 ++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 220 insertions(+), 12 deletions(-) diff --git a/include/evdev-properties.h b/include/evdev-properties.h index 29f2bd9..9f17b66 100644 --- a/include/evdev-properties.h +++ b/include/evdev-properties.h @@ -94,4 +94,5 @@ /* Kiosk Touch mode */ #define EVDEV_PROP_KIOSK_TOUCH "Evdev Kiosk Touch Mode" #define EVDEV_PROP_KIOSK_BUTTON "Evdev Kiosk Touch Button" +#define EVDEV_PROP_KIOSK_BUTTON_DELAY "Evdev Kiosk Touch Button Delay" #endif diff --git a/man/evdev.man b/man/evdev.man index 404a88d..a9f67a0 100644 --- a/man/evdev.man +++ b/man/evdev.man @@ -258,6 +258,18 @@ Specifies the Kiosk Touch button number to use. Button range: 0-255. Default: "0". Property: "Evdev Kiosk Touch Button". +.TP 7 +.BI "Option \*qKioskTouchButtonDelay\*q \*q" "N" \*q +Specifies the delay between a button press and release event in ms. A delay +will help to notice a visual feedback on a button press event. During the +delay no motion events are generated. Instead the final location is stored +and a motion event to this location is sent after the button release has been +sent at the end of the delay period. +Button +.I N +range: 0-65535. +Default: "0". Property: +"Evdev Kiosk Touch Button Delay". .SH SUPPORTED PROPERTIES The following properties are provided by the @@ -307,6 +319,9 @@ value. .TP 7 .BI "Evdev Kiosk Touch Button" 1 8-bit positive value. +.TP 7 +.BI "Evdev Kiosk Touch Button Delay" +1 16-bit positive value. .SH AUTHORS Kristian Høgsberg, Peter Hutterer diff --git a/src/evdev.c b/src/evdev.c index a72a866..ab0ed31 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -482,6 +482,7 @@ EvdevProcessValuators(InputInfoPtr pInfo) EvdevSwapAbsValuators(pEvdev, pEvdev->abs_vals); EvdevApplyCalibration(pEvdev, pEvdev->abs_vals); Evdev3BEmuProcessAbsMotion(pInfo, pEvdev->abs_vals); + EvdevKioskTouchMotionEvents(pInfo, pEvdev->abs_vals); } } @@ -1966,6 +1967,7 @@ EvdevOn(DeviceIntPtr device) xf86AddEnabledDevice(pInfo); EvdevMBEmuOn(pInfo); Evdev3BEmuOn(pInfo); + EvdevKioskTouchOn(pInfo); pEvdev->flags |= EVDEV_INITIALIZED; device->public.on = TRUE; @@ -1995,6 +1997,7 @@ EvdevProc(DeviceIntPtr device, int what) { EvdevMBEmuFinalize(pInfo); Evdev3BEmuFinalize(pInfo); + EvdevKioskTouchFinalize(pInfo); } if (pInfo->fd != -1) { @@ -2011,6 +2014,7 @@ EvdevProc(DeviceIntPtr device, int what) xf86IDrvMsg(pInfo, X_INFO, "Close\n"); EvdevCloseDevice(pInfo); EvdevFreeMasks(pEvdev); + EvdevKioskTouchClose(pInfo); pEvdev->min_maj = 0; break; diff --git a/src/evdev.h b/src/evdev.h index 27c1cb2..4a01e37 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -225,7 +225,12 @@ typedef struct { /* 0: 0ff, 1: click on touch, 2: click on release */ unsigned int button_queued; unsigned int button; + unsigned int delay; + unsigned int delay_queued; unsigned int state; + int old_v[2]; + ValuatorMask *vals; + Time expires; } kioskTouch; /* run-time calibration */ @@ -303,7 +308,11 @@ void EvdevAppleInitProperty(DeviceIntPtr); /* Kiosk Touch */ void EvdevKioskTouchPreInit(InputInfoPtr pInfo); -BOOL EvdevKioskTouchFilterButton(InputInfoPtr pInfo, unsigned int button, int value); void EvdevKioskTouchInitProperty(DeviceIntPtr); +BOOL EvdevKioskTouchFilterButton(InputInfoPtr pInfo, unsigned int button, int value); BOOL EvdevKioskTouchFilterTouch(EvdevPtr pEvdev); +void EvdevKioskTouchOn(InputInfoPtr pInfo); +void EvdevKioskTouchFinalize(InputInfoPtr pInfo); +void EvdevKioskTouchMotionEvents(InputInfoPtr pInfo, ValuatorMask *vals); +void EvdevKioskTouchClose(InputInfoPtr pInfo); #endif diff --git a/src/kioskTouch.c b/src/kioskTouch.c index d32f386..f0dd3cb 100644 --- a/src/kioskTouch.c +++ b/src/kioskTouch.c @@ -42,8 +42,11 @@ static Atom prop_ktouch = 0; /* Kiosk touch emulation on/off property */ static Atom prop_ktouch_button = 0; /* Kiosk touch emulation button property */ +static Atom prop_ktouch_button_delay = 0; /* Kiosk touch emulation button delay property */ #define KTOUCH_STATE_ACTIVE 1U << 0 +#define KTOUCH_STATE_DELAY 1U << 1 +#define KTOUCH_STATE_DELAY_MOTION 1U << 2 void EvdevKioskTouchPreInit(InputInfoPtr pInfo) @@ -75,18 +78,21 @@ EvdevKioskTouchPreInit(InputInfoPtr pInfo) val = 0; } pEvdev->kioskTouch.button = pEvdev->kioskTouch.button_queued = val; + val = xf86SetIntOption(pInfo->options, "KioskTouchButtonDelay", 0); + pEvdev->kioskTouch.delay = pEvdev->kioskTouch.delay_queued = val; pEvdev->kioskTouch.state = 0; - xf86Msg(X_INFO, "%s: KioskTouchpad mode initialized to %s - button: %d\n", + xf86Msg(X_INFO, "%s: KioskTouchpad mode initialized to %s - button: %d delay: %d\n", pInfo->name, (pEvdev->kioskTouch.mode == 0) ? "disabled" : (pEvdev->kioskTouch.mode == 1 ? "click-on-touch" : "click-on-release"), - pEvdev->kioskTouch.button); + pEvdev->kioskTouch.button, pEvdev->kioskTouch.delay); } static void EvdevKioskTouchSwitchQueued(EvdevPtr pEvdev) { if (pEvdev->kioskTouch.mode != pEvdev->kioskTouch.mode_queued || - pEvdev->kioskTouch.button != pEvdev->kioskTouch.button_queued) { + pEvdev->kioskTouch.button != pEvdev->kioskTouch.button_queued || + pEvdev->kioskTouch.delay != pEvdev->kioskTouch.delay_queued) { if (pEvdev->kioskTouch.state & KTOUCH_STATE_ACTIVE) return; if (pEvdev->mt_mask) { @@ -98,6 +104,7 @@ EvdevKioskTouchSwitchQueued(EvdevPtr pEvdev) } pEvdev->kioskTouch.mode = pEvdev->kioskTouch.mode_queued; pEvdev->kioskTouch.button = pEvdev->kioskTouch.button_queued; + pEvdev->kioskTouch.delay = pEvdev->kioskTouch.delay_queued; } } @@ -108,6 +115,33 @@ EvdevKioskTouchFilterTouch(EvdevPtr pEvdev) return (pEvdev->kioskTouch.mode > 0) ? TRUE : FALSE; } +static BOOL +EvdevKioskTouchStopButtonTimer(InputInfoPtr pInfo, BOOL queue) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + + if ((pEvdev->kioskTouch.state & KTOUCH_STATE_DELAY) == 0) + return FALSE; + + pEvdev->kioskTouch.state &= ~KTOUCH_STATE_DELAY; + if (queue) { + if (pEvdev->kioskTouch.state & KTOUCH_STATE_DELAY_MOTION) { + EvdevPostButtonEvent(pInfo, pEvdev->kioskTouch.button, BUTTON_RELEASE); + valuator_mask_copy(pEvdev->abs_vals, pEvdev->kioskTouch.vals); + valuator_mask_zero(pEvdev->kioskTouch.vals); + } else + EvdevQueueButtonEvent(pInfo, pEvdev->kioskTouch.button, 0); + } else { + EvdevPostButtonEvent(pInfo, pEvdev->kioskTouch.button, BUTTON_RELEASE); + if (pEvdev->kioskTouch.state & KTOUCH_STATE_DELAY_MOTION) { + xf86PostMotionEventM(pInfo->dev, Absolute, pEvdev->kioskTouch.vals); + valuator_mask_zero(pEvdev->kioskTouch.vals); + } + } + pEvdev->kioskTouch.state &= ~KTOUCH_STATE_DELAY_MOTION; + return TRUE; +} + BOOL EvdevKioskTouchFilterButton(InputInfoPtr pInfo, unsigned int button, int value) { @@ -118,28 +152,46 @@ EvdevKioskTouchFilterButton(InputInfoPtr pInfo, unsigned int button, int value) if (value == 1) pEvdev->kioskTouch.state = KTOUCH_STATE_ACTIVE; else - pEvdev->kioskTouch.state = 0; + pEvdev->kioskTouch.state &= ~KTOUCH_STATE_ACTIVE; switch (pEvdev->kioskTouch.mode) { case 0: - DEBUG((ErrorF("%s: mode 1 button %d value %d\n", \ + DEBUG((ErrorF("%s: mode 0 button %d value %d\n", \ __func__, button, value))) return FALSE; case 1: if (value == 1) { - DEBUG((ErrorF("%s: Sending ButtonDown/ButtonUp\n",__func__))) - EvdevQueueButtonClicks(pInfo, button, 1); + DEBUG((ErrorF("%s: Sending ButtonDown\n",__func__))) + EvdevQueueButtonEvent(pInfo, button, 1); + if (pEvdev->kioskTouch.delay > 0) { + pEvdev->kioskTouch.state |= KTOUCH_STATE_DELAY; + pEvdev->kioskTouch.expires = GetTimeInMillis () + + pEvdev->kioskTouch.delay; + } else { + DEBUG((ErrorF("%s: Sending ButtonUp\n",__func__))); + EvdevQueueButtonEvent(pInfo, button, 0); + } } else if (value == 0) { + DEBUG((ErrorF("%s: EvdevKioskTouchFilterButton: Filter Button UP\n",__func__))); + if (pEvdev->kioskTouch.state & KTOUCH_STATE_DELAY) + EvdevKioskTouchStopButtonTimer(pInfo, TRUE); pEvdev->kioskTouch.state = 0; } return TRUE; case 2: DEBUG((ErrorF("%s: mode 2 button %d value %d\n", \ - __func__, button, value))) - if (value == 1) + __func__, button, value))); + if (value == 1) { + EvdevKioskTouchStopButtonTimer(pInfo, TRUE); return TRUE; - else if (value == 0) { - DEBUG((ErrorF("%s: Sending ButtonDown\n",__func__))) + } else if (value == 0) { EvdevQueueButtonEvent(pInfo, button, 1); + if (pEvdev->kioskTouch.delay > 0) { + pEvdev->kioskTouch.state |= KTOUCH_STATE_DELAY; + pEvdev->kioskTouch.expires = GetTimeInMillis () + + pEvdev->kioskTouch.delay; + return TRUE; + } else + DEBUG((ErrorF("%s: Sending ButtonDown\n",__func__))); } return FALSE; default: @@ -150,6 +202,104 @@ EvdevKioskTouchFilterButton(InputInfoPtr pInfo, unsigned int button, int value) return FALSE; } +void EvdevKioskTouchMotionEvents(InputInfoPtr pInfo, ValuatorMask *vals) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + + if (pEvdev->kioskTouch.mode == 1) { + int val; + if ((pEvdev->kioskTouch.state & KTOUCH_STATE_DELAY_MOTION) == 0) { + valuator_mask_fetch(vals, 0, &pEvdev->kioskTouch.old_v[0]); + valuator_mask_fetch(vals, 1, &pEvdev->kioskTouch.old_v[1]); + } + if (pEvdev->kioskTouch.state & KTOUCH_STATE_DELAY) { + if (valuator_mask_fetch(vals, 0, &val)) { + valuator_mask_set(pEvdev->kioskTouch.vals, 0, val); + valuator_mask_set(vals, 0, pEvdev->kioskTouch.old_v[0]); + } + if (valuator_mask_fetch(vals, 1, &val)) { + valuator_mask_set(pEvdev->kioskTouch.vals, 1, val); + valuator_mask_set(vals, 1, pEvdev->kioskTouch.old_v[1]); + } + pEvdev->kioskTouch.state |= KTOUCH_STATE_DELAY_MOTION; + } else { + valuator_mask_fetch(vals, 0, &pEvdev->kioskTouch.old_v[0]); + valuator_mask_fetch(vals, 1, &pEvdev->kioskTouch.old_v[1]); + } + } +} + +static void +EvdevKioskTouchWakeupHandler(pointer data, + int i, + pointer LastSelectMask) +{ + InputInfoPtr pInfo = (InputInfoPtr)data; + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + int ms; + + if (pEvdev->kioskTouch.state & KTOUCH_STATE_DELAY) { + ms = pEvdev->kioskTouch.expires - GetTimeInMillis(); + if (ms <= 0) { + int sigstate; + + sigstate = xf86BlockSIGIO (); + pEvdev->kioskTouch.state &= ~KTOUCH_STATE_DELAY; + DEBUG((ErrorF("Delayed ButtonUP\n"))); + EvdevPostButtonEvent(pInfo, pEvdev->kioskTouch.button, BUTTON_RELEASE); + if (pEvdev->kioskTouch.state & KTOUCH_STATE_DELAY_MOTION) { + xf86PostMotionEventM(pInfo->dev, Absolute, pEvdev->kioskTouch.vals); + valuator_mask_zero(pEvdev->kioskTouch.vals); + pEvdev->kioskTouch.state &= ~KTOUCH_STATE_DELAY_MOTION; + } + xf86UnblockSIGIO (sigstate); + } + } +} + +static void +EvdevKioskTouchBlockHandler(pointer data, + struct timeval **waitTime, + pointer LastSelectMask) +{ + InputInfoPtr pInfo = (InputInfoPtr)data; + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + int ms; + + if (pEvdev->kioskTouch.state & KTOUCH_STATE_DELAY) { + ms = pEvdev->kioskTouch.expires - GetTimeInMillis (); + if (ms <= 0) + ms = 0; + AdjustWaitForDelay (waitTime, ms); + } +} + +void +EvdevKioskTouchOn(InputInfoPtr pInfo) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + + if (pEvdev->kioskTouch.mode == -1) + return; + + RegisterBlockAndWakeupHandlers (EvdevKioskTouchBlockHandler, + EvdevKioskTouchWakeupHandler, + (pointer)pInfo); +} + +void +EvdevKioskTouchFinalize(InputInfoPtr pInfo) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + + if (pEvdev->kioskTouch.mode == -1) + return; + + RemoveBlockAndWakeupHandlers (EvdevKioskTouchBlockHandler, + EvdevKioskTouchWakeupHandler, + (pointer)pInfo); +} + static int EvdevKioskTouchSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val, BOOL checkonly) @@ -171,6 +321,13 @@ EvdevKioskTouchSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val, if (!checkonly) pEvdev->kioskTouch.button_queued = *((unsigned char*)val->data); + } else if (atom == prop_ktouch_button_delay) { + + if (val->format != 16 || val->size != 1 || val->type != XA_INTEGER) + return BadMatch; + + if (!checkonly) + pEvdev->kioskTouch.delay_queued = *((CARD16*)val->data); } return Success; @@ -183,10 +340,14 @@ EvdevKioskTouchInitProperty(DeviceIntPtr dev) EvdevPtr pEvdev = pInfo->private; int rc; + /* This doesn't really belong to property handling, however it is convenient to do + * here and avoids another function to be called */ if (pEvdev->mt_mask && !libevdev_has_event_code(pEvdev->dev, EV_KEY, BTN_TOUCH)) { xf86Msg(X_WARNING, "%s: MultiTouch device has no BTN_TOUCH event: " "no Kiosk Mode support\n", pInfo->name); pEvdev->kioskTouch.mode = -1; + } else { + pEvdev->kioskTouch.vals = valuator_mask_new(2); } if (pEvdev->kioskTouch.mode < 0) @@ -211,5 +372,23 @@ EvdevKioskTouchInitProperty(DeviceIntPtr dev) return; XISetDevicePropertyDeletable(dev, prop_ktouch_button, FALSE); + prop_ktouch_button_delay = + MakeAtom(EVDEV_PROP_KIOSK_BUTTON_DELAY, strlen(EVDEV_PROP_KIOSK_BUTTON_DELAY), TRUE); + rc = XIChangeDeviceProperty(dev, prop_ktouch_button_delay, XA_INTEGER, 16, + PropModeReplace, 1, + &pEvdev->kioskTouch.delay, + FALSE); + if (rc != Success) + return; + XISetDevicePropertyDeletable(dev, prop_ktouch_button_delay, FALSE); + XIRegisterPropertyHandler(dev, EvdevKioskTouchSetProperty, NULL, NULL); } + +void +EvdevKioskTouchClose(InputInfoPtr pInfo) +{ + EvdevPtr pEvdev = pInfo->private; + + valuator_mask_free(&pEvdev->kioskTouch.vals); +}