diff options
| author | Henrik Rydberg <rydberg@euromail.se> | 2010-06-16 18:48:53 +0200 |
|---|---|---|
| committer | Henrik Rydberg <rydberg@euromail.se> | 2010-06-16 18:54:52 +0200 |
| commit | 21a363a2486a713434e890fc1f6b86a180755230 (patch) | |
| tree | ca788ccf7c8269c5cbd41350463fa478aa499903 | |
| parent | 523d193b089111849873d9de0ec1bf29f4176fbc (diff) | |
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 <rydberg@euromail.se>
| -rw-r--r-- | driver/multitouch.c | 8 | ||||
| -rw-r--r-- | include/gestures.h | 4 | ||||
| -rw-r--r-- | include/memory.h | 13 | ||||
| -rw-r--r-- | include/mtdev-iobuf.h | 1 | ||||
| -rw-r--r-- | include/mtouch.h | 2 | ||||
| -rw-r--r-- | mtdev/iobuf.c | 9 | ||||
| -rw-r--r-- | src/gestures.c | 43 | ||||
| -rw-r--r-- | src/memory.c | 84 | ||||
| -rw-r--r-- | src/mtouch.c | 8 | ||||
| -rw-r--r-- | src/test.c | 4 |
10 files changed, 176 insertions, 0 deletions
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, | |||
| 245 | int step = 1 + rot_fraction * get_cap_xsize(caps); | 245 | int step = 1 + rot_fraction * get_cap_xsize(caps); |
| 246 | button_scroll(local, 14, 15, &rot, step, gs->rot); | 246 | button_scroll(local, 14, 15, &rot, step, gs->rot); |
| 247 | } | 247 | } |
| 248 | if (GETBIT(gs->type, GS_TAP) && gs->ntap == 1) { | ||
| 249 | foreach_bit(i, gs->tapmask) | ||
| 250 | tickle_button(local, i + 1); | ||
| 251 | } | ||
| 248 | } | 252 | } |
| 249 | 253 | ||
| 250 | /* called for each full received packet from the touchpad */ | 254 | /* called for each full received packet from the touchpad */ |
| @@ -258,6 +262,10 @@ static void read_input(LocalDevicePtr local) | |||
| 258 | extract_gestures(&gs, mt); | 262 | extract_gestures(&gs, mt); |
| 259 | handle_gestures(local, &gs, &mt->caps); | 263 | handle_gestures(local, &gs, &mt->caps); |
| 260 | } | 264 | } |
| 265 | if (mt_is_idle(mt, local->fd)) { | ||
| 266 | extract_delayed_gestures(&gs, mt); | ||
| 267 | handle_gestures(local, &gs, &mt->caps); | ||
| 268 | } | ||
| 261 | } | 269 | } |
| 262 | } | 270 | } |
| 263 | 271 | ||
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 @@ | |||
| 32 | #define GS_HSWIPE 5 | 32 | #define GS_HSWIPE 5 |
| 33 | #define GS_SCALE 6 | 33 | #define GS_SCALE 6 |
| 34 | #define GS_ROTATE 7 | 34 | #define GS_ROTATE 7 |
| 35 | #define GS_TAP 8 | ||
| 35 | 36 | ||
| 36 | struct Gestures { | 37 | struct Gestures { |
| 37 | bitmask_t type, btmask, btdata; | 38 | bitmask_t type, btmask, btdata; |
| 38 | int same_fingers, dx, dy, scale, rot; | 39 | int same_fingers, dx, dy, scale, rot; |
| 40 | bitmask_t tapmask; | ||
| 41 | int ntap; | ||
| 39 | }; | 42 | }; |
| 40 | 43 | ||
| 41 | void extract_gestures(struct Gestures *gs, struct MTouch* mt); | 44 | void extract_gestures(struct Gestures *gs, struct MTouch* mt); |
| 45 | void extract_delayed_gestures(struct Gestures *gs, struct MTouch* mt); | ||
| 42 | void output_gesture(const struct Gestures *gs); | 46 | void output_gesture(const struct Gestures *gs); |
| 43 | 47 | ||
| 44 | #endif | 48 | #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 @@ | |||
| 40 | * @mvforget: movement before this point in time is discarded | 40 | * @mvforget: movement before this point in time is discarded |
| 41 | * @dx: array of accumulated horiontal movement per finger | 41 | * @dx: array of accumulated horiontal movement per finger |
| 42 | * @dy: array of accumulated vertical movement per finger | 42 | * @dy: array of accumulated vertical movement per finger |
| 43 | * @tpdown: time of first touch | ||
| 44 | * @tpup: time of last release | ||
| 45 | * @tprelax: time of next possible touch | ||
| 46 | * @xdown: x position of first touch | ||
| 47 | * @ydown: y position of first touch | ||
| 48 | * @xup: x position of last release | ||
| 49 | * @yup: y position of last release | ||
| 50 | * @wait: time to wait for a second tap | ||
| 51 | * @maxtap: max number of pointing fingers during touch | ||
| 52 | * @ntap: number of taps in sequence | ||
| 43 | * | 53 | * |
| 44 | */ | 54 | */ |
| 45 | struct Memory { | 55 | struct Memory { |
| @@ -49,6 +59,9 @@ struct Memory { | |||
| 49 | int ybar; | 59 | int ybar; |
| 50 | mstime_t mvhold, mvforget; | 60 | mstime_t mvhold, mvforget; |
| 51 | int dx[DIM_FINGER], dy[DIM_FINGER]; | 61 | int dx[DIM_FINGER], dy[DIM_FINGER]; |
| 62 | mstime_t tpdown, tpup, tprelax; | ||
| 63 | int xdown, ydown, xup, yup; | ||
| 64 | int wait, maxtap, ntap; | ||
| 52 | }; | 65 | }; |
| 53 | 66 | ||
| 54 | void init_memory(struct Memory *mem); | 67 | 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 { | |||
| 33 | 33 | ||
| 34 | void init_iobuf(struct IOBuffer *buf); | 34 | void init_iobuf(struct IOBuffer *buf); |
| 35 | const struct input_event *get_iobuf_event(struct IOBuffer *buf, int fd); | 35 | const struct input_event *get_iobuf_event(struct IOBuffer *buf, int fd); |
| 36 | int poll_iobuf(struct IOBuffer *buf, int fd, int ms); | ||
| 36 | 37 | ||
| 37 | #endif | 38 | #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); | |||
| 40 | 40 | ||
| 41 | int parse_event(struct MTouch *mt, const struct input_event *ev); | 41 | int parse_event(struct MTouch *mt, const struct input_event *ev); |
| 42 | 42 | ||
| 43 | int mt_is_idle(struct MTouch *mt, int fd); | ||
| 44 | |||
| 43 | static inline void mt_delay_movement(struct MTouch *mt, int t) | 45 | static inline void mt_delay_movement(struct MTouch *mt, int t) |
| 44 | { | 46 | { |
| 45 | mem_hold_movement(&mt->mem, mt->state.evtime + t); | 47 | 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 @@ | |||
| 20 | **************************************************************************/ | 20 | **************************************************************************/ |
| 21 | 21 | ||
| 22 | #include <mtdev-iobuf.h> | 22 | #include <mtdev-iobuf.h> |
| 23 | #include <sys/poll.h> | ||
| 23 | 24 | ||
| 24 | void init_iobuf(struct IOBuffer *buf) | 25 | void init_iobuf(struct IOBuffer *buf) |
| 25 | { | 26 | { |
| @@ -52,3 +53,11 @@ const struct input_event *get_iobuf_event(struct IOBuffer *buf, int fd) | |||
| 52 | buf->at += EVENT_SIZE; | 53 | buf->at += EVENT_SIZE; |
| 53 | return ev; | 54 | return ev; |
| 54 | } | 55 | } |
| 56 | |||
| 57 | int poll_iobuf(struct IOBuffer *buf, int fd, int ms) | ||
| 58 | { | ||
| 59 | struct pollfd fds = { fd, POLLIN, 0 }; | ||
| 60 | if (buf->top != buf->at) | ||
| 61 | return 1; | ||
| 62 | return poll(&fds, 1, ms); | ||
| 63 | } | ||
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 @@ | |||
| 27 | 27 | ||
| 28 | static const int FINGER_THUMB_MS = 600; | 28 | static const int FINGER_THUMB_MS = 600; |
| 29 | static const int BUTTON_HOLD_MS = 200; | 29 | static const int BUTTON_HOLD_MS = 200; |
| 30 | #define use_tapping 0 | ||
| 30 | 31 | ||
| 31 | /** | 32 | /** |
| 32 | * extract_buttons | 33 | * extract_buttons |
| @@ -50,6 +51,12 @@ static void extract_buttons(struct Gestures *gs, struct MTouch* mt) | |||
| 50 | gs->btmask = (btdata ^ mt->mem.btdata) & BITONES(DIM_BUTTON); | 51 | gs->btmask = (btdata ^ mt->mem.btdata) & BITONES(DIM_BUTTON); |
| 51 | gs->btdata = btdata; | 52 | gs->btdata = btdata; |
| 52 | mt->mem.btdata = btdata; | 53 | mt->mem.btdata = btdata; |
| 54 | } else if (btdata == 0 && mt->mem.ntap) { | ||
| 55 | if (npoint == 1 && mt->mem.maxtap == 1) | ||
| 56 | btdata = BITMASK(MT_BUTTON_LEFT); | ||
| 57 | gs->btmask = (btdata ^ mt->mem.btdata) & BITONES(DIM_BUTTON); | ||
| 58 | gs->btdata = btdata; | ||
| 59 | mt->mem.btdata = btdata; | ||
| 53 | } | 60 | } |
| 54 | if (gs->btmask) { | 61 | if (gs->btmask) { |
| 55 | mt_delay_movement(mt, BUTTON_HOLD_MS); | 62 | mt_delay_movement(mt, BUTTON_HOLD_MS); |
| @@ -178,6 +185,40 @@ void extract_gestures(struct Gestures *gs, struct MTouch* mt) | |||
| 178 | mt->prev_state = mt->state; | 185 | mt->prev_state = mt->state; |
| 179 | } | 186 | } |
| 180 | 187 | ||
| 188 | /** | ||
| 189 | * extract_delayed_gestures | ||
| 190 | * | ||
| 191 | * Extract delayed gestures, such as tapping | ||
| 192 | * | ||
| 193 | * Reset memory after use. | ||
| 194 | * | ||
| 195 | */ | ||
| 196 | void extract_delayed_gestures(struct Gestures *gs, struct MTouch* mt) | ||
| 197 | { | ||
| 198 | mt->mem.wait = 0; | ||
| 199 | |||
| 200 | if (!use_tapping && mt->caps.has_left) | ||
| 201 | return; | ||
| 202 | |||
| 203 | if (mt->mem.tpdown < mt->mem.tpup) { | ||
| 204 | switch (mt->mem.maxtap) { | ||
| 205 | case 1: | ||
| 206 | gs->tapmask = BITMASK(MT_BUTTON_LEFT); | ||
| 207 | break; | ||
| 208 | case 2: | ||
| 209 | gs->tapmask = BITMASK(MT_BUTTON_RIGHT); | ||
| 210 | break; | ||
| 211 | case 3: | ||
| 212 | gs->tapmask = BITMASK(MT_BUTTON_MIDDLE); | ||
| 213 | break; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | if (gs->tapmask) | ||
| 218 | SETBIT(gs->type, GS_TAP); | ||
| 219 | |||
| 220 | gs->ntap = mt->mem.ntap; | ||
| 221 | } | ||
| 181 | 222 | ||
| 182 | void output_gesture(const struct Gestures *gs) | 223 | void output_gesture(const struct Gestures *gs) |
| 183 | { | 224 | { |
| @@ -199,4 +240,6 @@ void output_gesture(const struct Gestures *gs) | |||
| 199 | xf86Msg(X_INFO, "scale: %d\n", gs->scale); | 240 | xf86Msg(X_INFO, "scale: %d\n", gs->scale); |
| 200 | if (GETBIT(gs->type, GS_ROTATE)) | 241 | if (GETBIT(gs->type, GS_ROTATE)) |
| 201 | xf86Msg(X_INFO, "rotate: %d\n", gs->rot); | 242 | xf86Msg(X_INFO, "rotate: %d\n", gs->rot); |
| 243 | foreach_bit(i, gs->tapmask) | ||
| 244 | xf86Msg(X_INFO, "tap: %d %d\n", i, gs->ntap); | ||
| 202 | } | 245 | } |
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 @@ | |||
| 27 | /* fraction of max movement threshold */ | 27 | /* fraction of max movement threshold */ |
| 28 | #define DELTA_CUT(x) (0.5 * (x)) | 28 | #define DELTA_CUT(x) (0.5 * (x)) |
| 29 | 29 | ||
| 30 | /* fraction of tap movement threshold */ | ||
| 31 | #define TAP_XMOVE(c) (0.05 * get_cap_xsize(c)) | ||
| 32 | #define TAP_YMOVE(c) (0.05 * get_cap_ysize(c)) | ||
| 33 | |||
| 30 | /* timer for cursor stability on finger touch/release */ | 34 | /* timer for cursor stability on finger touch/release */ |
| 31 | static const int FINGER_ATTACK_MS = 40; | 35 | static const int FINGER_ATTACK_MS = 40; |
| 32 | static const int FINGER_DECAY_MS = 120; | 36 | static const int FINGER_DECAY_MS = 120; |
| 33 | static const int FINGER_CORNER_MS = 300; | 37 | static const int FINGER_CORNER_MS = 300; |
| 34 | 38 | ||
| 39 | /* tapping timings */ | ||
| 40 | static const int TAP_SETTLE_MS = 400; | ||
| 41 | static const int TAP_TOUCH_MS = 120; | ||
| 42 | static const int TAP_GAP_MS = 200; | ||
| 43 | |||
| 35 | static inline int dxval(const struct FingerState *a, | 44 | static inline int dxval(const struct FingerState *a, |
| 36 | const struct FingerState *b) | 45 | const struct FingerState *b) |
| 37 | { | 46 | { |
| @@ -43,6 +52,23 @@ static inline int dyval(const struct FingerState *a, | |||
| 43 | return a->position_y - b->position_y; | 52 | return a->position_y - b->position_y; |
| 44 | } | 53 | } |
| 45 | 54 | ||
| 55 | static inline void relax_tapping(struct Memory *m, const struct MTState *state) | ||
| 56 | { | ||
| 57 | m->tprelax = state->evtime + TAP_SETTLE_MS; | ||
| 58 | m->wait = 0; | ||
| 59 | m->ntap = 0; | ||
| 60 | } | ||
| 61 | |||
| 62 | static inline int unrelated_taps(const struct Memory *m, | ||
| 63 | const struct Capabilities *caps) | ||
| 64 | { | ||
| 65 | return abs(m->xdown - m->xup) > TAP_XMOVE(caps) || | ||
| 66 | abs(m->ydown - m->yup) > TAP_YMOVE(caps); | ||
| 67 | } | ||
| 68 | |||
| 69 | /** | ||
| 70 | * init_memory | ||
| 71 | */ | ||
| 46 | void init_memory(struct Memory *mem) | 72 | void init_memory(struct Memory *mem) |
| 47 | { | 73 | { |
| 48 | memset(mem, 0, sizeof(struct Memory)); | 74 | memset(mem, 0, sizeof(struct Memory)); |
| @@ -188,6 +214,63 @@ static void update_movement(struct Memory *m, | |||
| 188 | m->moving = m->pending; | 214 | m->moving = m->pending; |
| 189 | } | 215 | } |
| 190 | 216 | ||
| 217 | /** | ||
| 218 | * update_tapping | ||
| 219 | * | ||
| 220 | * Update tpdown, tpup, tprelax, wait, maxtap, ntap. | ||
| 221 | * | ||
| 222 | * Precondition: pointing and thumb are set | ||
| 223 | * | ||
| 224 | * Postcondition: tpdown, tpup, tprelax, wait, maxtap, ntap set | ||
| 225 | * | ||
| 226 | */ | ||
| 227 | static void update_tapping(struct Memory *m, | ||
| 228 | const struct MTState *prev_state, | ||
| 229 | const struct MTState *state, | ||
| 230 | const struct Capabilities *caps) | ||
| 231 | { | ||
| 232 | int x, y, i, npoint = bitcount(m->pointing); | ||
| 233 | |||
| 234 | /* use a position independent on the number of fingers */ | ||
| 235 | if (state->nfinger) { | ||
| 236 | x = state->finger[0].position_x; | ||
| 237 | y = state->finger[0].position_y; | ||
| 238 | for (i = 1; i < state->nfinger; i++) { | ||
| 239 | x = minval(x, state->finger[i].position_x); | ||
| 240 | y = minval(y, state->finger[i].position_y); | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | if (m->thumb || state->evtime < m->mvforget) { | ||
| 245 | relax_tapping(m, state); | ||
| 246 | } else if (state->nfinger && !prev_state->nfinger) { | ||
| 247 | m->tpdown = state->evtime; | ||
| 248 | m->xdown = x; | ||
| 249 | m->ydown = y; | ||
| 250 | m->wait = 0; | ||
| 251 | m->maxtap = npoint; | ||
| 252 | if (m->tpdown > m->tpup + TAP_GAP_MS) | ||
| 253 | m->ntap = 0; | ||
| 254 | else if (unrelated_taps(m, caps)) | ||
| 255 | relax_tapping(m, state); | ||
| 256 | m->xup = x; | ||
| 257 | m->yup = y; | ||
| 258 | } else if (!state->nfinger && prev_state->nfinger) { | ||
| 259 | m->tpup = state->evtime; | ||
| 260 | if (m->tpup > m->tprelax && | ||
| 261 | m->tpup <= m->tpdown + TAP_TOUCH_MS) { | ||
| 262 | m->wait = TAP_GAP_MS; | ||
| 263 | m->ntap++; | ||
| 264 | if (unrelated_taps(m, caps)) | ||
| 265 | relax_tapping(m, state); | ||
| 266 | } | ||
| 267 | } else if (state->nfinger) { | ||
| 268 | m->xup = minval(m->xup, x); | ||
| 269 | m->yup = minval(m->yup, y); | ||
| 270 | m->maxtap = maxval(m->maxtap, npoint); | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 191 | void refresh_memory(struct Memory *m, | 274 | void refresh_memory(struct Memory *m, |
| 192 | const struct MTState *prev_state, | 275 | const struct MTState *prev_state, |
| 193 | const struct MTState *state, | 276 | const struct MTState *state, |
| @@ -196,6 +279,7 @@ void refresh_memory(struct Memory *m, | |||
| 196 | update_configuration(m, prev_state, state); | 279 | update_configuration(m, prev_state, state); |
| 197 | update_pointers(m, state, caps); | 280 | update_pointers(m, state, caps); |
| 198 | update_movement(m, prev_state, state, caps); | 281 | update_movement(m, prev_state, state, caps); |
| 282 | update_tapping(m, prev_state, state, caps); | ||
| 199 | } | 283 | } |
| 200 | 284 | ||
| 201 | void output_memory(const struct Memory *m) | 285 | 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) | |||
| 73 | #endif | 73 | #endif |
| 74 | return 1; | 74 | return 1; |
| 75 | } | 75 | } |
| 76 | |||
| 77 | int mt_is_idle(struct MTouch *mt, int fd) | ||
| 78 | { | ||
| 79 | return mt->mem.wait && | ||
| 80 | evbuf_empty(&mt->dev.outbuf) && | ||
| 81 | evbuf_empty(&mt->dev.inbuf) && | ||
| 82 | poll_iobuf(&mt->buf, fd, mt->mem.wait) == 0; | ||
| 83 | } | ||
| @@ -42,6 +42,10 @@ static void loop_device(int fd) | |||
| 42 | extract_gestures(&gs, &mt); | 42 | extract_gestures(&gs, &mt); |
| 43 | output_gesture(&gs); | 43 | output_gesture(&gs); |
| 44 | } | 44 | } |
| 45 | if (mt_is_idle(&mt, fd)) { | ||
| 46 | extract_delayed_gestures(&gs, &mt); | ||
| 47 | output_gesture(&gs); | ||
| 48 | } | ||
| 45 | } | 49 | } |
| 46 | close_mtouch(&mt, fd); | 50 | close_mtouch(&mt, fd); |
| 47 | } | 51 | } |
