* A mouse shake detecting BMessageFilter that's easily integrated in any app.

* A test app for it. I added a src/test/kits/shared folder as i found it was the
 most logical place for it. Shake it up.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@32602 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Alexandre Deckner 2009-08-22 13:43:21 +00:00
parent eb47b26534
commit 8e43b9e35e
7 changed files with 316 additions and 0 deletions

View File

@ -0,0 +1,62 @@
/*
* Copyright 2009, Alexandre Deckner, alex@zappotek.com
* Distributed under the terms of the MIT License.
*/
#ifndef SHAKE_TRACKING_FILTER_H
#define SHAKE_TRACKING_FILTER_H
#include <MessageFilter.h>
#include <Point.h>
class BView;
class BHandler;
class BMessageRunner;
namespace BPrivate {
class LowPassFilter {
public:
LowPassFilter(uint32 size);
~LowPassFilter();
void Input(const BPoint& p);
BPoint Output() const;
private:
BPoint* fPoints;
uint32 fSize;
BPoint fSum;
};
class ShakeTrackingFilter : public BMessageFilter {
public:
ShakeTrackingFilter(
BView* targetView,
uint32 messageWhat,
uint32 countThreshold = 2,
bigtime_t timeTreshold = 400000);
~ShakeTrackingFilter();
filter_result Filter(BMessage* message, BHandler** _target);
private:
BView* fTargetView;
uint32 fMessageWhat;
BMessageRunner* fCancelRunner;
LowPassFilter fLowPass;
BPoint fLastDelta;
BPoint fLastPosition;
uint32 fCounter;
uint32 fCountThreshold;
bigtime_t fTimeThreshold;
};
} // namespace BPrivate
using BPrivate::ShakeTrackingFilter;
using BPrivate::LowPassFilter;
#endif // SHAKE_TRACKING_FILTER_H

View File

@ -20,6 +20,7 @@ StaticLibrary libshared.a :
DragTrackingFilter.cpp
HashString.cpp
RWLockManager.cpp
ShakeTrackingFilter.cpp
Variant.cpp
;

View File

@ -0,0 +1,157 @@
/*
* Copyright 2009, Alexandre Deckner, alex@zappotek.com
* Distributed under the terms of the MIT License.
*/
/*!
\class ShakeTrackingFilter
\brief A simple mouse shake detection filter
*
* A simple mouse filter that detects quick mouse shakes.
*
* It's detecting rough edges (u-turns) in the mouse movement
* and counts them within a time window.
* You can configure the message sent, the u-turn count threshold
* and the time threshold.
* It sends the count along with the message.
* For now, detection is limited within the view bounds, but
* it might be modified to accept a BRegion mask.
*
*/
#include <ShakeTrackingFilter.h>
#include <Message.h>
#include <Messenger.h>
#include <MessageRunner.h>
#include <View.h>
ShakeTrackingFilter::ShakeTrackingFilter(BView* targetView, uint32 messageWhat,
uint32 countThreshold, bigtime_t timeThreshold)
:
BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
fTargetView(targetView),
fMessageWhat(messageWhat),
fCancelRunner(NULL),
fLowPass(8),
fLastDelta(0, 0),
fCounter(0),
fCountThreshold(countThreshold),
fTimeThreshold(timeThreshold)
{
}
ShakeTrackingFilter::~ShakeTrackingFilter()
{
delete fCancelRunner;
}
filter_result
ShakeTrackingFilter::Filter(BMessage* message, BHandler** /*_target*/)
{
if (fTargetView == NULL)
return B_DISPATCH_MESSAGE;
switch (message->what) {
case B_MOUSE_MOVED:
{
BPoint position;
message->FindPoint("be:view_where", &position);
// TODO: allow using BRegion masks
if (!fTargetView->Bounds().Contains(position))
return B_DISPATCH_MESSAGE;
fLowPass.Input(position - fLastPosition);
BPoint delta = fLowPass.Output();
// normalized dot product
float norm = delta.x * delta.x + delta.y * delta.y;
if (norm > 0.01) {
delta.x /= norm;
delta.y /= norm;
}
norm = fLastDelta.x * fLastDelta.x + fLastDelta.y * fLastDelta.y;
if (norm > 0.01) {
fLastDelta.x /= norm;
fLastDelta.y /= norm;
}
float dot = delta.x * fLastDelta.x + delta.y * fLastDelta.y;
if (dot < 0.0) {
if (fCounter == 0) {
BMessage * cancelMessage = new BMessage(kMsgCancel);
fCancelRunner = new BMessageRunner(BMessenger(fTargetView),
cancelMessage, fTimeThreshold, 1);
}
fCounter++;
if (fCounter >= fCountThreshold) {
BMessage shakeMessage(fMessageWhat);
shakeMessage.AddUInt32("count", fCounter);
BMessenger messenger(fTargetView);
messenger.SendMessage(&shakeMessage);
}
}
fLastDelta = fLowPass.Output();
fLastPosition = position;
return B_DISPATCH_MESSAGE;
}
case kMsgCancel:
delete fCancelRunner;
fCancelRunner = NULL;
fCounter = 0;
return B_SKIP_MESSAGE;
default:
break;
}
return B_DISPATCH_MESSAGE;
}
// #pragma mark -
LowPassFilter::LowPassFilter(uint32 size)
:
fSize(size)
{
fPoints = new BPoint[fSize];
}
LowPassFilter::~LowPassFilter()
{
delete [] fPoints;
}
void
LowPassFilter::Input(const BPoint& p)
{
// A fifo buffer that maintains a sum of its elements
fSum -= fPoints[0];
for (uint32 i = 0; i < fSize - 1; i++)
fPoints[i] = fPoints[i + 1];
fPoints[fSize - 1] = p;
fSum += p;
}
BPoint
LowPassFilter::Output() const
{
return BPoint(fSum.x / (float) fSize, fSum.y / (float) fSize);
}

