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