... and this is the complete link: https://marc.info/?l=openbsd-tech&m=165006255922269&w=2
On 5/3/22 10:03, Ulf Brosziewski wrote: > The implementation of the tapping mechanism in wsmouse(4) has a bug > concerning the transitions into the hold/drag state, see > https://marc.info/... > for details. The patch proposed in that message is obsolete. I've > been made aware that there is another problem, the transition only > works with left-button taps. > > This patch merges tap detection and the handling of hold/drag states, > which is a cleaner and more generic approach. It fixes the problems > mentioned above, and it permits a better synchronization of button > states when tap inputs and the use of hardware buttons are mixed. > > OK? > > > 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 2 May 2022 22:17:57 -0000 > @@ -82,18 +82,17 @@ enum tap_state { > TAP_DETECT, > TAP_IGNORE, > TAP_LIFTED, > - TAP_2ND_TOUCH, > TAP_LOCKED, > - TAP_NTH_TOUCH, > + TAP_LOCKED_DRAG, > }; > > enum tpad_cmd { > CLEAR_MOTION_DELTAS, > SOFTBUTTON_DOWN, > SOFTBUTTON_UP, > + TAPBUTTON_SYNC, > TAPBUTTON_DOWN, > TAPBUTTON_UP, > - TAPBUTTON_DOUBLECLK, > VSCROLL, > HSCROLL, > }; > @@ -212,8 +211,10 @@ struct wstpad { > struct { > enum tap_state state; > int contacts; > - int centered; > + int valid; > + u_int pending; > u_int button; > + int masked; > int maxdist; > struct timeout to; > /* parameters: */ > @@ -651,6 +652,7 @@ wstpad_softbuttons(struct wsmouseinput * > } > } > > +/* Check whether the duration of t is within the tap limit. */ > int > wstpad_is_tap(struct wstpad *tp, struct tpad_touch *t) > { > @@ -675,7 +677,7 @@ wstpad_tap_filter(struct wstpad *tp, str > dy = abs(t->y - t->orig.y) * tp->ratio; > dist = (dx >= dy ? dx + 3 * dy / 8 : dy + 3 * dx / 8); > } > - tp->tap.centered = (CENTERED(t) && dist <= (tp->tap.maxdist << 12)); > + tp->tap.valid = (CENTERED(t) && dist <= (tp->tap.maxdist << 12)); > } > > > @@ -694,7 +696,7 @@ wstpad_tap_touch(struct wsmouseinput *in > lifted = (input->mt.sync[MTS_TOUCH] & ~input->mt.touches); > FOREACHBIT(lifted, slot) { > s = &tp->tpad_touches[slot]; > - if (tp->tap.state == TAP_DETECT && !tp->tap.centered) > + if (tp->tap.state == TAP_DETECT && !tp->tap.valid) > wstpad_tap_filter(tp, s); > if (t == NULL || timespeccmp(&t->orig.time, > &s->orig.time, >)) > @@ -703,7 +705,7 @@ wstpad_tap_touch(struct wsmouseinput *in > } else { > if (tp->t->state == TOUCH_END) { > t = tp->t; > - if (tp->tap.state == TAP_DETECT && !tp->tap.centered) > + if (tp->tap.state == TAP_DETECT && !tp->tap.valid) > wstpad_tap_filter(tp, t); > } > } > @@ -711,30 +713,31 @@ wstpad_tap_touch(struct wsmouseinput *in > return (t); > } > > -/* > - * If each contact in a sequence of contacts that overlap in time > - * is a tap, a button event may be generated when the number of > - * contacts drops to zero, or to one if there is a masked touch. > - */ > -static inline int > -tap_finished(struct wstpad *tp, int nmasked) > +/* Determine the "tap button", keep track of whether a touch is masked. */ > +static inline u_int > +tap_button(struct wstpad *tp) > { > - return (tp->contacts == nmasked > - && (nmasked == 0 || !wstpad_is_tap(tp, tp->t))); > + int n = tp->tap.contacts - tp->contacts - 1; > + > + tp->tap.masked = tp->contacts; > + > + return (n >= 0 && n < TAP_BTNMAP_SIZE ? tp->tap.btnmap[n] : 0); > } > > -static inline u_int > -tap_btn(struct wstpad *tp, int nmasked) > +/* > + * In the hold/drag state, do not mask touches if no masking was involved > + * in the preceding tap gesture. > + */ > +static inline int > +tap_unmask(struct wstpad *tp) > { > - int n = tp->tap.contacts - nmasked; > - > - return (n <= TAP_BTNMAP_SIZE ? tp->tap.btnmap[n - 1] : 0); > + return ((tp->tap.button || tp->tap.pending) && tp->tap.masked == 0); > } > > /* > - * This handler supports one-, two-, and three-finger-taps, which > - * are mapped to left-button, right-button and middle-button events, > - * respectively; moreover, it supports tap-and-drag operations with > + * In the default configuration, this handler maps one-, two-, and > + * three-finger taps to left-button, right-button, and middle-button > + * events, respectively. Setting the LOCKTIME parameter enables > * "locked drags", which are finished by a timeout or a tap-to-end > * gesture. > */ > @@ -743,140 +746,111 @@ wstpad_tap(struct wsmouseinput *input, u > { > struct wstpad *tp = input->tp; > struct tpad_touch *t; > - int nmasked, err = 0; > + int contacts, is_tap, slot, err = 0; > > - if (tp->btns) { > - /* > - * Don't process tapping while hardware buttons are being > - * pressed. If the handler is not in its initial state, > - * release the "tap button". > - */ > - if (tp->tap.state > TAP_IGNORE) { > - timeout_del(&tp->tap.to); > - *cmds |= 1 << TAPBUTTON_UP; > - } > - /* > - * It might be possible to produce a click within the tap > - * timeout; ignore the current touch. > - */ > - tp->tap.state = TAP_IGNORE; > - tp->tap.contacts = 0; > - tp->tap.centered = 0; > - } > + /* Synchronize the button states, if necessary. */ > + if (input->btn.sync) > + *cmds |= 1 << TAPBUTTON_SYNC; > > /* > - * If a touch from the bottom area is masked, reduce the > - * contact counts and ignore it. > + * It is possible to produce a click within the tap timeout. > + * Wait for a new touch before generating new button events. > */ > - nmasked = (input->mt.ptr_mask ? 1 : 0); > + if (PRIMARYBTN_RELEASED(tp)) > + tp->tap.contacts = 0; > + > + /* Reset the detection state whenever a new touch starts. */ > + if (tp->contacts > tp->prev_contacts || (IS_MT(tp) && > + (input->mt.touches & input->mt.sync[MTS_TOUCH]))) { > + tp->tap.contacts = tp->contacts; > + tp->tap.valid = 0; > + } > > /* > - * Only touches in the TOUCH_END state are relevant here. > - * t is NULL if no touch has been lifted. > + * The filtered number of active touches excludes a masked > + * touch if its duration exceeds the tap limit. > */ > - t = wstpad_tap_touch(input); > + contacts = tp->contacts; > + if ((slot = ffs(input->mt.ptr_mask) - 1) >= 0 > + && !wstpad_is_tap(tp, &tp->tpad_touches[slot]) > + && !tap_unmask(tp)) { > + contacts--; > + } > > switch (tp->tap.state) { > case TAP_DETECT: > - if (tp->contacts > tp->tap.contacts) > - tp->tap.contacts = tp->contacts; > - > + /* Find the oldest touch in the TOUCH_END state. */ > + t = wstpad_tap_touch(input); > if (t) { > - if (wstpad_is_tap(tp, t)) { > - if (tap_finished(tp, nmasked)) { > - if (tp->tap.centered) { > - tp->tap.state = TAP_LIFTED; > - tp->tap.button = > - tap_btn(tp, nmasked); > - } > - tp->tap.contacts = 0; > - tp->tap.centered = 0; > + is_tap = wstpad_is_tap(tp, t); > + if (is_tap && contacts == 0) { > + if (tp->tap.button) > + *cmds |= 1 << TAPBUTTON_UP; > + tp->tap.pending = (tp->tap.valid > + ? tap_button(tp) : 0); > + if (tp->tap.pending) { > + tp->tap.state = TAP_LIFTED; > + err = !timeout_add_msec(&tp->tap.to, > + CLICKDELAY_MS); > } > - } else { > - if (tp->contacts > nmasked) > + } else if (!is_tap && tp->tap.locktime == 0) { > + if (contacts == 0 && tp->tap.button) > + *cmds |= 1 << TAPBUTTON_UP; > + else if (contacts) > tp->tap.state = TAP_IGNORE; > - tp->tap.contacts = 0; > - tp->tap.centered = 0; > - } > - if (tp->tap.state == TAP_LIFTED) { > - if (tp->tap.button != 0) { > - *cmds |= 1 << TAPBUTTON_DOWN; > + } else if (!is_tap && tp->tap.button) { > + if (contacts == 0) { > + tp->tap.state = TAP_LOCKED; > err = !timeout_add_msec(&tp->tap.to, > - tp->tap.clicktime); > + tp->tap.locktime); > } else { > - tp->tap.state = TAP_DETECT; > + tp->tap.state = TAP_LOCKED_DRAG; > } > } > } > break; > - > case TAP_IGNORE: > - if (tp->contacts == nmasked) > + if (contacts == 0) { > tp->tap.state = TAP_DETECT; > - break; > - case TAP_LIFTED: > - if (tp->contacts > nmasked) { > - timeout_del(&tp->tap.to); > - if (tp->tap.button == LEFTBTN) { > - tp->tap.state = TAP_2ND_TOUCH; > - } else { > + if (tp->tap.button) > *cmds |= 1 << TAPBUTTON_UP; > - tp->tap.state = TAP_DETECT; > - } > } > break; > - case TAP_2ND_TOUCH: > - if (t) { > - if (wstpad_is_tap(tp, t)) { > - *cmds |= 1 << TAPBUTTON_DOUBLECLK; > - tp->tap.state = TAP_LIFTED; > - err = !timeout_add_msec(&tp->tap.to, > - CLICKDELAY_MS); > - } else if (tp->contacts == nmasked) { > - if (tp->tap.locktime == 0) { > - *cmds |= 1 << TAPBUTTON_UP; > - tp->tap.state = TAP_DETECT; > - } else { > - tp->tap.state = TAP_LOCKED; > - err = !timeout_add_msec(&tp->tap.to, > - tp->tap.locktime); > - } > - } > - } else if (tp->contacts != nmasked + 1) { > - *cmds |= 1 << TAPBUTTON_UP; > + case TAP_LIFTED: > + if (contacts) { > + timeout_del(&tp->tap.to); > tp->tap.state = TAP_DETECT; > + if (tp->tap.pending) > + *cmds |= 1 << TAPBUTTON_DOWN; > } > break; > case TAP_LOCKED: > - if (tp->contacts > nmasked) { > + if (contacts) { > timeout_del(&tp->tap.to); > - tp->tap.state = TAP_NTH_TOUCH; > + tp->tap.state = TAP_LOCKED_DRAG; > } > break; > - case TAP_NTH_TOUCH: > - if (t) { > - if (wstpad_is_tap(tp, t)) { > + case TAP_LOCKED_DRAG: > + if (contacts == 0) { > + t = wstpad_tap_touch(input); > + if (t && wstpad_is_tap(tp, t)) { > /* "tap-to-end" */ > *cmds |= 1 << TAPBUTTON_UP; > tp->tap.state = TAP_DETECT; > - } else if (tp->contacts == nmasked) { > + } else { > tp->tap.state = TAP_LOCKED; > err = !timeout_add_msec(&tp->tap.to, > tp->tap.locktime); > } > - } else if (tp->contacts != nmasked + 1) { > - *cmds |= 1 << TAPBUTTON_UP; > - tp->tap.state = TAP_DETECT; > } > break; > } > > if (err) { /* Did timeout_add fail? */ > - if (tp->tap.state == TAP_LIFTED) > - *cmds &= ~(1 << TAPBUTTON_DOWN); > - else > - *cmds |= 1 << TAPBUTTON_UP; > - > + input->sbtn.buttons &= ~tp->tap.button; > + input->sbtn.sync |= tp->tap.button; > + tp->tap.pending = 0; > + tp->tap.button = 0; > tp->tap.state = TAP_DETECT; > } > } > @@ -888,19 +862,31 @@ 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; > - btn = ffs(tp->tap.button) - 1; > + > + if (tp->tap.pending) { > + tp->tap.button = tp->tap.pending; > + tp->tap.pending = 0; > + ev = BTN_DOWN_EV; > + btn = ffs(tp->tap.button) - 1; > + input->sbtn.buttons |= tp->tap.button; > + timeout_add_msec(&tp->tap.to, tp->tap.clicktime); > + } else { > + ev = BTN_UP_EV; > + btn = ffs(tp->tap.button) - 1; > + input->sbtn.buttons &= ~tp->tap.button; > + tp->tap.button = 0; > + tp->tap.state = TAP_DETECT; > + } > 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 +914,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 tapbtn, sbtns_dn = 0, sbtns_up = 0; > int n; > > FOREACHBIT(cmds, n) { > @@ -955,24 +938,24 @@ wstpad_cmds(struct wsmouseinput *input, > sbtns_up |= tp->softbutton; > tp->softbutton = 0; > continue; > + case TAPBUTTON_SYNC: > + if ((tapbtn = (tp->tap.button | tp->tap.pending))) { > + input->btn.sync &= ~tapbtn; > + sbtns_up &= ~tapbtn; > + sbtns_dn &= ~tapbtn; > + } > + continue; > case TAPBUTTON_DOWN: > + tp->tap.button = tp->tap.pending; > + tp->tap.pending = 0; > sbtns_dn |= tp->tap.button; > continue; > 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); > + if ((tp->tap.button & input->btn.buttons) == 0 > + && tp->tap.button != tp->softbutton) { > + sbtns_up |= tp->tap.button; > + } > + tp->tap.button = 0; > continue; > case HSCROLL: > input->motion.dw = tp->scroll.dw; > @@ -1276,7 +1259,7 @@ wstpad_process_input(struct wsmouseinput > } > } > > - wstpad_cmds(input, evq, cmds); > + wstpad_cmds(input, cmds); > > if (IS_MT(tp)) > clear_touchstates(input, TOUCH_NONE); >