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