From 21a363a2486a713434e890fc1f6b86a180755230 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 16 Jun 2010 18:48:53 +0200 Subject: Add tapping logic This patch adds tap-to-click, tap-and-hold for dragging, two-finger and three-finger taps. Turned on by default for touch screens only; switch on in gestures.c. Signed-off-by: Henrik Rydberg --- driver/multitouch.c | 8 +++++ include/gestures.h | 4 +++ include/memory.h | 13 ++++++++ include/mtdev-iobuf.h | 1 + include/mtouch.h | 2 ++ mtdev/iobuf.c | 9 ++++++ src/gestures.c | 43 ++++++++++++++++++++++++++ src/memory.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/mtouch.c | 8 +++++ src/test.c | 4 +++ 10 files changed, 176 insertions(+) diff --git a/driver/multitouch.c b/driver/multitouch.c index 888c4c4..73a025d 100644 --- a/driver/multitouch.c +++ b/driver/multitouch.c @@ -245,6 +245,10 @@ static void handle_gestures(LocalDevicePtr local, int step = 1 + rot_fraction * get_cap_xsize(caps); button_scroll(local, 14, 15, &rot, step, gs->rot); } + if (GETBIT(gs->type, GS_TAP) && gs->ntap == 1) { + foreach_bit(i, gs->tapmask) + tickle_button(local, i + 1); + } } /* called for each full received packet from the touchpad */ @@ -258,6 +262,10 @@ static void read_input(LocalDevicePtr local) extract_gestures(&gs, mt); handle_gestures(local, &gs, &mt->caps); } + if (mt_is_idle(mt, local->fd)) { + extract_delayed_gestures(&gs, mt); + handle_gestures(local, &gs, &mt->caps); + } } } diff --git a/include/gestures.h b/include/gestures.h index 1d70a25..6bc92cd 100644 --- a/include/gestures.h +++ b/include/gestures.h @@ -32,13 +32,17 @@ #define GS_HSWIPE 5 #define GS_SCALE 6 #define GS_ROTATE 7 +#define GS_TAP 8 struct Gestures { bitmask_t type, btmask, btdata; int same_fingers, dx, dy, scale, rot; + bitmask_t tapmask; + int ntap; }; void extract_gestures(struct Gestures *gs, struct MTouch* mt); +void extract_delayed_gestures(struct Gestures *gs, struct MTouch* mt); void output_gesture(const struct Gestures *gs); #endif diff --git a/include/memory.h b/include/memory.h index 1a1b688..3054828 100644 --- a/include/memory.h +++ b/include/memory.h @@ -40,6 +40,16 @@ * @mvforget: movement before this point in time is discarded * @dx: array of accumulated horiontal movement per finger * @dy: array of accumulated vertical movement per finger + * @tpdown: time of first touch + * @tpup: time of last release + * @tprelax: time of next possible touch + * @xdown: x position of first touch + * @ydown: y position of first touch + * @xup: x position of last release + * @yup: y position of last release + * @wait: time to wait for a second tap + * @maxtap: max number of pointing fingers during touch + * @ntap: number of taps in sequence * */ struct Memory { @@ -49,6 +59,9 @@ struct Memory { int ybar; mstime_t mvhold, mvforget; int dx[DIM_FINGER], dy[DIM_FINGER]; + mstime_t tpdown, tpup, tprelax; + int xdown, ydown, xup, yup; + int wait, maxtap, ntap; }; void init_memory(struct Memory *mem); diff --git a/include/mtdev-iobuf.h b/include/mtdev-iobuf.h index 976b85b..7d4265c 100644 --- a/include/mtdev-iobuf.h +++ b/include/mtdev-iobuf.h @@ -33,5 +33,6 @@ struct IOBuffer { void init_iobuf(struct IOBuffer *buf); const struct input_event *get_iobuf_event(struct IOBuffer *buf, int fd); +int poll_iobuf(struct IOBuffer *buf, int fd, int ms); #endif diff --git a/include/mtouch.h b/include/mtouch.h index 2b76652..c73eb29 100644 --- a/include/mtouch.h +++ b/include/mtouch.h @@ -40,6 +40,8 @@ int close_mtouch(struct MTouch *mt, int fd); int parse_event(struct MTouch *mt, const struct input_event *ev); +int mt_is_idle(struct MTouch *mt, int fd); + static inline void mt_delay_movement(struct MTouch *mt, int t) { mem_hold_movement(&mt->mem, mt->state.evtime + t); diff --git a/mtdev/iobuf.c b/mtdev/iobuf.c index 8e367b8..65c33f6 100644 --- a/mtdev/iobuf.c +++ b/mtdev/iobuf.c @@ -20,6 +20,7 @@ **************************************************************************/ #include +#include void init_iobuf(struct IOBuffer *buf) { @@ -52,3 +53,11 @@ const struct input_event *get_iobuf_event(struct IOBuffer *buf, int fd) buf->at += EVENT_SIZE; return ev; } + +int poll_iobuf(struct IOBuffer *buf, int fd, int ms) +{ + struct pollfd fds = { fd, POLLIN, 0 }; + if (buf->top != buf->at) + return 1; + return poll(&fds, 1, ms); +} diff --git a/src/gestures.c b/src/gestures.c index a5a17ca..3f59a1e 100644 --- a/src/gestures.c +++ b/src/gestures.c @@ -27,6 +27,7 @@ static const int FINGER_THUMB_MS = 600; static const int BUTTON_HOLD_MS = 200; +#define use_tapping 0 /** * extract_buttons @@ -50,6 +51,12 @@ static void extract_buttons(struct Gestures *gs, struct MTouch* mt) gs->btmask = (btdata ^ mt->mem.btdata) & BITONES(DIM_BUTTON); gs->btdata = btdata; mt->mem.btdata = btdata; + } else if (btdata == 0 && mt->mem.ntap) { + if (npoint == 1 && mt->mem.maxtap == 1) + btdata = BITMASK(MT_BUTTON_LEFT); + gs->btmask = (btdata ^ mt->mem.btdata) & BITONES(DIM_BUTTON); + gs->btdata = btdata; + mt->mem.btdata = btdata; } if (gs->btmask) { mt_delay_movement(mt, BUTTON_HOLD_MS); @@ -178,6 +185,40 @@ void extract_gestures(struct Gestures *gs, struct MTouch* mt) mt->prev_state = mt->state; } +/** + * extract_delayed_gestures + * + * Extract delayed gestures, such as tapping + * + * Reset memory after use. + * + */ +void extract_delayed_gestures(struct Gestures *gs, struct MTouch* mt) +{ + mt->mem.wait = 0; + + if (!use_tapping && mt->caps.has_left) + return; + + if (mt->mem.tpdown < mt->mem.tpup) { + switch (mt->mem.maxtap) { + case 1: + gs->tapmask = BITMASK(MT_BUTTON_LEFT); + break; + case 2: + gs->tapmask = BITMASK(MT_BUTTON_RIGHT); + break; + case 3: + gs->tapmask = BITMASK(MT_BUTTON_MIDDLE); + break; + } + } + + if (gs->tapmask) + SETBIT(gs->type, GS_TAP); + + gs->ntap = mt->mem.ntap; +} void output_gesture(const struct Gestures *gs) { @@ -199,4 +240,6 @@ void output_gesture(const struct Gestures *gs) xf86Msg(X_INFO, "scale: %d\n", gs->scale); if (GETBIT(gs->type, GS_ROTATE)) xf86Msg(X_INFO, "rotate: %d\n", gs->rot); + foreach_bit(i, gs->tapmask) + xf86Msg(X_INFO, "tap: %d %d\n", i, gs->ntap); } diff --git a/src/memory.c b/src/memory.c index cd88d7a..2bb4d95 100644 --- a/src/memory.c +++ b/src/memory.c @@ -27,11 +27,20 @@ /* fraction of max movement threshold */ #define DELTA_CUT(x) (0.5 * (x)) +/* fraction of tap movement threshold */ +#define TAP_XMOVE(c) (0.05 * get_cap_xsize(c)) +#define TAP_YMOVE(c) (0.05 * get_cap_ysize(c)) + /* timer for cursor stability on finger touch/release */ static const int FINGER_ATTACK_MS = 40; static const int FINGER_DECAY_MS = 120; static const int FINGER_CORNER_MS = 300; +/* tapping timings */ +static const int TAP_SETTLE_MS = 400; +static const int TAP_TOUCH_MS = 120; +static const int TAP_GAP_MS = 200; + static inline int dxval(const struct FingerState *a, const struct FingerState *b) { @@ -43,6 +52,23 @@ static inline int dyval(const struct FingerState *a, return a->position_y - b->position_y; } +static inline void relax_tapping(struct Memory *m, const struct MTState *state) +{ + m->tprelax = state->evtime + TAP_SETTLE_MS; + m->wait = 0; + m->ntap = 0; +} + +static inline int unrelated_taps(const struct Memory *m, + const struct Capabilities *caps) +{ + return abs(m->xdown - m->xup) > TAP_XMOVE(caps) || + abs(m->ydown - m->yup) > TAP_YMOVE(caps); +} + +/** + * init_memory + */ void init_memory(struct Memory *mem) { memset(mem, 0, sizeof(struct Memory)); @@ -188,6 +214,63 @@ static void update_movement(struct Memory *m, m->moving = m->pending; } +/** + * update_tapping + * + * Update tpdown, tpup, tprelax, wait, maxtap, ntap. + * + * Precondition: pointing and thumb are set + * + * Postcondition: tpdown, tpup, tprelax, wait, maxtap, ntap set + * + */ +static void update_tapping(struct Memory *m, + const struct MTState *prev_state, + const struct MTState *state, + const struct Capabilities *caps) +{ + int x, y, i, npoint = bitcount(m->pointing); + + /* use a position independent on the number of fingers */ + if (state->nfinger) { + x = state->finger[0].position_x; + y = state->finger[0].position_y; + for (i = 1; i < state->nfinger; i++) { + x = minval(x, state->finger[i].position_x); + y = minval(y, state->finger[i].position_y); + } + } + + if (m->thumb || state->evtime < m->mvforget) { + relax_tapping(m, state); + } else if (state->nfinger && !prev_state->nfinger) { + m->tpdown = state->evtime; + m->xdown = x; + m->ydown = y; + m->wait = 0; + m->maxtap = npoint; + if (m->tpdown > m->tpup + TAP_GAP_MS) + m->ntap = 0; + else if (unrelated_taps(m, caps)) + relax_tapping(m, state); + m->xup = x; + m->yup = y; + } else if (!state->nfinger && prev_state->nfinger) { + m->tpup = state->evtime; + if (m->tpup > m->tprelax && + m->tpup <= m->tpdown + TAP_TOUCH_MS) { + m->wait = TAP_GAP_MS; + m->ntap++; + if (unrelated_taps(m, caps)) + relax_tapping(m, state); + } + } else if (state->nfinger) { + m->xup = minval(m->xup, x); + m->yup = minval(m->yup, y); + m->maxtap = maxval(m->maxtap, npoint); + } +} + void refresh_memory(struct Memory *m, const struct MTState *prev_state, const struct MTState *state, @@ -196,6 +279,7 @@ void refresh_memory(struct Memory *m, update_configuration(m, prev_state, state); update_pointers(m, state, caps); update_movement(m, prev_state, state, caps); + update_tapping(m, prev_state, state, caps); } void output_memory(const struct Memory *m) diff --git a/src/mtouch.c b/src/mtouch.c index 7272edf..14c0fd9 100644 --- a/src/mtouch.c +++ b/src/mtouch.c @@ -73,3 +73,11 @@ int parse_event(struct MTouch *mt, const struct input_event *ev) #endif return 1; } + +int mt_is_idle(struct MTouch *mt, int fd) +{ + return mt->mem.wait && + evbuf_empty(&mt->dev.outbuf) && + evbuf_empty(&mt->dev.inbuf) && + poll_iobuf(&mt->buf, fd, mt->mem.wait) == 0; +} diff --git a/src/test.c b/src/test.c index 0088cdd..ce2fdf5 100644 --- a/src/test.c +++ b/src/test.c @@ -42,6 +42,10 @@ static void loop_device(int fd) extract_gestures(&gs, &mt); output_gesture(&gs); } + if (mt_is_idle(&mt, fd)) { + extract_delayed_gestures(&gs, &mt); + output_gesture(&gs); + } } close_mtouch(&mt, fd); } -- cgit v1.2.3