From 296cc6e9d0fe0d69045cb288f9f470f3760c4a43 Mon Sep 17 00:00:00 2001 From: Chase Douglas Date: Mon, 14 Feb 2011 16:35:13 +0100 Subject: Add in time boundaries for recognition 50 ms: Minimum time waiting for touch configuration to settle before flushing touch events to X. We don't want a two finger gesture triggering X events for the first finger before the second finger lands. 200 ms: Maximum time for touches to begin a gesture. If no gesture is found within this time period, the events are flushed to X. If after 50 ms there are no possible gestures for the number of active touches, the events are immediately flushed to X. If events are flushed to X, no more gesture recognition is possible until all touches are lifted. Likewise, if any gesture is recognized, no events may be flushed to X until all touches are lifted. [rydberg@euromail.se: this patch is severely restrictive and not final] Signed-off-by: Chase Douglas Signed-off-by: Henrik Rydberg --- src/grail-api.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 136 insertions(+), 18 deletions(-) (limited to 'src/grail-api.c') diff --git a/src/grail-api.c b/src/grail-api.c index 22724ee..1071825 100644 --- a/src/grail-api.c +++ b/src/grail-api.c @@ -34,6 +34,11 @@ #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; @@ -127,34 +132,147 @@ 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) +{ + 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; + } + + 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); + } + + return ge->get_clients(ge, &client, 1, pos, frame->num_active, + types, DIM_GRAIL_TYPE_BYTES); +} + static void report_frame(struct grail *ge, const struct utouch_frame *frame, 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; - gin_frame_begin(ge, frame); - gru_recognize(ge, frame); - gin_frame_end(ge, frame); - - if (grailbuf_empty(&impl->gbuf)) { - while (!evbuf_empty(&impl->evbuf)) { - evbuf_get(&impl->evbuf, &iev); - if (ge->event) - ge->event(ge, &iev); - } - } else { - while (!grailbuf_empty(&impl->gbuf)) { - grailbuf_get(&impl->gbuf, &gev); - if (ge->gesture) - ge->gesture(ge, &gev); - } - evbuf_clear(&impl->evbuf); + /* Reset timer when number of touches changes. */ + if (frame->prev->revision != frame->revision && frame->num_active) { + gru->start_time = frame->time; + } + + /* 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 (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; + } + + /* 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; } } -- cgit v1.2.3