The touchpad input driver of wsmouse(4) uses a flawed shortcut for handling double taps, which has the effect that tap-and-drag inputs with multiple taps before the final contact almost never work correctly when the number of the taps is even: https://marc.info/?l=openbsd-misc&m=164986426617019&w=2
After each second tap, the driver generates a button-up and a button-down event at once, and issues a second button-up event almost immediately afterwards in order to make that work. It doesn't wait for another touch within the proper wait interval (WSMOUSECG_TAP_CLICKTIME). With this patch, the driver serializes the events properly - with an additional timeout, if necessary. A new "tap state" keeps the input handler and the timeout function in sync. Tests and OKs would be welcome. Index: dev/wscons/wstpad.c =================================================================== RCS file: /cvs/src/sys/dev/wscons/wstpad.c,v retrieving revision 1.30 diff -u -p -r1.30 wstpad.c --- dev/wscons/wstpad.c 24 Mar 2021 18:28:24 -0000 1.30 +++ dev/wscons/wstpad.c 15 Apr 2022 17:27:35 -0000 @@ -61,7 +61,7 @@ #define TAP_LOCKTIME_DEFAULT 0 #define TAP_BTNMAP_SIZE 3 -#define CLICKDELAY_MS 20 +#define CLICKDELAY_MS 10 #define FREEZE_MS 100 #define MATCHINTERVAL_MS 45 #define STOPINTERVAL_MS 55 @@ -83,6 +83,7 @@ enum tap_state { TAP_IGNORE, TAP_LIFTED, TAP_2ND_TOUCH, + TAP_REPEATED, TAP_LOCKED, TAP_NTH_TOUCH, }; @@ -93,7 +94,6 @@ enum tpad_cmd { SOFTBUTTON_UP, TAPBUTTON_DOWN, TAPBUTTON_UP, - TAPBUTTON_DOUBLECLK, VSCROLL, HSCROLL, }; @@ -828,8 +828,8 @@ wstpad_tap(struct wsmouseinput *input, u case TAP_2ND_TOUCH: if (t) { if (wstpad_is_tap(tp, t)) { - *cmds |= 1 << TAPBUTTON_DOUBLECLK; - tp->tap.state = TAP_LIFTED; + *cmds |= 1 << TAPBUTTON_UP; + tp->tap.state = TAP_REPEATED; err = !timeout_add_msec(&tp->tap.to, CLICKDELAY_MS); } else if (tp->contacts == nmasked) { @@ -847,6 +847,14 @@ wstpad_tap(struct wsmouseinput *input, u tp->tap.state = TAP_DETECT; } break; + case TAP_REPEATED: + if (tp->contacts > nmasked) { + timeout_del(&tp->tap.to); + tp->tap.state = TAP_2ND_TOUCH; + if ((input->sbtn.buttons & tp->tap.button) == 0) + *cmds |= 1 << TAPBUTTON_DOWN; + } + break; case TAP_LOCKED: if (tp->contacts > nmasked) { timeout_del(&tp->tap.to); @@ -888,19 +896,28 @@ wstpad_tap_timeout(void *p) struct wstpad *tp = input->tp; struct evq_access evq; u_int btn; - int s; + int s, ev; s = spltty(); evq.evar = *input->evar; - if (evq.evar != NULL && tp != NULL && - (tp->tap.state == TAP_LIFTED || tp->tap.state == TAP_LOCKED)) { - tp->tap.state = TAP_DETECT; - input->sbtn.buttons &= ~tp->tap.button; + if (evq.evar != NULL && tp != NULL && (tp->tap.state == TAP_LIFTED + || tp->tap.state == TAP_REPEATED || tp->tap.state == TAP_LOCKED)) { + + if (tp->tap.state != TAP_REPEATED + || (input->sbtn.buttons & tp->tap.button)) { + ev = BTN_UP_EV; + input->sbtn.buttons &= ~tp->tap.button; + tp->tap.state = TAP_DETECT; + } else { + ev = BTN_DOWN_EV; + input->sbtn.buttons |= tp->tap.button; + timeout_add_msec(&tp->tap.to, tp->tap.clicktime); + } btn = ffs(tp->tap.button) - 1; evq.put = evq.evar->put; evq.result = EVQ_RESULT_NONE; getnanotime(&evq.ts); - wsmouse_evq_put(&evq, BTN_UP_EV, btn); + wsmouse_evq_put(&evq, ev, btn); wsmouse_evq_put(&evq, SYNC_EV, 0); if (evq.result == EVQ_RESULT_SUCCESS) { if (input->flags & LOG_EVENTS) { @@ -928,15 +945,12 @@ wstpad_click(struct wsmouseinput *input) set_freeze_ts(tp, 0, FREEZE_MS); } -/* - * Translate the "command" bits into the sync-state of wsmouse, or into - * wscons events. - */ +/* Translate the "command" bits into the sync-state of wsmouse. */ void -wstpad_cmds(struct wsmouseinput *input, struct evq_access *evq, u_int cmds) +wstpad_cmds(struct wsmouseinput *input, u_int cmds) { struct wstpad *tp = input->tp; - u_int btn, sbtns_dn = 0, sbtns_up = 0; + u_int sbtns_dn = 0, sbtns_up = 0; int n; FOREACHBIT(cmds, n) { @@ -961,19 +975,6 @@ wstpad_cmds(struct wsmouseinput *input, case TAPBUTTON_UP: sbtns_up |= tp->tap.button; continue; - case TAPBUTTON_DOUBLECLK: - /* - * We cannot add the final BTN_UP event here, a - * delay is required. This is the reason why the - * tap handler returns from the 2ND_TOUCH state - * into the LIFTED state with a short timeout - * (CLICKDELAY_MS). - */ - btn = ffs(PRIMARYBTN) - 1; - wsmouse_evq_put(evq, BTN_UP_EV, btn); - wsmouse_evq_put(evq, SYNC_EV, 0); - wsmouse_evq_put(evq, BTN_DOWN_EV, btn); - continue; case HSCROLL: input->motion.dw = tp->scroll.dw; input->motion.sync |= SYNC_DELTAS; @@ -1276,7 +1277,7 @@ wstpad_process_input(struct wsmouseinput } } - wstpad_cmds(input, evq, cmds); + wstpad_cmds(input, cmds); if (IS_MT(tp)) clear_touchstates(input, TOUCH_NONE);