1 /*
2 * Copyright 2009, Alexandre Deckner, alex@zappotek.com
3 * Distributed under the terms of the MIT License.
4 */
5
6 /*!
7 \class ShakeTrackingFilter
8 \brief A simple mouse shake detection filter
9 *
10 * A simple mouse filter that detects quick mouse shakes.
11 *
12 * It's detecting rough edges (u-turns) in the mouse movement
13 * and counts them within a time window.
14 * You can configure the message sent, the u-turn count threshold
15 * and the time threshold.
16 * It sends the count along with the message.
17 * For now, detection is limited within the view bounds, but
18 * it might be modified to accept a BRegion mask.
19 *
20 */
21
22
23 #include <ShakeTrackingFilter.h>
24
25 #include <Message.h>
26 #include <Messenger.h>
27 #include <MessageRunner.h>
28 #include <View.h>
29
30
31 const uint32 kMsgCancel = 'Canc';
32
33
ShakeTrackingFilter(BView * targetView,uint32 messageWhat,uint32 countThreshold,bigtime_t timeThreshold)34 ShakeTrackingFilter::ShakeTrackingFilter(BView* targetView, uint32 messageWhat,
35 uint32 countThreshold, bigtime_t timeThreshold)
36 :
37 BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
38 fTargetView(targetView),
39 fMessageWhat(messageWhat),
40 fCancelRunner(NULL),
41 fLowPass(8),
42 fLastDelta(0, 0),
43 fCounter(0),
44 fCountThreshold(countThreshold),
45 fTimeThreshold(timeThreshold)
46 {
47 }
48
49
~ShakeTrackingFilter()50 ShakeTrackingFilter::~ShakeTrackingFilter()
51 {
52 delete fCancelRunner;
53 }
54
55
56 filter_result
Filter(BMessage * message,BHandler **)57 ShakeTrackingFilter::Filter(BMessage* message, BHandler** /*_target*/)
58 {
59 if (fTargetView == NULL)
60 return B_DISPATCH_MESSAGE;
61
62 switch (message->what) {
63 case B_MOUSE_MOVED:
64 {
65 BPoint position;
66 message->FindPoint("be:view_where", &position);
67
68 // TODO: allow using BRegion masks
69 if (!fTargetView->Bounds().Contains(position))
70 return B_DISPATCH_MESSAGE;
71
72 fLowPass.Input(position - fLastPosition);
73
74 BPoint delta = fLowPass.Output();
75
76 // normalized dot product
77 float norm = delta.x * delta.x + delta.y * delta.y;
78 if (norm > 0.01) {
79 delta.x /= norm;
80 delta.y /= norm;
81 }
82
83 norm = fLastDelta.x * fLastDelta.x + fLastDelta.y * fLastDelta.y;
84 if (norm > 0.01) {
85 fLastDelta.x /= norm;
86 fLastDelta.y /= norm;
87 }
88
89 float dot = delta.x * fLastDelta.x + delta.y * fLastDelta.y;
90
91 if (dot < 0.0) {
92 if (fCounter == 0) {
93 BMessage * cancelMessage = new BMessage(kMsgCancel);
94 fCancelRunner = new BMessageRunner(BMessenger(fTargetView),
95 cancelMessage, fTimeThreshold, 1);
96 }
97
98 fCounter++;
99
100 if (fCounter >= fCountThreshold) {
101 BMessage shakeMessage(fMessageWhat);
102 shakeMessage.AddUInt32("count", fCounter);
103 BMessenger messenger(fTargetView);
104 messenger.SendMessage(&shakeMessage);
105 }
106 }
107
108 fLastDelta = fLowPass.Output();
109 fLastPosition = position;
110
111 return B_DISPATCH_MESSAGE;
112 }
113
114 case kMsgCancel:
115 delete fCancelRunner;
116 fCancelRunner = NULL;
117 fCounter = 0;
118 return B_SKIP_MESSAGE;
119
120 default:
121 break;
122 }
123
124 return B_DISPATCH_MESSAGE;
125 }
126
127
128 // #pragma mark -
129
130
LowPassFilter(uint32 size)131 LowPassFilter::LowPassFilter(uint32 size)
132 :
133 fSize(size)
134 {
135 fPoints = new BPoint[fSize];
136 }
137
138
~LowPassFilter()139 LowPassFilter::~LowPassFilter()
140 {
141 delete [] fPoints;
142 }
143
144
145 void
Input(const BPoint & p)146 LowPassFilter::Input(const BPoint& p)
147 {
148 // A fifo buffer that maintains a sum of its elements
149 fSum -= fPoints[0];
150 for (uint32 i = 0; i < fSize - 1; i++)
151 fPoints[i] = fPoints[i + 1];
152 fPoints[fSize - 1] = p;
153 fSum += p;
154 }
155
156
157 BPoint
Output() const158 LowPassFilter::Output() const
159 {
160 return BPoint(fSum.x / (float) fSize, fSum.y / (float) fSize);
161 }
162