src/video/uikit/SDL_uikitview.m
author Alex Szpakowski <slime73@gmail.com>
Sun, 16 Jun 2019 13:52:27 -0300
changeset 12866 286fefb0209c
parent 12682 aec854f934db
child 12979 bbbb30026158
permissions -rw-r--r--
iOS: remove some code which could affect the state of UIViews that aren't owned by SDL.

It was originally added to work around an input event problem in the code of a specific app which mixed SDL and native UIViews, but that app solved its problems in a better manner since then.
slouken@6079
     1
 /*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@12503
     3
  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
slouken@5262
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@5262
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@5262
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@5262
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@6044
    22
slouken@6044
    23
#if SDL_VIDEO_DRIVER_UIKIT
slouken@2765
    24
slouken@6518
    25
#include "SDL_uikitview.h"
slouken@2765
    26
slouken@11831
    27
#include "SDL_hints.h"
slouken@4490
    28
#include "../../events/SDL_mouse_c.h"
jimtla@4677
    29
#include "../../events/SDL_touch_c.h"
slime73@9532
    30
#include "../../events/SDL_events_c.h"
slouken@4490
    31
slime73@9532
    32
#import "SDL_uikitappdelegate.h"
slime73@9532
    33
#import "SDL_uikitmodes.h"
slime73@9532
    34
#import "SDL_uikitwindow.h"
slouken@2765
    35
slouken@11846
    36
/* This is defined in SDL_sysjoystick.m */
slouken@11846
    37
extern int SDL_AppleTVRemoteOpenedAsJoystick;
slouken@11846
    38
slime73@9501
    39
@implementation SDL_uikitview {
slime73@9532
    40
    SDL_Window *sdlwindow;
slime73@9501
    41
slime73@12404
    42
    SDL_TouchID directTouchId;
slime73@12404
    43
    SDL_TouchID indirectTouchId;
slime73@9501
    44
}
slouken@2765
    45
slime73@9532
    46
- (instancetype)initWithFrame:(CGRect)frame
kees@6003
    47
{
slime73@9532
    48
    if ((self = [super initWithFrame:frame])) {
slime73@10710
    49
#if TARGET_OS_TV
slouken@11846
    50
        /* Apple TV Remote touchpad swipe gestures. */
slouken@11846
    51
        UISwipeGestureRecognizer *swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
slouken@11846
    52
        swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
slouken@11846
    53
        [self addGestureRecognizer:swipeUp];
slime73@10710
    54
slouken@11846
    55
        UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
slouken@11846
    56
        swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
slouken@11846
    57
        [self addGestureRecognizer:swipeDown];
slime73@10710
    58
slouken@11846
    59
        UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
slouken@11846
    60
        swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
slouken@11846
    61
        [self addGestureRecognizer:swipeLeft];
slime73@10710
    62
slouken@11846
    63
        UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
slouken@11846
    64
        swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
slouken@11846
    65
        [self addGestureRecognizer:swipeRight];
slime73@10710
    66
#endif
slime73@10710
    67
slime73@9532
    68
        self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
slime73@9532
    69
        self.autoresizesSubviews = YES;
slouken@2765
    70
slime73@12404
    71
        directTouchId = 1;
slime73@12404
    72
        indirectTouchId = 2;
slime73@12404
    73
slime73@10340
    74
#if !TARGET_OS_TV
slime73@9505
    75
        self.multipleTouchEnabled = YES;
slime73@12404
    76
        SDL_AddTouch(directTouchId, SDL_TOUCH_DEVICE_DIRECT, "");
slime73@10340
    77
#endif
slime73@9505
    78
    }
slouken@4661
    79
slouken@5131
    80
    return self;
slouken@2765
    81
}
slouken@2765
    82
slime73@9532
    83
