Merge lp:~dandrader/grail/lp949916 into lp:grail

Proposed by Daniel d'Andrada
Status: Merged
Merge reported by: Daniel d'Andrada
Merged at revision: not available
Proposed branch: lp:~dandrader/grail/lp949916
Merge into: lp:grail
Diff against target: 2681 lines (+1881/-475)
17 files modified
src/Makefile.am (+4/-0)
src/v3/atomic-recognizer.cpp (+308/-0)
src/v3/atomic-recognizer.h (+52/-0)
src/v3/forward.h (+14/-1)
src/v3/gesture.cpp (+39/-1)
src/v3/gesture.h (+5/-0)
src/v3/handle.cpp (+29/-5)
src/v3/handle.h (+1/-0)
src/v3/log.cpp (+4/-0)
src/v3/log.h (+4/-0)
src/v3/recognizer.cpp (+11/-453)
src/v3/recognizer.h (+33/-15)
src/v3/regular-recognizer.cpp (+396/-0)
src/v3/regular-recognizer.h (+52/-0)
test/Makefile.am (+1/-0)
test/recordings/apple-wtrackpad-synced-4-drag.event (+713/-0)
test/x11/no-premature-gestures.cpp (+215/-0)
To merge this branch: bzr merge lp:~dandrader/grail/lp949916
Reviewer Review Type Date Requested Status
Chase Douglas (community) Approve
Review via email: mp+97408@code.launchpad.net

This proposal supersedes a proposal from 2012-03-13.

Description of the change

Don't emit Touch slices prematurely with atomic rules (LP #949916)

With atomic gesture rules being used, even when, say, 4 touches were coming with the very same timestamp, gestures for 2 and then 3 touches were being created and ended before the fourth touch finally got processed.

This branch first splits Recognizer into AtomicRecognizer and RegularRecognizer. Then it fixes the bug by wainting kCompositionTime before accepting a gesture in the AtomicRecognizer.

It also doesn't send slices from a gesture until it gets accepted. This ensures that Grail clients using subscriptions with atomic gestures rules won't get noise from gestures that won't be accepted in the end.

To post a comment you must log in.
Revision history for this message
Chase Douglas (chasedouglas) wrote : Posted in a previous version of this proposal

* In AtomicRecognizer::HandleNewTouchesForAcceptedGesture(), when a gesture is ended you have the following comment:

// OBS: Should they also go back to unaccepted_touches_ here?

An accepted touch cannot return to an unaccepted state. This is a property of the window manager, and of touch handling in general. If one client uses a touch, no one else may use the touch, period.

* Please use /* */ style comments for consistency.

* The fix for the bug involves dropping touch events until a timeout period is reached. This will make the gesture recognition inconsistent because the beginning frame event of a gesture will be indeterminate. If the user rolls the touches onto the touchpad vs putting them down all at once, the recognition might behave differently.

I think a better approach would be to prevent a gesture from becoming fully recognized until the construction finished timeout is reached.

* The test looks good, and I like the subclassing of recognizers :).

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

Updated according to review comments.

lp:~dandrader/grail/lp949916 updated
196. By Daniel d'Andrada

Merged "Erase ended touches from start_times_ map"

Revision history for this message
Chase Douglas (chasedouglas) wrote :

* I'm confused about what the correct Unity guidelines are for C++ code, which we use for utouch-grail and utouch-frame. It appears the style has changed without notification. For now, I'm going to overlook any style issues.

* I'm not crazy about the name SetKeepSlicesEnabled(). I think the "Enabled" is redundant. Perhaps rename it to SetKeepSlices()? It would follow the unity coding guidelines here: https://wiki.ubuntu.com/Unity/CodingStyle.

Everything else looks good! If you change the keep slices method name as suggested, feel free to merge :).

review: Approve
lp:~dandrader/grail/lp949916 updated
197. By Daniel d'Andrada

Refactoring v3/Recognizer. Split into Regular and Atomic recognizers

Instead of having many "if (atomic_)" sprinkled throughout the code, have
two recognizers: of for atomic gesture rules and another for regular rules.

That will also make the atomic gestures code flow easier to understand and
tune. Likewise for regular rules.

198. By Daniel d'Andrada

Atomic rules: Don't send slices from premature gestures. (lp:949916)

Wait a bit until accepting a gesture to avoid premature gestures that
will immediately get cancelled due to the apparition of a new touch
point on the following events.
e.g. like when a user puts four fingers on a touch screen but
the corresponding touch points come in separate events (because fingers
don't land precisely in sync and/or UTouch frame doesn't process their
arrivals in the very same event). We should generate only the "final"
4-touches' gesture and not the intermediates 2-touches and 3-touches
gestures.

199. By Daniel d'Andrada

Regression test for lp:949916

