src/video/uikit/SDL_uikitview.m
author Manuel Alfayate Corchete <redwindwanderer@gmail.com>
Fri, 07 Aug 2020 11:53:04 +0200
changeset 13979 f49ae406f093
parent 13899 4ecca6f93aa5
permissions -rw-r--r--
kmsdrm: wait for possible pending atomic commits before destroying surfaces, and before restoring video on quit. Move messages to the SDL_Log* functions.
     1  /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2020 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 /* The maximum number of mouse buttons we support */
    37 #define MAX_MOUSE_BUTTONS    5
    38 
    39 /* This is defined in SDL_sysjoystick.m */
    40 #if !SDL_JOYSTICK_DISABLED
    41 extern int SDL_AppleTVRemoteOpenedAsJoystick;
    42 #endif
    43 
    44 @implementation SDL_uikitview {
    45     SDL_Window *sdlwindow;
    46 
    47     SDL_TouchID directTouchId;
    48     SDL_TouchID indirectTouchId;
    49 }
    50 
    51 - (instancetype)initWithFrame:(CGRect)frame
    52 {
    53     if ((self = [super initWithFrame:frame])) {
    54 #if TARGET_OS_TV
    55         /* Apple TV Remote touchpad swipe gestures. */
    56         UISwipeGestureRecognizer *swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
    57         swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
    58         [self addGestureRecognizer:swipeUp];
    59 
    60         UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
    61         swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
    62         [self addGestureRecognizer:swipeDown];
    63 
    64         UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
    65         swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
    66         [self addGestureRecognizer:swipeLeft];
    67 
    68         UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
    69         swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
    70         [self addGestureRecognizer:swipeRight];
    71 #else
    72         if (@available(iOS 13.4, *)) {
    73             UIPanGestureRecognizer *mouseWheelRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(mouseWheelGesture:)];
    74             mouseWheelRecognizer.allowedScrollTypesMask = UIScrollTypeMaskDiscrete;
    75             mouseWheelRecognizer.allowedTouchTypes = @[ @(UITouchTypeIndirectPointer) ];
    76             mouseWheelRecognizer.cancelsTouchesInView = NO;
    77             mouseWheelRecognizer.delaysTouchesBegan = NO;
    78             mouseWheelRecognizer.delaysTouchesEnded = NO;
    79             [self addGestureRecognizer:mouseWheelRecognizer];
    80         }
    81 #endif
    82 
    83         self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    84         self.autoresizesSubviews = YES;
    85 
    86         directTouchId = 1;
    87         indirectTouchId = 2;
    88 
    89 #if !TARGET_OS_TV
    90         self.multipleTouchEnabled = YES;
    91         SDL_AddTouch(directTouchId, SDL_TOUCH_DEVICE_DIRECT, "");
    92 #endif
    93 
    94 #if !TARGET_OS_TV && defined(__IPHONE_13_4)
    95         if (@available(iOS 13.4, *)) {
    96             [self addInteraction:[[UIPointerInteraction alloc] initWithDelegate:self]];
    97         }
    98 #endif
    99     }
   100 
   101     return self;
   102 }
   103 
   104 - (void)setSDLWindow:(SDL_Window *)window
   105 {
   106     SDL_WindowData *data = nil;
   107 
   108     if (window == sdlwindow) {
   109         return;
   110     }
   111 
   112     /* Remove ourself from the old window. */
   113     if (sdlwindow) {
   114         SDL_uikitview *view = nil;
   115         data = (__bridge SDL_WindowData *) sdlwindow->driverdata;
   116 
   117         [data.views removeObject:self];
   118 
   119         [self removeFromSuperview];
   120 
   121         /* Restore the next-oldest view in the old window. */
   122         view = data.views.lastObject;
   123 
   124         data.viewcontroller.view = view;
   125 
   126         data.uiwindow.rootViewController = nil;
   127         data.uiwindow.rootViewController = data.viewcontroller;
   128 
   129         [data.uiwindow layoutIfNeeded];
   130     }
   131 
   132     /* Add ourself to the new window. */
   133     if (window) {
   134         data = (__bridge SDL_WindowData *) window->driverdata;
   135 
   136         /* Make sure the SDL window has a strong reference to this view. */
   137         [data.views addObject:self];
   138 
   139         /* Replace the view controller's old view with this one. */
   140         [data.viewcontroller.view removeFromSuperview];
   141         data.viewcontroller.view = self;
   142 
   143         /* The root view controller handles rotation and the status bar.
   144          * Assigning it also adds the controller's view to the window. We
   145          * explicitly re-set it to make sure the view is properly attached to
   146          * the window. Just adding the sub-view if the root view controller is
   147          * already correct causes orientation issues on iOS 7 and below. */
   148         data.uiwindow.rootViewController = nil;
   149         data.uiwindow.rootViewController = data.viewcontroller;
   150 
   151         /* The view's bounds may not be correct until the next event cycle. That
   152          * might happen after the current dimensions are queried, so we force a
   153          * layout now to immediately update the bounds. */
   154         [data.uiwindow layoutIfNeeded];
   155     }
   156 
   157     sdlwindow = window;
   158 }
   159 
   160 #if !TARGET_OS_TV && defined(__IPHONE_13_4)
   161 - (UIPointerRegion *)pointerInteraction:(UIPointerInteraction *)interaction regionForRequest:(UIPointerRegionRequest *)request defaultRegion:(UIPointerRegion *)defaultRegion API_AVAILABLE(ios(13.4)){
   162     if (request != nil) {
   163         CGPoint origin = self.bounds.origin;
   164         CGPoint point = request.location;
   165 
   166         point.x -= origin.x;
   167         point.y -= origin.y;
   168 
   169         SDL_SendMouseMotion(sdlwindow, 0, 0, (int)point.x, (int)point.y);
   170     }
   171     return [UIPointerRegion regionWithRect:self.bounds identifier:nil];
   172 }
   173 
   174 - (UIPointerStyle *)pointerInteraction:(UIPointerInteraction *)interaction styleForRegion:(UIPointerRegion *)region  API_AVAILABLE(ios(13.4)){
   175     if (SDL_ShowCursor(-1)) {
   176         return nil;
   177     } else {
   178         return [UIPointerStyle hiddenPointerStyle];
   179     }
   180 }
   181 #endif /* !TARGET_OS_TV && __IPHONE_13_4 */
   182 
   183 - (SDL_TouchDeviceType)touchTypeForTouch:(UITouch *)touch
   184 {
   185 #ifdef __IPHONE_9_0
   186     if ([touch respondsToSelector:@selector((type))]) {
   187         if (touch.type == UITouchTypeIndirect) {
   188             return SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
   189         }
   190     }
   191 #endif
   192 
   193     return SDL_TOUCH_DEVICE_DIRECT;
   194 }
   195 
   196 - (SDL_TouchID)touchIdForType:(SDL_TouchDeviceType)type
   197 {
   198     switch (type) {
   199         case SDL_TOUCH_DEVICE_DIRECT:
   200         default:
   201             return directTouchId;
   202         case SDL_TOUCH_DEVICE_INDIRECT_RELATIVE:
   203             return indirectTouchId;
   204     }
   205 }
   206 
   207 - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
   208 {
   209     CGPoint point = [touch locationInView:self];
   210 
   211     if (normalize) {
   212         CGRect bounds = self.bounds;
   213         point.x /= bounds.size.width;
   214         point.y /= bounds.size.height;
   215     }
   216 
   217     return point;
   218 }
   219 
   220 - (float)pressureForTouch:(UITouch *)touch
   221 {
   222 #ifdef __IPHONE_9_0
   223     if ([touch respondsToSelector:@selector(force)]) {
   224         return (float) touch.force;
   225     }
   226 #endif
   227 
   228     return 1.0f;
   229 }
   230 
   231 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
   232 {
   233     for (UITouch *touch in touches) {
   234         BOOL handled = NO;
   235 
   236 #if !TARGET_OS_TV && defined(__IPHONE_13_4)
   237         if (@available(iOS 13.4, *)) {
   238             if (touch.type == UITouchTypeIndirectPointer) {
   239                 int i;
   240 
   241                 for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) {
   242                     if (event.buttonMask & SDL_BUTTON(i)) {
   243                         Uint8 button;
   244 
   245                         switch (i) {
   246                         case 1:
   247                             button = SDL_BUTTON_LEFT;
   248                             break;
   249                         case 2:
   250                             button = SDL_BUTTON_RIGHT;
   251                             break;
   252                         case 3:
   253                             button = SDL_BUTTON_MIDDLE;
   254                             break;
   255                         default:
   256                             button = (Uint8)i;
   257                             break;
   258                         }
   259                         SDL_SendMouseButton(sdlwindow, 0, SDL_PRESSED, button);
   260                     }
   261                 }
   262                 handled = YES;
   263             }
   264         }
   265 #endif
   266         if (!handled) {
   267             SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
   268             SDL_TouchID touchId = [self touchIdForType:touchType];
   269             float pressure = [self pressureForTouch:touch];
   270 
   271             if (SDL_AddTouch(touchId, touchType, "") < 0) {
   272                 continue;
   273             }
   274 
   275             /* FIXME, need to send: int clicks = (int) touch.tapCount; ? */
   276 
   277             CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   278             SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch), sdlwindow,
   279                           SDL_TRUE, locationInView.x, locationInView.y, pressure);
   280         }
   281     }
   282 }
   283 
   284 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
   285 {
   286     for (UITouch *touch in touches) {
   287         BOOL handled = NO;
   288 
   289 #if !TARGET_OS_TV && defined(__IPHONE_13_4)
   290         if (@available(iOS 13.4, *)) {
   291             if (touch.type == UITouchTypeIndirectPointer) {
   292                 int i;
   293 
   294                 for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) {
   295                     if (!(event.buttonMask & SDL_BUTTON(i))) {
   296                         Uint8 button;
   297 
   298                         switch (i) {
   299                         case 1:
   300                             button = SDL_BUTTON_LEFT;
   301                             break;
   302                         case 2:
   303                             button = SDL_BUTTON_RIGHT;
   304                             break;
   305                         case 3:
   306                             button = SDL_BUTTON_MIDDLE;
   307                             break;
   308                         default:
   309                             button = (Uint8)i;
   310                             break;
   311                         }
   312                         SDL_SendMouseButton(sdlwindow, 0, SDL_RELEASED, button);
   313                     }
   314                 }
   315                 handled = YES;
   316             }
   317         }
   318 #endif
   319         if (!handled) {
   320             SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
   321             SDL_TouchID touchId = [self touchIdForType:touchType];
   322             float pressure = [self pressureForTouch:touch];
   323 
   324             if (SDL_AddTouch(touchId, touchType, "") < 0) {
   325                 continue;
   326             }
   327 
   328             /* FIXME, need to send: int clicks = (int) touch.tapCount; ? */
   329 
   330             CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   331             SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch), sdlwindow,
   332                           SDL_FALSE, locationInView.x, locationInView.y, pressure);
   333         }
   334     }
   335 }
   336 
   337 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
   338 {
   339     [self touchesEnded:touches withEvent:event];
   340 }
   341 
   342 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
   343 {
   344     for (UITouch *touch in touches) {
   345         BOOL handled = NO;
   346 
   347 #if !TARGET_OS_TV && defined(__IPHONE_13_4)
   348         if (@available(iOS 13.4, *)) {
   349             if (touch.type == UITouchTypeIndirectPointer) {
   350                 /* Already handled in pointerInteraction callback */
   351                 handled = YES;
   352             }
   353         }
   354 #endif
   355         if (!handled) {
   356             SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
   357             SDL_TouchID touchId = [self touchIdForType:touchType];
   358             float pressure = [self pressureForTouch:touch];
   359 
   360             if (SDL_AddTouch(touchId, touchType, "") < 0) {
   361                 continue;
   362             }
   363 
   364             CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   365             SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch), sdlwindow,
   366                                 locationInView.x, locationInView.y, pressure);
   367         }
   368     }
   369 }
   370 
   371 #if TARGET_OS_TV || defined(__IPHONE_9_1)
   372 - (SDL_Scancode)scancodeFromPress:(UIPress*)press
   373 {
   374 #ifdef __IPHONE_13_4
   375     if ([press respondsToSelector:@selector((key))]) {
   376         if (press.key != nil) {
   377             return (SDL_Scancode)press.key.keyCode;
   378         }
   379     }
   380 #endif
   381 
   382 #if !SDL_JOYSTICK_DISABLED
   383     /* Presses from Apple TV remote */
   384     if (!SDL_AppleTVRemoteOpenedAsJoystick) {
   385         switch (press.type) {
   386         case UIPressTypeUpArrow:
   387             return SDL_SCANCODE_UP;
   388         case UIPressTypeDownArrow:
   389             return SDL_SCANCODE_DOWN;
   390         case UIPressTypeLeftArrow:
   391             return SDL_SCANCODE_LEFT;
   392         case UIPressTypeRightArrow:
   393             return SDL_SCANCODE_RIGHT;
   394         case UIPressTypeSelect:
   395             /* HIG says: "primary button behavior" */
   396             return SDL_SCANCODE_RETURN;
   397         case UIPressTypeMenu:
   398             /* HIG says: "returns to previous screen" */
   399             return SDL_SCANCODE_ESCAPE;
   400         case UIPressTypePlayPause:
   401             /* HIG says: "secondary button behavior" */
   402             return SDL_SCANCODE_PAUSE;
   403         default:
   404             break;
   405         }
   406     }
   407 #endif /* !SDL_JOYSTICK_DISABLED */
   408 
   409     return SDL_SCANCODE_UNKNOWN;
   410 }
   411 
   412 - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   413 {
   414     for (UIPress *press in presses) {
   415         SDL_Scancode scancode = [self scancodeFromPress:press];
   416         SDL_SendKeyboardKey(SDL_PRESSED, scancode);
   417     }
   418     [super pressesBegan:presses withEvent:event];
   419 }
   420 
   421 - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   422 {
   423     for (UIPress *press in presses) {
   424         SDL_Scancode scancode = [self scancodeFromPress:press];
   425         SDL_SendKeyboardKey(SDL_RELEASED, scancode);
   426     }
   427     [super pressesEnded:presses withEvent:event];
   428 }
   429 
   430 - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   431 {
   432     for (UIPress *press in presses) {
   433         SDL_Scancode scancode = [self scancodeFromPress:press];
   434         SDL_SendKeyboardKey(SDL_RELEASED, scancode);
   435     }
   436     [super pressesCancelled:presses withEvent:event];
   437 }
   438 
   439 - (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   440 {
   441     /* This is only called when the force of a press changes. */
   442     [super pressesChanged:presses withEvent:event];
   443 }
   444 
   445 #endif /* TARGET_OS_TV || defined(__IPHONE_9_1) */
   446 
   447 -(void)mouseWheelGesture:(UIPanGestureRecognizer *)gesture
   448 {
   449     if (gesture.state == UIGestureRecognizerStateBegan ||
   450         gesture.state == UIGestureRecognizerStateChanged ||
   451         gesture.state == UIGestureRecognizerStateEnded) {
   452         CGPoint velocity = [gesture velocityInView:self];
   453 
   454         if (velocity.x > 0.0f) {
   455             velocity.x = -1.0;
   456         } else if (velocity.x < 0.0f) {
   457             velocity.x = 1.0f;
   458         }
   459         if (velocity.y > 0.0f) {
   460             velocity.y = -1.0;
   461         } else if (velocity.y < 0.0f) {
   462             velocity.y = 1.0f;
   463         }
   464         if (velocity.x != 0.0f || velocity.y != 0.0f) {
   465             SDL_SendMouseWheel(sdlwindow, 0, velocity.x, velocity.y, SDL_MOUSEWHEEL_NORMAL);
   466         }
   467     }
   468 }
   469 
   470 #if TARGET_OS_TV
   471 -(void)swipeGesture:(UISwipeGestureRecognizer *)gesture
   472 {
   473     /* Swipe gestures don't trigger begin states. */
   474     if (gesture.state == UIGestureRecognizerStateEnded) {
   475 #if !SDL_JOYSTICK_DISABLED
   476         if (!SDL_AppleTVRemoteOpenedAsJoystick) {
   477             /* Send arrow key presses for now, as we don't have an external API
   478              * which better maps to swipe gestures. */
   479             switch (gesture.direction) {
   480             case UISwipeGestureRecognizerDirectionUp:
   481                 SDL_SendKeyboardKeyAutoRelease(SDL_SCANCODE_UP);
   482                 break;
   483             case UISwipeGestureRecognizerDirectionDown:
   484                 SDL_SendKeyboardKeyAutoRelease(SDL_SCANCODE_DOWN);
   485                 break;
   486             case UISwipeGestureRecognizerDirectionLeft:
   487                 SDL_SendKeyboardKeyAutoRelease(SDL_SCANCODE_LEFT);
   488                 break;
   489             case UISwipeGestureRecognizerDirectionRight:
   490                 SDL_SendKeyboardKeyAutoRelease(SDL_SCANCODE_RIGHT);
   491                 break;
   492             }
   493         }
   494 #endif /* !SDL_JOYSTICK_DISABLED */
   495     }
   496 }
   497 #endif /* TARGET_OS_TV */
   498 
   499 @end
   500 
   501 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   502 
   503 /* vi: set ts=4 sw=4 expandtab: */