[dasher: 11/21] iPhone MultiTouch input
- From: Patrick Welche <pwelche src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [dasher: 11/21] iPhone MultiTouch input
- Date: Thu, 14 Jul 2011 17:32:04 +0000 (UTC)
commit 2b5a0a74369de72284f527aa20244244d0a3f60a
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date: Sat Jul 2 19:57:27 2011 +0100
iPhone MultiTouch input
EAGLView provides GetAllTouchCoordsInto:(vector<CGPoint> *),
and passes on extra touches as keyid 101, 102, ...
Added CIPhoneTwoFinger{Input,Filter}: 2 touches are min/max of target (dasherY)
range, >=3 touches to reverse.
Src/iPhone/Classes/CDasherInterfaceBridge.h | 6 +--
Src/iPhone/Classes/CDasherInterfaceBridge.mm | 31 ++++++++---
Src/iPhone/Classes/DasherAppDelegate.mm | 8 ++-
Src/iPhone/Classes/EAGLView.h | 14 +++--
Src/iPhone/Classes/EAGLView.mm | 70 ++++++++++++++++++--------
Src/iPhone/Classes/IPhoneFilters.h | 10 ++++
Src/iPhone/Classes/IPhoneFilters.mm | 32 ++++++++++++
Src/iPhone/Classes/IPhoneInputs.h | 17 +++++-
Src/iPhone/Classes/IPhoneInputs.mm | 38 ++++++++++++--
Src/iPhone/Classes/InputMethodSelector.mm | 1 +
10 files changed, 176 insertions(+), 51 deletions(-)
---
diff --git a/Src/iPhone/Classes/CDasherInterfaceBridge.h b/Src/iPhone/Classes/CDasherInterfaceBridge.h
index 852fda5..b4e5f25 100644
--- a/Src/iPhone/Classes/CDasherInterfaceBridge.h
+++ b/Src/iPhone/Classes/CDasherInterfaceBridge.h
@@ -83,11 +83,9 @@ private:
void HandleEvent(int iParameter);
DasherAppDelegate *dasherApp; // objc counterpart
-
- CIPhoneTiltFilter *m_pTiltFilter;
- CIPhoneTouchFilter *m_pTouchFilter;
-
+
CIPhoneMouseInput *m_pMouseDevice;
CIPhoneTiltInput *m_pTiltDevice;
UndoubledTouch *m_pUndoubledTouch;
+ CIPhoneTwoFingerInput *m_pTwoFingerDevice;
};
diff --git a/Src/iPhone/Classes/CDasherInterfaceBridge.mm b/Src/iPhone/Classes/CDasherInterfaceBridge.mm
index 41588c2..f8ef86a 100644
--- a/Src/iPhone/Classes/CDasherInterfaceBridge.mm
+++ b/Src/iPhone/Classes/CDasherInterfaceBridge.mm
@@ -18,11 +18,18 @@
#import "ButtonMode.h"
#import "TwoButtonDynamicFilter.h"
#import "TwoPushDynamicFilter.h"
+#import "EAGLView.h"
#import <iostream>
#import <fcntl.h>
#import <sys/stat.h>
+//declare some "friend" methods
+ interface DasherAppDelegate ()
+-(EAGLView *)glView;
+-(UIWebView *)getWebView;
+ end;
+
using namespace std;
class IPhoneGameModule : public CGameModule {
@@ -69,13 +76,13 @@ CDasherInterfaceBridge::CDasherInterfaceBridge(DasherAppDelegate *aDasherApp) :
}
void CDasherInterfaceBridge::CreateModules() {
- //create the default set...a good idea?!?!
-
- RegisterModule(m_pUndoubledTouch = new UndoubledTouch());
+ //don't create the default set...just the ones accessible from the GUI.
+ RegisterModule(m_pUndoubledTouch = new UndoubledTouch([dasherApp glView]));
RegisterModule(m_pMouseDevice =
- new CIPhoneMouseInput(this));
+ new CIPhoneMouseInput(this,[dasherApp glView]));
RegisterModule(m_pTiltDevice =
new CIPhoneTiltInput());
+ RegisterModule(m_pTwoFingerDevice=new CIPhoneTwoFingerInput([dasherApp glView]));
SetDefaultInputDevice(m_pMouseDevice);
RegisterModule(new CButtonMode(this, this, true, 9, "Menu Mode"));
@@ -83,16 +90,20 @@ void CDasherInterfaceBridge::CreateModules() {
RegisterModule(new CTwoButtonDynamicFilter(this, this));
RegisterModule(new CTwoPushDynamicFilter(this, this));
- RegisterModule(m_pTiltFilter =
- new CIPhoneTiltFilter(this, this, 16, m_pMouseDevice));
- RegisterModule(m_pTouchFilter =
- new CIPhoneTouchFilter(this, this, 17, m_pUndoubledTouch, m_pTiltDevice));
- SetDefaultInputMethod(m_pTouchFilter);
+ RegisterModule(new CIPhoneTiltFilter(this, this, 16, m_pMouseDevice));
+ //Touch filter is stylus filter with optional Tilt X....
+ CIPhoneTouchFilter *pTouchFilter =
+ new CIPhoneTouchFilter(this, this, 17, m_pUndoubledTouch, m_pTiltDevice);
+ RegisterModule(pTouchFilter);
+ SetDefaultInputMethod(pTouchFilter);
+
+ RegisterModule(new CIPhoneTwoFingerFilter(this,this,18));
}
CDasherInterfaceBridge::~CDasherInterfaceBridge() {
delete m_pMouseDevice;
delete m_pTiltDevice;
+ delete m_pTwoFingerDevice;
delete m_pUndoubledTouch;
//(ACL)registered input filters should be automatically free'd by the module mgr?
}
@@ -112,6 +123,8 @@ void CDasherInterfaceBridge::Realize() {
[dasherApp setAlphabet:GetActiveAlphabet()];
//don't call HandleEvent, would call superclass and reconstruct the NCManager!
//TODO maybe better to make ChangeAlphabet virtual and override that?
+
+ [[dasherApp glView] startAnimation];
}
void CDasherInterfaceBridge::SetupPaths() {
diff --git a/Src/iPhone/Classes/DasherAppDelegate.mm b/Src/iPhone/Classes/DasherAppDelegate.mm
index efcdcc0..ac13d69 100644
--- a/Src/iPhone/Classes/DasherAppDelegate.mm
+++ b/Src/iPhone/Classes/DasherAppDelegate.mm
@@ -88,6 +88,11 @@ static SModuleSettings _miscSettings[] = { //note iStep and string description a
@synthesize allowsRotation = m_bAllowsRotation;
@synthesize window;
+//a private method called only by CDasherInterfaceBridge
+-(EAGLView *)glView {
+ return glView;
+}
+
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
return NO;
@@ -163,7 +168,7 @@ static SModuleSettings _miscSettings[] = { //note iStep and string description a
messageLabel = [[[UITextView alloc] init] autorelease];
tools = [[UIToolbar alloc] init]; //retain a reference (until dealloc) because of rotation
glView = [[[EAGLView alloc] initWithFrame:[self doLayout:UIInterfaceOrientationPortrait] Delegate:self] autorelease];
- //that last, calls ChangeScreen on the interface, so now we can:
+ glView.multipleTouchEnabled = YES;
//start Realization i.e. training in a separate thread. (Has to be after first
// call to doLayout, or get a black band across top of screen)
@@ -243,7 +248,6 @@ static SModuleSettings _miscSettings[] = { //note iStep and string description a
//training takes too long to perform in applicationDidFinishLaunching;
// so we do it here instead (having let the main thread display a "training" message);
_dasherInterface->Realize();
- [glView startAnimation];
//the rest has to be done on the main thread to avoid problems with OpenGL contexts
// (which are local to one thread); however, we'll have the background thread wait...
[self performSelectorOnMainThread:@selector(finishStartup) withObject:nil waitUntilDone:YES];
diff --git a/Src/iPhone/Classes/EAGLView.h b/Src/iPhone/Classes/EAGLView.h
index 2047a00..68570b5 100644
--- a/Src/iPhone/Classes/EAGLView.h
+++ b/Src/iPhone/Classes/EAGLView.h
@@ -23,7 +23,6 @@ public:
CDasherScreenBridge(EAGLView *_view, Dasher::screenint iWidth, Dasher::screenint iHeight, GLshort backingWidth, GLshort backingHeight, GLfloat tc_x, GLfloat tc_y, GLuint *textures);
///Only for EAGLView to call...
void resize(Dasher::screenint iWidth, Dasher::screenint iHeight, GLshort backingWidth, GLshort backingHeight, GLfloat tc_x, GLfloat tc_y);
- bool GetTouchCoords(Dasher::screenint &iX, Dasher::screenint &iY);
void Display();
void SendMarker(int iMarker);
@@ -47,7 +46,7 @@ Note that setting the view non-opaque will only work if the EAGL surface has an
/* OpenGL names for the renderbuffer and framebuffers used to render to this view */
GLuint viewRenderbuffer, viewFramebuffer;
- BOOL animating, doneLayout, anyDown;
+ BOOL animating, doneLayout;
NSTimeInterval animationInterval;
DasherAppDelegate *dasherApp;
@@ -55,11 +54,16 @@ Note that setting the view non-opaque will only work if the EAGL surface has an
GLuint textures[2];
GLint backingWidth, backingHeight;
GLfloat texw,texh;
-
- CGPoint lastTouchCoords;
+
+ std::map<UITouch*,int> allTouches;
+ std::map<int,CGPoint> fingerPosns;
}
- property (readonly,assign) CGPoint lastTouchCoords;
+//Co-ordinates of earliest-started touch still touching
+-(CGPoint)lastTouchCoords;
+
+-(void)getAllTouchCoordsInto:(std::vector<CGPoint> *)into;
+
//OpenGL context (needed to do any OGL operation) is only current per-thread, so must call this
// if doing anything on any thread other than the main thread.
-(void)makeContextCurrent;
diff --git a/Src/iPhone/Classes/EAGLView.mm b/Src/iPhone/Classes/EAGLView.mm
index 33301dd..8c09bd9 100644
--- a/Src/iPhone/Classes/EAGLView.mm
+++ b/Src/iPhone/Classes/EAGLView.mm
@@ -31,14 +31,7 @@ CDasherScreenBridge::CDasherScreenBridge(EAGLView *_view, screenint iWidth, scre
void CDasherScreenBridge::resize(screenint iWidth, screenint iHeight, GLshort backingWidth, GLshort backingHeight, GLfloat tc_x, GLfloat tc_y) {
OpenGLScreen::resize(iWidth, iHeight, backingWidth, backingHeight, tc_x, tc_y);
}
-
-bool CDasherScreenBridge::GetTouchCoords(screenint &iX, screenint &iY) {
- CGPoint p = view.lastTouchCoords;
- if (p.x==-1) return false;
- iX=p.x; iY=p.y;
- return true;
-}
-
+
void CDasherScreenBridge::Display() {
if (!view.readyToDisplay) return; //can't display anything yet!
OpenGLScreen::Display();
@@ -71,8 +64,27 @@ CGSize CDasherScreenBridge::TextSize(NSString *str, unsigned int iFontSize, bool
: [str sizeWithFont:font];
}
+bool operator<(CGPoint p,CGPoint q) {
+ if (p.x<q.x) return true;
+ if (p.x>q.x) return false;
+ //equal x's
+ return (p.y<q.y);
+}
+
+bool operator==(CGPoint p,CGPoint q) {
+ return CGPointEqualToPoint(p, q);
+}
+
@implementation EAGLView
- synthesize lastTouchCoords;
+
+-(CGPoint)lastTouchCoords {
+ return fingerPosns.empty() ? CGPointMake(-1,-1) : fingerPosns.begin()->second;
+}
+
+-(void)getAllTouchCoordsInto:(vector<CGPoint> *)into {
+ for (map<int,CGPoint>::iterator it=fingerPosns.begin(); it!=fingerPosns.end(); it++)
+ into->push_back(it->second);
+}
// You must implement this method
+ (Class)layerClass {
@@ -118,25 +130,39 @@ CGSize CDasherScreenBridge::TextSize(NSString *str, unsigned int iFontSize, bool
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
- NSAssert([touches count] == 1, @"Multitouch?!");
- lastTouchCoords = [((UITouch *)[touches anyObject]) locationInView:self];
- NSAssert(!anyDown,@"Touches began when already in progress - multitouch enabled?!?!\n");
- anyDown = YES;
- dasherApp.dasherInterface->KeyDown(get_time(), 100, true, lastTouchCoords.x, lastTouchCoords.y);
+ const unsigned long time(get_time());
+ for (UITouch *touch in touches) {
+ const int finger(fingerPosns.empty() ? 0 : fingerPosns.rbegin()->first+1);
+ NSAssert(allTouches.find(touch)==allTouches.end(),@"Touch beginning already in progress?");
+ CGPoint p=[touch locationInView:self];
+ allTouches[touch]=finger;
+ fingerPosns[finger]=p;
+ //first finger with button id 100 = left button, then 101 and so on (element already inserted)
+ dasherApp.dasherInterface->KeyDown(time, allTouches.size()+99, true , p.x, p.y);
+ }
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
- lastTouchCoords = [[touches anyObject] locationInView:self];
+ for (UITouch *touch in touches) {
+ const int finger(allTouches[touch]);
+ map<int,CGPoint>::iterator it = fingerPosns.find(finger);
+ NSAssert(it!=fingerPosns.end(),@"Moving finger not found?");
+ it->second=[touch locationInView:self];
+ }
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
- NSAssert([touches count] == 1, @"Multitouch?!");
- NSAssert(anyDown,@"Touches ended when not in progress - multitouch enabled?!?!\n");
- lastTouchCoords = [(UITouch *)[touches anyObject] locationInView:self];
- dasherApp.dasherInterface->KeyUp(get_time(), 100, true, lastTouchCoords.x, lastTouchCoords.y);
- //finished dealing with touch-up event. Finger is now officially off the screen...
- lastTouchCoords.x = lastTouchCoords.y = -1;
- anyDown = NO;
+ const unsigned long time(get_time());
+ for (UITouch *touch in touches) {
+ map<UITouch*,int>::iterator it = allTouches.find(touch);
+ NSAssert(it != allTouches.end(), @"Release touch not in progress?");
+ map<int,CGPoint>::iterator it2=fingerPosns.find(it->second);
+ NSAssert(it2 != fingerPosns.end(), @"No coordinates for touch?");
+ CGPoint p=it2->second;
+ fingerPosns.erase(it2);
+ allTouches.erase(it);
+ dasherApp.dasherInterface->KeyUp(time, allTouches.size()+100, true, p.x, p.y);
+ }
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
diff --git a/Src/iPhone/Classes/IPhoneFilters.h b/Src/iPhone/Classes/IPhoneFilters.h
index 36e2366..4c45bcf 100644
--- a/Src/iPhone/Classes/IPhoneFilters.h
+++ b/Src/iPhone/Classes/IPhoneFilters.h
@@ -33,6 +33,8 @@ extern NSString *TOUCH_USE_TILT_X;
#define TILT_FILTER "IPhone Tilt Filter"
#define TOUCH_FILTER "IPhone Touch Filter"
+#define TWO_FINGER_FILTER "Two-finger filter"
+
class CIPhoneTiltFilter : public COneDimensionalFilter, private IPhonePrefsObserver {
public:
CIPhoneTiltFilter(CSettingsUser *pCreator, CDasherInterfaceBase *pInterface, ModuleID_t iID, CDasherInput *pTouch);
@@ -73,4 +75,12 @@ private:
bool bUseTiltX;
};
+class CIPhoneTwoFingerFilter : public CDefaultFilter {
+public:
+ CIPhoneTwoFingerFilter(CSettingsUser *pCreator, CDasherInterfaceBase *pInterface, ModuleID_t iID);
+ virtual void KeyUp(int iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel);
+ virtual void KeyDown(int iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel, CUserLogBase *pUserLog);
+ virtual bool DecorateView(CDasherView *pView, CDasherInput *pInput);
+};
+
/// @}
\ No newline at end of file
diff --git a/Src/iPhone/Classes/IPhoneFilters.mm b/Src/iPhone/Classes/IPhoneFilters.mm
index 3f3a6dd..a453e76 100644
--- a/Src/iPhone/Classes/IPhoneFilters.mm
+++ b/Src/iPhone/Classes/IPhoneFilters.mm
@@ -171,4 +171,36 @@ void CIPhoneTouchFilter::ApplyTransform(myint &iDasherX, myint &iDasherY, CDashe
m_pTilt->GetDasherCoords(iDasherX,temp,pView);
}
CStylusFilter::ApplyTransform(iDasherX, iDasherY, pView);
+}
+
+CIPhoneTwoFingerFilter::CIPhoneTwoFingerFilter(CSettingsUser *pCreator, CDasherInterfaceBase *pInterface, ModuleID_t iID)
+: CDefaultFilter(pCreator, pInterface, iID, TWO_FINGER_FILTER) {
+}
+
+void CIPhoneTwoFingerFilter::KeyDown(int iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel, CUserLogBase *pUserLog) {
+ if (iId==101) m_pInterface->Unpause(iTime);
+ else if (iId!=100) CDefaultFilter::KeyDown(iTime, iId, pView, pInput, pModel, pUserLog);
+}
+
+void CIPhoneTwoFingerFilter::KeyUp(int iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel) {
+ if (iId==101) m_pInterface->Stop();
+ else if (iId!=100) CDefaultFilter::KeyUp(iTime, iId, pView, pInput, pModel);
+}
+
+bool CIPhoneTwoFingerFilter::DecorateView(CDasherView *pView, CDasherInput *pInput) {
+ if (GetBoolParameter(BP_DRAW_MOUSE_LINE)) {
+ myint x[2], y[2];
+ x[0] = m_iLastX; x[1] = 0;
+ y[0] = m_iLastY;
+ for (y[1] = m_iLastY - m_iLastX; ;) {
+ if (GetBoolParameter(BP_CURVE_MOUSE_LINE))
+ pView->DasherSpaceLine(x[0], y[0], x[1], y[1], GetLongParameter(LP_LINE_WIDTH), 1);
+ else
+ pView->DasherPolyline(x, y, 2, GetLongParameter(LP_LINE_WIDTH), 1);
+ if (y[1] == m_iLastY + m_iLastX) break;
+ y[1] = m_iLastY + m_iLastX;
+ }
+ return true;
+ }
+ return false;
}
\ No newline at end of file
diff --git a/Src/iPhone/Classes/IPhoneInputs.h b/Src/iPhone/Classes/IPhoneInputs.h
index 178dd99..d1aacfd 100644
--- a/Src/iPhone/Classes/IPhoneInputs.h
+++ b/Src/iPhone/Classes/IPhoneInputs.h
@@ -17,7 +17,9 @@
#define UNDOUBLED_TOUCH "Undoubled Touch"
#define TOUCH_INPUT "Mouse Input"
#define TILT_INPUT "Tilt Input"
+#define TWO_FINGER_INPUT "Two-finger (multitouch) input"
+ class EAGLView;
namespace Dasher {
class CIPhoneTiltInput : public CScreenCoordInput {
@@ -51,17 +53,26 @@ private:
class UndoubledTouch : public CScreenCoordInput {
public:
- UndoubledTouch();
+ UndoubledTouch(EAGLView *pView);
bool GetScreenCoords(screenint &iX, screenint &iY, CDasherView *pView);
protected:
- UndoubledTouch(ModuleID_t iId, const char *szName);
+ UndoubledTouch(ModuleID_t iId, const char *szName, EAGLView *pView);
+ EAGLView * const m_pView;
};
class CIPhoneMouseInput : public UndoubledTouch, protected CSettingsUser {
public:
- CIPhoneMouseInput(CSettingsUser *pCreator);
+ CIPhoneMouseInput(CSettingsUser *pCreator, EAGLView *pView);
bool GetScreenCoords(screenint &iX, screenint &iY, CDasherView *pView);
};
+
+ class CIPhoneTwoFingerInput : public CDasherCoordInput {
+ public:
+ CIPhoneTwoFingerInput(EAGLView *pView);
+ bool GetDasherCoords(myint &iX, myint &iY, CDasherView *pView);
+ protected:
+ EAGLView * const m_pGlView;
+ };
}
diff --git a/Src/iPhone/Classes/IPhoneInputs.mm b/Src/iPhone/Classes/IPhoneInputs.mm
index 8e1d183..a97166f 100644
--- a/Src/iPhone/Classes/IPhoneInputs.mm
+++ b/Src/iPhone/Classes/IPhoneInputs.mm
@@ -100,20 +100,22 @@ bool CIPhoneTiltInput::GetScreenCoords(screenint &iX, screenint &iY, CDasherView
return true;
}
-UndoubledTouch::UndoubledTouch() : CScreenCoordInput(7, UNDOUBLED_TOUCH) {
+UndoubledTouch::UndoubledTouch(EAGLView *pView) : CScreenCoordInput(7, UNDOUBLED_TOUCH), m_pView(pView) {
}
-UndoubledTouch::UndoubledTouch(ModuleID_t iId, const char *szName) : CScreenCoordInput(iId, szName) {
+UndoubledTouch::UndoubledTouch(ModuleID_t iId, const char *szName, EAGLView *pView) : CScreenCoordInput(iId, szName), m_pView(pView) {
}
bool UndoubledTouch::GetScreenCoords(screenint &iX, screenint &iY, CDasherView *pView) {
- CDasherScreenBridge *sb(static_cast<CDasherScreenBridge *>(pView->Screen()));
- return sb->GetTouchCoords(iX, iY);
+ CGPoint p = [m_pView lastTouchCoords];
+ if (p.x==-1) return false;
+ iX = p.x; iY=p.y;
+ return true;
}
-CIPhoneMouseInput::CIPhoneMouseInput(CSettingsUser *pCreator)
- : UndoubledTouch(9, TOUCH_INPUT), CSettingsUser(pCreator) {
+CIPhoneMouseInput::CIPhoneMouseInput(CSettingsUser *pCreator, EAGLView *pView)
+ : UndoubledTouch(9, TOUCH_INPUT, pView), CSettingsUser(pCreator) {
};
bool CIPhoneMouseInput::GetScreenCoords(screenint &iX, screenint &iY, CDasherView *pView) {
@@ -138,3 +140,27 @@ bool CIPhoneMouseInput::GetScreenCoords(screenint &iX, screenint &iY, CDasherVie
}
return true;
}
+
+CIPhoneTwoFingerInput::CIPhoneTwoFingerInput(EAGLView *pView) : CDasherCoordInput(10, TWO_FINGER_INPUT), m_pGlView(pView) {
+}
+
+bool CIPhoneTwoFingerInput::GetDasherCoords(myint &iX, myint &iY, CDasherView *pView) {
+ vector<CGPoint> pts;
+ [m_pGlView getAllTouchCoordsInto:&pts];
+ if (pts.size()<2) return false;
+ myint x1,y1,x2,y2;
+
+ //target Y is midpoint of Y coordinates of first two touches
+ pView->Screen2Dasher(pts[0].x, pts[0].y, x1, y1);
+ pView->Screen2Dasher(pts[1].x, pts[1].y, x2, y2);
+ iY = (y1+y2)/2;
+ //target X is half the distance between them (-> top/bottom of target range = top/bottom of first two touches)
+ iX = abs(y1-y2)/2;
+
+ if (pts.size()>2) {
+ //three or more fingers, go backwards...
+ pView->VisibleRegion(x1, y1, x2, y2); //x2 is now maxX
+ iX = (x2*iX)/2048; //(first two) fingers wider apart, will go backwards faster.
+ }
+ return true;
+}
diff --git a/Src/iPhone/Classes/InputMethodSelector.mm b/Src/iPhone/Classes/InputMethodSelector.mm
index b1c3663..302cb2f 100644
--- a/Src/iPhone/Classes/InputMethodSelector.mm
+++ b/Src/iPhone/Classes/InputMethodSelector.mm
@@ -31,6 +31,7 @@ NSString *calibBtn=@"Calibrate...";//pointer equality used to mark cell for spec
SFilterDesc asNormalFilters[] = {
{@"Touch Control", @"Tap or Hold", TOUCH_INPUT, TOUCH_FILTER, touchSettings},
+ {@"Multitouch",@"Two fingers, further apart = slower", TWO_FINGER_INPUT, TWO_FINGER_FILTER, NULL},
{@"Tilt Control", calibBtn, TILT_INPUT, TILT_FILTER, tiltSettings},
};
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]