18e43b9e3SAlexandre Deckner /*
28e43b9e3SAlexandre Deckner * Copyright 2009, Alexandre Deckner, alex@zappotek.com
38e43b9e3SAlexandre Deckner * Distributed under the terms of the MIT License.
48e43b9e3SAlexandre Deckner */
58e43b9e3SAlexandre Deckner
68e43b9e3SAlexandre Deckner /*!
78e43b9e3SAlexandre Deckner \class ShakeTrackingFilter
88e43b9e3SAlexandre Deckner \brief A simple mouse shake detection filter
98e43b9e3SAlexandre Deckner *
108e43b9e3SAlexandre Deckner * A simple mouse filter that detects quick mouse shakes.
118e43b9e3SAlexandre Deckner *
128e43b9e3SAlexandre Deckner * It's detecting rough edges (u-turns) in the mouse movement
138e43b9e3SAlexandre Deckner * and counts them within a time window.
148e43b9e3SAlexandre Deckner * You can configure the message sent, the u-turn count threshold
158e43b9e3SAlexandre Deckner * and the time threshold.
168e43b9e3SAlexandre Deckner * It sends the count along with the message.
178e43b9e3SAlexandre Deckner * For now, detection is limited within the view bounds, but
188e43b9e3SAlexandre Deckner * it might be modified to accept a BRegion mask.
198e43b9e3SAlexandre Deckner *
208e43b9e3SAlexandre Deckner */
218e43b9e3SAlexandre Deckner
22*569634e9SAlexandre Deckner
238e43b9e3SAlexandre Deckner #include <ShakeTrackingFilter.h>
248e43b9e3SAlexandre Deckner
258e43b9e3SAlexandre Deckner #include <Message.h>
268e43b9e3SAlexandre Deckner #include <Messenger.h>
278e43b9e3SAlexandre Deckner #include <MessageRunner.h>
288e43b9e3SAlexandre Deckner #include <View.h>
298e43b9e3SAlexandre Deckner
30*569634e9SAlexandre Deckner
31abd3d52bSAlexandre Deckner const uint32 kMsgCancel = 'Canc';
328e43b9e3SAlexandre Deckner
33*569634e9SAlexandre Deckner
ShakeTrackingFilter(BView * targetView,uint32 messageWhat,uint32 countThreshold,bigtime_t timeThreshold)348e43b9e3SAlexandre Deckner ShakeTrackingFilter::ShakeTrackingFilter(BView* targetView, uint32 messageWhat,
358e43b9e3SAlexandre Deckner uint32 countThreshold, bigtime_t timeThreshold)
368e43b9e3SAlexandre Deckner :
378e43b9e3SAlexandre Deckner BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
388e43b9e3SAlexandre Deckner fTargetView(targetView),
398e43b9e3SAlexandre Deckner fMessageWhat(messageWhat),
408e43b9e3SAlexandre Deckner fCancelRunner(NULL),
418e43b9e3SAlexandre Deckner fLowPass(8),
428e43b9e3SAlexandre Deckner fLastDelta(0, 0),
438e43b9e3SAlexandre Deckner fCounter(0),
448e43b9e3SAlexandre Deckner fCountThreshold(countThreshold),
458e43b9e3SAlexandre Deckner fTimeThreshold(timeThreshold)
468e43b9e3SAlexandre Deckner {
478e43b9e3SAlexandre Deckner }
488e43b9e3SAlexandre Deckner
498e43b9e3SAlexandre Deckner
~ShakeTrackingFilter()508e43b9e3SAlexandre Deckner ShakeTrackingFilter::~ShakeTrackingFilter()
518e43b9e3SAlexandre Deckner {
528e43b9e3SAlexandre Deckner delete fCancelRunner;
538e43b9e3SAlexandre Deckner }
548e43b9e3SAlexandre Deckner
558e43b9e3SAlexandre Deckner
568e43b9e3SAlexandre Deckner filter_result
Filter(BMessage * message,BHandler **)578e43b9e3SAlexandre Deckner ShakeTrackingFilter::Filter(BMessage* message, BHandler** /*_target*/)
588e43b9e3SAlexandre Deckner {
598e43b9e3SAlexandre Deckner if (fTargetView == NULL)
608e43b9e3SAlexandre Deckner return B_DISPATCH_MESSAGE;
618e43b9e3SAlexandre Deckner
628e43b9e3SAlexandre Deckner switch (message->what) {
638e43b9e3SAlexandre Deckner case B_MOUSE_MOVED:
648e43b9e3SAlexandre Deckner {
658e43b9e3SAlexandre Deckner BPoint position;
668e43b9e3SAlexandre Deckner message->FindPoint("be:view_where", &position);
678e43b9e3SAlexandre Deckner
688e43b9e3SAlexandre Deckner // TODO: allow using BRegion masks
698e43b9e3SAlexandre Deckner if (!fTargetView->Bounds().Contains(position))
708e43b9e3SAlexandre Deckner return B_DISPATCH_MESSAGE;
718e43b9e3SAlexandre Deckner
728e43b9e3SAlexandre Deckner fLowPass.Input(position - fLastPosition);
738e43b9e3SAlexandre Deckner
748e43b9e3SAlexandre Deckner BPoint delta = fLowPass.Output();
758e43b9e3SAlexandre Deckner
768e43b9e3SAlexandre Deckner // normalized dot product
778e43b9e3SAlexandre Deckner float norm = delta.x * delta.x + delta.y * delta.y;
788e43b9e3SAlexandre Deckner if (norm > 0.01) {
798e43b9e3SAlexandre Deckner delta.x /= norm;
808e43b9e3SAlexandre Deckner delta.y /= norm;
818e43b9e3SAlexandre Deckner }
828e43b9e3SAlexandre Deckner
838e43b9e3SAlexandre Deckner norm = fLastDelta.x * fLastDelta.x + fLastDelta.y * fLastDelta.y;
848e43b9e3SAlexandre Deckner if (norm > 0.01) {
858e43b9e3SAlexandre Deckner fLastDelta.x /= norm;
868e43b9e3SAlexandre Deckner fLastDelta.y /= norm;
878e43b9e3SAlexandre Deckner }
888e43b9e3SAlexandre Deckner
898e43b9e3SAlexandre Deckner float dot = delta.x * fLastDelta.x + delta.y * fLastDelta.y;
908e43b9e3SAlexandre Deckner
918e43b9e3SAlexandre Deckner if (dot < 0.0) {
928e43b9e3SAlexandre Deckner if (fCounter == 0) {
938e43b9e3SAlexandre Deckner BMessage * cancelMessage = new BMessage(kMsgCancel);
948e43b9e3SAlexandre Deckner fCancelRunner = new BMessageRunner(BMessenger(fTargetView),
958e43b9e3SAlexandre Deckner cancelMessage, fTimeThreshold, 1);
968e43b9e3SAlexandre Deckner }
978e43b9e3SAlexandre Deckner
988e43b9e3SAlexandre Deckner fCounter++;
998e43b9e3SAlexandre Deckner
1008e43b9e3SAlexandre Deckner if (fCounter >= fCountThreshold) {
1018e43b9e3SAlexandre Deckner BMessage shakeMessage(fMessageWhat);
1028e43b9e3SAlexandre Deckner shakeMessage.AddUInt32("count", fCounter);
1038e43b9e3SAlexandre Deckner BMessenger messenger(fTargetView);
1048e43b9e3SAlexandre Deckner messenger.SendMessage(&shakeMessage);
1058e43b9e3SAlexandre Deckner }
1068e43b9e3SAlexandre Deckner }
1078e43b9e3SAlexandre Deckner
1088e43b9e3SAlexandre Deckner fLastDelta = fLowPass.Output();
1098e43b9e3SAlexandre Deckner fLastPosition = position;
1108e43b9e3SAlexandre Deckner
1118e43b9e3SAlexandre Deckner return B_DISPATCH_MESSAGE;
1128e43b9e3SAlexandre Deckner }
1138e43b9e3SAlexandre Deckner
1148e43b9e3SAlexandre Deckner case kMsgCancel:
1158e43b9e3SAlexandre Deckner delete fCancelRunner;
1168e43b9e3SAlexandre Deckner fCancelRunner = NULL;
1178e43b9e3SAlexandre Deckner fCounter = 0;
1188e43b9e3SAlexandre Deckner return B_SKIP_MESSAGE;
1198e43b9e3SAlexandre Deckner
1208e43b9e3SAlexandre Deckner default:
1218e43b9e3SAlexandre Deckner break;
1228e43b9e3SAlexandre Deckner }
1238e43b9e3SAlexandre Deckner
1248e43b9e3SAlexandre Deckner return B_DISPATCH_MESSAGE;
1258e43b9e3SAlexandre Deckner }
1268e43b9e3SAlexandre Deckner
1278e43b9e3SAlexandre Deckner
1288e43b9e3SAlexandre Deckner // #pragma mark -
1298e43b9e3SAlexandre Deckner
1308e43b9e3SAlexandre Deckner
LowPassFilter(uint32 size)1318e43b9e3SAlexandre Deckner LowPassFilter::LowPassFilter(uint32 size)
1328e43b9e3SAlexandre Deckner :
1338e43b9e3SAlexandre Deckner fSize(size)
1348e43b9e3SAlexandre Deckner {
1358e43b9e3SAlexandre Deckner fPoints = new BPoint[fSize];
1368e43b9e3SAlexandre Deckner }
1378e43b9e3SAlexandre Deckner
1388e43b9e3SAlexandre Deckner
~LowPassFilter()1398e43b9e3SAlexandre Deckner LowPassFilter::~LowPassFilter()
1408e43b9e3SAlexandre Deckner {
1418e43b9e3SAlexandre Deckner delete [] fPoints;
1428e43b9e3SAlexandre Deckner }
1438e43b9e3SAlexandre Deckner
1448e43b9e3SAlexandre Deckner
1458e43b9e3SAlexandre Deckner void
Input(const BPoint & p)1468e43b9e3SAlexandre Deckner LowPassFilter::Input(const BPoint& p)
1478e43b9e3SAlexandre Deckner {
1488e43b9e3SAlexandre Deckner // A fifo buffer that maintains a sum of its elements
1498e43b9e3SAlexandre Deckner fSum -= fPoints[0];
1508e43b9e3SAlexandre Deckner for (uint32 i = 0; i < fSize - 1; i++)
1518e43b9e3SAlexandre Deckner fPoints[i] = fPoints[i + 1];
1528e43b9e3SAlexandre Deckner fPoints[fSize - 1] = p;
1538e43b9e3SAlexandre Deckner fSum += p;
1548e43b9e3SAlexandre Deckner }
1558e43b9e3SAlexandre Deckner
1568e43b9e3SAlexandre Deckner
1578e43b9e3SAlexandre Deckner BPoint
Output() const1588e43b9e3SAlexandre Deckner LowPassFilter::Output() const
1598e43b9e3SAlexandre Deckner {
1608e43b9e3SAlexandre Deckner return BPoint(fSum.x / (float) fSize, fSum.y / (float) fSize);
1618e43b9e3SAlexandre Deckner }
162