- (void)setSDLWindow:(SDL_Window *)window
slime73@9527
    84
{
slime73@9532
    85
    SDL_WindowData *data = nil;
slime73@9532
    86
slime73@9532
    87
    if (window == sdlwindow) {
slime73@9532
    88
        return;
slime73@9532
    89
    }
slime73@9532
    90
slime73@9727
    91
    /* Remove ourself from the old window. */
slime73@9532
    92
    if (sdlwindow) {
slime73@9532
    93
        SDL_uikitview *view = nil;
slime73@9532
    94
        data = (__bridge SDL_WindowData *) sdlwindow->driverdata;
slime73@9532
    95
slime73@9532
    96
        [data.views removeObject:self];
slime73@9532
    97
slime73@9532
    98
        [self removeFromSuperview];
slime73@9532
    99
slime73@9532
   100
        /* Restore the next-oldest view in the old window. */
slime73@9727
   101
        view = data.views.lastObject;
slime73@9532
   102
slime73@9532
   103
        data.viewcontroller.view = view;
slime73@9532
   104
slime73@9551
   105
        data.uiwindow.rootViewController = nil;
slime73@9551
   106
        data.uiwindow.rootViewController = data.viewcontroller;
slime73@9532
   107
slime73@9532
   108
        [data.uiwindow layoutIfNeeded];
slime73@9532
   109
    }
slime73@9532
   110
slime73@9727
   111
    /* Add ourself to the new window. */
slime73@9532
   112
    if (window) {
slime73@9532
   113
        data = (__bridge SDL_WindowData *) window->driverdata;
slime73@9532
   114
slime73@9532
   115
        /* Make sure the SDL window has a strong reference to this view. */
slime73@9532
   116
        [data.views addObject:self];
slime73@9532
   117
slime73@9532
   118
        /* Replace the view controller's old view with this one. */
slime73@9532
   119
        [data.viewcontroller.view removeFromSuperview];
slime73@9532
   120
        data.viewcontroller.view = self;
slime73@9532
   121
slime73@9551
   122
        /* The root view controller handles rotation and the status bar.
slime73@9551
   123
         * Assigning it also adds the controller's view to the window. We
slime73@9551
   124
         * explicitly re-set it to make sure the view is properly attached to
slime73@9551
   125
         * the window. Just adding the sub-view if the root view controller is
slime73@9551
   126
         * already correct causes orientation issues on iOS 7 and below. */
slime73@9551
   127
        data.uiwindow.rootViewController = nil;
slime73@9551
   128
        data.uiwindow.rootViewController = data.viewcontroller;
slime73@9532
   129
slime73@9532
   130
        /* The view's bounds may not be correct until the next event cycle. That
slime73@9532
   131
         * might happen after the current dimensions are queried, so we force a
slime73@9532
   132
         * layout now to immediately update the bounds. */
slime73@9532
   133
        [data.uiwindow layoutIfNeeded];
slime73@9532
   134
    }
slime73@9532
   135
slime73@9532
   136
    sdlwindow = window;
slime73@9527
   137
}
slime73@9527
   138
slime73@12404
   139
- (SDL_TouchDeviceType)touchTypeForTouch:(UITouch *)touch
slime73@12404
   140
{
slime73@12404
   141
#ifdef __IPHONE_9_0
slime73@12404
   142
    if ([touch respondsToSelector:@selector((type))]) {
slime73@12404
   143
        if (touch.type == UITouchTypeIndirect) {
slime73@12404
   144
            return SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
slime73@12404
   145
        }
slime73@12404
   146
    }
slime73@12404
   147
#endif
slime73@12404
   148
slime73@12404
   149
    return SDL_TOUCH_DEVICE_DIRECT;
slime73@12404
   150
}
slime73@12404
   151
slime73@12404
   152
- (SDL_TouchID)touchIdForType:(SDL_TouchDeviceType)type
slime73@12404
   153
{
slime73@12404
   154
    switch (type) {
slime73@12404
   155
        case SDL_TOUCH_DEVICE_DIRECT:
slime73@12404
   156
        default:
slime73@12404
   157
            return directTouchId;
slime73@12404
   158
        case SDL_TOUCH_DEVICE_INDIRECT_RELATIVE:
slime73@12404
   159
            return indirectTouchId;
slime73@12404
   160
    }
slime73@12404
   161
}
slime73@12404
   162
slouken@6436
   163
