sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_uikitview.m (17427B)


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