Check that, when using atomic gestures, we don't get
premature slices of 2 or 3 touches when a roughly synced 4-fingers'
gesture is performed.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/Makefile.am'
2--- src/Makefile.am 2012-02-01 16:24:12 +0000
3+++ src/Makefile.am 2012-03-14 19:17:21 +0000
4@@ -38,6 +38,8 @@
5 if HAVE_XI
6 v3dir = v3
7 libutouch_grail_la_SOURCES += \
8+ v3/atomic-recognizer.cpp \
9+ v3/atomic-recognizer.h \
10 v3/event.cpp \
11 v3/event.h \
12 v3/forward.h \
13@@ -49,6 +51,8 @@
14 v3/log.h \
15 v3/recognizer.cpp \
16 v3/recognizer.h \
17+ v3/regular-recognizer.cpp \
18+ v3/regular-recognizer.h \
19 v3/slice.cpp \
20 v3/slice.h \
21 v3/subscription.cpp \
22
23=== added file 'src/v3/atomic-recognizer.cpp'
24--- src/v3/atomic-recognizer.cpp 1970-01-01 00:00:00 +0000
25+++ src/v3/atomic-recognizer.cpp 2012-03-14 19:17:21 +0000
26@@ -0,0 +1,308 @@
27+/*****************************************************************************
28+ *
29+ * grail - Gesture Recognition And Instantiation Library
30+ *
31+ * Copyright (C) 2012 Canonical Ltd.
32+ *
33+ * This program is free software: you can redistribute it and/or modify it
34+ * under the terms of the GNU General Public License as published by the
35+ * Free Software Foundation, either version 3 of the License, or (at your
36+ * option) any later version.
37+ *
38+ * This program is distributed in the hope that it will be useful, but
39+ * WITHOUT ANY WARRANTY; without even the implied warranty of
40+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41+ * General Public License for more details.
42+ *
43+ * You should have received a copy of the GNU General Public License along
44+ * with this program. If not, see <http://www.gnu.org/licenses/>.
45+ *
46+ ****************************************************************************/
47+
48+#include "v3/atomic-recognizer.h"
49+
50+#include <algorithm>
51+#include <cassert>
52+#include <cstdint>
53+#include <cstdio>
54+#include <limits>
55+
56+#include <utouch/frame.h>
57+#include <utouch/frame_x11.h>
58+
59+#include "v3/handle.h"
60+#include "v3/gesture.h"
61+#include "v3/log.h"
62+
63+namespace {
64+const uint64_t MAX_TOUCHES_FOR_GESTURES = 5;
65+} // namespace
66+
67+namespace utouch {
68+namespace grail {
69+
70+/**
71+ * @internal
72+ * Create a new atomic recognizer for a given device and window
73+ */
74+AtomicRecognizer::AtomicRecognizer(UGHandle* handle, const UFDevice device, UFWindowId window)
75+ : Recognizer(handle, device, window) {
76+}
77+
78+/**
79+ * @internal
80+ * Process a uTouch-Frame event
81+ */
82+void AtomicRecognizer::ProcessFrameEvent(const UFEvent event) {
83+ LOG(Dbg) << "new event " << event << " with time "
84+ << frame_event_get_time(event) << "\n";
85+
86+ uint64_t event_time = frame_event_get_time(event);
87+
88+ UpdateTime(event_time);
89+ CollectNewTouches(event);
90+ if (new_touches_.size() > 0) {
91+ /* process all new touches at once to avoid the premature initiation of
92+ gestures for less touches than what the event brings */
93+ MatchSubscriptionsForNewTouches();
94+ }
95+ ProcessEvent(event);
96+ FindGestureToAccept(event_time);
97+}
98+
99+/**
100+ * @internal
101+ * Perform tasks necessary for when new touches occur and there is an existing
102+ * accepted gesture
103+ *
104+ * If a gesture may add touches without crossing the maximum for the
105+ * subscription, add the touches to the gesture and accept them. Otherwise, end
106+ * the gesture and add the current gesture touches to the free touches list.
107+ */
108+void AtomicRecognizer::HandleNewTouchesForAcceptedGesture(const SharedGesture& gesture) {
109+ UGSubscription* subscription = gesture->subscription();
110+ if (gesture->touches().size() + new_touches_.size() <= subscription->touches_max()) {
111+ gesture->AddTouches(new_touches_);
112+ LOG(Dbg) << "new_touches_ have been added to atomic gesture "
113+ << gesture->id() << "\n";
114+
115+ for (UFTouchId touch_id : new_touches_) {
116+ if (frame_x11_accept_touch(device_, window_id_, touch_id) !=
117+ UFStatusSuccess)
118+ LOG(Err) << "touch " << touch_id << " failed to be accepted\n";
119+
120+ LOG(Dbg) << "touch " << touch_id
121+ << " has been accepted because it has been added to an atomic gesture\n";
122+
123+ INSERT_TOUCH(touch_id, accepted_touches_);
124+ ERASE_TOUCH(touch_id, unaccepted_touches_);
125+ ERASE_TOUCH(touch_id, free_touches_);
126+ }
127+ CLEAR_TOUCHES(new_touches_);
128+ } else {
129+ for (UFTouchId touch : gesture->touches())
130+ INSERT_TOUCH(touch, free_touches_);
131+ gesture->End();
132+ LOG(Dbg) << "ended active atomic gesture " << gesture->id()
133+ << " because " << new_touches_.size() << " new touch(es) began and the "
134+ "max touches has been reached\n";
135+ accepted_gestures_.erase(gesture);
136+ }
137+}
138+
139+/**
140+ * @internal
141+ * Perform tasks necessary for when new touches occur and there is an existing
142+ * unaccepted gesture
143+ *
144+ * If a gesture may receive the new touches without crossing the maximum for
145+ * the subscription, add the touches to the gesture. Otherwise, cancel the
146+ * gesture and add the current gesture touches to the free touches list.
147+ */
148+void AtomicRecognizer::HandleNewTouchesForUnacceptedGesture(
149+ const SharedGesture& gesture) {
150+ UGSubscription* subscription = gesture->subscription();
151+ if (gesture->touches().size() + new_touches_.size() <= subscription->touches_max()) {
152+ for (UFTouchId touch_id : new_touches_) {
153+ gesture->AddTouch(touch_id);
154+ LOG(Dbg) << "touch " << touch_id << " has been added to atomic gesture "
155+ << gesture->id() << "\n";
156+ }
157+ } else {
158+ for (UFTouchId touch_id : gesture->touches())
159+ INSERT_TOUCH(touch_id, free_touches_);
160+ gesture->Cancel();
161+ LOG(Dbg) << "canceled inactive atomic gesture " << gesture->id()
162+ << " because a new touch began and the max touches has been "
163+ << "reached\n";
164+ unaccepted_gestures_.erase(gesture);
165+ }
166+}
167+
168+/**
169+ * @internal
170+ * Register all new touches present in the given uTouch-Frame event.
171+ */
172+void AtomicRecognizer::CollectNewTouches(const UFEvent event) {
173+ /* Check if any subscriptions are active before doing any processing */
174+ if (num_subscriptions_ == 0)
175+ return;
176+
177+ UFFrame frame;
178+ UFStatus status = frame_event_get_property(event, UFEventPropertyFrame,
179+ &frame);
180+ if (status != UFStatusSuccess) {
181+ LOG(Warn) << "failed to get frame from event\n";
182+ return;
183+ }
184+
185+ unsigned int num_touches = frame_frame_get_num_touches(frame);
186+ uint64_t touch_start_time;
187+ UFTouchId touch_id;
188+ for (unsigned int i = 0; i < num_touches; ++i) {
189+ UFTouch touch;
190+ status = frame_frame_get_touch_by_index(frame, i, &touch);
191+ if (status != UFStatusSuccess) {
192+ LOG(Warn) << "failed to get touch from frame\n";
193+ continue;
194+ }
195+
196+ touch_id = frame_touch_get_id(touch);
197+
198+ switch (frame_touch_get_state(touch)) {
199+ case UFTouchStateBegin:
200+ touch_start_time = frame_touch_get_start_time(touch);
201+
202+ /* Note touch start time and add to initial touch lists */
203+ start_times_[touch_id] = touch_start_time;
204+ LOG(Dbg) << "touch " << touch_id << " began with start time "
205+ << start_times_[touch_id] << "\n";
206+
207+ INSERT_TOUCH(touch_id, new_touches_);
208+ break;
209+
210+ case UFTouchStateEnd:
211+ ERASE_TOUCH(touch_id, new_touches_);
212+ break;
213+
214+ default:
215+ break;
216+ }
217+ }
218+}
219+
220+/**
221+ * @internal
222+ * Consume the new touches.
223+ * Check if any new atomic gestures should begin because of the new touches
224+ * that came.
225+ */
226+void AtomicRecognizer::MatchSubscriptionsForNewTouches() {
227+ /* Check if any subscriptions are active before doing any processing */
228+ if (num_subscriptions_ == 0)
229+ return;
230+
231+ // The new touches can now be used
232+ for (UFTouchId touch_id : new_touches_) {
233+ INSERT_TOUCH(touch_id, unaccepted_touches_);
234+ INSERT_TOUCH(touch_id, free_touches_);
235+ }
236+
237+ // Under atomic gestures rules there can be only one accepted gesture
238+ assert(accepted_gestures_.size() <= 1);
239+ if (accepted_gestures_.size() != 0) {
240+ const SharedGesture& gesture = *accepted_gestures_.begin();
241+ HandleNewTouchesForAcceptedGesture(gesture);
242+ }
243+
244+ if (new_touches_.size() == 0) {
245+ // they've all been consumed by the accepted gesture.
246+ return;
247+ }
248+
249+ /* HandleNewTouchForUnacceptedGesture may erase the gesture from
250+ * accepted_gestures_, so we can't use range-based for loops */
251+ for (auto it = unaccepted_gestures_.begin();
252+ it != unaccepted_gestures_.end();
253+ ) {
254+ const SharedGesture& gesture = *it++;
255+ HandleNewTouchesForUnacceptedGesture(gesture);
256+ }
257+
258+ MatchGestures();
259+ CLEAR_TOUCHES(new_touches_);
260+}
261+
262+void AtomicRecognizer::MatchGestures() {
263+ if (free_touches_.size() == 0
264+ || free_touches_.size() > MAX_TOUCHES_FOR_GESTURES)
265+ return;
266+
267+ uint64_t min_start_time = std::numeric_limits<uint64_t>::max();
268+ uint64_t max_start_time = 0;
269+ for (UFTouchId touch_id : free_touches_) {
270+ if (unaccepted_touches_.find(touch_id) == unaccepted_touches_.end())
271+ continue;
272+
273+ if (start_times_[touch_id] < min_start_time)
274+ min_start_time = start_times_[touch_id];
275+
276+ if (start_times_[touch_id] > max_start_time)
277+ max_start_time = start_times_[touch_id];
278+ }
279+
280+ /* All touches in a gesture must begin within a composition timeframe */
281+ if ((max_start_time - min_start_time) >= kCompositionTime)
282+ return;
283+
284+ for (UGSubscription* subscription : subscriptions_[free_touches_.size()-1]) {
285+ Gesture* gesture = new Gesture(this, subscription, free_touches_,
286+ max_start_time);
287+
288+ /* hold slice events until we accept the gesture */
289+ gesture->SetKeepSlices(true);
290+
291+ unaccepted_gestures_.insert(SharedGesture(gesture));
292+
293+ LOG(Dbg) << "New tentative gesture " << gesture->id()
294+ << " matched subscription " << subscription << " with mask "
295+ << subscription->mask() << " for touches " << free_touches_.ToString()
296+ << std::endl;
297+ }
298+}
299+
300+void AtomicRecognizer::FindGestureToAccept(uint64_t event_time)
301+{
302+ uint64_t delta_time;
303+ for (auto it = unaccepted_gestures_.begin();
304+ it != unaccepted_gestures_.end();
305+ ) {
306+ const SharedGesture& gesture = *it++;
307+
308+ delta_time = event_time - gesture->start_time();
309+
310+ /* Atomic gestures must be accepted if they meet the subscription
311+ criteria.
312+ Wait a bit until accepting them to avoid premature gestures that
313+ will immediately get cancelled due to the apparition of a new touch
314+ point on the following events.
315+ e.g. like when a user puts four fingers on a touch screen but
316+ the corresponding touch points come in separate events (because fingers
317+ don't land precisely in sync and/or UTouch frame doesn't process their
318+ arrivals in the very same event). We should generate only the "final"
319+ 4-touches' gesture and not the intermediates 2-touches and 3-touches
320+ gestures.
321+ */
322+ if (gesture->IsActive() && delta_time > 0
323+ && delta_time >= kCompositionTime) {
324+
325+ gesture->SetKeepSlices(false);
326+
327+ LOG(Dbg) << "accepting active atomic gesture " << gesture->id() << "\n";
328+ AcceptGesture(gesture->id());
329+ }
330+ }
331+}
332+
333+} // namespace grail
334+} // namespace utouch
335
336=== added file 'src/v3/atomic-recognizer.h'
337--- src/v3/atomic-recognizer.h 1970-01-01 00:00:00 +0000
338+++ src/v3/atomic-recognizer.h 2012-03-14 19:17:21 +0000
339@@ -0,0 +1,52 @@
340+/*****************************************************************************
341+ *
342+ * grail - Gesture Recognition And Instantiation Library
343+ *
344+ * Copyright (C) 2012 Canonical Ltd.
345+ *
346+ * This program is free software: you can redistribute it and/or modify it
347+ * under the terms of the GNU General Public License as published by the
348+ * Free Software Foundation, either version 3 of the License, or (at your
349+ * option) any later version.
350+ *
351+ * This program is distributed in the hope that it will be useful, but
352+ * WITHOUT ANY WARRANTY; without even the implied warranty of
353+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
354+ * General Public License for more details.
355+ *
356+ * You should have received a copy of the GNU General Public License along
357+ * with this program. If not, see <http://www.gnu.org/licenses/>.
358+ *
359+ ****************************************************************************/
360+
361+#ifndef UTOUCH_GRAIL_ATOMIC_RECOGNIZER_H_
362+#define UTOUCH_GRAIL_ATOMIC_RECOGNIZER_H_
363+
364+#include "v3/recognizer.h"
365+
366+namespace utouch {
367+namespace grail {
368+
369+class AtomicRecognizer : public Recognizer {
370+ public:
371+ AtomicRecognizer(UGHandle* handle, const UFDevice device, UFWindowId window);
372+
373+ virtual bool atomic() const {return true;}
374+ virtual void ProcessFrameEvent(const UFEvent event);
375+
376+ private:
377+ void HandleNewTouchesForAcceptedGesture(const SharedGesture& gesture);
378+ void HandleNewTouchesForUnacceptedGesture(const SharedGesture& gesture);
379+ void CollectNewTouches(const UFEvent event);
380+ void MatchSubscriptionsForNewTouches();
381+ void MatchGestures();
382+ void FindGestureToAccept(uint64_t event_time);
383+
384+ // Touches that have begun but not yet been matched against subscriptions
385+ // (for the creation of new gestures) or used to update existing gestures.
386+ TouchSet new_touches_;
387+};
388+
389+} // namespace grail
390+} // namespace utouch
391+#endif
392
393=== modified file 'src/v3/forward.h'
394--- src/v3/forward.h 2011-11-25 18:11:49 +0000
395+++ src/v3/forward.h 2012-03-14 19:17:21 +0000
396@@ -24,6 +24,7 @@
397
398 #include <memory>
399 #include <set>
400+#include <sstream>
401
402 #include <utouch/frame.h>
403
404@@ -48,7 +49,19 @@
405 class UGSubscription;
406 typedef std::shared_ptr<UGSubscription> SharedUGSubscription;
407
408-typedef std::set<UFTouchId> TouchSet;
409+class TouchSet : public std::set<UFTouchId> {
410+
411+public:
412+ std::string ToString() const {
413+ std::ostringstream stream;
414+ auto it = begin();
415+ if (it != end())
416+ stream << *it++;
417+ while (it != end())
418+ stream << ", " << *it++;
419+ return stream.str();
420+ }
421+};
422
423 } // namespace grail
424 } // namespace utouch
425
426=== modified file 'src/v3/gesture.cpp'
427--- src/v3/gesture.cpp 2012-02-15 04:36:48 +0000
428+++ src/v3/gesture.cpp 2012-03-14 19:17:21 +0000
429@@ -51,7 +51,8 @@
430 not_owned_(false),
431 recognized_(0),
432 canceled_(false),
433- ended_(false) {
434+ ended_(false),
435+ keep_slices_(false) {
436 }
437
438 /**
439@@ -83,6 +84,23 @@
440
441 /**
442 * @internal
443+ * Add a new set of touches to an existing gesture.
444+ */
445+void Gesture::AddTouches(TouchSet touch_set) {
446+ current_touches_.insert(touch_set.begin(), touch_set.end());
447+ unowned_touches_.insert(touch_set.begin(), touch_set.end());
448+}
449+
450+/**
451+ * @internal
452+ * Returns whether that gesture contains the given touch.
453+ */
454+bool Gesture::ContainsTouch(UFTouchId touch_id) const {
455+ return current_touches_.find(touch_id) != current_touches_.end();
456+}
457+
458+/**
459+ * @internal
460 * Update a gesture with the passed in frame event and list of modified touches
461 */
462 void Gesture::Update(UFEvent frame_event, TouchSet& modified_touches) {
463@@ -298,6 +316,9 @@
464 * @internal
465 * Flush all pending gesture slices to the client as grail events */
466 void Gesture::FlushSlices() {
467+ if (keep_slices_)
468+ return;
469+
470 while (!slices_.empty()) {
471 UGEvent* event = new UGEvent(slices_.front());
472 slices_.front()->ReferenceGesture();
473@@ -373,5 +394,22 @@
474 unowned_touches_.clear();
475 }
476
477+/**
478+ * @internal
479+ * If true, FlushSlices() will have no effect. The Gesture will keep all its
480+ * slices until this property is disabled again, which will cause all pending
481+ * slices to be flushed.
482+ *
483+ * By default, this property is false. */
484+void Gesture::SetKeepSlices(bool keep_slices)
485+{
486+ if (keep_slices_ && !keep_slices) {
487+ keep_slices_ = keep_slices;
488+ FlushSlices();
489+ } else {
490+ keep_slices_ = keep_slices;
491+ }
492+}
493+
494 } // namespace grail
495 } // namespace utouch
496
497=== modified file 'src/v3/gesture.h'
498--- src/v3/gesture.h 2012-02-15 04:36:48 +0000
499+++ src/v3/gesture.h 2012-03-14 19:17:21 +0000
500@@ -42,6 +42,8 @@
501 Gesture(const Gesture* gesture, TouchSet& touches);
502
503 void AddTouch(UFTouchId touch_id);
504+ void AddTouches(TouchSet touch_set);
505+ bool ContainsTouch(UFTouchId touch_id) const;
506 void Update(UFEvent event, TouchSet& touches);
507 bool IsActive() const;
508 bool IsConstructionFinished() const;
509@@ -63,6 +65,8 @@
510 Gesture(const Gesture&) = delete;
511 Gesture& operator=(const Gesture&) = delete;
512
513+ void SetKeepSlices(bool keep_slices);
514+
515 private:
516 void CheckOwned();
517 void FlushSlices();
518@@ -81,6 +85,7 @@
519 bool canceled_;
520 bool ended_;
521 std::map<UFTouchId, float> angles_;
522+ bool keep_slices_;
523 };
524
525 } // namespace grail
526
527=== modified file 'src/v3/handle.cpp'
528--- src/v3/handle.cpp 2012-01-21 00:00:16 +0000
529+++ src/v3/handle.cpp 2012-03-14 19:17:21 +0000
530@@ -32,9 +32,10 @@
531
532 #include <utouch/frame_x11.h>
533
534+#include "v3/atomic-recognizer.h"
535 #include "v3/event.h"
536 #include "v3/log.h"
537-#include "v3/recognizer.h"
538+#include "v3/regular-recognizer.h"
539 #include "v3/subscription.h"
540
541 namespace utouch {
542@@ -49,6 +50,25 @@
543 }
544 }
545
546+Recognizer *UGHandle::CreateRecognizerForSubscription(
547+ UGSubscription* subscription) {
548+ Recognizer* recognizer;
549+
550+ if (subscription->atomic())
551+ recognizer = new AtomicRecognizer(this,
552+ subscription->device(),
553+ subscription->window_id());
554+ else
555+ recognizer = new RegularRecognizer(this,
556+ subscription->device(),
557+ subscription->window_id());
558+
559+ recognizers_[subscription->device()][subscription->window_id()] =
560+ std::move(UniqueRecognizer(recognizer));
561+
562+ return recognizer;
563+}
564+
565 UGStatus UGHandle::ActivateSubscription(UGSubscription* subscription) {
566 Recognizer* recognizer;
567
568@@ -59,11 +79,15 @@
569 recognizers_[subscription->device()].find(subscription->window_id());
570 if (it != recognizers_[subscription->device()].end()) {
571 recognizer = it->second.get();
572+
573+ if (recognizer->atomic() != subscription->atomic()
574+ && recognizer->num_subscriptions() == 0) {
575+ // Fix the mismatch
576+ recognizer = CreateRecognizerForSubscription(subscription);
577+ }
578+
579 } else {
580- recognizer = new Recognizer(this, subscription->device(),
581- subscription->window_id());
582- recognizers_[subscription->device()][subscription->window_id()] =
583- std::move(UniqueRecognizer(recognizer));
584+ recognizer = CreateRecognizerForSubscription(subscription);
585 }
586
587 return recognizer->ActivateSubscription(subscription);
588
589=== modified file 'src/v3/handle.h'
590--- src/v3/handle.h 2011-11-29 20:13:02 +0000
591+++ src/v3/handle.h 2012-03-14 19:17:21 +0000
592@@ -43,6 +43,7 @@
593 ~UGHandle();
594
595 int event_fd() const { return event_fd_; }
596+ Recognizer *CreateRecognizerForSubscription(UGSubscription* subscription);
597 UGStatus ActivateSubscription(UGSubscription* subscription);
598 void DeactivateSubscription(UGSubscription* subscription);
599 unsigned int NewGestureID(Recognizer* recognizer);
600
601=== modified file 'src/v3/log.cpp'
602--- src/v3/log.cpp 2012-01-06 20:00:08 +0000
603+++ src/v3/log.cpp 2012-03-14 19:17:21 +0000
604@@ -59,5 +59,9 @@
605 }
606 }
607
608+Logger& Logger::instance() {
609+ return logger;
610+}
611+
612 } // namespace grail
613 } // namespace utouch
614
615=== modified file 'src/v3/log.h'
616--- src/v3/log.h 2012-01-06 20:00:08 +0000
617+++ src/v3/log.h 2012-03-14 19:17:21 +0000
618@@ -44,6 +44,10 @@
619
620 static std::ostream& Log(Level level);
621
622+ static Logger& instance();
623+
624+ Level level() const {return static_cast<Level>(level_);};
625+
626 private:
627 int level_;
628 NullStreamBuf null_buf_;
629
630=== modified file 'src/v3/recognizer.cpp'
631--- src/v3/recognizer.cpp 2012-03-14 14:54:09 +0000
632+++ src/v3/recognizer.cpp 2012-03-14 19:17:21 +0000
633@@ -22,6 +22,7 @@
634 #include "v3/recognizer.h"
635
636 #include <algorithm>
637+#include <cassert>
638 #include <cstdint>
639 #include <cstdio>
640 #include <limits>
641@@ -33,27 +34,11 @@
642 #include "v3/gesture.h"
643 #include "v3/log.h"
644
645-#define INSERT_TOUCH(element, set) \
646- { \
647- (set).insert(element); \
648- LOG(Dbg) << "touch " << element << " has been added to " #set "\n"; \
649- }
650-
651-#define ERASE_TOUCH(element, set) \
652- { \
653- (set).erase(element); \
654- LOG(Dbg) << "touch " << element << " has been erased from " #set "\n"; \
655- }
656-
657-namespace {
658-
659-const uint64_t COMPOSITION_TIME = 60;
660-
661-} // namespace
662-
663 namespace utouch {
664 namespace grail {
665
666+uint64_t Recognizer::kCompositionTime = 60;
667+
668 /**
669 * @internal
670 * Create a new recognizer for a given device and window
671@@ -64,7 +49,6 @@
672 device_(device),
673 window_id_(window_id),
674 device_direct_(true),
675- atomic_(false),
676 num_subscriptions_(0) {
677 /* Save direct property for gesture processing */
678 UFStatus status = frame_device_get_property(device, UFDevicePropertyDirect,
679@@ -107,13 +91,11 @@
680 */
681 UGStatus Recognizer::ActivateSubscription(UGSubscription* subscription) {
682 /* All the subscriptions must be atomic or non-atomic, mixes break things */
683- for (auto subscriptions : subscriptions_)
684- if (subscriptions.size() > 0 && subscription->atomic() != atomic_)
685+ if (subscription->atomic() != atomic())
686 return UGStatusErrorAtomicity;
687
688 /* Save the subscription and update atomicity */
689 subscriptions_[subscription->touches_start() - 1].insert(subscription);
690- atomic_ = subscription->atomic();
691 num_subscriptions_++;
692
693 return UGStatusSuccess;
694@@ -157,425 +139,6 @@
695 num_subscriptions_--;
696 }
697
698-/**
699- * @internal
700- * Process a uTouch-Frame event
701- */
702-void Recognizer::ProcessFrameEvent(const UFEvent event) {
703- LOG(Dbg) << "new event " << event << " with time "
704- << frame_event_get_time(event) << "\n";
705- UpdateTime(frame_event_get_time(event));
706- MatchSubscriptionsForEvent(event);
707- ProcessEvent(event);
708-}
709-
710-/**
711- * @internal
712- * Analyze a uTouch-Frame event to see if any new gestures should begin
713- */
714-void Recognizer::MatchSubscriptionsForEvent(const UFEvent event) {
715- /* Check if any subscriptions are active before doing any processing */
716- if (num_subscriptions_ == 0)
717- return;
718-
719- UFFrame frame;
720- UFStatus status = frame_event_get_property(event, UFEventPropertyFrame,
721- &frame);
722- if (status != UFStatusSuccess) {
723- LOG(Warn) << "failed to get frame from event\n";
724- return;
725- }
726-
727- /* Process all the touches that began in this frame */
728- unsigned int num_touches = frame_frame_get_num_touches(frame);
729- for (unsigned int i = 0; i < num_touches; ++i) {
730- UFTouch touch;
731- status = frame_frame_get_touch_by_index(frame, i, &touch);
732- if (status != UFStatusSuccess) {
733- LOG(Warn) << "failed to get touch from frame\n";
734- continue;
735- }
736-
737- if (frame_touch_get_state(touch) != UFTouchStateBegin)
738- continue;
739-
740- UFTouchId touch_id = frame_touch_get_id(touch);
741-
742- /* Note touch start time and add to initial touch lists */
743- start_times_[touch_id] = frame_touch_get_start_time(touch);
744- LOG(Dbg) << "touch " << touch_id << " began with start time "
745- << start_times_[touch_id] << "\n";
746-
747- INSERT_TOUCH(touch_id, unaccepted_touches_);
748- INSERT_TOUCH(touch_id, free_touches_);
749-
750- /* HandleNewTouchForAcceptedGesture may erase the gesture from
751- * accepted_gestures_, so we can't use range-based for loops */
752- for (auto it = accepted_gestures_.begin();
753- it != accepted_gestures_.end();
754- ) {
755- const SharedGesture& gesture = *it++;
756- HandleNewTouchForAcceptedGesture(touch_id, gesture);
757- }
758-
759- /* HandleNewTouchForAcceptedGesture may erase the gesture from
760- * accepted_gestures_, so we can't use range-based for loops */
761- for (auto it = unaccepted_gestures_.begin();
762- it != unaccepted_gestures_.end();
763- ) {
764- const SharedGesture& gesture = *it++;
765- HandleNewTouchForUnacceptedGesture(touch_id, gesture);
766- }
767-
768- /* Attempt to match new gestures for active subscriptions */
769- MatchOneTouchGestures(touch_id);
770- MatchTwoTouchGestures(touch_id);
771- MatchThreeTouchGestures(touch_id);
772- MatchFourTouchGestures(touch_id);
773- MatchFiveTouchGestures(touch_id);
774- }
775-}
776-
777-/**
778- * @internal
779- * Perform tasks necessary for when a new touch occurs and there is an existing
780- * accepted gesture
781- *
782- * Under normal processing:
783- * If a gesture may add a touch without crossing the maximum for the
784- * subscription, create a new unaccepted gesture with the new touch.
785- * Otherwise, do nothing. New gestures may still begin elsewhere.
786- *
787- * Under atomic gesture processing:
788- * If a gesture may add a touch without crossing the maximum for the
789- * subscription, add the touch to the gesture and accept it. Otherwise, end
790- * the gesture and add the current gesture touches to the free touches list.
791- */
792-void Recognizer::HandleNewTouchForAcceptedGesture(
793- UFTouchId touch_id,
794- const SharedGesture& gesture) {
795- UGSubscription* subscription = gesture->subscription();
796- if (gesture->touches().size() < subscription->touches_max()) {
797- if (atomic_) {
798- gesture->AddTouch(touch_id);
799- LOG(Dbg) << "touch " << touch_id << " has been added to atomic gesture "
800- << gesture->id() << "\n";
801- if (frame_x11_accept_touch(device_, window_id_, touch_id) !=
802- UFStatusSuccess)
803- LOG(Err) << "touch " << touch_id << " failed to be accepted\n";
804- LOG(Dbg) << "touch " << touch_id
805- << " has been accepted because it has been added to an atomic "
806- "gesture";
807- INSERT_TOUCH(touch_id, accepted_touches_);
808- ERASE_TOUCH(touch_id, unaccepted_touches_);
809- ERASE_TOUCH(touch_id, free_touches_);
810- } else {
811- TouchSet set(gesture->touches());
812- set.insert(touch_id);
813- Gesture* new_gesture = new Gesture(gesture.get(), set);
814- LOG(Dbg) << "touch " << touch_id << " has been added to accepted gesture "
815- << gesture->id() << " to create new gesture "
816- << new_gesture->id() << "\n";
817- unaccepted_gestures_.insert(SharedGesture(new_gesture));
818- LOG(Dbg) << "gesture " << new_gesture << " has been added to unaccepted "
819- "gestures\n";
820- }
821- } else if (atomic_) {
822- for (UFTouchId touch : gesture->touches())
823- INSERT_TOUCH(touch, free_touches_);
824- gesture->End();
825- LOG(Dbg) << "ended active atomic gesture " << gesture->id()
826- << " because a new touch began and the max touches has been "
827- << "reached\n";
828- accepted_gestures_.erase(gesture);
829- }
830-}
831-
832-/**
833- * @internal
834- * Perform tasks necessary for when a new touch occurs and there is an existing
835- * unaccepted gesture
836- *
837- * Under normal processing:
838- * If a gesture may add a touch without crossing the maximum for the
839- * subscription, create a new unaccepted gesture with the new touch.
840- * Otherwise, do nothing. New gestures may still begin elsewhere.
841- *
842- * Under atomic gesture processing:
843- * If a gesture may add a touch without crossing the maximum for the
844- * subscription, add the touch to the gesture and accept it. Otherwise, cancel
845- * the gesture and add the current gesture touches to the free touches list.
846- */
847-void Recognizer::HandleNewTouchForUnacceptedGesture(
848- UFTouchId touch_id,
849- const SharedGesture& gesture) {
850- UGSubscription* subscription = gesture->subscription();
851- if (gesture->touches().size() < subscription->touches_max()) {
852- if (atomic_) {
853- gesture->AddTouch(touch_id);
854- LOG(Dbg) << "touch " << touch_id << " has been added to atomic gesture "
855- << gesture->id() << "\n";
856- ERASE_TOUCH(touch_id, free_touches_);
857- } else {
858- TouchSet set(gesture->touches());
859- set.insert(touch_id);
860- Gesture* new_gesture = new Gesture(gesture.get(), set);
861- LOG(Dbg) << "touch " << touch_id
862- << " has been added to unaccepted gesture " << gesture->id()
863- << " to create new gesture " << new_gesture->id() << "\n";
864- unaccepted_gestures_.insert(SharedGesture(new_gesture));
865- LOG(Dbg) << "gesture " << new_gesture << " has been added to unaccepted "
866- "gestures\n";
867- }
868- } else if (atomic_) {
869- for (UFTouchId touch : gesture->touches())
870- INSERT_TOUCH(touch, free_touches_);
871- gesture->Cancel();
872- LOG(Dbg) << "canceled inactive atomic gesture " << gesture->id()
873- << " because a new touch began and the max touches has been "
874- << "reached\n";
875- unaccepted_gestures_.erase(gesture);
876- }
877-}
878-
879-/**
880- * @internal
881- * Attempt to match the given touch against one touch subscriptions
882- */
883-void Recognizer::MatchOneTouchGestures(UFTouchId touch_id) {
884- /* Under atomic gesture processing, don't begin a new gesture unless it
885- * includes all the free touches */
886- if (atomic_ && free_touches_.size() != 1)
887- return;
888-
889- for (UGSubscription* subscription : subscriptions_[0]) {
890- TouchSet set;
891- set.insert(touch_id);
892- Gesture* gesture = new Gesture(this, subscription, set,
893- start_times_[touch_id]);
894- unaccepted_gestures_.insert(SharedGesture(gesture));
895-
896- LOG(Dbg) << "New tentative gesture " << gesture->id()
897- << " matched subscription " << subscription << " with mask "
898- << subscription->mask() << " for touch " << touch_id << "\n";
899- }
900-}
901-
902-/**
903- * @internal
904- * Attempt to match the given touch against two touch subscriptions
905- */
906-void Recognizer::MatchTwoTouchGestures(UFTouchId touch_id) {
907- /* Under atomic gesture processing, don't begin a new gesture unless it
908- * includes all the free touches */
909- if (atomic_ && free_touches_.size() != 2)
910- return;
911-
912- for (UGSubscription* subscription : subscriptions_[1]) {
913- for (UFTouchId other_id : free_touches_) {
914- if (other_id == touch_id)
915- continue;
916-
917- /* All touches in a gesture must begin within a composition timeframe */
918- uint64_t min_start_time = start_times_[touch_id];
919- if (start_times_[other_id] < min_start_time &&
920- unaccepted_touches_.find(other_id) != unaccepted_touches_.end())
921- min_start_time = start_times_[other_id];
922-
923- if (start_times_[touch_id] - min_start_time < COMPOSITION_TIME) {
924- TouchSet set;
925- set.insert(touch_id);
926- set.insert(other_id);
927- Gesture* gesture = new Gesture(this, subscription, set,
928- start_times_[touch_id]);
929- unaccepted_gestures_.insert(SharedGesture(gesture));
930-
931- LOG(Dbg) << "New tentative gesture " << gesture->id()
932- << " matched subscription " << subscription << " with mask "
933- << subscription->mask() << " for touches " << touch_id << ", "
934- << other_id << "\n";
935- }
936- }
937- }
938-}
939-
940-/**
941- * @internal
942- * Attempt to match the given touch against three touch subscriptions
943- */
944-void Recognizer::MatchThreeTouchGestures(UFTouchId touch_id) {
945- /* Under atomic gesture processing, don't begin a new gesture unless it
946- * includes all the free touches */
947- if (atomic_ && free_touches_.size() != 3)
948- return;
949-
950- for (UGSubscription* subscription : subscriptions_[2]) {
951- for (UFTouchId other_id_1 : free_touches_) {
952- if (other_id_1 == touch_id)
953- continue;
954-
955- for (UFTouchId other_id_2 : free_touches_) {
956- if (other_id_2 <= other_id_1 || other_id_2 == touch_id)
957- continue;
958-
959- /* All touches in a gesture must begin within a composition timeframe */
960- uint64_t min_start_time = start_times_[touch_id];
961- if (start_times_[other_id_1] < min_start_time &&
962- unaccepted_touches_.find(other_id_1) != unaccepted_touches_.end())
963- min_start_time = start_times_[other_id_1];
964- if (start_times_[other_id_2] < min_start_time &&
965- unaccepted_touches_.find(other_id_2) != unaccepted_touches_.end())
966- min_start_time = start_times_[other_id_2];
967-
968- if (start_times_[touch_id] - min_start_time < COMPOSITION_TIME) {
969- TouchSet set;
970- set.insert(touch_id);
971- set.insert(other_id_1);
972- set.insert(other_id_2);
973- Gesture* gesture = new Gesture(this, subscription, set,
974- start_times_[touch_id]);
975- unaccepted_gestures_.insert(SharedGesture(gesture));
976-
977- LOG(Dbg) << "New tentative gesture " << gesture->id()
978- << " matched subscription " << subscription << " with mask "
979- << subscription->mask() << " for touches " << touch_id
980- << ", " << other_id_1 << ", " << other_id_2 << "\n";
981- }
982- }
983- }
984- }
985-}
986-
987-/**
988- * @internal
989- * Attempt to match the given touch against four touch subscriptions
990- */
991-void Recognizer::MatchFourTouchGestures(UFTouchId touch_id) {
992- /* Under atomic gesture processing, don't begin a new gesture unless it
993- * includes all the free touches */
994- if (atomic_ && free_touches_.size() != 4)
995- return;
996-
997- for (UGSubscription* subscription : subscriptions_[3]) {
998- for (UFTouchId other_id_1 : free_touches_) {
999- if (other_id_1 == touch_id)
1000- continue;
1001-
1002- for (UFTouchId other_id_2 : free_touches_) {
1003- if (other_id_2 <= other_id_1 || other_id_2 == touch_id)
1004- continue;
1005-
1006- for (UFTouchId other_id_3 : free_touches_) {
1007- if (other_id_3 <= other_id_2 || other_id_3 == touch_id)
1008- continue;
1009-
1010- /* All touches in a gesture must begin within a composition
1011- * timeframe */
1012- uint64_t min_start_time = start_times_[touch_id];
1013- if (start_times_[other_id_1] < min_start_time &&
1014- unaccepted_touches_.find(other_id_1) != unaccepted_touches_.end())
1015- min_start_time = start_times_[other_id_1];
1016- if (start_times_[other_id_2] < min_start_time &&
1017- unaccepted_touches_.find(other_id_2) != unaccepted_touches_.end())
1018- min_start_time = start_times_[other_id_2];
1019- if (start_times_[other_id_3] < min_start_time &&
1020- unaccepted_touches_.find(other_id_3) != unaccepted_touches_.end())
1021- min_start_time = start_times_[other_id_3];
1022-
1023- if (start_times_[touch_id] - min_start_time < COMPOSITION_TIME) {
1024- TouchSet set;
1025- set.insert(touch_id);
1026- set.insert(other_id_1);
1027- set.insert(other_id_2);
1028- set.insert(other_id_3);
1029- Gesture* gesture = new Gesture(this, subscription, set,
1030- start_times_[touch_id]);
1031- unaccepted_gestures_.insert(SharedGesture(gesture));
1032-
1033- LOG(Dbg) << "New tentative gesture " << gesture->id()
1034- << " matched subscription " << subscription
1035- << " with mask " << subscription->mask() << " for touches "
1036- << touch_id << ", " << other_id_1 << ", " << other_id_2
1037- << ", " << other_id_3 << "\n";
1038- }
1039- }
1040- }
1041- }
1042- }
1043-}
1044-
1045-/**
1046- * @internal
1047- * Attempt to match the given touch against five touch subscriptions
1048- */
1049-void Recognizer::MatchFiveTouchGestures(UFTouchId touch_id) {
1050- /* Under atomic gesture processing, don't begin a new gesture unless it
1051- * includes all the free touches */
1052- if (atomic_ && free_touches_.size() != 5)
1053- return;
1054-
1055- for (UGSubscription* subscription : subscriptions_[4]) {
1056- for (UFTouchId other_id_1 : free_touches_) {
1057- if (other_id_1 == touch_id)
1058- continue;
1059-
1060- for (UFTouchId other_id_2 : free_touches_) {
1061- if (other_id_2 <= other_id_1 || other_id_2 == touch_id)
1062- continue;
1063-
1064- for (UFTouchId other_id_3 : free_touches_) {
1065- if (other_id_3 <= other_id_2 || other_id_3 == touch_id)
1066- continue;
1067-
1068- for (UFTouchId other_id_4 : free_touches_) {
1069- if (other_id_4 <= other_id_3 || other_id_4 == touch_id)
1070- continue;
1071-
1072- /* All touches in a gesture must begin within a composition
1073- * timeframe */
1074- uint64_t min_start_time = start_times_[touch_id];
1075- if (start_times_[other_id_1] < min_start_time &&
1076- unaccepted_touches_.find(other_id_1) !=
1077- unaccepted_touches_.end())
1078- min_start_time = start_times_[other_id_1];
1079- if (start_times_[other_id_2] < min_start_time &&
1080- unaccepted_touches_.find(other_id_2) !=
1081- unaccepted_touches_.end())
1082- min_start_time = start_times_[other_id_2];
1083- if (start_times_[other_id_3] < min_start_time &&
1084- unaccepted_touches_.find(other_id_3) !=
1085- unaccepted_touches_.end())
1086- min_start_time = start_times_[other_id_3];
1087- if (start_times_[other_id_4] < min_start_time &&
1088- unaccepted_touches_.find(other_id_4) !=
1089- unaccepted_touches_.end())
1090- min_start_time = start_times_[other_id_4];
1091-
1092- if (start_times_[touch_id] - min_start_time < COMPOSITION_TIME) {
1093- TouchSet set;
1094- set.insert(touch_id);
1095- set.insert(other_id_1);
1096- set.insert(other_id_2);
1097- set.insert(other_id_3);
1098- set.insert(other_id_4);
1099- Gesture* gesture = new Gesture(this, subscription, set,
1100- start_times_[touch_id]);
1101- unaccepted_gestures_.insert(SharedGesture(gesture));
1102-
1103- LOG(Dbg) << "New tentative gesture " << gesture->id()
1104- << " matched subscription " << subscription
1105- << " with mask " << subscription->mask()
1106- << " for touches " << touch_id << ", " << other_id_1
1107- << ", " << other_id_2 << ", " << other_id_3 << ", "
1108- << other_id_4 << "\n";
1109- }
1110- }
1111- }
1112- }
1113- }
1114- }
1115-}
1116-
1117 namespace {
1118
1119 /**
1120@@ -670,11 +233,6 @@
1121 LOG(Dbg) << "rejecting gesture " << gesture->id() << " because it has "
1122 " been canceled\n";
1123 RejectGesture(gesture);
1124- } else if (gesture->IsActive() && gesture->subscription()->atomic()) {
1125- /* Atomic gestures must be accepted if they meet the subscription criteria
1126- */
1127- LOG(Dbg) << "accepting active atomic gesture " << gesture->id() << "\n";
1128- AcceptGesture(gesture->id());
1129 }
1130 }
1131
1132@@ -713,7 +271,7 @@
1133 * gesture has not crossed the recognition thresholds yet)
1134 */
1135 void Recognizer::CheckConstructionFinished(uint64_t time) {
1136- if (atomic_)
1137+ if (atomic())
1138 return;
1139
1140 for (const SharedGesture& gesture : unaccepted_gestures_) {
1141@@ -721,7 +279,7 @@
1142 continue;
1143
1144 for (UFTouchId touch_id : gesture->touches()) {
1145- if (time - start_times_[touch_id] < COMPOSITION_TIME)
1146+ if (time - start_times_[touch_id] < kCompositionTime)
1147 goto next_gesture;
1148
1149 for (const SharedGesture& other_gesture : unaccepted_gestures_) {
1150@@ -771,7 +329,7 @@
1151 it != unaccepted_touches_.end();
1152 ) {
1153 UFTouchId touch_id = *it++;
1154- if (time - start_times_[touch_id] < COMPOSITION_TIME)
1155+ if (time - start_times_[touch_id] < kCompositionTime)
1156 continue;
1157
1158 for (const SharedGesture& gesture : unaccepted_gestures_)
1159@@ -808,8 +366,8 @@
1160 if (gesture->touches().find(touch_id) != gesture->touches().end())
1161 goto next_touch;
1162
1163- if (COMPOSITION_TIME + start_times_[touch_id] < min_timeout)
1164- min_timeout = start_times_[touch_id] + COMPOSITION_TIME;
1165+ if (kCompositionTime + start_times_[touch_id] < min_timeout)
1166+ min_timeout = start_times_[touch_id] + kCompositionTime;
1167
1168 next_touch: ;
1169 }
1170@@ -859,7 +417,7 @@
1171 * gesture. Cancel the old gesture and remove any gesture events for it
1172 * from the event queue. Atomic behavior does not allow for gestures to be
1173 * extended in this way, and gestures may overlap.*/
1174- if (!atomic_) {
1175+ if (!atomic()) {
1176 for (auto it = accepted_gestures_.begin();
1177 it != accepted_gestures_.end();
1178 ) {
1179@@ -886,7 +444,7 @@
1180
1181 /* Reject any overlapping unaccepted gestures. Atomic subscriptions may have
1182 * overlapping gestures due to historical behavior. */
1183- if (!atomic_) {
1184+ if (!atomic()) {
1185 for (auto it = unaccepted_gestures_.begin();
1186 it != unaccepted_gestures_.end();
1187 ) {
1188
1189=== modified file 'src/v3/recognizer.h'
1190--- src/v3/recognizer.h 2012-01-27 11:57:35 +0000
1191+++ src/v3/recognizer.h 2012-03-14 19:17:21 +0000
1192@@ -33,17 +33,43 @@
1193 #include "v3/forward.h"
1194 #include "v3/subscription.h"
1195
1196+#define INSERT_TOUCH(element, set) \
1197+ { \
1198+ (set).insert(element); \
1199+ LOG(Dbg) << "touch " << element << " has been added to " #set "\n"; \
1200+ }
1201+
1202+#define ERASE_TOUCH(element, set) \
1203+ { \
1204+ (set).erase(element); \
1205+ LOG(Dbg) << "touch " << element << " has been erased from " #set "\n"; \
1206+ }
1207+
1208+// OBS: it avoids the "expensive" ToString() call when debug output is not wanted.
1209+#define CLEAR_TOUCHES(set) \
1210+ { \
1211+ if (utouch::grail::Logger::instance().level() <= utouch::grail::Logger::Dbg) \
1212+ if ((set).size() > 0) \
1213+ LOG(Dbg) << "touch(es) " << (set).ToString() \
1214+ << " have been erased from " #set "\n"; \
1215+ (set).clear(); \
1216+ }
1217+
1218 namespace utouch {
1219 namespace grail {
1220
1221 class Recognizer {
1222 public:
1223 Recognizer(UGHandle* handle, const UFDevice device, UFWindowId window);
1224- ~Recognizer();
1225+ virtual ~Recognizer();
1226+
1227+ virtual bool atomic() const = 0;
1228+ virtual void ProcessFrameEvent(const UFEvent event) = 0;
1229+
1230+ unsigned int num_subscriptions() const {return num_subscriptions_;}
1231
1232 UGStatus ActivateSubscription(UGSubscription* subscription);
1233 void DeactivateSubscription(UGSubscription* subscription);
1234- void ProcessFrameEvent(const UFEvent event);
1235 void UpdateTime(uint64_t time);
1236 uint64_t NextTimeout();
1237 UGStatus AcceptGesture(unsigned int id);
1238@@ -57,7 +83,9 @@
1239 Recognizer(const Recognizer&) = delete;
1240 Recognizer& operator=(const Recognizer&) = delete;
1241
1242- private:
1243+ protected:
1244+ static uint64_t kCompositionTime; /* in milliseconds */
1245+
1246 UGHandle* const handle_;
1247 const UFDevice device_;
1248 const UFWindowId window_id_;
1249@@ -72,22 +100,12 @@
1250 TouchSet unaccepted_touches_;
1251 TouchSet accepted_touches_;
1252 TouchSet free_touches_;
1253+
1254 unsigned int num_subscriptions_;
1255
1256- void HandleNewTouchForAcceptedGesture(UFTouchId touch_id,
1257- const SharedGesture& gesture);
1258- void HandleNewTouchForUnacceptedGesture(UFTouchId touch_id,
1259- const SharedGesture& gesture);
1260- void MatchSubscriptionsForEvent(const UFEvent event);
1261- void MatchSubscriptionsForTouch(UFTouchId touch_id);
1262- void MatchOneTouchGestures(UFTouchId touch_id);
1263- void MatchTwoTouchGestures(UFTouchId touch_id);
1264- void MatchThreeTouchGestures(UFTouchId touch_id);
1265- void MatchFourTouchGestures(UFTouchId touch_id);
1266- void MatchFiveTouchGestures(UFTouchId touch_id);
1267- void ProcessEvent(const UFEvent& event);
1268 void CheckConstructionFinished(uint64_t time);
1269 void RejectGesture(SharedGesture gesture);
1270+ void ProcessEvent(const UFEvent& event);
1271 };
1272
1273 } // namespace grail
1274
1275=== added file 'src/v3/regular-recognizer.cpp'
1276--- src/v3/regular-recognizer.cpp 1970-01-01 00:00:00 +0000
1277+++ src/v3/regular-recognizer.cpp 2012-03-14 19:17:21 +0000
1278@@ -0,0 +1,396 @@
1279+/*****************************************************************************
1280+ *
1281+ * grail - Gesture Recognition And Instantiation Library
1282+ *
1283+ * Copyright (C) 2012 Canonical Ltd.
1284+ *
1285+ * This program is free software: you can redistribute it and/or modify it
1286+ * under the terms of the GNU General Public License as published by the
1287+ * Free Software Foundation, either version 3 of the License, or (at your
1288+ * option) any later version.
1289+ *
1290+ * This program is distributed in the hope that it will be useful, but
1291+ * WITHOUT ANY WARRANTY; without even the implied warranty of
1292+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1293+ * General Public License for more details.
1294+ *
1295+ * You should have received a copy of the GNU General Public License along
1296+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1297+ *
1298+ ****************************************************************************/
1299+
1300+#include "v3/regular-recognizer.h"
1301+
1302+#include <algorithm>
1303+#include <cassert>
1304+#include <cstdint>
1305+#include <cstdio>
1306+#include <limits>
1307+
1308+#include <utouch/frame.h>
1309+#include <utouch/frame_x11.h>
1310+
1311+#include "v3/handle.h"
1312+#include "v3/gesture.h"
1313+#include "v3/log.h"
1314+
1315+namespace utouch {
1316+namespace grail {
1317+
1318+/**
1319+ * @internal
1320+ * Create a new regular recognizer for a given device and window
1321+ */
1322+RegularRecognizer::RegularRecognizer(UGHandle* handle, const UFDevice device,
1323+ UFWindowId window_id)
1324+ : Recognizer(handle, device, window_id) {
1325+}
1326+
1327+/**
1328+ * @internal
1329+ * Process a uTouch-Frame event
1330+ */
1331+void RegularRecognizer::ProcessFrameEvent(const UFEvent event) {
1332+ LOG(Dbg) << "new event " << event << " with time "
1333+ << frame_event_get_time(event) << "\n";
1334+ UpdateTime(frame_event_get_time(event));
1335+ MatchSubscriptionsForEvent(event);
1336+ ProcessEvent(event);
1337+}
1338+
1339+/**
1340+ * @internal
1341+ * Analyze a uTouch-Frame event to see if any new gestures should begin
1342+ */
1343+void RegularRecognizer::MatchSubscriptionsForEvent(const UFEvent event) {
1344+ /* Check if any subscriptions are active before doing any processing */
1345+ if (num_subscriptions_ == 0)
1346+ return;
1347+
1348+ UFFrame frame;
1349+ UFStatus status = frame_event_get_property(event, UFEventPropertyFrame,
1350+ &frame);
1351+ if (status != UFStatusSuccess) {
1352+ LOG(Warn) << "failed to get frame from event\n";
1353+ return;
1354+ }
1355+
1356+ /* Process all the touches that began in this frame */
1357+ unsigned int num_touches = frame_frame_get_num_touches(frame);
1358+ for (unsigned int i = 0; i < num_touches; ++i) {
1359+ UFTouch touch;
1360+ status = frame_frame_get_touch_by_index(frame, i, &touch);
1361+ if (status != UFStatusSuccess) {
1362+ LOG(Warn) << "failed to get touch from frame\n";
1363+ continue;
1364+ }
1365+
1366+ if (frame_touch_get_state(touch) != UFTouchStateBegin)
1367+ continue;
1368+
1369+ UFTouchId touch_id = frame_touch_get_id(touch);
1370+
1371+ /* Note touch start time and add to initial touch lists */
1372+ start_times_[touch_id] = frame_touch_get_start_time(touch);
1373+ LOG(Dbg) << "touch " << touch_id << " began with start time "
1374+ << start_times_[touch_id] << "\n";
1375+
1376+ INSERT_TOUCH(touch_id, unaccepted_touches_);
1377+ INSERT_TOUCH(touch_id, free_touches_);
1378+
1379+ /* HandleNewTouchForAcceptedGesture may erase the gesture from
1380+ * accepted_gestures_, so we can't use range-based for loops */
1381+ for (auto it = accepted_gestures_.begin();
1382+ it != accepted_gestures_.end();
1383+ ) {
1384+ const SharedGesture& gesture = *it++;
1385+ HandleNewTouchForAcceptedGesture(touch_id, gesture);
1386+ }
1387+
1388+ /* HandleNewTouchForAcceptedGesture may erase the gesture from
1389+ * accepted_gestures_, so we can't use range-based for loops */
1390+ for (auto it = unaccepted_gestures_.begin();
1391+ it != unaccepted_gestures_.end();
1392+ ) {
1393+ const SharedGesture& gesture = *it++;
1394+ HandleNewTouchForUnacceptedGesture(touch_id, gesture);
1395+ }
1396+
1397+ /* Attempt to match new gestures for active subscriptions */
1398+ MatchOneTouchGestures(touch_id);
1399+ MatchTwoTouchGestures(touch_id);
1400+ MatchThreeTouchGestures(touch_id);
1401+ MatchFourTouchGestures(touch_id);
1402+ MatchFiveTouchGestures(touch_id);
1403+ }
1404+}
1405+
1406+/**
1407+ * @internal
1408+ * Perform tasks necessary for when a new touch occurs and there is an existing
1409+ * accepted gesture
1410+ *
1411+ * If a gesture may add a touch without crossing the maximum for the
1412+ * subscription, create a new unaccepted gesture with the new touch.
1413+ * Otherwise, do nothing. New gestures may still begin elsewhere.
1414+ */
1415+void RegularRecognizer::HandleNewTouchForAcceptedGesture(
1416+ UFTouchId touch_id,
1417+ const SharedGesture& gesture) {
1418+ UGSubscription* subscription = gesture->subscription();
1419+ if (gesture->touches().size() < subscription->touches_max()
1420+ && !gesture->ContainsTouch(touch_id)) {
1421+ TouchSet set(gesture->touches());
1422+ set.insert(touch_id);
1423+ Gesture* new_gesture = new Gesture(gesture.get(), set);
1424+ LOG(Dbg) << "touch " << touch_id << " has been added to accepted gesture "
1425+ << gesture->id() << " to create new gesture "
1426+ << new_gesture->id() << "\n";
1427+ unaccepted_gestures_.insert(SharedGesture(new_gesture));
1428+ LOG(Dbg) << "gesture " << new_gesture << " has been added to unaccepted "
1429+ "gestures\n";
1430+ }
1431+}
1432+
1433+/**
1434+ * @internal
1435+ * Perform tasks necessary for when a new touch occurs and there is an existing
1436+ * unaccepted gesture
1437+ *
1438+ * If a gesture may add a touch without crossing the maximum for the
1439+ * subscription, create a new unaccepted gesture with the new touch.
1440+ * Otherwise, do nothing. New gestures may still begin elsewhere.
1441+ */
1442+void RegularRecognizer::HandleNewTouchForUnacceptedGesture(
1443+ UFTouchId touch_id,
1444+ const SharedGesture& gesture) {
1445+ UGSubscription* subscription = gesture->subscription();
1446+ if (gesture->touches().size() < subscription->touches_max()
1447+ && !gesture->ContainsTouch(touch_id)) {
1448+ TouchSet set(gesture->touches());
1449+ set.insert(touch_id);
1450+ Gesture* new_gesture = new Gesture(gesture.get(), set);
1451+ LOG(Dbg) << "touch " << touch_id
1452+ << " has been added to unaccepted gesture " << gesture->id()
1453+ << " to create new gesture " << new_gesture->id() << "\n";
1454+ unaccepted_gestures_.insert(SharedGesture(new_gesture));
1455+ LOG(Dbg) << "gesture " << new_gesture << " has been added to unaccepted "
1456+ "gestures\n";
1457+ }
1458+}
1459+
1460+/**
1461+ * @internal
1462+ * Attempt to match the given touch against one touch subscriptions
1463+ */
1464+void RegularRecognizer::MatchOneTouchGestures(UFTouchId touch_id) {
1465+ for (UGSubscription* subscription : subscriptions_[0]) {
1466+ TouchSet set;
1467+ set.insert(touch_id);
1468+ Gesture* gesture = new Gesture(this, subscription, set,
1469+ start_times_[touch_id]);
1470+ unaccepted_gestures_.insert(SharedGesture(gesture));
1471+
1472+ LOG(Dbg) << "New tentative gesture " << gesture->id()
1473+ << " matched subscription " << subscription << " with mask "
1474+ << subscription->mask() << " for touch " << touch_id << "\n";
1475+ }
1476+}
1477+
1478+/**
1479+ * @internal
1480+ * Attempt to match the given touch against two touch subscriptions
1481+ */
1482+void RegularRecognizer::MatchTwoTouchGestures(UFTouchId touch_id) {
1483+ for (UGSubscription* subscription : subscriptions_[1]) {
1484+ for (UFTouchId other_id : free_touches_) {
1485+ if (other_id == touch_id)
1486+ continue;
1487+
1488+ /* All touches in a gesture must begin within a composition timeframe */
1489+ uint64_t min_start_time = start_times_[touch_id];
1490+ if (start_times_[other_id] < min_start_time &&
1491+ unaccepted_touches_.find(other_id) != unaccepted_touches_.end())
1492+ min_start_time = start_times_[other_id];
1493+
1494+ if (start_times_[touch_id] - min_start_time < kCompositionTime) {
1495+ TouchSet set;
1496+ set.insert(touch_id);
1497+ set.insert(other_id);
1498+ Gesture* gesture = new Gesture(this, subscription, set,
1499+ start_times_[touch_id]);
1500+ unaccepted_gestures_.insert(SharedGesture(gesture));
1501+
1502+ LOG(Dbg) << "New tentative gesture " << gesture->id()
1503+ << " matched subscription " << subscription << " with mask "
1504+ << subscription->mask() << " for touches " << touch_id << ", "
1505+ << other_id << "\n";
1506+ }
1507+ }
1508+ }
1509+}
1510+
1511+/**
1512+ * @internal
1513+ * Attempt to match the given touch against three touch subscriptions
1514+ */
1515+void RegularRecognizer::MatchThreeTouchGestures(UFTouchId touch_id) {
1516+ for (UGSubscription* subscription : subscriptions_[2]) {
1517+ for (UFTouchId other_id_1 : free_touches_) {
1518+ if (other_id_1 == touch_id)
1519+ continue;
1520+
1521+ for (UFTouchId other_id_2 : free_touches_) {
1522+ if (other_id_2 <= other_id_1 || other_id_2 == touch_id)
1523+ continue;
1524+
1525+ /* All touches in a gesture must begin within a composition timeframe */
1526+ uint64_t min_start_time = start_times_[touch_id];
1527+ if (start_times_[other_id_1] < min_start_time &&
1528+ unaccepted_touches_.find(other_id_1) != unaccepted_touches_.end())
1529+ min_start_time = start_times_[other_id_1];
1530+ if (start_times_[other_id_2] < min_start_time &&
1531+ unaccepted_touches_.find(other_id_2) != unaccepted_touches_.end())
1532+ min_start_time = start_times_[other_id_2];
1533+
1534+ if (start_times_[touch_id] - min_start_time < kCompositionTime) {
1535+ TouchSet set;
1536+ set.insert(touch_id);
1537+ set.insert(other_id_1);
1538+ set.insert(other_id_2);
1539+ Gesture* gesture = new Gesture(this, subscription, set,
1540+ start_times_[touch_id]);
1541+ unaccepted_gestures_.insert(SharedGesture(gesture));
1542+
1543+ LOG(Dbg) << "New tentative gesture " << gesture->id()
1544+ << " matched subscription " << subscription << " with mask "
1545+ << subscription->mask() << " for touches " << touch_id
1546+ << ", " << other_id_1 << ", " << other_id_2 << "\n";
1547+ }
1548+ }
1549+ }
1550+ }
1551+}
1552+
1553+/**
1554+ * @internal
1555+ * Attempt to match the given touch against four touch subscriptions
1556+ */
1557+void RegularRecognizer::MatchFourTouchGestures(UFTouchId touch_id) {
1558+ for (UGSubscription* subscription : subscriptions_[3]) {
1559+ for (UFTouchId other_id_1 : free_touches_) {
1560+ if (other_id_1 == touch_id)
1561+ continue;
1562+
1563+ for (UFTouchId other_id_2 : free_touches_) {
1564+ if (other_id_2 <= other_id_1 || other_id_2 == touch_id)
1565+ continue;
1566+
1567+ for (UFTouchId other_id_3 : free_touches_) {
1568+ if (other_id_3 <= other_id_2 || other_id_3 == touch_id)
1569+ continue;
1570+
1571+ /* All touches in a gesture must begin within a composition
1572+ * timeframe */
1573+ uint64_t min_start_time = start_times_[touch_id];
1574+ if (start_times_[other_id_1] < min_start_time &&
1575+ unaccepted_touches_.find(other_id_1) != unaccepted_touches_.end())
1576+ min_start_time = start_times_[other_id_1];
1577+ if (start_times_[other_id_2] < min_start_time &&
1578+ unaccepted_touches_.find(other_id_2) != unaccepted_touches_.end())
1579+ min_start_time = start_times_[other_id_2];
1580+ if (start_times_[other_id_3] < min_start_time &&
1581+ unaccepted_touches_.find(other_id_3) != unaccepted_touches_.end())
1582+ min_start_time = start_times_[other_id_3];
1583+
1584+ if (start_times_[touch_id] - min_start_time < kCompositionTime) {
1585+ TouchSet set;
1586+ set.insert(touch_id);
1587+ set.insert(other_id_1);
1588+ set.insert(other_id_2);
1589+ set.insert(other_id_3);
1590+ Gesture* gesture = new Gesture(this, subscription, set,
1591+ start_times_[touch_id]);
1592+ unaccepted_gestures_.insert(SharedGesture(gesture));
1593+
1594+ LOG(Dbg) << "New tentative gesture " << gesture->id()
1595+ << " matched subscription " << subscription
1596+ << " with mask " << subscription->mask() << " for touches "
1597+ << touch_id << ", " << other_id_1 << ", " << other_id_2
1598+ << ", " << other_id_3 << "\n";
1599+ }
1600+ }
1601+ }
1602+ }
1603+ }
1604+}
1605+
1606+/**
1607+ * @internal
1608+ * Attempt to match the given touch against five touch subscriptions
1609+ */
1610+void RegularRecognizer::MatchFiveTouchGestures(UFTouchId touch_id) {
1611+ for (UGSubscription* subscription : subscriptions_[4]) {
1612+ for (UFTouchId other_id_1 : free_touches_) {
1613+ if (other_id_1 == touch_id)
1614+ continue;
1615+
1616+ for (UFTouchId other_id_2 : free_touches_) {
1617+ if (other_id_2 <= other_id_1 || other_id_2 == touch_id)
1618+ continue;
1619+
1620+ for (UFTouchId other_id_3 : free_touches_) {
1621+ if (other_id_3 <= other_id_2 || other_id_3 == touch_id)
1622+ continue;
1623+
1624+ for (UFTouchId other_id_4 : free_touches_) {
1625+ if (other_id_4 <= other_id_3 || other_id_4 == touch_id)
1626+ continue;
1627+
1628+ /* All touches in a gesture must begin within a composition
1629+ * timeframe */
1630+ uint64_t min_start_time = start_times_[touch_id];
1631+ if (start_times_[other_id_1] < min_start_time &&
1632+ unaccepted_touches_.find(other_id_1) !=
1633+ unaccepted_touches_.end())
1634+ min_start_time = start_times_[other_id_1];
1635+ if (start_times_[other_id_2] < min_start_time &&
1636+ unaccepted_touches_.find(other_id_2) !=
1637+ unaccepted_touches_.end())
1638+ min_start_time = start_times_[other_id_2];
1639+ if (start_times_[other_id_3] < min_start_time &&
1640+ unaccepted_touches_.find(other_id_3) !=
1641+ unaccepted_touches_.end())
1642+ min_start_time = start_times_[other_id_3];
1643+ if (start_times_[other_id_4] < min_start_time &&
1644+ unaccepted_touches_.find(other_id_4) !=
1645+ unaccepted_touches_.end())
1646+ min_start_time = start_times_[other_id_4];
1647+
1648+ if (start_times_[touch_id] - min_start_time < kCompositionTime) {
1649+ TouchSet set;
1650+ set.insert(touch_id);
1651+ set.insert(other_id_1);
1652+ set.insert(other_id_2);
1653+ set.insert(other_id_3);
1654+ set.insert(other_id_4);
1655+ Gesture* gesture = new Gesture(this, subscription, set,
1656+ start_times_[touch_id]);
1657+ unaccepted_gestures_.insert(SharedGesture(gesture));
1658+
1659+ LOG(Dbg) << "New tentative gesture " << gesture->id()
1660+ << " matched subscription " << subscription
1661+ << " with mask " << subscription->mask()
1662+ << " for touches " << touch_id << ", " << other_id_1
1663+ << ", " << other_id_2 << ", " << other_id_3 << ", "
1664+ << other_id_4 << "\n";
1665+ }
1666+ }
1667+ }
1668+ }
1669+ }
1670+ }
1671+}
1672+
1673+} // namespace grail
1674+} // namespace utouch
1675
1676=== added file 'src/v3/regular-recognizer.h'
1677--- src/v3/regular-recognizer.h 1970-01-01 00:00:00 +0000
1678+++ src/v3/regular-recognizer.h 2012-03-14 19:17:21 +0000
1679@@ -0,0 +1,52 @@
1680+/*****************************************************************************
1681+ *
1682+ * grail - Gesture Recognition And Instantiation Library
1683+ *
1684+ * Copyright (C) 2012 Canonical Ltd.
1685+ *
1686+ * This program is free software: you can redistribute it and/or modify it
1687+ * under the terms of the GNU General Public License as published by the
1688+ * Free Software Foundation, either version 3 of the License, or (at your
1689+ * option) any later version.
1690+ *
1691+ * This program is distributed in the hope that it will be useful, but
1692+ * WITHOUT ANY WARRANTY; without even the implied warranty of
1693+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1694+ * General Public License for more details.
1695+ *
1696+ * You should have received a copy of the GNU General Public License along
1697+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1698+ *
1699+ ****************************************************************************/
1700+
1701+#ifndef UTOUCH_GRAIL_REGULAR_RECOGNIZER_H_
1702+#define UTOUCH_GRAIL_REGULAR_RECOGNIZER_H_
1703+
1704+#include "v3/recognizer.h"
1705+
1706+namespace utouch {
1707+namespace grail {
1708+
1709+class RegularRecognizer : public Recognizer {
1710+ public:
1711+ RegularRecognizer(UGHandle* handle, const UFDevice device, UFWindowId window);
1712+
1713+ virtual bool atomic() const {return false;}
1714+ virtual void ProcessFrameEvent(const UFEvent event);
1715+
1716+ private:
1717+ void HandleNewTouchForAcceptedGesture(UFTouchId touch_id,
1718+ const SharedGesture& gesture);
1719+ void HandleNewTouchForUnacceptedGesture(UFTouchId touch_id,
1720+ const SharedGesture& gesture);
1721+ void MatchSubscriptionsForEvent(const UFEvent event);
1722+ void MatchOneTouchGestures(UFTouchId touch_id);
1723+ void MatchTwoTouchGestures(UFTouchId touch_id);
1724+ void MatchThreeTouchGestures(UFTouchId touch_id);
1725+ void MatchFourTouchGestures(UFTouchId touch_id);
1726+ void MatchFiveTouchGestures(UFTouchId touch_id);
1727+};
1728+
1729+} // namespace grail
1730+} // namespace utouch
1731+#endif
1732
1733=== modified file 'test/Makefile.am'
1734--- test/Makefile.am 2012-03-06 20:12:40 +0000
1735+++ test/Makefile.am 2012-03-14 19:17:21 +0000
1736@@ -29,6 +29,7 @@
1737 recording.h \
1738 x11/fixture.cpp \
1739 x11/fixture.h \
1740+ x11/no-premature-gestures.cpp \
1741 x11/no-tap-after-drag.cpp \
1742 x11/twodrag.cpp \
1743 x11/dragthresh.cpp \
1744
1745=== added file 'test/recordings/apple-wtrackpad-synced-4-drag.event'
1746--- test/recordings/apple-wtrackpad-synced-4-drag.event 1970-01-01 00:00:00 +0000
1747+++ test/recordings/apple-wtrackpad-synced-4-drag.event 2012-03-14 19:17:21 +0000
1748@@ -0,0 +1,713 @@
1749+E: 1331641155.505566 0003 0039 1
1750+E: 1331641155.505567 0003 0030 84
1751+E: 1331641155.505568 0003 0031 132
1752+E: 1331641155.505568 0003 0034 8
1753+E: 1331641155.505569 0003 0035 -1940
1754+E: 1331641155.505570 0003 0036 -910
1755+E: 1331641155.505571 0000 0002 0
1756+E: 1331641155.505571 0003 0039 2
1757+E: 1331641155.505572 0003 0030 64
1758+E: 1331641155.505573 0003 0031 76
1759+E: 1331641155.505573 0003 0034 1
1760+E: 1331641155.505574 0003 0035 1649
1761+E: 1331641155.505575 0003 0036 -1430
1762+E: 1331641155.505575 0000 0002 0
1763+E: 1331641155.505576 0003 0039 3
1764+E: 1331641155.505577 0003 0030 164
1765+E: 1331641155.505577 0003 0031 124
1766+E: 1331641155.505578 0003 0034 0
1767+E: 1331641155.505578 0003 0035 -1055
1768+E: 1331641155.505579 0003 0036 -1523
1769+E: 1331641155.505580 0000 0002 0
1770+E: 1331641155.505580 0003 0039 8
1771+E: 1331641155.505581 0003 0030 180
1772+E: 1331641155.505582 0003 0031 188
1773+E: 1331641155.505582 0003 0034 32
1774+E: 1331641155.505583 0003 0035 249
1775+E: 1331641155.505584 0003 0036 -1808
1776+E: 1331641155.505584 0000 0002 0
1777+E: 1331641155.505585 0001 014a 1
1778+E: 1331641155.505587 0001 014f 1
1779+E: 1331641155.505587 0003 0000 -1940
1780+E: 1331641155.505588 0003 0001 -910
1781+E: 1331641155.505589 0000 0000 0
1782+E: 1331641155.530563 0003 0039 1
1783+E: 1331641155.530564 0003 0030 100
1784+E: 1331641155.530564 0003 0031 152
1785+E: 1331641155.530565 0003 0034 0
1786+E: 1331641155.530565 0003 0035 -1950
1787+E: 1331641155.530566 0003 0036 -898
1788+E: 1331641155.530567 0000 0002 0
1789+E: 1331641155.530568 0003 0039 2
1790+E: 1331641155.530568 0003 0030 96
1791+E: 1331641155.530569 0003 0031 132
1792+E: 1331641155.530569 0003 0034 0
1793+E: 1331641155.530570 0003 0035 1631
1794+E: 1331641155.530571 0003 0036 -1420
1795+E: 1331641155.530571 0000 0002 0
1796+E: 1331641155.530572 0003 0039 3
1797+E: 1331641155.530573 0003 0030 148
1798+E: 1331641155.530573 0003 0031 136
1799+E: 1331641155.530574 0003 0034 0
1800+E: 1331641155.530575 0003 0035 -1061
1801+E: 1331641155.530575 0003 0036 -1516
1802+E: 1331641155.530576 0000 0002 0
1803+E: 1331641155.530577 0003 0039 8
1804+E: 1331641155.530577 0003 0030 176
1805+E: 1331641155.530578 0003 0031 168
1806+E: 1331641155.530579 0003 0034 0
1807+E: 1331641155.530579 0003 0035 251
1808+E: 1331641155.530580 0003 0036 -1814
1809+E: 1331641155.530581 0000 0002 0
1810+E: 1331641155.530583 0003 0000 -1950
1811+E: 1331641155.530584 0003 0001 -898
1812+E: 1331641155.530585 0000 0000 0
1813+E: 1331641155.546821 0003 0039 1
1814+E: 1331641155.546822 0003 0030 120
1815+E: 1331641155.546822 0003 0031 164
1816+E: 1331641155.546823 0003 0034 0
1817+E: 1331641155.546824 0003 0035 -1950
1818+E: 1331641155.546824 0003 0036 -868
1819+E: 1331641155.546825 0000 0002 0
1820+E: 1331641155.546826 0003 0039 2
1821+E: 1331641155.546826 0003 0030 104
1822+E: 1331641155.546827 0003 0031 168
1823+E: 1331641155.546828 0003 0034 0
1824+E: 1331641155.546828 0003 0035 1622
1825+E: 1331641155.546829 0003 0036 -1416
1826+E: 1331641155.546830 0000 0002 0
1827+E: 1331641155.546830 0003 0039 3
1828+E: 1331641155.546831 0003 0030 156
1829+E: 1331641155.546831 0003 0031 144
1830+E: 1331641155.546832 0003 0034 0
1831+E: 1331641155.546833 0003 0035 -1061
1832+E: 1331641155.546833 0003 0036 -1483
1833+E: 1331641155.546834 0000 0002 0
1834+E: 1331641155.546835 0003 0039 8
1835+E: 1331641155.546835 0003 0030 164
1836+E: 1331641155.546836 0003 0031 148
1837+E: 1331641155.546837 0003 0034 0
1838+E: 1331641155.546837 0003 0035 247
1839+E: 1331641155.546838 0003 0036 -1791
1840+E: 1331641155.546838 0000 0002 0
1841+E: 1331641155.546842 0003 0001 -868
1842+E: 1331641155.546842 0000 0000 0
1843+E: 1331641155.549309 0003 0039 1
1844+E: 1331641155.549310 0003 0030 132
1845+E: 1331641155.549310 0003 0031 176
1846+E: 1331641155.549311 0003 0034 0
1847+E: 1331641155.549312 0003 0035 -1954
1848+E: 1331641155.549312 0003 0036 -825
1849+E: 1331641155.549313 0000 0002 0
1850+E: 1331641155.549314 0003 0039 2
1851+E: 1331641155.549314 0003 0030 120
1852+E: 1331641155.549315 0003 0031 196
1853+E: 1331641155.549315 0003 0034 0
1854+E: 1331641155.549316 0003 0035 1616
1855+E: 1331641155.549317 0003 0036 -1382
1856+E: 1331641155.549317 0000 0002 0
1857+E: 1331641155.549318 0003 0039 3
1858+E: 1331641155.549319 0003 0030 176
1859+E: 1331641155.549319 0003 0031 180
1860+E: 1331641155.549320 0003 0034 0
1861+E: 1331641155.549321 0003 0035 -1056
1862+E: 1331641155.549321 0003 0036 -1443
1863+E: 1331641155.549322 0000 0002 0
1864+E: 1331641155.549323 0003 0039 8
1865+E: 1331641155.549323 0003 0030 156
1866+E: 1331641155.549324 0003 0031 152
1867+E: 1331641155.549325 0003 0034 0
1868+E: 1331641155.549325 0003 0035 241
1869+E: 1331641155.549326 0003 0036 -1760
1870+E: 1331641155.549327 0000 0002 0
1871+E: 1331641155.549329 0003 0000 -1952
1872+E: 1331641155.549330 0003 0001 -825
1873+E: 1331641155.549331 0000 0000 0
1874+E: 1331641155.551812 0003 0039 1
1875+E: 1331641155.551813 0003 0030 140
1876+E: 1331641155.551814 0003 0031 204
1877+E: 1331641155.551815 0003 0034 0
1878+E: 1331641155.551815 0003 0035 -1949
1879+E: 1331641155.551816 0003 0036 -767
1880+E: 1331641155.551817 0000 0002 0
1881+E: 1331641155.551817 0003 0039 2
1882+E: 1331641155.551818 0003 0030 132
1883+E: 1331641155.551819 0003 0031 208
1884+E: 1331641155.551819 0003 0034 0
1885+E: 1331641155.551820 0003 0035 1614
1886+E: 1331641155.551820 0003 0036 -1331
1887+E: 1331641155.551821 0000 0002 0
1888+E: 1331641155.551822 0003 0039 3
1889+E: 1331641155.551823 0003 0030 176
1890+E: 1331641155.551823 0003 0031 200
1891+E: 1331641155.551824 0003 0034 0
1892+E: 1331641155.551824 0003 0035 -1049
1893+E: 1331641155.551825 0003 0036 -1387
1894+E: 1331641155.551826 0000 0002 0
1895+E: 1331641155.551826 0003 0039 8
1896+E: 1331641155.551827 0003 0030 168
1897+E: 1331641155.551828 0003 0031 184
1898+E: 1331641155.551828 0003 0034 0
1899+E: 1331641155.551829 0003 0035 238
1900+E: 1331641155.551829 0003 0036 -1715
1901+E: 1331641155.551830 0000 0002 0
1902+E: 1331641155.551833 0003 0000 -1951
1903+E: 1331641155.551834 0003 0001 -767
1904+E: 1331641155.551834 0000 0000 0
1905+E: 1331641155.555561 0003 0039 1
1906+E: 1331641155.555562 0003 0030 156
1907+E: 1331641155.555563 0003 0031 216
1908+E: 1331641155.555563 0003 0034 0
1909+E: 1331641155.555564 0003 0035 -1940
1910+E: 1331641155.555565 0003 0036 -705
1911+E: 1331641155.555565 0000 0002 0
1912+E: 1331641155.555566 0003 0039 2
1913+E: 1331641155.555567 0003 0030 120
1914+E: 1331641155.555567 0003 0031 228
1915+E: 1331641155.555568 0003 0034 0
1916+E: 1331641155.555569 0003 0035 1618
1917+E: 1331641155.555569 0003 0036 -1275
1918+E: 1331641155.555570 0000 0002 0
1919+E: 1331641155.555571 0003 0039 3
1920+E: 1331641155.555571 0003 0030 180
1921+E: 1331641155.555572 0003 0031 180
1922+E: 1331641155.555572 0003 0034 0
1923+E: 1331641155.555573 0003 0035 -1039
1924+E: 1331641155.555574 0003 0036 -1323
1925+E: 1331641155.555574 0000 0002 0
1926+E: 1331641155.555575 0003 0039 8
1927+E: 1331641155.555576 0003 0030 184
1928+E: 1331641155.555576 0003 0031 192
1929+E: 1331641155.555577 0003 0034 -29
1930+E: 1331641155.555577 0003 0035 240
1931+E: 1331641155.555578 0003 0036 -1657
1932+E: 1331641155.555579 0000 0002 0
1933+E: 1331641155.555581 0003 0000 -1940
1934+E: 1331641155.555582 0003 0001 -705
1935+E: 1331641155.555583 0000 0000 0
1936+E: 1331641155.575560 0003 0039 1
1937+E: 1331641155.575561 0003 0030 148
1938+E: 1331641155.575562 0003 0031 208
1939+E: 1331641155.575563 0003 0034 0
1940+E: 1331641155.575563 0003 0035 -1928
1941+E: 1331641155.575564 0003 0036 -629
1942+E: 1331641155.575565 0000 0002 0
1943+E: 1331641155.575565 0003 0039 2
1944+E: 1331641155.575566 0003 0030 116
1945+E: 1331641155.575567 0003 0031 208
1946+E: 1331641155.575567 0003 0034 0
1947+E: 1331641155.575568 0003 0035 1625
1948+E: 1331641155.575569 0003 0036 -1207
1949+E: 1331641155.575569 0000 0002 0
1950+E: 1331641155.575570 0003 0039 3
1951+E: 1331641155.575571 0003 0030 168
1952+E: 1331641155.575571 0003 0031 164
1953+E: 1331641155.575572 0003 0034 0
1954+E: 1331641155.575573 0003 0035 -1025
1955+E: 1331641155.575573 0003 0036 -1243
1956+E: 1331641155.575574 0000 0002 0
1957+E: 1331641155.575575 0003 0039 8
1958+E: 1331641155.575575 0003 0030 172
1959+E: 1331641155.575576 0003 0031 168
1960+E: 1331641155.575576 0003 0034 0
1961+E: 1331641155.575577 0003 0035 245
1962+E: 1331641155.575578 0003 0036 -1591
1963+E: 1331641155.575578 0000 0002 0
1964+E: 1331641155.575581 0003 0000 -1928
1965+E: 1331641155.575582 0003 0001 -629
1966+E: 1331641155.575582 0000 0000 0
1967+E: 1331641155.578059 0003 0039 1
1968+E: 1331641155.578060 0003 0030 152
1969+E: 1331641155.578061 0003 0031 232
1970+E: 1331641155.578062 0003 0034 0
1971+E: 1331641155.578062 0003 0035 -1914
1972+E: 1331641155.578063 0003 0036 -549
1973+E: 1331641155.578064 0000 0002 0
1974+E: 1331641155.578064 0003 0039 2
1975+E: 1331641155.578065 0003 0030 116
1976+E: 1331641155.578066 0003 0031 196
1977+E: 1331641155.578066 0003 0034 0
1978+E: 1331641155.578067 0003 0035 1632
1979+E: 1331641155.578068 0003 0036 -1130
1980+E: 1331641155.578068 0000 0002 0
1981+E: 1331641155.578069 0003 0039 3
1982+E: 1331641155.578070 0003 0030 156
1983+E: 1331641155.578070 0003 0031 176
1984+E: 1331641155.578071 0003 0034 0
1985+E: 1331641155.578072 0003 0035 -999
1986+E: 1331641155.578072 0003 0036 -1120
1987+E: 1331641155.578073 0000 0002 0
1988+E: 1331641155.578074 0003 0039 8
1989+E: 1331641155.578074 0003 0030 188
1990+E: 1331641155.578074 0003 0031 188
1991+E: 1331641155.578075 0003 0034 0
1992+E: 1331641155.578076 0003 0035 253
1993+E: 1331641155.578076 0003 0036 -1511
1994+E: 1331641155.578077 0000 0002 0
1995+E: 1331641155.578080 0003 0000 -1914
1996+E: 1331641155.578080 0003 0001 -549
1997+E: 1331641155.578081 0000 0000 0
1998+E: 1331641155.585562 0003 0039 1
1999+E: 1331641155.585563 0003 0030 160
2000+E: 1331641155.585563 0003 0031 208
2001+E: 1331641155.585564 0003 0034 0
2002+E: 1331641155.585565 0003 0035 -1893
2003+E: 1331641155.585565 0003 0036 -424
2004+E: 1331641155.585566 0000 0002 0
2005+E: 1331641155.585567 0003 0039 2
2006+E: 1331641155.585567 0003 0030 124
2007+E: 1331641155.585568 0003 0031 200
2008+E: 1331641155.585569 0003 0034 0
2009+E: 1331641155.585569 0003 0035 1640
2010+E: 1331641155.585570 0003 0036 -1046
2011+E: 1331641155.585571 0000 0002 0
2012+E: 1331641155.585571 0003 0039 3
2013+E: 1331641155.585572 0003 0030 140
2014+E: 1331641155.585573 0003 0031 160
2015+E: 1331641155.585573 0003 0034 0
2016+E: 1331641155.585574 0003 0035 -958
2017+E: 1331641155.585575 0003 0036 -924
2018+E: 1331641155.585575 0000 0002 0
2019+E: 1331641155.585576 0003 0039 8
2020+E: 1331641155.585577 0003 0030 192
2021+E: 1331641155.585577 0003 0031 196
2022+E: 1331641155.585578 0003 0034 -30
2023+E: 1331641155.585578 0003 0035 267
2024+E: 1331641155.585579 0003 0036 -1391
2025+E: 1331641155.585580 0000 0002 0
2026+E: 1331641155.585582 0003 0000 -1893
2027+E: 1331641155.585583 0003 0001 -424
2028+E: 1331641155.585584 0000 0000 0
2029+E: 1331641155.605561 0003 0039 1
2030+E: 1331641155.605562 0003 0030 164
2031+E: 1331641155.605563 0003 0031 232
2032+E: 1331641155.605563 0003 0034 0
2033+E: 1331641155.605564 0003 0035 -1868
2034+E: 1331641155.605564 0003 0036 -286
2035+E: 1331641155.605565 0000 0002 0
2036+E: 1331641155.605566 0003 0039 2
2037+E: 1331641155.605567 0003 0030 116
2038+E: 1331641155.605567 0003 0031 204
2039+E: 1331641155.605568 0003 0034 0
2040+E: 1331641155.605568 0003 0035 1657
2041+E: 1331641155.605569 0003 0036 -877
2042+E: 1331641155.605570 0000 0002 0
2043+E: 1331641155.605571 0003 0039 3
2044+E: 1331641155.605571 0003 0030 160
2045+E: 1331641155.605571 0003 0031 160
2046+E: 1331641155.605572 0003 0034 0
2047+E: 1331641155.605573 0003 0035 -934
2048+E: 1331641155.605573 0003 0036 -805
2049+E: 1331641155.605574 0000 0002 0
2050+E: 1331641155.605575 0003 0039 8
2051+E: 1331641155.605575 0003 0030 196
2052+E: 1331641155.605576 0003 0031 184
2053+E: 1331641155.605577 0003 0034 32
2054+E: 1331641155.605577 0003 0035 285
2055+E: 1331641155.605578 0003 0036 -1253
2056+E: 1331641155.605578 0000 0002 0
2057+E: 1331641155.605581 0003 0000 -1868
2058+E: 1331641155.605582 0003 0001 -286
2059+E: 1331641155.605582 0000 0000 0
2060+E: 1331641155.608299 0003 0039 1
2061+E: 1331641155.608300 0003 0030 164
2062+E: 1331641155.608301 0003 0031 236
2063+E: 1331641155.608302 0003 0034 0
2064+E: 1331641155.608302 0003 0035 -1842
2065+E: 1331641155.608303 0003 0036 -135
2066+E: 1331641155.608304 0000 0002 0
2067+E: 1331641155.608304 0003 0039 2
2068+E: 1331641155.608305 0003 0030 132
2069+E: 1331641155.608306 0003 0031 224
2070+E: 1331641155.608306 0003 0034 0
2071+E: 1331641155.608307 0003 0035 1677
2072+E: 1331641155.608308 0003 0036 -724
2073+E: 1331641155.608308 0000 0002 0
2074+E: 1331641155.608309 0003 0039 3
2075+E: 1331641155.608310 0003 0030 176
2076+E: 1331641155.608310 0003 0031 160
2077+E: 1331641155.608311 0003 0034 32
2078+E: 1331641155.608311 0003 0035 -887
2079+E: 1331641155.608312 0003 0036 -562
2080+E: 1331641155.608313 0000 0002 0
2081+E: 1331641155.608313 0003 0039 8
2082+E: 1331641155.608314 0003 0030 200
2083+E: 1331641155.608315 0003 0031 204
2084+E: 1331641155.608315 0003 0034 0
2085+E: 1331641155.608316 0003 0035 307
2086+E: 1331641155.608317 0003 0036 -1102
2087+E: 1331641155.608317 0000 0002 0
2088+E: 1331641155.608320 0003 0000 -1842
2089+E: 1331641155.608321 0003 0001 -135
2090+E: 1331641155.608321 0000 0000 0
2091+E: 1331641155.628292 0003 0039 1
2092+E: 1331641155.628293 0003 0030 176
2093+E: 1331641155.628293 0003 0031 216
2094+E: 1331641155.628294 0003 0034 0
2095+E: 1331641155.628295 0003 0035 -1815
2096+E: 1331641155.628295 0003 0036 24
2097+E: 1331641155.628296 0000 0002 0
2098+E: 1331641155.628297 0003 0039 2
2099+E: 1331641155.628297 0003 0030 128
2100+E: 1331641155.628298 0003 0031 204
2101+E: 1331641155.628299 0003 0034 0
2102+E: 1331641155.628299 0003 0035 1700
2103+E: 1331641155.628300 0003 0036 -565
2104+E: 1331641155.628300 0000 0002 0
2105+E: 1331641155.628301 0003 0039 3
2106+E: 1331641155.628302 0003 0030 176
2107+E: 1331641155.628302 0003 0031 164
2108+E: 1331641155.628303 0003 0034 0
2109+E: 1331641155.628304 0003 0035 -863
2110+E: 1331641155.628304 0003 0036 -435
2111+E: 1331641155.628305 0000 0002 0
2112+E: 1331641155.628306 0003 0039 8
2113+E: 1331641155.628306 0003 0030 200
2114+E: 1331641155.628307 0003 0031 188
2115+E: 1331641155.628308 0003 0034 -30
2116+E: 1331641155.628308 0003 0035 332
2117+E: 1331641155.628309 0003 0036 -942
2118+E: 1331641155.628310 0000 0002 0
2119+E: 1331641155.628312 0003 0000 -1815
2120+E: 1331641155.628313 0003 0001 24
2121+E: 1331641155.628314 0000 0000 0
2122+E: 1331641155.630546 0003 0039 1
2123+E: 1331641155.630547 0003 0030 168
2124+E: 1331641155.630548 0003 0031 188
2125+E: 1331641155.630548 0003 0034 0
2126+E: 1331641155.630549 0003 0035 -1787
2127+E: 1331641155.630549 0003 0036 190
2128+E: 1331641155.630550 0000 0002 0
2129+E: 1331641155.630551 0003 0039 2
2130+E: 1331641155.630552 0003 0030 136
2131+E: 1331641155.630552 0003 0031 204
2132+E: 1331641155.630553 0003 0034 0
2133+E: 1331641155.630554 0003 0035 1725
2134+E: 1331641155.630554 0003 0036 -398
2135+E: 1331641155.630555 0000 0002 0
2136+E: 1331641155.630556 0003 0039 3
2137+E: 1331641155.630556 0003 0030 184
2138+E: 1331641155.630557 0003 0031 148
2139+E: 1331641155.630558 0003 0034 32
2140+E: 1331641155.630558 0003 0035 -830
2141+E: 1331641155.630559 0003 0036 -246
2142+E: 1331641155.630559 0000 0002 0
2143+E: 1331641155.630560 0003 0039 8
2144+E: 1331641155.630561 0003 0030 204
2145+E: 1331641155.630561 0003 0031 192
2146+E: 1331641155.630562 0003 0034 -31
2147+E: 1331641155.630563 0003 0035 359
2148+E: 1331641155.630563 0003 0036 -773
2149+E: 1331641155.630564 0000 0002 0
2150+E: 1331641155.630567 0003 0000 -1787
2151+E: 1331641155.630567 0003 0001 190
2152+E: 1331641155.630568 0000 0000 0
2153+E: 1331641155.636814 0003 0039 1
2154+E: 1331641155.636815 0003 0030 156
2155+E: 1331641155.636815 0003 0031 188
2156+E: 1331641155.636816 0003 0034 0
2157+E: 1331641155.636817 0003 0035 -1757
2158+E: 1331641155.636817 0003 0036 358
2159+E: 1331641155.636818 0000 0002 0
2160+E: 1331641155.636819 0003 0039 2
2161+E: 1331641155.636819 0003 0030 148
2162+E: 1331641155.636820 0003 0031 204
2163+E: 1331641155.636821 0003 0034 0
2164+E: 1331641155.636821 0003 0035 1751
2165+E: 1331641155.636822 0003 0036 -224
2166+E: 1331641155.636823 0000 0002 0
2167+E: 1331641155.636823 0003 0039 3
2168+E: 1331641155.636824 0003 0030 180
2169+E: 1331641155.636825 0003 0031 136
2170+E: 1331641155.636825 0003 0034 0
2171+E: 1331641155.636826 0003 0035 -800
2172+E: 1331641155.636826 0003 0036 -58
2173+E: 1331641155.636827 0000 0002 0
2174+E: 1331641155.636828 0003 0039 8
2175+E: 1331641155.636828 0003 0030 184
2176+E: 1331641155.636829 0003 0031 204
2177+E: 1331641155.636830 0003 0034 0
2178+E: 1331641155.636830 0003 0035 388
2179+E: 1331641155.636831 0003 0036 -601
2180+E: 1331641155.636832 0000 0002 0
2181+E: 1331641155.636834 0003 0000 -1757
2182+E: 1331641155.636835 0003 0001 358
2183+E: 1331641155.636836 0000 0000 0
2184+E: 1331641155.656821 0003 0039 1
2185+E: 1331641155.656822 0003 0030 156
2186+E: 1331641155.656822 0003 0031 156
2187+E: 1331641155.656823 0003 0034 0
2188+E: 1331641155.656824 0003 0035 -1729
2189+E: 1331641155.656824 0003 0036 529
2190+E: 1331641155.656825 0000 0002 0
2191+E: 1331641155.656826 0003 0039 2
2192+E: 1331641155.656826 0003 0030 144
2193+E: 1331641155.656827 0003 0031 188
2194+E: 1331641155.656828 0003 0034 0
2195+E: 1331641155.656828 0003 0035 1777
2196+E: 1331641155.656829 0003 0036 -47
2197+E: 1331641155.656830 0000 0002 0
2198+E: 1331641155.656830 0003 0039 3
2199+E: 1331641155.656831 0003 0030 160
2200+E: 1331641155.656832 0003 0031 132
2201+E: 1331641155.656832 0003 0034 0
2202+E: 1331641155.656833 0003 0035 -771
2203+E: 1331641155.656833 0003 0036 121
2204+E: 1331641155.656834 0000 0002 0
2205+E: 1331641155.656835 0003 0039 8
2206+E: 1331641155.656835 0003 0030 184
2207+E: 1331641155.656836 0003 0031 208
2208+E: 1331641155.656837 0003 0034 0
2209+E: 1331641155.656837 0003 0035 417
2210+E: 1331641155.656838 0003 0036 -425
2211+E: 1331641155.656839 0000 0002 0
2212+E: 1331641155.656842 0003 0000 -1729
2213+E: 1331641155.656842 0003 0001 529
2214+E: 1331641155.656843 0000 0000 0
2215+E: 1331641155.659312 0003 0039 1
2216+E: 1331641155.659313 0003 0030 148
2217+E: 1331641155.659314 0003 0031 172
2218+E: 1331641155.659315 0003 0034 0
2219+E: 1331641155.659315 0003 0035 -1703
2220+E: 1331641155.659316 0003 0036 696
2221+E: 1331641155.659317 0000 0002 0
2222+E: 1331641155.659317 0003 0039 2
2223+E: 1331641155.659318 0003 0030 144
2224+E: 1331641155.659319 0003 0031 204
2225+E: 1331641155.659319 0003 0034 0
2226+E: 1331641155.659320 0003 0035 1804
2227+E: 1331641155.659321 0003 0036 129
2228+E: 1331641155.659321 0000 0002 0
2229+E: 1331641155.659322 0003 0039 3
2230+E: 1331641155.659323 0003 0030 156
2231+E: 1331641155.659323 0003 0031 128
2232+E: 1331641155.659324 0003 0034 0
2233+E: 1331641155.659325 0003 0035 -746
2234+E: 1331641155.659325 0003 0036 298
2235+E: 1331641155.659326 0000 0002 0
2236+E: 1331641155.659327 0003 0039 8
2237+E: 1331641155.659327 0003 0030 180
2238+E: 1331641155.659328 0003 0031 160
2239+E: 1331641155.659328 0003 0034 0
2240+E: 1331641155.659329 0003 0035 448
2241+E: 1331641155.659330 0003 0036 -250
2242+E: 1331641155.659330 0000 0002 0
2243+E: 1331641155.659333 0003 0000 -1703
2244+E: 1331641155.659334 0003 0001 696
2245+E: 1331641155.659334 0000 0000 0
2246+E: 1331641155.666794 0003 0039 1
2247+E: 1331641155.666795 0003 0030 152
2248+E: 1331641155.666795 0003 0031 168
2249+E: 1331641155.666796 0003 0034 0
2250+E: 1331641155.666797 0003 0035 -1674
2251+E: 1331641155.666797 0003 0036 859
2252+E: 1331641155.666798 0000 0002 0
2253+E: 1331641155.666799 0003 0039 2
2254+E: 1331641155.666799 0003 0030 128
2255+E: 1331641155.666800 0003 0031 184
2256+E: 1331641155.666801 0003 0034 0
2257+E: 1331641155.666801 0003 0035 1830
2258+E: 1331641155.666802 0003 0036 306
2259+E: 1331641155.666803 0000 0002 0
2260+E: 1331641155.666803 0003 0039 3
2261+E: 1331641155.666804 0003 0030 144
2262+E: 1331641155.666805 0003 0031 124
2263+E: 1331641155.666805 0003 0034 0
2264+E: 1331641155.666806 0003 0035 -724
2265+E: 1331641155.666807 0003 0036 466
2266+E: 1331641155.666807 0000 0002 0
2267+E: 1331641155.666808 0003 0039 8
2268+E: 1331641155.666809 0003 0030 176
2269+E: 1331641155.666809 0003 0031 192
2270+E: 1331641155.666810 0003 0034 0
2271+E: 1331641155.666811 0003 0035 478
2272+E: 1331641155.666811 0003 0036 -75
2273+E: 1331641155.666812 0000 0002 0
2274+E: 1331641155.666815 0003 0000 -1674
2275+E: 1331641155.666815 0003 0001 859
2276+E: 1331641155.666816 0000 0000 0
2277+E: 1331641155.686806 0003 0039 1
2278+E: 1331641155.686807 0003 0030 140
2279+E: 1331641155.686808 0003 0031 148
2280+E: 1331641155.686808 0003 0034 0
2281+E: 1331641155.686809 0003 0035 -1644
2282+E: 1331641155.686810 0003 0036 1019
2283+E: 1331641155.686810 0000 0002 0
2284+E: 1331641155.686811 0003 0039 2
2285+E: 1331641155.686812 0003 0030 100
2286+E: 1331641155.686812 0003 0031 156
2287+E: 1331641155.686813 0003 0034 0
2288+E: 1331641155.686814 0003 0035 1859
2289+E: 1331641155.686814 0003 0036 482
2290+E: 1331641155.686815 0000 0002 0
2291+E: 1331641155.686816 0003 0039 3
2292+E: 1331641155.686816 0003 0030 132
2293+E: 1331641155.686817 0003 0031 124
2294+E: 1331641155.686818 0003 0034 0
2295+E: 1331641155.686818 0003 0035 -711
2296+E: 1331641155.686819 0003 0036 570
2297+E: 1331641155.686820 0000 0002 0
2298+E: 1331641155.686820 0003 0039 8
2299+E: 1331641155.686821 0003 0030 168
2300+E: 1331641155.686821 0003 0031 164
2301+E: 1331641155.686822 0003 0034 0
2302+E: 1331641155.686823 0003 0035 509
2303+E: 1331641155.686823 0003 0036 94
2304+E: 1331641155.686824 0000 0002 0
2305+E: 1331641155.686827 0003 0000 -1644
2306+E: 1331641155.686827 0003 0001 1019
2307+E: 1331641155.686828 0000 0000 0
2308+E: 1331641155.689315 0003 0039 1
2309+E: 1331641155.689316 0003 0030 140
2310+E: 1331641155.689316 0003 0031 160
2311+E: 1331641155.689317 0003 0034 0
2312+E: 1331641155.689317 0003 0035 -1622
2313+E: 1331641155.689318 0003 0036 1115
2314+E: 1331641155.689319 0000 0002 0
2315+E: 1331641155.689320 0003 0039 2
2316+E: 1331641155.689320 0003 0030 92
2317+E: 1331641155.689321 0003 0031 152
2318+E: 1331641155.689321 0003 0034 0
2319+E: 1331641155.689322 0003 0035 1887
2320+E: 1331641155.689323 0003 0036 654
2321+E: 1331641155.689323 0000 0002 0
2322+E: 1331641155.689324 0003 0039 3
2323+E: 1331641155.689325 0003 0030 120
2324+E: 1331641155.689325 0003 0031 140
2325+E: 1331641155.689326 0003 0034 0
2326+E: 1331641155.689327 0003 0035 -689
2327+E: 1331641155.689327 0003 0036 717
2328+E: 1331641155.689328 0000 0002 0
2329+E: 1331641155.689329 0003 0039 8
2330+E: 1331641155.689329 0003 0030 168
2331+E: 1331641155.689330 0003 0031 156
2332+E: 1331641155.689331 0003 0034 0
2333+E: 1331641155.689331 0003 0035 538
2334+E: 1331641155.689332 0003 0036 260
2335+E: 1331641155.689332 0000 0002 0
2336+E: 1331641155.689335 0003 0000 -1622
2337+E: 1331641155.689336 0003 0001 1115
2338+E: 1331641155.689336 0000 0000 0
2339+E: 1331641155.709313 0003 0039 1
2340+E: 1331641155.709314 0003 0030 128
2341+E: 1331641155.709315 0003 0031 136
2342+E: 1331641155.709316 0003 0034 0
2343+E: 1331641155.709316 0003 0035 -1579
2344+E: 1331641155.709317 0003 0036 1308
2345+E: 1331641155.709318 0000 0002 0
2346+E: 1331641155.709318 0003 0039 2
2347+E: 1331641155.709319 0003 0030 76
2348+E: 1331641155.709320 0003 0031 144
2349+E: 1331641155.709320 0003 0034 0
2350+E: 1331641155.709321 0003 0035 1915
2351+E: 1331641155.709321 0003 0036 823
2352+E: 1331641155.709322 0000 0002 0
2353+E: 1331641155.709323 0003 0039 3
2354+E: 1331641155.709323 0003 0030 124
2355+E: 1331641155.709324 0003 0031 132
2356+E: 1331641155.709325 0003 0034 0
2357+E: 1331641155.709325 0003 0035 -666
2358+E: 1331641155.709326 0003 0036 850
2359+E: 1331641155.709327 0000 0002 0
2360+E: 1331641155.709327 0003 0039 8
2361+E: 1331641155.709328 0003 0030 152
2362+E: 1331641155.709329 0003 0031 160
2363+E: 1331641155.709329 0003 0034 0
2364+E: 1331641155.709330 0003 0035 567
2365+E: 1331641155.709331 0003 0036 417
2366+E: 1331641155.709331 0000 0002 0
2367+E: 1331641155.709334 0003 0000 -1579
2368+E: 1331641155.709335 0003 0001 1308
2369+E: 1331641155.709335 0000 0000 0
2370+E: 1331641155.711809 0003 0039 1
2371+E: 1331641155.711810 0003 0030 108
2372+E: 1331641155.711811 0003 0031 120
2373+E: 1331641155.711811 0003 0034 0
2374+E: 1331641155.711812 0003 0035 -1556
2375+E: 1331641155.711813 0003 0036 1394
2376+E: 1331641155.711813 0000 0002 0
2377+E: 1331641155.711814 0003 0039 2
2378+E: 1331641155.711815 0003 0030 0
2379+E: 1331641155.711815 0003 0031 0
2380+E: 1331641155.711815 0003 0034 0
2381+E: 1331641155.711816 0003 0035 1915
2382+E: 1331641155.711817 0003 0036 823
2383+E: 1331641155.711817 0000 0002 0
2384+E: 1331641155.711818 0003 0039 3
2385+E: 1331641155.711819 0003 0030 100
2386+E: 1331641155.711819 0003 0031 112
2387+E: 1331641155.711820 0003 0034 0
2388+E: 1331641155.711820 0003 0035 -649
2389+E: 1331641155.711821 0003 0036 925
2390+E: 1331641155.711822 0000 0002 0
2391+E: 1331641155.711823 0003 0039 8
2392+E: 1331641155.711823 0003 0030 136
2393+E: 1331641155.711823 0003 0031 136
2394+E: 1331641155.711824 0003 0034 0
2395+E: 1331641155.711825 0003 0035 584
2396+E: 1331641155.711825 0003 0036 511
2397+E: 1331641155.711826 0000 0002 0
2398+E: 1331641155.711829 0003 0000 -1556
2399+E: 1331641155.711829 0003 0001 1394
2400+E: 1331641155.711830 0000 0000 0
2401+E: 1331641155.718302 0003 0039 1
2402+E: 1331641155.718303 0003 0030 48
2403+E: 1331641155.718303 0003 0031 88
2404+E: 1331641155.718304 0003 0034 0
2405+E: 1331641155.718305 0003 0035 -1523
2406+E: 1331641155.718305 0003 0036 1525
2407+E: 1331641155.718306 0000 0002 0
2408+E: 1331641155.718307 0003 0039 3
2409+E: 1331641155.718308 0003 0030 100
2410+E: 1331641155.718308 0003 0031 80
2411+E: 1331641155.718309 0003 0034 0
2412+E: 1331641155.718310 0003 0035 -616
2413+E: 1331641155.718310 0003 0036 1083
2414+E: 1331641155.718311 0000 0002 0
2415+E: 1331641155.718312 0003 0039 8
2416+E: 1331641155.718312 0003 0030 124
2417+E: 1331641155.718313 0003 0031 92
2418+E: 1331641155.718313 0003 0034 0
2419+E: 1331641155.718314 0003 0035 612
2420+E: 1331641155.718315 0003 0036 644
2421+E: 1331641155.718315 0000 0002 0
2422+E: 1331641155.718317 0001 014e 1
2423+E: 1331641155.718318 0001 014f 0
2424+E: 1331641155.718319 0003 0000 -1523
2425+E: 1331641155.718320 0003 0001 1525
2426+E: 1331641155.718320 0000 0000 0
2427+E: 1331641155.738295 0003 0039 1
2428+E: 1331641155.738296 0003 0030 0
2429+E: 1331641155.738297 0003 0031 0
2430+E: 1331641155.738297 0003 0034 0
2431+E: 1331641155.738297 0003 0035 -1523
2432+E: 1331641155.738298 0003 0036 1525
2433+E: 1331641155.738299 0000 0002 0
2434+E: 1331641155.738300 0003 0039 3
2435+E: 1331641155.738300 0003 0030 120
2436+E: 1331641155.738301 0003 0031 72
2437+E: 1331641155.738301 0003 0034 0
2438+E: 1331641155.738302 0003 0035 -596
2439+E: 1331641155.738303 0003 0036 1145
2440+E: 1331641155.738303 0000 0002 0
2441+E: 1331641155.738304 0003 0039 8
2442+E: 1331641155.738305 0003 0030 0
2443+E: 1331641155.738305 0003 0031 0
2444+E: 1331641155.738305 0003 0034 0
2445+E: 1331641155.738306 0003 0035 612
2446+E: 1331641155.738306 0003 0036 644
2447+E: 1331641155.738307 0000 0002 0
2448+E: 1331641155.738311 0000 0000 0
2449+E: 1331641155.740540 0003 0039 3
2450+E: 1331641155.740541 0003 0030 0
2451+E: 1331641155.740541 0003 0031 0
2452+E: 1331641155.740541 0003 0034 0
2453+E: 1331641155.740542 0003 0035 -596
2454+E: 1331641155.740543 0003 0036 1145
2455+E: 1331641155.740543 0000 0002 0
2456+E: 1331641155.740544 0001 0145 1
2457+E: 1331641155.740545 0001 014e 0
2458+E: 1331641155.740546 0000 0000 0
2459+E: 1331641155.748043 0001 014a 0
2460+E: 1331641155.748044 0001 0145 0
2461+E: 1331641155.748044 0000 0000 0
2462
2463=== added file 'test/x11/no-premature-gestures.cpp'
2464--- test/x11/no-premature-gestures.cpp 1970-01-01 00:00:00 +0000
2465+++ test/x11/no-premature-gestures.cpp 2012-03-14 19:17:21 +0000
2466@@ -0,0 +1,215 @@
2467+/*****************************************************************************
2468+ *
2469+ * grail - Gesture Recognition And Instantiation Library
2470+ *
2471+ * Copyright (C) 2012 Canonical Ltd.
2472+ *
2473+ * This program is free software: you can redistribute it and/or modify it
2474+ * under the terms of the GNU General Public License as published by the
2475+ * Free Software Foundation, either version 3 of the License, or (at your
2476+ * option) any later version.
2477+ *
2478+ * This program is distributed in the hope that it will be useful, but
2479+ * WITHOUT ANY WARRANTY; without even the implied warranty of
2480+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2481+ * General Public License for more details.
2482+ *
2483+ * You should have received a copy of the GNU General Public License along
2484+ * with this program. If not, see <http://www.gnu.org/licenses/>.
2485+ *
2486+ ****************************************************************************/
2487+
2488+/**
2489+ * @internal
2490+ * @file "No Premature Gestures" Test
2491+ *
2492+ * This test plays a four-touches' drag where the four fingers land almost in
2493+ * sync and checks that only 4-touches' slices are generated.
2494+ * Three subscriptions are used:
2495+ * (A) An atomic two-touches Touch
2496+ * (B) An atomic three-touches Touch
2497+ * (C) An atomic four-touches Touch.
2498+ *
2499+ * It's a regression test for https://bugs.launchpad.net/utouch-grail/+bug/949916
2500+ *
2501+ * We don't want to see gestures being unnecessarily created (and therefore
2502+ * their slices) for subscriptions (A) and (B) since we already know from the
2503+ * very first events that there are four touches in total, not only two or three.
2504+ *
2505+ * Note that this logic holds only when atomic gesture rules are being used.
2506+ */
2507+
2508+#include <future>
2509+#include <gtest/gtest.h>
2510+#include <utouch/frame_x11.h>
2511+
2512+// evemu wrappers
2513+#include "device.h"
2514+#include "recording.h"
2515+
2516+#include "x11/fixture.h"
2517+
2518+class NoPrematureGestures : public utouch::grail::x11::testing::Test {
2519+ public:
2520+ NoPrematureGestures() : device_(nullptr) {}
2521+ protected:
2522+ virtual void ProcessFrameEvents();
2523+ virtual void ProcessGrailEvents();
2524+
2525+ // Holds the device we are interested in getting input from.
2526+ // More specifically, the fake one we will create via evemu.
2527+ UFDevice device_;
2528+
2529+ private:
2530+ void ProcessFrameEventDeviceAdded(UFEvent event);
2531+ void CreateSubscriptions();
2532+ void CreateSubscription(unsigned int num_touches,
2533+ UGGestureTypeMask gesture_mask);
2534+ void CheckSlice(UGSlice slice);
2535+};
2536+
2537+void NoPrematureGestures::ProcessFrameEvents() {
2538+ UFEvent event;
2539+
2540+ UFStatus status;
2541+ while ((status = frame_get_event(frame_handle(), &event)) == UFStatusSuccess) {
2542+ grail_process_frame_event(grail_handle(), event);
2543+
2544+ if (frame_event_get_type(event) == UFEventTypeDeviceAdded) {
2545+ ProcessFrameEventDeviceAdded(event);
2546+ }
2547+
2548+ frame_event_unref(event);
2549+ }
2550+
2551+ EXPECT_EQ(UFStatusErrorNoEvent, status);
2552+}
2553+
2554+void NoPrematureGestures::ProcessFrameEventDeviceAdded(UFEvent event)
2555+{
2556+ UFStatus status;
2557+ UFDevice device;
2558+ const char* name;
2559+
2560+ status = frame_event_get_property(event, UFEventPropertyDevice, &device);
2561+ ASSERT_EQ(UFStatusSuccess, status);
2562+
2563+ status = frame_device_get_property(device, UFDevicePropertyName, &name);
2564+ ASSERT_EQ(UFStatusSuccess, status);
2565+
2566+ if (strcmp(name, "Apple Wireless Trackpad") == 0) {
2567+ EXPECT_EQ(nullptr, device_);
2568+ device_ = device;
2569+ CreateSubscriptions();
2570+ }
2571+}
2572+
2573+void NoPrematureGestures::CreateSubscriptions() {
2574+ CreateSubscription(2, UGGestureTypeTouch);
2575+ CreateSubscription(3, UGGestureTypeTouch);
2576+ CreateSubscription(4, UGGestureTypeTouch);
2577+}
2578+
2579+void NoPrematureGestures::CreateSubscription(unsigned int num_touches,
2580+ UGGestureTypeMask gesture_mask) {
2581+ UGSubscription subscription;
2582+ UGStatus status;
2583+
2584+ status = grail_subscription_new(&subscription);
2585+ ASSERT_EQ(UGStatusSuccess, status);
2586+
2587+ status = grail_subscription_set_property(subscription,
2588+ UGSubscriptionPropertyDevice,
2589+ &device_);
2590+ ASSERT_EQ(UFStatusSuccess, status);
2591+
2592+ UFWindowId window_id =
2593+ frame_x11_create_window_id(DefaultRootWindow(Display()));
2594+ status = grail_subscription_set_property(subscription,
2595+ UGSubscriptionPropertyWindow,
2596+ &window_id);
2597+ ASSERT_EQ(UFStatusSuccess, status);
2598+
2599+ status = grail_subscription_set_property(subscription,
2600+ UGSubscriptionPropertyTouchesStart,
2601+ &num_touches);
2602+ ASSERT_EQ(UFStatusSuccess, status);
2603+
2604+ status = grail_subscription_set_property(subscription,
2605+ UGSubscriptionPropertyTouchesMaximum,
2606+ &num_touches);
2607+ ASSERT_EQ(UFStatusSuccess, status);
2608+
2609+ status = grail_subscription_set_property(subscription,
2610+ UGSubscriptionPropertyTouchesMinimum,
2611+ &num_touches);
2612+ ASSERT_EQ(UFStatusSuccess, status);
2613+
2614+ status = grail_subscription_set_property(subscription,
2615+ UGSubscriptionPropertyMask,
2616+ &gesture_mask);
2617+ ASSERT_EQ(UFStatusSuccess, status);
2618+
2619+ int use_atomic_gestures = 1;
2620+ status = grail_subscription_set_property(subscription,
2621+ UGSubscriptionPropertyAtomicGestures,
2622+ &use_atomic_gestures);
2623+ ASSERT_EQ(UFStatusSuccess, status);
2624+
2625+ status = grail_subscription_activate(grail_handle(), subscription);
2626+ ASSERT_EQ(UFStatusSuccess, status);
2627+}
2628+
2629+void NoPrematureGestures::ProcessGrailEvents() {
2630+ UGEvent event;
2631+
2632+ UGStatus status;
2633+ while ((status = grail_get_event(grail_handle(), &event)) == UGStatusSuccess) {
2634+ ASSERT_EQ(UGEventTypeSlice, grail_event_get_type(event));
2635+
2636+ UGSlice slice;
2637+ status = grail_event_get_property(event, UGEventPropertySlice, &slice);
2638+ ASSERT_EQ(UGStatusSuccess, status);
2639+
2640+ CheckSlice(slice);
2641+
2642+ grail_event_unref(event);
2643+ }
2644+
2645+ EXPECT_EQ(UGStatusErrorNoEvent, status);
2646+}
2647+
2648+void NoPrematureGestures::CheckSlice(UGSlice slice) {
2649+ UGStatus status;
2650+ unsigned int num_touches;
2651+
2652+ status = grail_slice_get_property(slice, UGSlicePropertyNumTouches,
2653+ &num_touches);
2654+ ASSERT_EQ(UGStatusSuccess, status);
2655+
2656+ // The main point of this test:
2657+ // Check that we don't get any slices from a 2 or 3 touches gesture.
2658+ EXPECT_EQ(4, num_touches)
2659+ << "Got a slice with " << num_touches << " touches from a synced 4-fingers drag.";
2660+}
2661+
2662+TEST_F(NoPrematureGestures, Recording) {
2663+ utouch::evemu::Device device("recordings/apple-wireless-trackpad.prop");
2664+
2665+ /* Pump once to ensure the X server has initialized the device */
2666+ PumpEvents();
2667+ ASSERT_NE(device_, nullptr) << "X server failed to initialize trackpad";
2668+
2669+ utouch::evemu::Recording recording(device, "recordings/apple-wtrackpad-synced-4-drag.event");
2670+
2671+ /* We use the c++11 future module so any exceptions thrown by the thread can
2672+ * be caught later on. If we used the thread module, exceptions would take the
2673+ * whole thing down. */
2674+ std::future<void> future = std::async(std::launch::async,
2675+ &utouch::evemu::Recording::Play,
2676+ &recording);
2677+
2678+ PumpEvents();
2679+
2680+ future.wait();
2681+}

Subscribers

People subscribed via source and target branches