- (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
slouken@6079
   164
{
slime73@9524
   165
    CGPoint point = [touch locationInView:self];
slouken@6436
   166
slouken@6436
   167
    if (normalize) {
slime73@9501
   168
        CGRect bounds = self.bounds;
slouken@6439
   169
        point.x /= bounds.size.width;
slouken@6439
   170
        point.y /= bounds.size.height;
slouken@6436
   171
    }
slime73@9488
   172
slouken@6079
   173
    return point;
slouken@6079
   174
}
slouken@6079
   175
slime73@9861
   176
- (float)pressureForTouch:(UITouch *)touch
slime73@9861
   177
{
slime73@9861
   178
#ifdef __IPHONE_9_0
slime73@9861
   179
    if ([touch respondsToSelector:@selector(force)]) {
slime73@9861
   180
        return (float) touch.force;
slime73@9861
   181
    }
slime73@9861
   182
#endif
slime73@9861
   183
slime73@9861
   184
    return 1.0f;
slime73@9861
   185
}
slime73@9861
   186
kees@6003
   187
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   188
{
slime73@9487
   189
    for (UITouch *touch in touches) {
slime73@12404
   190
        SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
slime73@12404
   191
        SDL_TouchID touchId = [self touchIdForType:touchType];
slime73@9861
   192
        float pressure = [self pressureForTouch:touch];
slime73@9861
   193
slime73@12404
   194
        if (SDL_AddTouch(touchId, touchType, "") < 0) {
slime73@12404
   195
            continue;
slime73@12404
   196
        }
slime73@12404
   197
sylvain@12682
   198
        /* FIXME, need to send: int clicks = (int) touch.tapCount; ? */
slouken@6597
   199
slouken@6436
   200
        CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
icculus@7050
   201
        SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
slime73@9861
   202
                      SDL_TRUE, locationInView.x, locationInView.y, pressure);
slouken@5131
   203
    }
slouken@2765
   204
}
slouken@2765
   205
kees@6003
   206
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   207
{
slime73@9487
   208
    for (UITouch *touch in touches) {
slime73@12404
   209
        SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
slime73@12404
   210
        SDL_TouchID touchId = [self touchIdForType:touchType];
slime73@9861
   211
        float pressure = [self pressureForTouch:touch];
slime73@9861
   212
slime73@12404
   213
        if (SDL_AddTouch(touchId, touchType, "") < 0) {
slime73@12404
   214
            continue;
slime73@12404
   215
        }
slime73@12404
   216
sylvain@12682
   217
        /* FIXME, need to send: int clicks = (int) touch.tapCount; ? */
slouken@4661
   218
slouken@6436
   219
        CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
slime73@9501
   220
        SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
slime73@9861
   221
                      SDL_FALSE, locationInView.x, locationInView.y, pressure);
slouken@5131
   222
    }
slouken@2765
   223
}
slouken@2765
   224
kees@6003
   225
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   226
{
slime73@9524
   227
    [self touchesEnded:touches withEvent:event];
slouken@2765
   228
}
slouken@2765
   229
kees@6003
   230
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   231
{
slime73@9487
   232
    for (UITouch *touch in touches) {
slime73@12404
   233
        SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
slime73@12404
   234
        SDL_TouchID touchId = [self touchIdForType:touchType];
slime73@9861
   235
        float pressure = [self pressureForTouch:touch];
slime73@9861
   236
slime73@12404
   237
        if (SDL_AddTouch(touchId, touchType, "") < 0) {
slime73@12404
   238
            continue;
slime73@12404
   239
        }
slime73@12404
   240
slouken@6436
   241
        CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
slime73@9501
   242
        SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
slime73@9861
   243
                            locationInView.x, locationInView.y, pressure);
slouken@5131
   244
    }
slouken@2765
   245
}
slouken@2765
   246
slime73@10340
   247
#if TARGET_OS_TV || defined(__IPHONE_9_1)
slime73@10340
   248
- (SDL_Scancode)scancodeFromPressType:(UIPressType)presstype
slime73@10340
   249
{
slime73@10340
   250
    switch (presstype) {
slime73@10340
   251
    case UIPressTypeUpArrow:
slime73@10340
   252
        return SDL_SCANCODE_UP;
slime73@10340
   253
    case UIPressTypeDownArrow:
slime73@10340
   254
        return SDL_SCANCODE_DOWN;
slime73@10340
   255
    case UIPressTypeLeftArrow:
slime73@10340
   256
        return SDL_SCANCODE_LEFT;
slime73@10340
   257
    case UIPressTypeRightArrow:
slime73@10340
   258
        return SDL_SCANCODE_RIGHT;
slime73@10340
   259
    case UIPressTypeSelect:
slime73@10340
   260
        /* HIG says: "primary button behavior" */
slouken@11845
   261
        return SDL_SCANCODE_RETURN;
slime73@10340
   262
    case UIPressTypeMenu:
slime73@10340
   263
        /* HIG says: "returns to previous screen" */
slouken@11845
   264
        return SDL_SCANCODE_ESCAPE;
slime73@10340
   265
    case UIPressTypePlayPause:
slime73@10340
   266
        /* HIG says: "secondary button behavior" */
slime73@10340
   267
        return SDL_SCANCODE_PAUSE;
slime73@10340
   268
    default:
slime73@10340
   269
        return SDL_SCANCODE_UNKNOWN;
slime73@10340
   270
    }
slime73@10340
   271
}
slime73@10340
   272