View File

@ -7,6 +7,7 @@ SubInclude HAIKU_TOP src tests kits locale ;
SubInclude HAIKU_TOP src tests kits media ;
SubInclude HAIKU_TOP src tests kits midi ;
SubInclude HAIKU_TOP src tests kits net ;
SubInclude HAIKU_TOP src tests kits shared ;
SubInclude HAIKU_TOP src tests kits storage ;
SubInclude HAIKU_TOP src tests kits support ;
SubInclude HAIKU_TOP src tests kits translation ;

View File

@ -0,0 +1,6 @@
SubDir HAIKU_TOP src tests kits shared ;
SetSubDirSupportedPlatformsBeOSCompatible ;
AddSubDirSupportedPlatforms libbe_test ;
SubInclude HAIKU_TOP src tests kits shared shake_filter ;

View File

@ -0,0 +1,8 @@
SubDir HAIKU_TOP src tests kits shared shake_filter ;
UsePrivateHeaders shared ;
Application ShakeFilterTest :
ShakeFilterTest.cpp
: libshared.a be $(TARGET_LIBSUPC++)
;

View File

@ -0,0 +1,81 @@
/*
* Copyright 2009, Alexandre Deckner <alex@zappotek.com>
* Distributed under the terms of the MIT License.
*/
#include <Application.h>
#include <ShakeTrackingFilter.h>
#include <Window.h>
#include <View.h>
#include <stdio.h>
static const uint32 kMsgShake = 'Shke';
class View : public BView {
public:
View(BRect rect, const char* name, uint32 followMode);
virtual ~View();
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage* msg);
};
View::View(BRect rect, const char* name, uint32 followMode)
:
BView(rect, name, followMode, B_WILL_DRAW)
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
SetHighColor(255, 0, 0);
}
View::~View()
{
}
void
View::AttachedToWindow()
{
AddFilter(new ShakeTrackingFilter(this, kMsgShake, 3, 500000));
}
void
View::MessageReceived(BMessage* msg)
{
if (msg->what == kMsgShake) {
uint32 count = 0;
msg->FindUInt32("count", &count);
printf("Shake! %lu\n", count);
} else {
BView::MessageReceived(msg);
}
}
// #pragma mark -
int
main(int argc, char **argv)
{
BApplication app("application/x-vnd.haiku-ShakeFilterTest");
BWindow* window = new BWindow(BRect(100, 100, 400, 400),
"ShakeFilter-Test", B_TITLED_WINDOW,
B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE);
BRect frame = window->Bounds();
window->AddChild(new View(frame, "L ", B_FOLLOW_ALL));
window->Show();
app.Run();
return 0;
}