From f92c18c344c04a2501e01ae402441a92883da382 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Thu, 24 Feb 2011 23:38:01 +0100 Subject: Introduce gesture timeouts The timeout of a gesture is integral to the gesture type. This patch introduces timeout per gesture type, potentially enabling more control over gesture timing aspects. The timeout logic replaces the recently added recognition state. Signed-off-by: Henrik Rydberg --- src/gestures-drag.c | 14 ++++ src/gestures-pinch.c | 14 ++++ src/gestures-rotate.c | 14 ++++ src/grail-api.c | 173 +++++++++++++------------------------------------ src/grail-gestures.c | 10 ++- src/grail-gestures.h | 2 +- src/grail-impl.h | 2 + src/grail-recognizer.h | 8 --- 8 files changed, 98 insertions(+), 139 deletions(-) (limited to 'src') diff --git a/src/gestures-drag.c b/src/gestures-drag.c index 05272da..9f2bcaf 100644 --- a/src/gestures-drag.c +++ b/src/gestures-drag.c @@ -72,6 +72,13 @@ int gru_drag(struct grail *ge, state->active = 0; } } + if ((move->timeout & fm_mask) == fm_mask) { + if (state->active) { + gin_gid_discard(ge, state->gid); + state->active = 0; + } + return 0; + } if (!state->active) { int type = getype[move->ntouch]; if (type < 0) @@ -102,6 +109,13 @@ int gru_windrag(struct grail *ge, state->active = 0; } } + if ((move->timeout & fm_mask) == fm_mask) { + if (state->active) { + gin_gid_discard(ge, state->gid); + state->active = 0; + } + return 0; + } if (!state->active) { if (move->ntouch == 4) { state->gid = gin_gid_begin(ge, GRAIL_TYPE_MDRAG, diff --git a/src/gestures-pinch.c b/src/gestures-pinch.c index fa8a626..e022e8d 100644 --- a/src/gestures-pinch.c +++ b/src/gestures-pinch.c @@ -62,6 +62,13 @@ int gru_pinch(struct grail *ge, } return 0; } + if ((move->timeout & fm_mask) == fm_mask) { + if (state->active) { + gin_gid_discard(ge, state->gid); + state->active = 0; + } + return 0; + } if (!(move->tickle & mask)) return 0; if (!state->active) { @@ -93,6 +100,13 @@ int gru_winpinch(struct grail *ge, } return 0; } + if ((move->timeout & fm_mask) == fm_mask) { + if (state->active) { + gin_gid_discard(ge, state->gid); + state->active = 0; + } + return 0; + } if (!(move->tickle & mask)) return 0; if (!state->active) { diff --git a/src/gestures-rotate.c b/src/gestures-rotate.c index fde0db8..dd95c32 100644 --- a/src/gestures-rotate.c +++ b/src/gestures-rotate.c @@ -61,6 +61,13 @@ int gru_rotate(struct grail *ge, } return 0; } + if ((move->timeout & fm_mask) == fm_mask) { + if (state->active) { + gin_gid_discard(ge, state->gid); + state->active = 0; + } + return 0; + } if (!(move->tickle & mask)) return 0; if (!state->active) { @@ -92,6 +99,13 @@ int gru_winrotate(struct grail *ge, } return 0; } + if ((move->timeout & fm_mask) == fm_mask) { + if (state->active) { + gin_gid_discard(ge, state->gid); + state->active = 0; + } + return 0; + } if (!(move->tickle & mask)) return 0; if (!state->active) { diff --git a/src/grail-api.c b/src/grail-api.c index 1071825..6521f84 100644 --- a/src/grail-api.c +++ b/src/grail-api.c @@ -34,11 +34,6 @@ #define DIM_FRAMES 100 #define FRAME_RATE 100 -/* Wait time (ms) for number of touches to stabilize. */ -static const unsigned int stable_time = 50; -/* Maximum time (ms) for gesture to be recognized. */ -static const unsigned int recognition_time = 200; - void grail_filter_abs_events(struct grail *ge, int usage) { struct grail_impl *x = ge->impl; @@ -132,62 +127,37 @@ void grail_get_units(const struct grail *ge, max->y = s->max_y; } -/* Check for any potential gestures given the number of touch points and where - * they occur on screen. */ -static int check_potential_gestures(struct grail *ge, - const struct utouch_frame *frame) +static void flush_events(struct grail *ge) { - grail_mask_t types[DIM_GRAIL_TYPE_BYTES] = {0}; - struct grail_client_info client; - struct grail_coord pos[frame->num_active]; - int i; - - switch (frame->num_active) { - case 1: - grail_mask_set(types, GRAIL_TYPE_DRAG1); - grail_mask_set(types, GRAIL_TYPE_PINCH1); - grail_mask_set(types, GRAIL_TYPE_ROTATE1); - grail_mask_set(types, GRAIL_TYPE_TAP1); - break; - case 2: - grail_mask_set(types, GRAIL_TYPE_DRAG2); - grail_mask_set(types, GRAIL_TYPE_PINCH2); - grail_mask_set(types, GRAIL_TYPE_ROTATE2); - grail_mask_set(types, GRAIL_TYPE_TAP2); - break; - case 3: - grail_mask_set(types, GRAIL_TYPE_DRAG3); - grail_mask_set(types, GRAIL_TYPE_PINCH3); - grail_mask_set(types, GRAIL_TYPE_ROTATE3); - grail_mask_set(types, GRAIL_TYPE_TAP3); - grail_mask_set(types, GRAIL_TYPE_EDRAG); - grail_mask_set(types, GRAIL_TYPE_EPINCH); - grail_mask_set(types, GRAIL_TYPE_EROTATE); - break; - case 4: - grail_mask_set(types, GRAIL_TYPE_DRAG4); - grail_mask_set(types, GRAIL_TYPE_PINCH4); - grail_mask_set(types, GRAIL_TYPE_ROTATE4); - grail_mask_set(types, GRAIL_TYPE_TAP4); - grail_mask_set(types, GRAIL_TYPE_MDRAG); - grail_mask_set(types, GRAIL_TYPE_MPINCH); - grail_mask_set(types, GRAIL_TYPE_MROTATE); - break; - case 5: - grail_mask_set(types, GRAIL_TYPE_DRAG5); - grail_mask_set(types, GRAIL_TYPE_PINCH5); - grail_mask_set(types, GRAIL_TYPE_ROTATE5); - grail_mask_set(types, GRAIL_TYPE_TAP5); - break; + struct grail_impl *impl = ge->impl; + struct input_event ev; + + grailbuf_clear(&impl->gbuf); + while (!evbuf_empty(&impl->evbuf)) { + evbuf_get(&impl->evbuf, &ev); + if (ge->event) + ge->event(ge, &ev); } +} - for (i = 0; i < frame->num_active; i++) { - pos[i].x = gin_prop_x(ge->gin, frame->active[i]->x); - pos[i].y = gin_prop_y(ge->gin, frame->active[i]->y); +static void flush_gestures(struct grail *ge) +{ + struct grail_impl *impl = ge->impl; + struct grail_event ev; + + evbuf_clear(&impl->evbuf); + while (!grailbuf_empty(&impl->gbuf)) { + grailbuf_get(&impl->gbuf, &ev); + if (ge->gesture) + ge->gesture(ge, &ev); } +} - return ge->get_clients(ge, &client, 1, pos, frame->num_active, - types, DIM_GRAIL_TYPE_BYTES); +static int gesture_timeout(struct grail *ge, const struct utouch_frame *frame) +{ + struct gesture_inserter *gin = ge->gin; + + return grail_mask_count(gin->used, sizeof(gin->used)) == 0; } static void report_frame(struct grail *ge, @@ -195,85 +165,27 @@ static void report_frame(struct grail *ge, const struct input_event *syn) { struct grail_impl *impl = ge->impl; - struct gesture_recognizer *gru = ge->gru; - struct input_event iev; struct grail_event gev; ge->impl->frame = frame; - /* Reset timer when number of touches changes. */ - if (frame->prev->revision != frame->revision && frame->num_active) { - gru->start_time = frame->time; + if (frame->num_active && !frame->prev->num_active) { + impl->ongoing = 1; + impl->gesture = 0; } - /* Once touches have stabilized, check if there are any potential - * gestures registered for the number of touches. */ - if (gru->state == RECOGNIZING && - frame->time > gru->start_time + stable_time && - check_potential_gestures(ge, frame) == 0) - gru->state = UNRECOGNIZED; - - /* Process the frame for gestures unless we're sure there are none. */ - if (gru->state != UNRECOGNIZED) { - gin_frame_begin(ge, frame); - gru_recognize(ge, frame); - gin_frame_end(ge, frame); - } + if (!impl->ongoing) + return; - if (gru->state == RECOGNIZING) { - /* If a gesture is recognized and either all touches lifted or - * we are within the recognition interval, set state to - * recognized. Note that taps do not stay in the used array, so - * we must check if any gesture events are buffered. */ - if ((grail_mask_count(ge->gin->used, DIM_INSTANCE_BYTES) || - !grailbuf_empty(&impl->gbuf)) && - (frame->num_active == 0 || - (frame->time > gru->start_time + stable_time && - frame->time <= gru->start_time + recognition_time))) - gru->state = RECOGNIZED; - /* If there is no gesture and all touches lifted, set state to - * unrecognized. */ - else if (frame->num_active == 0) - gru->state = UNRECOGNIZED; - } + gin_frame_begin(ge, frame); + gru_recognize(ge, frame); + gin_frame_end(ge, frame); - /* Now we have the state set, process events. */ - switch (gru->state) { - case RECOGNIZED: - while (!grailbuf_empty(&impl->gbuf)) { - grailbuf_get(&impl->gbuf, &gev); - if (ge->gesture) - ge->gesture(ge, &gev); - } - evbuf_clear(&impl->evbuf); - - /* Once all touches are lifted and no gestures are - * active, reset recognition state. */ - if (grail_mask_count(ge->gin->used, DIM_INSTANCE_BYTES) == 0 && - frame->num_active == 0) - gru->state = RECOGNIZING; - break; - - case RECOGNIZING: - /* If we're still actively recognizing, stop here. */ - if (frame->time <= gru->start_time + recognition_time) - break; - gru->state = UNRECOGNIZED; - - case UNRECOGNIZED: - while (!evbuf_empty(&impl->evbuf)) { - evbuf_get(&impl->evbuf, &iev); - if (ge->event) - ge->event(ge, &iev); - } - grailbuf_clear(&impl->gbuf); - - /* Once all touches are lifted, reset recognition - * state. */ - if (frame->num_active == 0) - gru->state = RECOGNIZING; - break; - } + if (!grailbuf_empty(&impl->gbuf)) + impl->gesture = 1; + + if (frame->num_active == 0 || gesture_timeout(ge, frame)) + impl->ongoing &= impl->gesture; } static void grail_pump_mtdev(struct grail *ge, const struct input_event *ev) @@ -288,6 +200,13 @@ static void grail_pump_mtdev(struct grail *ge, const struct input_event *ev) if (frame) report_frame(ge, frame, ev); } + + if (ev->type == EV_SYN) { + if (!impl->ongoing) + flush_events(ge); + if (impl->gesture) + flush_gestures(ge); + } } int grail_pull(struct grail *ge, int fd) diff --git a/src/grail-gestures.c b/src/grail-gestures.c index 035a199..efadde5 100644 --- a/src/grail-gestures.c +++ b/src/grail-gestures.c @@ -111,6 +111,7 @@ static void move_reset(struct move_model *m, int i, float x, grail_time_t t) fm->sample_ms = t; m->tickle &= ~(1 << i); m->active &= ~(1 << i); + m->timeout &= ~(1 << i); } static void move_update(struct move_model *m, int i, float x, grail_time_t t) @@ -132,11 +133,14 @@ static void move_update(struct move_model *m, int i, float x, grail_time_t t) if (m->active & (1 << i)) return; fm->action_delta = x - fm->original; - if (fabs(fm->action_delta) > fm->bar || - t - fm->original_ms > fm->bar_ms) + if (fabs(fm->action_delta) > fm->bar) { m->active |= (1 << i); - else + } else if (t - fm->original_ms > fm->bar_ms) { + m->active |= (1 << i); + m->timeout |= (1 << i); + } else { fm->action_delta = 0; + } } void gru_init_motion(struct grail *ge) diff --git a/src/grail-gestures.h b/src/grail-gestures.h index a6d51d5..c4c46c2 100644 --- a/src/grail-gestures.h +++ b/src/grail-gestures.h @@ -54,7 +54,7 @@ struct filter_model { struct move_model { struct filter_model fm[DIM_FM]; - int tickle, active; + int tickle, active, timeout; int single, multi, ntouch; grail_time_t time; }; diff --git a/src/grail-impl.h b/src/grail-impl.h index 595f0fb..2c63b90 100644 --- a/src/grail-impl.h +++ b/src/grail-impl.h @@ -42,6 +42,8 @@ struct grail_impl { struct evbuf evbuf; struct grailbuf gbuf; int filter_abs; + int ongoing; + int gesture; }; #endif diff --git a/src/grail-recognizer.h b/src/grail-recognizer.h index b08ff34..56e5b8d 100644 --- a/src/grail-recognizer.h +++ b/src/grail-recognizer.h @@ -25,12 +25,6 @@ #include "grail-gestures.h" -enum recognition_state { - RECOGNIZING, - RECOGNIZED, - UNRECOGNIZED -}; - struct gesture_recognizer { struct move_model move; struct combo_model drag; @@ -40,8 +34,6 @@ struct gesture_recognizer { struct combo_model winpinch; struct combo_model winrotate; struct tapping_model tapping; - utouch_frame_time_t start_time; - enum recognition_state state; }; int gru_init(struct grail *ge); -- cgit v1.2.3