src/video/uikit/SDL_uikitview.m
author Ethan Lee <flibitijibibo@flibitijibibo.com>
Wed, 17 Jul 2019 23:20:57 -0400
changeset 12950 05dddfb66b85
parent 12866 286fefb0209c
permissions -rw-r--r--
Copypaste SDL_NSLog to UIKit backend, document it as such
     1  /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 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 directTouchId;
    43     SDL_TouchID indirectTouchId;
    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         directTouchId = 1;
    72         indirectTouchId = 2;
    73 
    74 #if !TARGET_OS_TV
    75         self.multipleTouchEnabled = YES;
    76         SDL_AddTouch(directTouchId, SDL_TOUCH_DEVICE_DIRECT, "");
    77 #endif
    78     }
    79 
    80     return self;
    81 }
    82 
    83 - (void)setSDLWindow:(SDL_Window *)window
    84 {
    85     SDL_WindowData *data = nil;
    86 
    87     if (window == sdlwindow) {
    88         return;
    89     }
    90 
    91     /* Remove ourself from the old window. */
    92     if (sdlwindow) {
    93         SDL_uikitview *view = nil;
    94         data = (__bridge SDL_WindowData *) sdlwindow->driverdata;
    95 
    96         [data.views removeObject:self];
    97 
    98         [self removeFromSuperview];
    99 
   100         /* Restore the next-oldest view in the old window. */
   101         view = data.views.lastObject;
   102 
   103         data.viewcontroller.view = view;
   104 
   105         data.uiwindow.rootViewController = nil;
   106         data.uiwindow.rootViewController = data.viewcontroller;
   107 
   108         [data.uiwindow layoutIfNeeded];
   109     }
   110 
   111     /* Add ourself to the new window. */
   112     if (window) {
   113         data = (__bridge SDL_WindowData *) window->driverdata;
   114 
   115         /* Make sure the SDL window has a strong reference to this view. */
   116         [data.views addObject:self];
   117 
   118         /* Replace the view controller's old view with this one. */
   119         [data.viewcontroller.view removeFromSuperview];
   120         data.viewcontroller.view = self;
   121 
   122         /* The root view controller handles rotation and the status bar.
   123          * Assigning it also adds the controller's view to the window. We
   124          * explicitly re-set it to make sure the view is properly attached to
   125          * the window. Just adding the sub-view if the root view controller is
   126          * already correct causes orientation issues on iOS 7 and below. */
   127         data.uiwindow.rootViewController = nil;
   128         data.uiwindow.rootViewController = data.viewcontroller;
   129 
   130         /* The view's bounds may not be correct until the next event cycle. That
   131          * might happen after the current dimensions are queried, so we force a
   132          * layout now to immediately update the bounds. */
   133         [data.uiwindow layoutIfNeeded];
   134     }
   135 
   136     sdlwindow = window;
   137 }
   138 
   139 - (SDL_TouchDeviceType)touchTypeForTouch:(UITouch *)touch
   140 {
   141 #ifdef __IPHONE_9_0
   142     if ([touch respondsToSelector:@selector((type))]) {
   143         if (touch.type == UITouchTypeIndirect) {
   144             return SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
   145         }
   146     }
   147 #endif
   148 
   149     return SDL_TOUCH_DEVICE_DIRECT;
   150 }
   151 
   152 - (SDL_TouchID)touchIdForType:(SDL_TouchDeviceType)type
   153 {
   154     switch (type) {
   155         case SDL_TOUCH_DEVICE_DIRECT:
   156         default:
   157             return directTouchId;
   158         case SDL_TOUCH_DEVICE_INDIRECT_RELATIVE:
   159             return indirectTouchId;
   160     }
   161 }
   162 
   163 - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
   164 {
   165     CGPoint point = [touch locationInView:self];
   166 
   167     if (normalize) {
   168         CGRect bounds = self.bounds;
   169         point.x /= bounds.size.width;
   170         point.y /= bounds.size.height;
   171     }
   172 
   173     return point;
   174 }
   175 
   176 - (float)pressureForTouch:(UITouch *)touch
   177 {
   178 #ifdef __IPHONE_9_0
   179     if ([touch respondsToSelector:@selector(force)]) {
   180         return (float) touch.force;
   181     }
   182 #endif
   183 
   184     return 1.0f;
   185 }
   186 
   187 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
   188 {
   189     for (UITouch *touch in touches) {
   190         SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
   191         SDL_TouchID touchId = [self touchIdForType:touchType];
   192         float pressure = [self pressureForTouch:touch];
   193 
   194         if (SDL_AddTouch(touchId, touchType, "") < 0) {
   195             continue;
   196         }
   197 
   198         /* FIXME, need to send: int clicks = (int) touch.tapCount; ? */
   199 
   200         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   201         SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
   202                       SDL_TRUE, locationInView.x, locationInView.y, pressure);
   203     }
   204 }
   205 
   206 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
   207 {
   208     for (UITouch *touch in touches) {
   209         SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
   210         SDL_TouchID touchId = [self touchIdForType:touchType];
   211         float pressure = [self pressureForTouch:touch];
   212 
   213         if (SDL_AddTouch(touchId, touchType, "") < 0) {
   214             continue;
   215         }
   216 
   217         /* FIXME, need to send: int clicks = (int) touch.tapCount; ? */
   218 
   219         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   220         SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
   221                       SDL_FALSE, locationInView.x, locationInView.y, pressure);
   222     }
   223 }
   224 
   225 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
   226 {
   227     [self touchesEnded:touches withEvent:event];
   228 }
   229 
   230 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
   231 {
   232     for (UITouch *touch in touches) {
   233         SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
   234         SDL_TouchID touchId = [self touchIdForType:touchType];
   235         float pressure = [self pressureForTouch:touch];
   236 
   237         if (SDL_AddTouch(touchId, touchType, "") < 0) {
   238             continue;
   239         }
   240 
   241         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   242         SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
   243                             locationInView.x, locationInView.y, pressure);
   244     }
   245 }
   246 
   247 #if TARGET_OS_TV || defined(__IPHONE_9_1)
   248 - (SDL_Scancode)scancodeFromPressType:(UIPressType)presstype
   249 {
   250     switch (presstype) {
   251     case UIPressTypeUpArrow:
   252         return SDL_SCANCODE_UP;
   253     case UIPressTypeDownArrow:
   254         return SDL_SCANCODE_DOWN;
   255     case UIPressTypeLeftArrow:
   256         return SDL_SCANCODE_LEFT;
   257     case UIPressTypeRightArrow:
   258         return SDL_SCANCODE_RIGHT;
   259     case UIPressTypeSelect:
   260         /* HIG says: "primary button behavior" */
   261         return SDL_SCANCODE_RETURN;
   262     case UIPressTypeMenu:
   263         /* HIG says: "returns to previous screen" */
   264         return SDL_SCANCODE_ESCAPE;
   265     case UIPressTypePlayPause:
   266         /* HIG says: "secondary button behavior" */
   267         return SDL_SCANCODE_PAUSE;
   268     default:
   269         return SDL_SCANCODE_UNKNOWN;
   270     }
   271 }
   272 
   273 - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   274 {
   275     if (!SDL_AppleTVRemoteOpenedAsJoystick) {
   276         for (UIPress *press in presses) {
   277             SDL_Scancode scancode = [self scancodeFromPressType:press.type];
   278             SDL_SendKeyboardKey(SDL_PRESSED, scancode);
   279         }
   280     }
   281     [super pressesBegan:presses withEvent:event];
   282 }
   283 
   284 - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   285 {
   286     if (!SDL_AppleTVRemoteOpenedAsJoystick) {
   287         for (UIPress *press in presses) {
   288             SDL_Scancode scancode = [self scancodeFromPressType:press.type];
   289             SDL_SendKeyboardKey(SDL_RELEASED, scancode);
   290         }
   291     }
   292     [super pressesEnded:presses withEvent:event];
   293 }
   294 
   295 - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   296 {
   297     if (!SDL_AppleTVRemoteOpenedAsJoystick) {
   298         for (UIPress *press in presses) {
   299             SDL_Scancode scancode = [self scancodeFromPressType:press.type];
   300             SDL_SendKeyboardKey(SDL_RELEASED, scancode);
   301         }
   302     }
   303     [super pressesCancelled:presses withEvent:event];
   304 }
   305 
   306 - (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   307 {
   308     /* This is only called when the force of a press changes. */
   309     [super pressesChanged:presses withEvent:event];
   310 }
   311 #endif /* TARGET_OS_TV || defined(__IPHONE_9_1) */
   312 
   313 #if TARGET_OS_TV
   314 -(void)swipeGesture:(UISwipeGestureRecognizer *)gesture
   315 {
   316     /* Swipe gestures don't trigger begin states. */
   317     if (gesture.state == UIGestureRecognizerStateEnded) {
   318         if (!SDL_AppleTVRemoteOpenedAsJoystick) {
   319             /* Send arrow key presses for now, as we don't have an external API
   320              * which better maps to swipe gestures. */
   321             switch (gesture.direction) {
   322             case UISwipeGestureRecognizerDirectionUp:
   323                 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_UP);
   324                 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_UP);
   325                 break;
   326             case UISwipeGestureRecognizerDirectionDown:
   327                 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_DOWN);
   328                 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_DOWN);
   329                 break;
   330             case UISwipeGestureRecognizerDirectionLeft:
   331                 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LEFT);
   332                 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LEFT);
   333                 break;
   334             case UISwipeGestureRecognizerDirectionRight:
   335                 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RIGHT);
   336                 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RIGHT);
   337                 break;
   338             }
   339         }
   340     }
   341 }
   342 #endif /* TARGET_OS_TV */
   343 
   344 @end
   345 
   346 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   347 
   348 /* vi: set ts=4 sw=4 expandtab: */