Merge lp:~oif-team/grail/trunk.fixes into lp:grail
- trunk.fixes
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~oif-team/grail/trunk.fixes |
Merge into: | lp:grail |
Diff against target: |
562 lines (+151/-197) 11 files modified
src/evbuf.h (+1/-1) src/gestures-drag.c (+14/-0) src/gestures-pinch.c (+14/-0) src/gestures-rotate.c (+14/-0) src/gestures-tapping.c (+2/-0) src/grail-api.c (+86/-162) src/grail-event.c (+2/-16) src/grail-gestures.c (+14/-7) src/grail-gestures.h (+2/-1) src/grail-impl.h (+2/-0) src/grail-recognizer.h (+0/-10) |
To merge this branch: | bzr merge lp:~oif-team/grail/trunk.fixes |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Chase Douglas (community) | Needs Fixing | ||
Stephen M. Webb (community) | Approve | ||
Review via email: mp+51127@code.launchpad.net |
This proposal has been superseded by a proposal from 2011-02-24.
Commit message
Description of the change
A bunch of patches aiming to integrate the ideas of v1.0.19 more seamlessly into the grail code. This will fix the remaining tap bug and help further development. The only part not addressed by this branch is the sysflag, and a discussion around it is likely enough to see how to form a patch for it.
Chase Douglas (chasedouglas) wrote : | # |
In line 371 of the patch below, there's an ambiguity in the if statement "if (a || b && c)". This should be cleaned up.
I need to test this branch, but I think the changes look reasonable. However, I can't approve the request until it also handles the global flag. This is something I think we will really need in natty, since we're going to have more applications using gestures.
Chase Douglas (chasedouglas) wrote : | # |
Test results:
1. Regression: If you use a physical trackpad button to click while there's no movement on the trackpad, it's held up until a movement is performed.
2. Timeouts seem ok. I was able to request all 3, 4, and 5 finger gestures and still perform two finger multitouch reasonably. I will perform more thorough testing when the regressions are resolved.
3. Regression: I often get a tap event at the end of a drag gesture sequence. The tap touch count matches the drag touch count.
Henrik Rydberg (rydberg) wrote : | # |
Thank you very much for the tests! I have updated the branch with fixes for
the two problems found. I will add something for the global flag shortly.
- 115. By Henrik Rydberg
-
Revert "Add button passthrough"
- 116. By Henrik Rydberg
-
Revert "Add global vs local gesture handling"
- 117. By Henrik Rydberg
-
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. - 118. By Henrik Rydberg
-
Introduce activation hold time
Do not activate a drag, pinch or rotate immediately, even
if the activation threshold is met. This allows for a smoother
transition between fingers. - 119. By Henrik Rydberg
-
Wait for the right number of fingers before gesture timeout
A client listening only for multi-finger gestures will not get
the chance to recognize unless there is a gesture timeout. This
patch waits until the drag hold time has passed before releasing
the touch. - 120. By Henrik Rydberg
-
Skip events selectively
Buffer all events as planned, but strip the stream of all events
involved with pointer and MT, rather than bluntly removing all
events. In practise, this means button and switch events are let
through, undistorted although possibly slightly delayed. - 121. By Henrik Rydberg
-
Adjust gesture buffer sizes and timings
Adjust timings to put drag, pinch and rotate on more equal footing,
with regard to gesture timeouts. Also make sure events are not lost
during hold, now that all MT events are buffered as well. - 122. By Henrik Rydberg
-
Reset tap state after timeout
A touch and hold will not properly reset the tap state, such that
the next tap fails to register. Fixed with this patch. - 123. By Henrik Rydberg
-
Remove unused tapping state
This patch removes some cruft that was left over from the
removal of the short tap time logic. - 124. By Henrik Rydberg
-
Make reset logic of pinch and drag same as for drag
There is no direct reason for one-fingered pinch/rotate gestures
to be discarded immediately, so only reset gestures on touch down,
as for drags. - 125. By Henrik Rydberg
-
Make grabbing client exclusive
When a possible gesture is started for a global client (sysflag set),
it is desirable that gestures to other clients are disregarded until
all contacts have left the surface. In principle this could be refined
further, such that if gesture are not detected, they are passed on to
other clients, but that also affects how touches are passed onto lower
layers like XI2.1, so simply making sure a global system client gets
all events potentially leading to gestures is good enough for now.Also note that in the current combination of synaptics/evdev X servers
and grail, there can be no overlapping potential gestures between two
global clients, or the resulting event recipient is not correct. - 126. By Henrik Rydberg
-
Bump to v1.0.20
- 127. By Henrik Rydberg
-
Disable rotation gestures for semi-mt devices
To avoid poor user experience with semi-mt devices, disable
rotation detection, such that only drag and scale is emitted.Signed-off-by: Chase Douglas <email address hidden>
Signed-off-by: Henrik Rydberg <email address hidden>
Unmerged revisions
Preview Diff
1 | === modified file 'src/evbuf.h' |
2 | --- src/evbuf.h 2010-08-06 20:30:20 +0000 |
3 | +++ src/evbuf.h 2011-02-24 13:47:05 +0000 |
4 | @@ -29,7 +29,7 @@ |
5 | #ifndef GRAIL_EVBUF_H |
6 | #define GRAIL_EVBUF_H |
7 | |
8 | -#define DIM_EVENTS 512 |
9 | +#define DIM_EVENTS 4096 |
10 | |
11 | struct evbuf { |
12 | int head; |
13 | |
14 | === modified file 'src/gestures-drag.c' |
15 | --- src/gestures-drag.c 2011-01-02 12:08:08 +0000 |
16 | +++ src/gestures-drag.c 2011-02-24 13:47:05 +0000 |
17 | @@ -72,6 +72,13 @@ |
18 | state->active = 0; |
19 | } |
20 | } |
21 | + if ((move->timeout & fm_mask) == fm_mask) { |
22 | + if (state->active) { |
23 | + gin_gid_discard(ge, state->gid); |
24 | + state->active = 0; |
25 | + } |
26 | + return 0; |
27 | + } |
28 | if (!state->active) { |
29 | int type = getype[move->ntouch]; |
30 | if (type < 0) |
31 | @@ -102,6 +109,13 @@ |
32 | state->active = 0; |
33 | } |
34 | } |
35 | + if ((move->timeout & fm_mask) == fm_mask) { |
36 | + if (state->active) { |
37 | + gin_gid_discard(ge, state->gid); |
38 | + state->active = 0; |
39 | + } |
40 | + return 0; |
41 | + } |
42 | if (!state->active) { |
43 | if (move->ntouch == 4) { |
44 | state->gid = gin_gid_begin(ge, GRAIL_TYPE_MDRAG, |
45 | |
46 | === modified file 'src/gestures-pinch.c' |
47 | --- src/gestures-pinch.c 2011-01-02 12:08:08 +0000 |
48 | +++ src/gestures-pinch.c 2011-02-24 13:47:05 +0000 |
49 | @@ -62,6 +62,13 @@ |
50 | } |
51 | return 0; |
52 | } |
53 | + if ((move->timeout & fm_mask) == fm_mask) { |
54 | + if (state->active) { |
55 | + gin_gid_discard(ge, state->gid); |
56 | + state->active = 0; |
57 | + } |
58 | + return 0; |
59 | + } |
60 | if (!(move->tickle & mask)) |
61 | return 0; |
62 | if (!state->active) { |
63 | @@ -93,6 +100,13 @@ |
64 | } |
65 | return 0; |
66 | } |
67 | + if ((move->timeout & fm_mask) == fm_mask) { |
68 | + if (state->active) { |
69 | + gin_gid_discard(ge, state->gid); |
70 | + state->active = 0; |
71 | + } |
72 | + return 0; |
73 | + } |
74 | if (!(move->tickle & mask)) |
75 | return 0; |
76 | if (!state->active) { |
77 | |
78 | === modified file 'src/gestures-rotate.c' |
79 | --- src/gestures-rotate.c 2011-01-02 12:08:08 +0000 |
80 | +++ src/gestures-rotate.c 2011-02-24 13:47:05 +0000 |
81 | @@ -61,6 +61,13 @@ |
82 | } |
83 | return 0; |
84 | } |
85 | + if ((move->timeout & fm_mask) == fm_mask) { |
86 | + if (state->active) { |
87 | + gin_gid_discard(ge, state->gid); |
88 | + state->active = 0; |
89 | + } |
90 | + return 0; |
91 | + } |
92 | if (!(move->tickle & mask)) |
93 | return 0; |
94 | if (!state->active) { |
95 | @@ -92,6 +99,13 @@ |
96 | } |
97 | return 0; |
98 | } |
99 | + if ((move->timeout & fm_mask) == fm_mask) { |
100 | + if (state->active) { |
101 | + gin_gid_discard(ge, state->gid); |
102 | + state->active = 0; |
103 | + } |
104 | + return 0; |
105 | + } |
106 | if (!(move->tickle & mask)) |
107 | return 0; |
108 | if (!state->active) { |
109 | |
110 | === modified file 'src/gestures-tapping.c' |
111 | --- src/gestures-tapping.c 2011-02-09 15:17:50 +0000 |
112 | +++ src/gestures-tapping.c 2011-02-24 13:47:05 +0000 |
113 | @@ -91,6 +91,8 @@ |
114 | if ((move->active & fm_mask) || |
115 | move->time - state->start > move->fm[FM_X].bar_ms) { |
116 | gin_gid_discard(ge, state->gid); |
117 | + state->mintouch = 0; |
118 | + state->maxtouch = 0; |
119 | state->active = 0; |
120 | } |
121 | return 0; |
122 | |
123 | === modified file 'src/grail-api.c' |
124 | --- src/grail-api.c 2011-02-21 01:22:08 +0000 |
125 | +++ src/grail-api.c 2011-02-24 13:47:05 +0000 |
126 | @@ -34,11 +34,6 @@ |
127 | #define DIM_FRAMES 100 |
128 | #define FRAME_RATE 100 |
129 | |
130 | -/* Wait time (ms) for number of touches to stabilize. */ |
131 | -static const unsigned int stable_time = 50; |
132 | -/* Maximum time (ms) for gesture to be recognized. */ |
133 | -static const unsigned int recognition_time = 300; |
134 | - |
135 | void grail_filter_abs_events(struct grail *ge, int usage) |
136 | { |
137 | struct grail_impl *x = ge->impl; |
138 | @@ -132,159 +127,103 @@ |
139 | max->y = s->max_y; |
140 | } |
141 | |
142 | -/* Check for any potential gestures given the number of touch points and where |
143 | - * they occur on screen. */ |
144 | -static int check_potential_gestures(struct grail *ge, |
145 | - const struct utouch_frame *frame) |
146 | -{ |
147 | - grail_mask_t types[DIM_GRAIL_TYPE_BYTES] = {0}; |
148 | - struct grail_client_info client; |
149 | - struct grail_coord pos[frame->num_active]; |
150 | - int i; |
151 | - |
152 | - switch (frame->num_active) { |
153 | - case 1: |
154 | - grail_mask_set(types, GRAIL_TYPE_DRAG1); |
155 | - grail_mask_set(types, GRAIL_TYPE_PINCH1); |
156 | - grail_mask_set(types, GRAIL_TYPE_ROTATE1); |
157 | - grail_mask_set(types, GRAIL_TYPE_TAP1); |
158 | - break; |
159 | - case 2: |
160 | - grail_mask_set(types, GRAIL_TYPE_DRAG2); |
161 | - grail_mask_set(types, GRAIL_TYPE_PINCH2); |
162 | - grail_mask_set(types, GRAIL_TYPE_ROTATE2); |
163 | - grail_mask_set(types, GRAIL_TYPE_TAP2); |
164 | - break; |
165 | - case 3: |
166 | - grail_mask_set(types, GRAIL_TYPE_DRAG3); |
167 | - grail_mask_set(types, GRAIL_TYPE_PINCH3); |
168 | - grail_mask_set(types, GRAIL_TYPE_ROTATE3); |
169 | - grail_mask_set(types, GRAIL_TYPE_TAP3); |
170 | - grail_mask_set(types, GRAIL_TYPE_EDRAG); |
171 | - grail_mask_set(types, GRAIL_TYPE_EPINCH); |
172 | - grail_mask_set(types, GRAIL_TYPE_EROTATE); |
173 | - break; |
174 | - case 4: |
175 | - grail_mask_set(types, GRAIL_TYPE_DRAG4); |
176 | - grail_mask_set(types, GRAIL_TYPE_PINCH4); |
177 | - grail_mask_set(types, GRAIL_TYPE_ROTATE4); |
178 | - grail_mask_set(types, GRAIL_TYPE_TAP4); |
179 | - grail_mask_set(types, GRAIL_TYPE_MDRAG); |
180 | - grail_mask_set(types, GRAIL_TYPE_MPINCH); |
181 | - grail_mask_set(types, GRAIL_TYPE_MROTATE); |
182 | - break; |
183 | - case 5: |
184 | - grail_mask_set(types, GRAIL_TYPE_DRAG5); |
185 | - grail_mask_set(types, GRAIL_TYPE_PINCH5); |
186 | - grail_mask_set(types, GRAIL_TYPE_ROTATE5); |
187 | - grail_mask_set(types, GRAIL_TYPE_TAP5); |
188 | - break; |
189 | - } |
190 | - |
191 | - for (i = 0; i < frame->num_active; i++) { |
192 | - pos[i].x = gin_prop_x(ge->gin, frame->active[i]->x); |
193 | - pos[i].y = gin_prop_y(ge->gin, frame->active[i]->y); |
194 | - } |
195 | - |
196 | - return ge->get_clients(ge, &client, 1, pos, frame->num_active, |
197 | - types, DIM_GRAIL_TYPE_BYTES); |
198 | +static void flush_events(struct grail *ge) |
199 | +{ |
200 | + struct grail_impl *impl = ge->impl; |
201 | + struct input_event iev; |
202 | + |
203 | + grailbuf_clear(&impl->gbuf); |
204 | + while (!evbuf_empty(&impl->evbuf)) { |
205 | + evbuf_get(&impl->evbuf, &iev); |
206 | + if (ge->event) |
207 | + ge->event(ge, &iev); |
208 | + } |
209 | +} |
210 | + |
211 | +static int skip_event(const struct input_event *ev, int count) |
212 | +{ |
213 | + switch (ev->type) { |
214 | + case EV_ABS: |
215 | + return 1; |
216 | + case EV_KEY: |
217 | + return ev->code >= BTN_DIGI && ev->code < BTN_WHEEL; |
218 | + case EV_SYN: |
219 | + switch (ev->code) { |
220 | + case SYN_MT_REPORT: |
221 | + return 1; |
222 | + case SYN_REPORT: |
223 | + return count == 0; |
224 | + default: |
225 | + return 0; |
226 | + } |
227 | + default: |
228 | + return 0; |
229 | + } |
230 | +} |
231 | + |
232 | +static void flush_gestures(struct grail *ge) |
233 | +{ |
234 | + struct grail_impl *impl = ge->impl; |
235 | + struct input_event iev; |
236 | + struct grail_event gev; |
237 | + int count = 0; |
238 | + |
239 | + while (!evbuf_empty(&impl->evbuf)) { |
240 | + evbuf_get(&impl->evbuf, &iev); |
241 | + if (skip_event(&iev, count)) |
242 | + continue; |
243 | + if (ge->event) |
244 | + ge->event(ge, &iev); |
245 | + if (iev.type == EV_SYN && iev.code == SYN_REPORT) |
246 | + count = 0; |
247 | + else |
248 | + count++; |
249 | + } |
250 | + while (!grailbuf_empty(&impl->gbuf)) { |
251 | + grailbuf_get(&impl->gbuf, &gev); |
252 | + if (ge->gesture) |
253 | + ge->gesture(ge, &gev); |
254 | + } |
255 | } |
256 | |
257 | static void report_frame(struct grail *ge, |
258 | const struct utouch_frame *frame, |
259 | const struct input_event *syn) |
260 | { |
261 | - struct grail_impl *impl = ge->impl; |
262 | struct gesture_recognizer *gru = ge->gru; |
263 | - struct input_event iev; |
264 | + struct gesture_inserter *gin = ge->gin; |
265 | + struct grail_impl *impl = ge->impl; |
266 | struct grail_event gev; |
267 | |
268 | ge->impl->frame = frame; |
269 | |
270 | - /* Reset timer when number of touches changes. */ |
271 | - if (frame->prev->revision != frame->revision && frame->num_active) { |
272 | - gru->start_time = frame->time; |
273 | - } |
274 | - |
275 | - /* Once touches have stabilized, check if there are any potential |
276 | - * gestures registered for the number of touches. */ |
277 | - if (gru->state == RECOGNIZING && |
278 | - frame->time > gru->start_time + stable_time && |
279 | - check_potential_gestures(ge, frame) == 0) |
280 | - gru->state = UNRECOGNIZED; |
281 | - |
282 | - /* Process the frame for gestures unless we're sure there are none. */ |
283 | - if (gru->state != UNRECOGNIZED) { |
284 | - gin_frame_begin(ge, frame); |
285 | - gru_recognize(ge, frame); |
286 | - gin_frame_end(ge, frame); |
287 | - } |
288 | - |
289 | - if (gru->state == RECOGNIZING) { |
290 | - /* If a gesture is recognized and either all touches lifted or |
291 | - * we are within the recognition interval, set state to |
292 | - * recognized. Note that taps do not stay in the used array, so |
293 | - * we must check if any gesture events are buffered. */ |
294 | - if ((grail_mask_count(ge->gin->used, DIM_INSTANCE_BYTES) || |
295 | - !grailbuf_empty(&impl->gbuf)) && |
296 | - (frame->num_active == 0 || |
297 | - (frame->time > gru->start_time + stable_time && |
298 | - frame->time <= gru->start_time + recognition_time))) { |
299 | - if (gru->global_client) |
300 | - gru->state = GLOBAL; |
301 | - else |
302 | - gru->state = LOCAL; |
303 | - } |
304 | - /* If there is no gesture and all touches lifted, set state to |
305 | - * unrecognized. */ |
306 | - else if (frame->num_active == 0) |
307 | - gru->state = UNRECOGNIZED; |
308 | - } |
309 | - |
310 | - /* Now we have the state set, process events. */ |
311 | - switch (gru->state) { |
312 | - case LOCAL: |
313 | - case GLOBAL: |
314 | - while (!grailbuf_empty(&impl->gbuf)) { |
315 | - grailbuf_get(&impl->gbuf, &gev); |
316 | - if (ge->gesture && |
317 | - ((gru->state == GLOBAL && |
318 | - gev.client_id.client == gru->global_client) || |
319 | - (gru->state == LOCAL && |
320 | - gev.client_id.client != gru->global_client))) |
321 | - ge->gesture(ge, &gev); |
322 | - } |
323 | - evbuf_clear(&impl->evbuf); |
324 | - |
325 | - /* Once all touches are lifted and no gestures are |
326 | - * active, reset recognition state. */ |
327 | - if (grail_mask_count(ge->gin->used, DIM_INSTANCE_BYTES) == 0 && |
328 | - frame->num_active == 0) { |
329 | - gru->state = RECOGNIZING; |
330 | - gru->global_client = 0; |
331 | - } |
332 | - break; |
333 | - |
334 | - case RECOGNIZING: |
335 | - /* If we're still actively recognizing, stop here. */ |
336 | - if (frame->time <= gru->start_time + recognition_time) |
337 | - break; |
338 | - gru->state = UNRECOGNIZED; |
339 | - |
340 | - case UNRECOGNIZED: |
341 | - while (!evbuf_empty(&impl->evbuf)) { |
342 | - evbuf_get(&impl->evbuf, &iev); |
343 | - if (ge->event) |
344 | - ge->event(ge, &iev); |
345 | - } |
346 | - grailbuf_clear(&impl->gbuf); |
347 | - |
348 | - if (frame->num_active == 0) { |
349 | - gru->state = RECOGNIZING; |
350 | - gru->global_client = 0; |
351 | - } |
352 | - break; |
353 | - } |
354 | + if (frame->num_active && !frame->prev->num_active) { |
355 | + impl->timeout = 0; |
356 | + impl->gesture = 0; |
357 | + } |
358 | + |
359 | + if (impl->timeout) { |
360 | + flush_events(ge); |
361 | + return; |
362 | + } |
363 | + |
364 | + gin_frame_begin(ge, frame); |
365 | + gru_recognize(ge, frame); |
366 | + gin_frame_end(ge, frame); |
367 | + |
368 | + if (!grailbuf_empty(&impl->gbuf)) |
369 | + impl->gesture = 1; |
370 | + |
371 | + if (!frame->num_active || |
372 | + !grail_mask_count(gin->used, sizeof(gin->used)) && |
373 | + frame->time - frame->mod_time > gru->move.fm[FM_X].hold_ms) |
374 | + impl->timeout = !impl->gesture; |
375 | + |
376 | + if (impl->timeout) |
377 | + flush_events(ge); |
378 | + |
379 | + if (impl->gesture) |
380 | + flush_gestures(ge); |
381 | } |
382 | |
383 | static void grail_pump_mtdev(struct grail *ge, const struct input_event *ev) |
384 | @@ -292,21 +231,6 @@ |
385 | struct grail_impl *impl = ge->impl; |
386 | const struct utouch_frame *frame; |
387 | |
388 | - if (ev->type == EV_KEY && (ev->code < BTN_DIGI || |
389 | - ev->code > BTN_TOOL_QUADTAP)) { |
390 | - struct input_event sync_ev; |
391 | - |
392 | - ge->event(ge, ev); |
393 | - |
394 | - sync_ev.type = EV_SYN; |
395 | - sync_ev.code = SYN_REPORT; |
396 | - sync_ev.value = 1; |
397 | - sync_ev.time = ev->time; |
398 | - ge->event(ge, &sync_ev); |
399 | - |
400 | - return; |
401 | - } |
402 | - |
403 | evbuf_put(&impl->evbuf, ev); |
404 | |
405 | if (ev->type == EV_SYN || ev->type == EV_ABS) { |
406 | |
407 | === modified file 'src/grail-event.c' |
408 | --- src/grail-event.c 2011-02-09 15:12:13 +0000 |
409 | +++ src/grail-event.c 2011-02-24 13:47:05 +0000 |
410 | @@ -22,7 +22,6 @@ |
411 | |
412 | #include "grail-inserter.h" |
413 | #include "grail-impl.h" |
414 | -#include "grail-recognizer.h" |
415 | #include <malloc.h> |
416 | #include <string.h> |
417 | #include <errno.h> |
418 | @@ -83,7 +82,7 @@ |
419 | const struct utouch_frame *frame) |
420 | { |
421 | struct grail_coord pos[DIM_TOUCH]; |
422 | - int i, npos = 0, num_clients; |
423 | + int i, npos = 0; |
424 | if (!ge->get_clients) |
425 | return 0; |
426 | grail_mask_foreach(i, span, bspan) { |
427 | @@ -91,20 +90,7 @@ |
428 | pos[npos].y = gin_prop_y(ge->gin, frame->slots[i]->y); |
429 | npos++; |
430 | } |
431 | - |
432 | - num_clients = ge->get_clients(ge, info, maxinfo, pos, npos, types, |
433 | - btypes); |
434 | - |
435 | - /* Check if client is handling global gestures, using SYSFLAG1. */ |
436 | - if (num_clients > 0 && grail_mask_get(info[0].mask, |
437 | - GRAIL_TYPE_SYSFLAG1)) { |
438 | - /* Save client id, and clear SYSFLAG1 as it isn't a real |
439 | - * gesture type. */ |
440 | - ge->gru->global_client = info[0].id.client; |
441 | - grail_mask_clear(info[0].mask, GRAIL_TYPE_SYSFLAG1); |
442 | - } |
443 | - |
444 | - return num_clients; |
445 | + return ge->get_clients(ge, info, maxinfo, pos, npos, types, btypes); |
446 | } |
447 | |
448 | void gin_send_event(struct grail *ge, struct slot_state *s, |
449 | |
450 | === modified file 'src/grail-gestures.c' |
451 | --- src/grail-gestures.c 2011-01-02 12:08:08 +0000 |
452 | +++ src/grail-gestures.c 2011-02-24 13:47:05 +0000 |
453 | @@ -24,9 +24,10 @@ |
454 | #include "grail-impl.h" |
455 | #include <math.h> |
456 | |
457 | -static const float FM_SN[DIM_FM] = { 1000, 1000, 200, 1000 }; |
458 | -static const float FM_BAR[DIM_FM] = { 50, 50, 30, 50 }; |
459 | -static const grail_time_t FM_BAR_MS[DIM_FM] = { 300, 300, 10000, 10000 }; |
460 | +static const float FM_SN[DIM_FM] = { 1000, 1000, 1000, 1000 }; |
461 | +static const float FM_BAR[DIM_FM] = { 50, 50, 50, 50 }; |
462 | +static const grail_time_t FM_HOLD_MS[DIM_FM] = { 60, 60, 60, 60 }; |
463 | +static const grail_time_t FM_BAR_MS[DIM_FM] = { 300, 300, 500, 500 }; |
464 | static const grail_time_t SAMPLE_MS = 10; |
465 | static const float EPS = 1e-3; |
466 | |
467 | @@ -111,6 +112,7 @@ |
468 | fm->sample_ms = t; |
469 | m->tickle &= ~(1 << i); |
470 | m->active &= ~(1 << i); |
471 | + m->timeout &= ~(1 << i); |
472 | } |
473 | |
474 | static void move_update(struct move_model *m, int i, float x, grail_time_t t) |
475 | @@ -132,11 +134,15 @@ |
476 | if (m->active & (1 << i)) |
477 | return; |
478 | fm->action_delta = x - fm->original; |
479 | - if (fabs(fm->action_delta) > fm->bar || |
480 | - t - fm->original_ms > fm->bar_ms) |
481 | - m->active |= (1 << i); |
482 | - else |
483 | + if (t - fm->original_ms > fm->hold_ms && |
484 | + fabs(fm->action_delta) > fm->bar) { |
485 | + m->active |= (1 << i); |
486 | + } else if (t - fm->original_ms > fm->bar_ms) { |
487 | + m->active |= (1 << i); |
488 | + m->timeout |= (1 << i); |
489 | + } else { |
490 | fm->action_delta = 0; |
491 | + } |
492 | } |
493 | |
494 | void gru_init_motion(struct grail *ge) |
495 | @@ -153,6 +159,7 @@ |
496 | for (i = 0; i < DIM_FM; i++) { |
497 | m->fm[i].fuzz = D[i] / FM_SN[i]; |
498 | m->fm[i].bar = D[i] / FM_BAR[i]; |
499 | + m->fm[i].hold_ms = FM_HOLD_MS[i]; |
500 | m->fm[i].bar_ms = FM_BAR_MS[i]; |
501 | } |
502 | } |
503 | |
504 | === modified file 'src/grail-gestures.h' |
505 | --- src/grail-gestures.h 2011-01-02 12:08:08 +0000 |
506 | +++ src/grail-gestures.h 2011-02-24 13:47:05 +0000 |
507 | @@ -49,12 +49,13 @@ |
508 | float bar; |
509 | grail_time_t original_ms; |
510 | grail_time_t sample_ms; |
511 | + grail_time_t hold_ms; |
512 | grail_time_t bar_ms; |
513 | }; |
514 | |
515 | struct move_model { |
516 | struct filter_model fm[DIM_FM]; |
517 | - int tickle, active; |
518 | + int tickle, active, timeout; |
519 | int single, multi, ntouch; |
520 | grail_time_t time; |
521 | }; |
522 | |
523 | === modified file 'src/grail-impl.h' |
524 | --- src/grail-impl.h 2011-02-02 22:16:20 +0000 |
525 | +++ src/grail-impl.h 2011-02-24 13:47:05 +0000 |
526 | @@ -42,6 +42,8 @@ |
527 | struct evbuf evbuf; |
528 | struct grailbuf gbuf; |
529 | int filter_abs; |
530 | + int timeout; |
531 | + int gesture; |
532 | }; |
533 | |
534 | #endif |
535 | |
536 | === modified file 'src/grail-recognizer.h' |
537 | --- src/grail-recognizer.h 2011-02-09 15:12:13 +0000 |
538 | +++ src/grail-recognizer.h 2011-02-24 13:47:05 +0000 |
539 | @@ -25,13 +25,6 @@ |
540 | |
541 | #include "grail-gestures.h" |
542 | |
543 | -enum recognition_state { |
544 | - RECOGNIZING, |
545 | - LOCAL, |
546 | - GLOBAL, |
547 | - UNRECOGNIZED |
548 | -}; |
549 | - |
550 | struct gesture_recognizer { |
551 | struct move_model move; |
552 | struct combo_model drag; |
553 | @@ -41,9 +34,6 @@ |
554 | struct combo_model winpinch; |
555 | struct combo_model winrotate; |
556 | struct tapping_model tapping; |
557 | - utouch_frame_time_t start_time; |
558 | - enum recognition_state state; |
559 | - int global_client; |
560 | }; |
561 | |
562 | int gru_init(struct grail *ge); |
A more elegant and nuanced way of handling the various timeout issues. Compiles and runs OK.