src/video/uikit/SDL_uikitview.m
author Alex Szpakowski <slime73@gmail.com>
Sun, 18 Dec 2016 13:05:14 -0400
changeset 10710 a6babd973955
parent 10366 e1b52e69578b
child 10737 3406a0f8b041
permissions -rw-r--r--
tvOS: Expose remote swipe gestures as arrow key presses (thanks oviano!)
     1  /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_DRIVER_UIKIT
    24 
    25 #include "SDL_uikitview.h"
    26 
    27 #include "../../events/SDL_mouse_c.h"
    28 #include "../../events/SDL_touch_c.h"
    29 #include "../../events/SDL_events_c.h"
    30 
    31 #import "SDL_uikitappdelegate.h"
    32 #import "SDL_uikitmodes.h"
    33 #import "SDL_uikitwindow.h"
    34 
    35 @implementation SDL_uikitview {
    36     SDL_Window *sdlwindow;
    37 
    38     SDL_TouchID touchId;
    39     UITouch * __weak firstFingerDown;
    40 }
    41 
    42 - (instancetype)initWithFrame:(CGRect)frame
    43 {
    44     if ((self = [super initWithFrame:frame])) {
    45         /* Apple TV Remote touchpad swipe gestures. */
    46 #if TARGET_OS_TV
    47         UISwipeGestureRecognizer *swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
    48         swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
    49         [self addGestureRecognizer:swipeUp];
    50 
    51         UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
    52         swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
    53         [self addGestureRecognizer:swipeDown];
    54 
    55         UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
    56         swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
    57         [self addGestureRecognizer:swipeLeft];
    58 
    59         UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
    60         swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
    61         [self addGestureRecognizer:swipeRight];
    62 #endif
    63 
    64         self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    65         self.autoresizesSubviews = YES;
    66 
    67 #if !TARGET_OS_TV
    68         self.multipleTouchEnabled = YES;
    69 #endif
    70 
    71         touchId = 1;
    72         SDL_AddTouch(touchId, "");
    73     }
    74 
    75     return self;
    76 }
    77 
    78 - (void)setSDLWindow:(SDL_Window *)window
    79 {
    80     SDL_WindowData *data = nil;
    81 
    82     if (window == sdlwindow) {
    83         return;
    84     }
    85 
    86     /* Remove ourself from the old window. */
    87     if (sdlwindow) {
    88         SDL_uikitview *view = nil;
    89         data = (__bridge SDL_WindowData *) sdlwindow->driverdata;
    90 
    91         [data.views removeObject:self];
    92 
    93         [self removeFromSuperview];
    94 
    95         /* Restore the next-oldest view in the old window. */
    96         view = data.views.lastObject;
    97 
    98         data.viewcontroller.view = view;
    99 
   100         data.uiwindow.rootViewController = nil;
   101         data.uiwindow.rootViewController = data.viewcontroller;
   102 
   103         [data.uiwindow layoutIfNeeded];
   104     }
   105 
   106     /* Add ourself to the new window. */
   107     if (window) {
   108         data = (__bridge SDL_WindowData *) window->driverdata;
   109 
   110         /* Make sure the SDL window has a strong reference to this view. */
   111         [data.views addObject:self];
   112 
   113         /* Replace the view controller's old view with this one. */
   114         [data.viewcontroller.view removeFromSuperview];
   115         data.viewcontroller.view = self;
   116 
   117         /* The root view controller handles rotation and the status bar.
   118          * Assigning it also adds the controller's view to the window. We
   119          * explicitly re-set it to make sure the view is properly attached to
   120          * the window. Just adding the sub-view if the root view controller is
   121          * already correct causes orientation issues on iOS 7 and below. */
   122         data.uiwindow.rootViewController = nil;
   123         data.uiwindow.rootViewController = data.viewcontroller;
   124 
   125         /* The view's bounds may not be correct until the next event cycle. That
   126          * might happen after the current dimensions are queried, so we force a
   127          * layout now to immediately update the bounds. */
   128         [data.uiwindow layoutIfNeeded];
   129     }
   130 
   131     sdlwindow = window;
   132 }
   133 
   134 - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
   135 {
   136     CGPoint point = [touch locationInView:self];
   137 
   138     if (normalize) {
   139         CGRect bounds = self.bounds;
   140         point.x /= bounds.size.width;
   141         point.y /= bounds.size.height;
   142     }
   143 
   144     return point;
   145 }
   146 
   147 - (float)pressureForTouch:(UITouch *)touch
   148 {
   149 #ifdef __IPHONE_9_0
   150     if ([touch respondsToSelector:@selector(force)]) {
   151         return (float) touch.force;
   152     }
   153 #endif
   154 
   155     return 1.0f;
   156 }
   157 
   158 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
   159 {
   160     for (UITouch *touch in touches) {
   161         float pressure = [self pressureForTouch:touch];
   162 
   163         if (!firstFingerDown) {
   164             CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
   165             int clicks = (int) touch.tapCount;
   166 
   167             /* send mouse moved event */
   168             SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
   169 
   170             /* send mouse down event */
   171             SDL_SendMouseButtonClicks(sdlwindow, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT, clicks);
   172 
   173             firstFingerDown = touch;
   174         }
   175 
   176         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   177         SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
   178                       SDL_TRUE, locationInView.x, locationInView.y, pressure);
   179     }
   180 }
   181 
   182 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
   183 {
   184     for (UITouch *touch in touches) {
   185         float pressure = [self pressureForTouch:touch];
   186 
   187         if (touch == firstFingerDown) {
   188             /* send mouse up */
   189             int clicks = (int) touch.tapCount;
   190             SDL_SendMouseButtonClicks(sdlwindow, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT, clicks);
   191             firstFingerDown = nil;
   192         }
   193 
   194         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   195         SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
   196                       SDL_FALSE, locationInView.x, locationInView.y, pressure);
   197     }
   198 }
   199 
   200 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
   201 {
   202     [self touchesEnded:touches withEvent:event];
   203 }
   204 
   205 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
   206 {
   207     for (UITouch *touch in touches) {
   208         float pressure = [self pressureForTouch:touch];
   209 
   210         if (touch == firstFingerDown) {
   211             CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
   212 
   213             /* send moved event */
   214             SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
   215         }
   216 
   217         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   218         SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
   219                             locationInView.x, locationInView.y, pressure);
   220     }
   221 }
   222 
   223 #if TARGET_OS_TV || defined(__IPHONE_9_1)
   224 - (SDL_Scancode)scancodeFromPressType:(UIPressType)presstype
   225 {
   226     switch (presstype) {
   227     case UIPressTypeUpArrow:
   228         return SDL_SCANCODE_UP;
   229     case UIPressTypeDownArrow:
   230         return SDL_SCANCODE_DOWN;
   231     case UIPressTypeLeftArrow:
   232         return SDL_SCANCODE_LEFT;
   233     case UIPressTypeRightArrow:
   234         return SDL_SCANCODE_RIGHT;
   235     case UIPressTypeSelect:
   236         /* HIG says: "primary button behavior" */
   237         return SDL_SCANCODE_SELECT;
   238     case UIPressTypeMenu:
   239         /* HIG says: "returns to previous screen" */
   240         return SDL_SCANCODE_MENU;
   241     case UIPressTypePlayPause:
   242         /* HIG says: "secondary button behavior" */
   243         return SDL_SCANCODE_PAUSE;
   244     default:
   245         return SDL_SCANCODE_UNKNOWN;
   246     }
   247 }
   248 
   249 - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   250 {
   251     for (UIPress *press in presses) {
   252         SDL_Scancode scancode = [self scancodeFromPressType:press.type];
   253         SDL_SendKeyboardKey(SDL_PRESSED, scancode);
   254     }
   255 
   256     [super pressesBegan:presses withEvent:event];
   257 }
   258 
   259 - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   260 {
   261     for (UIPress *press in presses) {
   262         SDL_Scancode scancode = [self scancodeFromPressType:press.type];
   263         SDL_SendKeyboardKey(SDL_RELEASED, scancode);
   264     }
   265 
   266     [super pressesEnded:presses withEvent:event];
   267 }
   268 
   269 - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   270 {
   271     for (UIPress *press in presses) {
   272         SDL_Scancode scancode = [self scancodeFromPressType:press.type];
   273         SDL_SendKeyboardKey(SDL_RELEASED, scancode);
   274     }
   275 
   276     [super pressesCancelled:presses withEvent:event];
   277 }
   278 
   279 - (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   280 {
   281     /* This is only called when the force of a press changes. */
   282     [super pressesChanged:presses withEvent:event];
   283 }
   284 #endif /* TARGET_OS_TV || defined(__IPHONE_9_1) */
   285 
   286 #if TARGET_OS_TV
   287 -(void)swipeGesture:(UISwipeGestureRecognizer *)gesture
   288 {
   289     /* Swipe gestures don't trigger begin states. */
   290     if (gesture.state == UIGestureRecognizerStateEnded) {
   291         /* Send arrow key presses for now, as we don't have an external API
   292          * which better maps to swipe gestures. */
   293         switch (gesture.direction) {
   294         case UISwipeGestureRecognizerDirectionUp:
   295             SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_UP);
   296             SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_UP);
   297             break;
   298         case UISwipeGestureRecognizerDirectionDown:
   299             SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_DOWN);
   300             SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_DOWN);
   301             break;
   302         case UISwipeGestureRecognizerDirectionLeft:
   303             SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LEFT);
   304             SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LEFT);
   305             break;
   306         case UISwipeGestureRecognizerDirectionRight:
   307             SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RIGHT);
   308             SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RIGHT);
   309             break;
   310         }
   311     }
   312 }
   313 #endif /* TARGET_OS_TV */
   314 
   315 @end
   316 
   317 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   318 
   319 /* vi: set ts=4 sw=4 expandtab: */