diff --git a/Limelight/Input/AbsoluteTouchHandler.m b/Limelight/Input/AbsoluteTouchHandler.m index a2735a8..a2e8f01 100644 --- a/Limelight/Input/AbsoluteTouchHandler.m +++ b/Limelight/Input/AbsoluteTouchHandler.m @@ -8,8 +8,28 @@ #import "AbsoluteTouchHandler.h" +#include + +// How long the fingers must be stationary to start a right click +#define LONG_PRESS_ACTIVATION_DELAY 0.650f + +// How far the finger can move before it cancels a right click +#define LONG_PRESS_ACTIVATION_DELTA 5 + +// How long the double tap deadzone stays in effect between touch up and touch down +#define DOUBLE_TAP_DEAD_ZONE_DELAY 0.250f + +// How far the finger can move before it can override the double tap deadzone +#define DOUBLE_TAP_DEAD_ZONE_DELTA 10 + @implementation AbsoluteTouchHandler { StreamView* view; + + NSTimer* longPressTimer; + UITouch* lastTouchDown; + CGPoint lastTouchDownLocation; + UITouch* lastTouchUp; + CGPoint lastTouchUpLocation; } - (id)initWithView:(StreamView*)view { @@ -18,16 +38,83 @@ return self; } +- (void)onLongPressStart:(NSTimer*)timer { + // Raise the left click and start a right click + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); + LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_RIGHT); +} + - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + // Ignore touch down events with more than one finger + if ([[event allTouches] count] > 1) { + return; + } + + UITouch* touch = [touches anyObject]; + CGPoint touchLocation = [touch locationInView:view]; + + // Don't reposition for finger down events within the deadzone. This makes double-clicking easier. + if (touch.timestamp - lastTouchUp.timestamp > DOUBLE_TAP_DEAD_ZONE_DELAY || + sqrt(pow(touchLocation.x - lastTouchUpLocation.x, 2) + + pow(touchLocation.y - lastTouchUpLocation.y, 2)) > DOUBLE_TAP_DEAD_ZONE_DELTA) { + [view updateCursorLocation:touchLocation]; + } + + // Press the left button down + LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT); + + // Start the long press timer + longPressTimer = [NSTimer scheduledTimerWithTimeInterval:LONG_PRESS_ACTIVATION_DELAY + target:self + selector:@selector(onLongPressStart:) + userInfo:nil + repeats:NO]; + + lastTouchDown = touch; + lastTouchDownLocation = touchLocation; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + // Ignore touch move events with more than one finger + if ([[event allTouches] count] > 1) { + return; + } + + UITouch* touch = [touches anyObject]; + CGPoint touchLocation = [touch locationInView:view]; + + if (sqrt(pow(touchLocation.x - lastTouchDownLocation.x, 2) + + pow(touchLocation.y - lastTouchDownLocation.y, 2)) > LONG_PRESS_ACTIVATION_DELTA) { + // Moved too far since touch down. Cancel the long press timer. + [longPressTimer invalidate]; + longPressTimer = nil; + } + + [view updateCursorLocation:[[touches anyObject] locationInView:view]]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + // Only fire this logic if all touches have ended + if ([[event allTouches] count] == [touches count]) { + // Cancel the long press timer + [longPressTimer invalidate]; + longPressTimer = nil; + + // Left button up on finger up + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); + + // Raise right button too in case we triggered a long press gesture + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT); + + // Remember this last touch for touch-down deadzoning + lastTouchUp = [touches anyObject]; + lastTouchUpLocation = [lastTouchUp locationInView:view]; + } } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + // Treat this as a normal touchesEnded event + [self touchesEnded:touches withEvent:event]; } @end