diff options
author | Ulf Brosziewski <bru@cvs.openbsd.org> | 2017-11-23 22:59:43 +0000 |
---|---|---|
committer | Ulf Brosziewski <bru@cvs.openbsd.org> | 2017-11-23 22:59:43 +0000 |
commit | 5a3fb18093425840eb47dc9e4c23db3f3cdc9f96 (patch) | |
tree | a254a856b2ccfef84a814068352bd87a420296e9 /sys | |
parent | 3826ff1e5b5947b47ceaf9e7d2ee78a255ed0728 (diff) |
1. Prepare a consistent treatment of edge areas. 2. Add mechanisms
that identify and mask touches resting in the bottom area.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/wscons/wsmouse.c | 17 | ||||
-rw-r--r-- | sys/dev/wscons/wsmouseinput.h | 3 | ||||
-rw-r--r-- | sys/dev/wscons/wstpad.c | 262 |
3 files changed, 213 insertions, 69 deletions
diff --git a/sys/dev/wscons/wsmouse.c b/sys/dev/wscons/wsmouse.c index 3c32297a57a..ec006ca7702 100644 --- a/sys/dev/wscons/wsmouse.c +++ b/sys/dev/wscons/wsmouse.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wsmouse.c,v 1.40 2017/07/16 18:30:24 bru Exp $ */ +/* $OpenBSD: wsmouse.c,v 1.41 2017/11/23 22:59:42 bru Exp $ */ /* $NetBSD: wsmouse.c,v 1.35 2005/02/27 00:27:52 perry Exp $ */ /* @@ -719,6 +719,7 @@ wsmouse_mtstate(struct device *sc, int slot, int x, int y, int pressure) mt->num_touches--; mt->touches ^= bit; mt->sync[MTS_TOUCH] |= bit; + mt->ptr_mask &= mt->touches; } } } @@ -851,10 +852,10 @@ wsmouse_ptr_ctrl(struct mt_state *mt) } /* - * If there is no pointer-controlling slot or it is inactive, - * select a new one. + * If there is no pointer-controlling slot, or if it should be + * masked, select a new one. */ - select = ((mt->ptr & mt->touches) == 0); + select = ((mt->ptr & mt->touches & ~mt->ptr_mask) == 0); /* Remove slots without X/Y deltas from the cycle. */ updates = (mt->sync[MTS_X] | mt->sync[MTS_Y]) & ~mt->sync[MTS_TOUCH]; @@ -867,8 +868,12 @@ wsmouse_ptr_ctrl(struct mt_state *mt) mt->ptr_cycle |= updates; } if (select) { - slot = (mt->ptr_cycle - ? ffs(mt->ptr_cycle) - 1 : ffs(mt->touches) - 1); + if (mt->ptr_cycle & ~mt->ptr_mask) + slot = ffs(mt->ptr_cycle & ~mt->ptr_mask) - 1; + else if (mt->touches & ~mt->ptr_mask) + slot = ffs(mt->touches & ~mt->ptr_mask) - 1; + else + slot = ffs(mt->touches) - 1; mt->ptr = (1 << slot); } } diff --git a/sys/dev/wscons/wsmouseinput.h b/sys/dev/wscons/wsmouseinput.h index 3f38fddfaa1..575f546cab5 100644 --- a/sys/dev/wscons/wsmouseinput.h +++ b/sys/dev/wscons/wsmouseinput.h @@ -1,4 +1,4 @@ -/* $OpenBSD: wsmouseinput.h,v 1.8 2017/06/06 21:53:07 bru Exp $ */ +/* $OpenBSD: wsmouseinput.h,v 1.9 2017/11/23 22:59:42 bru Exp $ */ /* * Copyright (c) 2015, 2016 Ulf Brosziewski @@ -91,6 +91,7 @@ struct mt_state { u_int ptr; u_int ptr_cycle; u_int prev_ptr; + u_int ptr_mask; /* a buffer for the MT tracking function */ int *matrix; diff --git a/sys/dev/wscons/wstpad.c b/sys/dev/wscons/wstpad.c index bacec99a1ba..f69b01244c5 100644 --- a/sys/dev/wscons/wstpad.c +++ b/sys/dev/wscons/wstpad.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wstpad.c,v 1.11 2017/08/25 20:57:35 bru Exp $ */ +/* $OpenBSD: wstpad.c,v 1.12 2017/11/23 22:59:42 bru Exp $ */ /* * Copyright (c) 2015, 2016 Ulf Brosziewski @@ -94,6 +94,13 @@ enum tpad_cmd { #define EDGES (L_EDGE | R_EDGE | T_EDGE | B_EDGE) +/* + * A touch is "centered" if it does not start and remain at the top + * edge or one of the vertical edges. Two-finger scrolling and tapping + * require that at least one touch is centered. + */ +#define CENTERED(t) (((t)->flags & (L_EDGE | R_EDGE | T_EDGE)) == 0) + enum touchstates { TOUCH_NONE, TOUCH_BEGIN, @@ -149,6 +156,7 @@ struct wstpad { struct tpad_touch *tpad_touches; u_int mtcycle; + u_int ignore; int dx; int dy; @@ -195,6 +203,7 @@ struct wstpad { struct { enum tap_state state; int contacts; + int centered; u_int button; int maxdist; struct timeout to; @@ -331,7 +340,7 @@ get_2nd_touch(struct wsmouseinput *input) int slot; if (IS_MT(tp)) { - slot = ffs(input->mt.touches & ~input->mt.ptr); + slot = ffs(input->mt.touches & ~(input->mt.ptr | tp->ignore)); if (slot) return &tp->tpad_touches[--slot]; } @@ -400,9 +409,16 @@ wstpad_f2scroll(struct wsmouseinput *input, u_int *cmds) { struct wstpad *tp = input->tp; struct tpad_touch *t2; - int dir, dx, dy; + int dir, dx, dy, centered; + + if (tp->ignore == 0) { + if (tp->contacts != 2) + return; + } else if (tp->contacts != 3 || (tp->ignore == input->mt.ptr)) { + return; + } - if (tp->contacts != 2 || !chk_scroll_state(tp)) + if (!chk_scroll_state(tp)) return; dir = tp->t->dir; @@ -410,6 +426,7 @@ wstpad_f2scroll(struct wsmouseinput *input, u_int *cmds) dx = EAST(dir) || WEST(dir) ? tp->dx : 0; if (dx || dy) { + centered = CENTERED(tp->t); if (IS_MT(tp)) { t2 = get_2nd_touch(input); if (t2 == NULL) @@ -419,9 +436,12 @@ wstpad_f2scroll(struct wsmouseinput *input, u_int *cmds) return; if ((dx > 0 && !EAST(dir)) || (dx < 0 && !WEST(dir))) return; + centered |= CENTERED(t2); + } + if (centered) { + wstpad_scroll(tp, dx, dy, cmds); + set_freeze_ts(tp, 0, FREEZE_MS); } - wstpad_scroll(tp, dx, dy, cmds); - set_freeze_ts(tp, 0, FREEZE_MS); } } @@ -520,10 +540,6 @@ wstpad_is_tap(struct wstpad *tp, struct tpad_touch *t) struct timespec ts; int dx, dy, dist = 0; - if (t->flags & EDGES) - /* No tapping in the edge areas. */ - return (0); - /* * No distance limit applies if there has been more than one contact * on a single-touch device. We cannot use (t->x - t->orig.x) in this @@ -544,27 +560,76 @@ wstpad_is_tap(struct wstpad *tp, struct tpad_touch *t) } /* + * Return the oldest touch in the TOUCH_END state, or NULL. + */ +struct tpad_touch * +wstpad_tap_touch(struct wsmouseinput *input) +{ + struct wstpad *tp = input->tp; + struct tpad_touch *s, *t = NULL; + u_int lifted; + int slot; + + if (IS_MT(tp)) { + 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 |= CENTERED(s); + if (t == NULL || timespeccmp(&t->orig.time, + &s->orig.time, >)) + t = s; + } + } else { + if (tp->t->state == TOUCH_END) { + t = tp->t; + if (tp->tap.state == TAP_DETECT) + tp->tap.centered = CENTERED(t); + } + } + + 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) +{ + return (tp->contacts == nmasked + && (nmasked == 0 || !wstpad_is_tap(tp, tp->t))); +} + +static inline u_int +tap_btn(struct wstpad *tp, int nmasked) +{ + int n = tp->tap.contacts - nmasked; + + return (n == 2 ? RIGHTBTN : (n == 3 ? MIDDLEBTN : LEFTBTN)); +} + +/* * 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 * "locked drags", which are finished by a timeout or a tap-to-end * gesture. - * Please note that if the touch t is derived from MT input it is the - * pointer-controlling touch. If the state of that touch is not - * TOUCH_UPDATE then no other touch will be in this state. */ void wstpad_tap(struct wsmouseinput *input, u_int *cmds) { struct wstpad *tp = input->tp; - struct tpad_touch *t = tp->t; - int err = 0; + struct tpad_touch *t; + int nmasked, err = 0; if (tp->btns) { /* * Don't process tapping while hardware buttons are being * pressed. If the handler is not in its initial state, - * the "tap button" will be released. + * release the "tap button". */ if (tp->tap.state > TAP_IGNORE) { timeout_del(&tp->tap.to); @@ -575,37 +640,53 @@ wstpad_tap(struct wsmouseinput *input, u_int *cmds) * timeout; ignore the current touch. */ tp->tap.state = TAP_IGNORE; + tp->tap.contacts = 0; + tp->tap.centered = 0; } + /* + * If a touch from the bottom area is masked, reduce the + * contact counts and ignore it. + */ + nmasked = (input->mt.ptr_mask ? 1 : 0); + + /* + * Only touches in the TOUCH_END state are relevant here. + * t is NULL if no touch has been lifted. + */ + t = wstpad_tap_touch(input); + switch (tp->tap.state) { case TAP_DETECT: - if (t->state == TOUCH_END) { - if (wstpad_is_tap(tp, t)) { - tp->tap.button = (tp->tap.contacts == 2 - ? RIGHTBTN : (tp->tap.contacts == 3 - ? MIDDLEBTN : LEFTBTN)); - *cmds |= 1 << TAPBUTTON_DOWN; - tp->tap.state = TAP_LIFTED; - err = !timeout_add_msec( - &tp->tap.to, tp->tap.clicktime); - } - tp->tap.contacts = 0; - } else if (tp->contacts != tp->tap.contacts) { - /* Check t before pointer-control might change. */ - if (!wstpad_is_tap(tp, t)) { + if (tp->contacts > tp->tap.contacts) + tp->tap.contacts = tp->contacts; + + if (t) { + if (!wstpad_is_tap(tp, t)) tp->tap.state = TAP_IGNORE; + else if (tap_finished(tp, nmasked)) + tp->tap.state = (tp->tap.centered + ? TAP_LIFTED : TAP_IGNORE); + + if (tp->tap.state != TAP_DETECT) { + if (tp->tap.state == TAP_LIFTED) { + tp->tap.button = tap_btn(tp, nmasked); + *cmds |= 1 << TAPBUTTON_DOWN; + err = !timeout_add_msec(&tp->tap.to, + tp->tap.clicktime); + } tp->tap.contacts = 0; - } else if (tp->contacts > tp->tap.contacts) { - tp->tap.contacts = tp->contacts; + tp->tap.centered = 0; } } break; + case TAP_IGNORE: - if (t->state != TOUCH_UPDATE) + if (tp->contacts == nmasked) tp->tap.state = TAP_DETECT; break; case TAP_LIFTED: - if (t->state >= TOUCH_BEGIN) { + if (tp->contacts > nmasked) { timeout_del(&tp->tap.to); if (tp->tap.button == LEFTBTN) { tp->tap.state = TAP_2ND_TOUCH; @@ -616,43 +697,45 @@ wstpad_tap(struct wsmouseinput *input, u_int *cmds) } break; case TAP_2ND_TOUCH: - if (t->state == TOUCH_END) { + 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->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); + 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 > 1) { + } else if (tp->contacts != nmasked + 1) { *cmds |= 1 << TAPBUTTON_UP; tp->tap.state = TAP_DETECT; } break; case TAP_LOCKED: - if (t->state >= TOUCH_BEGIN) { + if (tp->contacts > nmasked) { timeout_del(&tp->tap.to); tp->tap.state = TAP_NTH_TOUCH; } break; case TAP_NTH_TOUCH: - if (t->state == TOUCH_END) { + if (t) { if (wstpad_is_tap(tp, t)) { /* "tap-to-end" */ *cmds |= 1 << TAPBUTTON_UP; tp->tap.state = TAP_DETECT; - } else { + } else if (tp->contacts == nmasked) { tp->tap.state = TAP_LOCKED; - err = !timeout_add_msec( - &tp->tap.to, tp->tap.locktime); + err = !timeout_add_msec(&tp->tap.to, + tp->tap.locktime); } - } else if (tp->contacts > 1) { + } else if (tp->contacts != nmasked + 1) { *cmds |= 1 << TAPBUTTON_UP; tp->tap.state = TAP_DETECT; } @@ -823,7 +906,8 @@ wstpad_mt_inputs(struct wsmouseinput *input) if (touches & tp->mtcycle) { /* * Slot data may be synchronized separately, in any order, - * or not at all if they are "inactive" and there is no delta. + * or not at all if there is no delta. Identify the touches + * without deltas. */ inactive = input->mt.touches & ~tp->mtcycle; tp->mtcycle = touches; @@ -852,6 +936,52 @@ wstpad_mt_inputs(struct wsmouseinput *input) } void +wstpad_mt_masks(struct wsmouseinput *input) +{ + struct wstpad *tp = input->tp; + struct tpad_touch *t; + u_int mask; + int d, slot; + + tp->ignore &= input->mt.touches; + + if (tp->contacts < 2 || tp->ignore) + return; + + /* + * If there is exactly one touch in the bottom area, try to + * link pointer control to other touches (once set, the mask + * will only be cleared when the touch ends). + */ + if (input->mt.ptr_mask == 0) { + mask = ~0; + FOREACHBIT(input->mt.touches, slot) { + t = &tp->tpad_touches[slot]; + if (t->flags & B_EDGE) { + mask &= (1 << slot); + input->mt.ptr_mask = mask; + } + } + } + + /* + * If the pointer-controlling touch is moving stably while a masked + * touch is not, treat the latter as "thumb". It will not block + * pointer movement, and wstpad_f2scroll will ignore it. + */ + if ((tp->dx || tp->dy) && (input->mt.ptr_mask & ~input->mt.ptr)) { + slot = ffs(input->mt.ptr_mask) - 1; + t = &tp->tpad_touches[slot]; + if (t->flags & B_EDGE) { + d = tp->t->matches - t->matches; + /* Do not hamper upward scrolling. */ + if (d > STABLE && (!NORTH(t->dir) || d > 2 * STABLE)) + tp->ignore = input->mt.ptr_mask; + } + } +} + +void wstpad_touch_inputs(struct wsmouseinput *input) { struct wstpad *tp = input->tp; @@ -879,6 +1009,7 @@ wstpad_touch_inputs(struct wsmouseinput *input) slot = ffs(input->mt.ptr) - 1; tp->t = &tp->tpad_touches[slot]; } + wstpad_mt_masks(input); } else { t = tp->t; t->x = normalize_abs(&input->filter.h, input->motion.x); @@ -908,6 +1039,18 @@ wstpad_touch_inputs(struct wsmouseinput *input) } } +static inline int +t2_ignore(struct wsmouseinput *input) +{ + /* + * If there are two touches, do not block pointer movement if they + * perform a click-and-drag action, or if the second touch is + * resting in the bottom area. + */ + return (input->tp->contacts == 2 && ((input->tp->btns & PRIMARYBTN) + || (input->tp->ignore & ~input->mt.ptr))); +} + void wstpad_process_input(struct wsmouseinput *input, struct evq_access *evq) { @@ -943,14 +1086,12 @@ wstpad_process_input(struct wsmouseinput *input, struct evq_access *evq) } } - /* Check whether the motion deltas should be cleared. */ + /* Check whether pointer movement should be blocked. */ if (tp->dx || tp->dy) { if (DISABLE(tp) || (tp->t->flags & tp->freeze) || timespeccmp(&tp->time, &tp->freeze_ts, <) - /* Is there more than one contact, and no click-and-drag? */ - || tp->contacts > 2 - || (tp->contacts == 2 && !(tp->btns & PRIMARYBTN))) { + || (tp->contacts > 1 && !t2_ignore(input))) { cmds |= 1 << CLEAR_MOTION_DELTAS; } @@ -1086,14 +1227,11 @@ wstpad_weak_hysteresis(int *dx, int *dy, { *h_acc += *dx; *v_acc += *dy; - if ((*dx > 0 && *h_acc < *dx) - || (*dx < 0 && *h_acc > *dx)) + if ((*dx > 0 && *h_acc < *dx) || (*dx < 0 && *h_acc > *dx)) *h_acc = *dx; - if ((*dy > 0 && *v_acc < *dy) - || (*dy < 0 && *v_acc > *dy)) + if ((*dy > 0 && *v_acc < *dy) || (*dy < 0 && *v_acc > *dy)) *v_acc = *dy; - if (abs(*h_acc) < h_threshold - && abs(*v_acc) < v_threshold) + if (abs(*h_acc) < h_threshold && abs(*v_acc) < v_threshold) *dx = *dy = 0; } |