slime73@10340
   273
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
slime73@10340
   274
{
slouken@12201
   275
    if (!SDL_AppleTVRemoteOpenedAsJoystick) {
slouken@12201
   276
        for (UIPress *press in presses) {
slouken@12201
   277
            SDL_Scancode scancode = [self scancodeFromPressType:press.type];
slouken@12201
   278
            SDL_SendKeyboardKey(SDL_PRESSED, scancode);
slouken@12201
   279
        }
slouken@12201
   280
    }
slime73@10340
   281
    [super pressesBegan:presses withEvent:event];
slime73@10340
   282
}
slime73@10340
   283
slime73@10340
   284
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
slime73@10340
   285
{
slouken@12201
   286
    if (!SDL_AppleTVRemoteOpenedAsJoystick) {
slouken@12201
   287
        for (UIPress *press in presses) {
slouken@12201
   288
            SDL_Scancode scancode = [self scancodeFromPressType:press.type];
slouken@12201
   289
            SDL_SendKeyboardKey(SDL_RELEASED, scancode);
slouken@12201
   290
        }
slouken@12201
   291
    }
slime73@10340
   292
    [super pressesEnded:presses withEvent:event];
slime73@10340
   293
}
slime73@10340
   294
slime73@10340
   295
- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
slime73@10340
   296
{
slouken@12201
   297
    if (!SDL_AppleTVRemoteOpenedAsJoystick) {
slouken@12201
   298
        for (UIPress *press in presses) {
slouken@12201
   299
            SDL_Scancode scancode = [self scancodeFromPressType:press.type];
slouken@12201
   300
            SDL_SendKeyboardKey(SDL_RELEASED, scancode);
slouken@12201
   301
        }
slouken@12201
   302
    }
slime73@10340
   303
    [super pressesCancelled:presses withEvent:event];
slime73@10340
   304
}
slime73@10340
   305
slime73@10340
   306
- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
slime73@10340
   307
{
slime73@10340
   308
    /* This is only called when the force of a press changes. */
slime73@10340
   309
    [super pressesChanged:presses withEvent:event];
slime73@10340
   310
}
slime73@10340
   311
#endif /* TARGET_OS_TV || defined(__IPHONE_9_1) */
slime73@10340
   312
slime73@10710
   313
#if TARGET_OS_TV
slime73@10710
   314
-(void)swipeGesture:(UISwipeGestureRecognizer *)gesture
slime73@10710
   315
{
slime73@10710
   316
    /* Swipe gestures don't trigger begin states. */
slime73@10710
   317
    if (gesture.state == UIGestureRecognizerStateEnded) {
slouken@11846
   318
        if (!SDL_AppleTVRemoteOpenedAsJoystick) {
slouken@11846
   319
            /* Send arrow key presses for now, as we don't have an external API
slouken@11846
   320
             * which better maps to swipe gestures. */
slouken@11846
   321
            switch (gesture.direction) {
slouken@11846
   322
            case UISwipeGestureRecognizerDirectionUp:
slouken@11846
   323
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_UP);
slouken@11846
   324
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_UP);
slouken@11846
   325
                break;
slouken@11846
   326
            case UISwipeGestureRecognizerDirectionDown:
slouken@11846
   327
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_DOWN);
slouken@11846
   328
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_DOWN);
slouken@11846
   329
                break;
slouken@11846
   330
            case UISwipeGestureRecognizerDirectionLeft:
slouken@11846
   331
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LEFT);
slouken@11846
   332
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LEFT);
slouken@11846
   333
                break;
slouken@11846
   334
            case UISwipeGestureRecognizerDirectionRight:
slouken@11846
   335
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RIGHT);
slouken@11846
   336
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RIGHT);
slouken@11846
   337
                break;
slouken@11846
   338
            }
slime73@10710
   339
        }
slime73@10710
   340
    }
slime73@10710
   341
}
slime73@10710
   342
#endif /* TARGET_OS_TV */
slime73@10710
   343
slouken@2765
   344
@end
slouken@2765
   345
slouken@6044
   346
#endif /* SDL_VIDEO_DRIVER_UIKIT */
slouken@6044
   347
slouken@5132
   348
/* vi: set ts=4 sw=4 expandtab: */