src/video/uikit/SDL_uikitview.m
author Alex Szpakowski <slime73@gmail.com>
Sat, 10 Nov 2018 16:15:48 -0400
changeset 12404 eb60e952b13f
parent 12201 8bdc4d340419
child 12503 806492103856
permissions -rw-r--r--
Add SDL_TouchDeviceType enum and SDL_GetTouchDeviceType(SDL_TouchID id).

Touch device types include SDL_TOUCH_DEVICE_DIRECT (a touch screen with window-relative coordinates for touches), SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE (a trackpad-style device with absolute device coordinates), and SDL_TOUCH_DEVICE_INDIRECT_RELATIVE (a trackpad-style device with screen cursor-relative coordinates).

Phone screens are an example of a direct device type. Mac trackpads are the indirect-absolute touch device type. The Apple TV remote is an indirect-relative touch device type.
slouken@6079
     1
 /*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@11811
     3
  Copyright (C) 1997-2018 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@12404
    44
slime73@9532
    45
    UITouch * __weak firstFingerDown;
slime73@9501
    46
}
slouken@2765
    47
slime73@9532
    48
- (instancetype)initWithFrame:(CGRect)frame
kees@6003
    49
{
slime73@9532
    50
    if ((self = [super initWithFrame:frame])) {
slime73@10710
    51
#if TARGET_OS_TV
slouken@11846
    52
        /* Apple TV Remote touchpad swipe gestures. */
slouken@11846
    53
        UISwipeGestureRecognizer *swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
slouken@11846
    54
        swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
slouken@11846
    55
        [self addGestureRecognizer:swipeUp];
slime73@10710
    56
slouken@11846
    57
        UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
slouken@11846
    58
        swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
slouken@11846
    59
        [self addGestureRecognizer:swipeDown];
slime73@10710
    60
slouken@11846
    61
        UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
slouken@11846
    62
        swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
slouken@11846
    63
        [self addGestureRecognizer:swipeLeft];
slime73@10710
    64
slouken@11846
    65
        UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
slouken@11846
    66
        swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
slouken@11846
    67
        [self addGestureRecognizer:swipeRight];
slime73@10710
    68
#endif
slime73@10710
    69
slime73@9532
    70
        self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
slime73@9532
    71
        self.autoresizesSubviews = YES;
slouken@2765
    72
slime73@12404
    73
        directTouchId = 1;
slime73@12404
    74
        indirectTouchId = 2;
slime73@12404
    75
slime73@10340
    76
#if !TARGET_OS_TV
slime73@9505
    77
        self.multipleTouchEnabled = YES;
slime73@12404
    78
        SDL_AddTouch(directTouchId, SDL_TOUCH_DEVICE_DIRECT, "");
slime73@10340
    79
#endif
slime73@9505
    80
    }
slouken@4661
    81
slouken@5131
    82
    return self;
slouken@2765
    83
}
slouken@2765
    84
slime73@9532
    85
- (void)setSDLWindow:(SDL_Window *)window
slime73@9527
    86
{
slime73@9532
    87
    SDL_WindowData *data = nil;
slime73@9532
    88
slime73@9532
    89
    if (window == sdlwindow) {
slime73@9532
    90
        return;
slime73@9532
    91
    }
slime73@9532
    92
slime73@9727
    93
    /* Remove ourself from the old window. */
slime73@9532
    94
    if (sdlwindow) {
slime73@9532
    95
        SDL_uikitview *view = nil;
slime73@9532
    96
        data = (__bridge SDL_WindowData *) sdlwindow->driverdata;
slime73@9532
    97
slime73@9532
    98
        [data.views removeObject:self];
slime73@9532
    99
slime73@9532
   100
        [self removeFromSuperview];
slime73@9532
   101
slime73@9532
   102
        /* Restore the next-oldest view in the old window. */
slime73@9727
   103
        view = data.views.lastObject;
slime73@9532
   104
slime73@9532
   105
        data.viewcontroller.view = view;
slime73@9532
   106
slime73@9551
   107
        data.uiwindow.rootViewController = nil;
slime73@9551
   108
        data.uiwindow.rootViewController = data.viewcontroller;
slime73@9532
   109
slime73@9532
   110
        [data.uiwindow layoutIfNeeded];
slime73@9532
   111
    }
slime73@9532
   112
slime73@9727
   113
    /* Add ourself to the new window. */
slime73@9532
   114
    if (window) {
slime73@9532
   115
        data = (__bridge SDL_WindowData *) window->driverdata;
slime73@9532
   116
slime73@9532
   117
        /* Make sure the SDL window has a strong reference to this view. */
slime73@9532
   118
        [data.views addObject:self];
slime73@9532
   119
slime73@9532
   120
        /* Replace the view controller's old view with this one. */
slime73@9532
   121
        [data.viewcontroller.view removeFromSuperview];
slime73@9532
   122
        data.viewcontroller.view = self;
slime73@9532
   123
slime73@9551
   124
        /* The root view controller handles rotation and the status bar.
slime73@9551
   125
         * Assigning it also adds the controller's view to the window. We
slime73@9551
   126
         * explicitly re-set it to make sure the view is properly attached to
slime73@9551
   127
         * the window. Just adding the sub-view if the root view controller is
slime73@9551
   128
         * already correct causes orientation issues on iOS 7 and below. */
slime73@9551
   129
        data.uiwindow.rootViewController = nil;
slime73@9551
   130
        data.uiwindow.rootViewController = data.viewcontroller;
slime73@9532
   131
slime73@9532
   132
        /* The view's bounds may not be correct until the next event cycle. That
slime73@9532
   133
         * might happen after the current dimensions are queried, so we force a
slime73@9532
   134
         * layout now to immediately update the bounds. */
slime73@9532
   135
        [data.uiwindow layoutIfNeeded];
slime73@9532
   136
    }
slime73@9532
   137
slime73@9532
   138
    sdlwindow = window;
slime73@9527
   139
}
slime73@9527
   140
slime73@12404
   141
- (SDL_TouchDeviceType)touchTypeForTouch:(UITouch *)touch
slime73@12404
   142
{
slime73@12404
   143
#ifdef __IPHONE_9_0
slime73@12404
   144
    if ([touch respondsToSelector:@selector((type))]) {
slime73@12404
   145
        if (touch.type == UITouchTypeIndirect) {
slime73@12404
   146
            return SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
slime73@12404
   147
        }
slime73@12404
   148
    }
slime73@12404
   149
#endif
slime73@12404
   150
slime73@12404
   151
    return SDL_TOUCH_DEVICE_DIRECT;
slime73@12404
   152
}
slime73@12404
   153
slime73@12404
   154
- (SDL_TouchID)touchIdForType:(SDL_TouchDeviceType)type
slime73@12404
   155
{
slime73@12404
   156
    switch (type) {
slime73@12404
   157
        case SDL_TOUCH_DEVICE_DIRECT:
slime73@12404
   158
        default:
slime73@12404
   159
            return directTouchId;
slime73@12404
   160
        case SDL_TOUCH_DEVICE_INDIRECT_RELATIVE:
slime73@12404
   161
            return indirectTouchId;
slime73@12404
   162
    }
slime73@12404
   163
}
slime73@12404
   164
slouken@6436
   165
- (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
slouken@6079
   166
{
slime73@9524
   167
    CGPoint point = [touch locationInView:self];
slouken@6436
   168
slouken@6436
   169
    if (normalize) {
slime73@9501
   170
        CGRect bounds = self.bounds;
slouken@6439
   171
        point.x /= bounds.size.width;
slouken@6439
   172
        point.y /= bounds.size.height;
slouken@6436
   173
    }
slime73@9488
   174
slouken@6079
   175
    return point;
slouken@6079
   176
}
slouken@6079
   177
slime73@9861
   178
- (float)pressureForTouch:(UITouch *)touch
slime73@9861
   179
{
slime73@9861
   180
#ifdef __IPHONE_9_0
slime73@9861
   181
    if ([touch respondsToSelector:@selector(force)]) {
slime73@9861
   182
        return (float) touch.force;
slime73@9861
   183
    }
slime73@9861
   184
#endif
slime73@9861
   185
slime73@9861
   186
    return 1.0f;
slime73@9861
   187
}
slime73@9861
   188
kees@6003
   189
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   190
{
slime73@9487
   191
    for (UITouch *touch in touches) {
slime73@12404
   192
        SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
slime73@12404
   193
        SDL_TouchID touchId = [self touchIdForType:touchType];
slime73@9861
   194
        float pressure = [self pressureForTouch:touch];
slime73@9861
   195
slime73@12404
   196
        if (SDL_AddTouch(touchId, touchType, "") < 0) {
slime73@12404
   197
            continue;
slime73@12404
   198
        }
slime73@12404
   199
slime73@9532
   200
        if (!firstFingerDown) {
slouken@6597
   201
            CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
slime73@10366
   202
            int clicks = (int) touch.tapCount;
kees@6001
   203
slime73@9532
   204
            /* send mouse moved event */
slime73@9529
   205
            SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
slouken@4488
   206
slouken@6597
   207
            /* send mouse down event */
slime73@10366
   208
            SDL_SendMouseButtonClicks(sdlwindow, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT, clicks);
jim@4660
   209
slime73@9532
   210
            firstFingerDown = touch;
slouken@6597
   211
        }
slouken@6597
   212
slouken@6436
   213
        CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
icculus@7050
   214
        SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
slime73@9861
   215
                      SDL_TRUE, locationInView.x, locationInView.y, pressure);
slouken@5131
   216
    }
slouken@2765
   217
}
slouken@2765
   218
kees@6003
   219
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   220
{
slime73@9487
   221
    for (UITouch *touch in touches) {
slime73@12404
   222
        SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
slime73@12404
   223
        SDL_TouchID touchId = [self touchIdForType:touchType];
slime73@9861
   224
        float pressure = [self pressureForTouch:touch];
slime73@9861
   225
slime73@12404
   226
        if (SDL_AddTouch(touchId, touchType, "") < 0) {
slime73@12404
   227
            continue;
slime73@12404
   228
        }
slime73@12404
   229
slime73@9532
   230
        if (touch == firstFingerDown) {
slouken@6597
   231
            /* send mouse up */
slime73@10366
   232
            int clicks = (int) touch.tapCount;
slime73@10366
   233
            SDL_SendMouseButtonClicks(sdlwindow, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT, clicks);
slime73@9532
   234
            firstFingerDown = nil;
slouken@6597
   235
        }
slouken@4661
   236
slouken@6436
   237
        CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
slime73@9501
   238
        SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
slime73@9861
   239
                      SDL_FALSE, locationInView.x, locationInView.y, pressure);
slouken@5131
   240
    }
slouken@2765
   241
}
slouken@2765
   242
kees@6003
   243
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   244
{
slime73@9524
   245
    [self touchesEnded:touches withEvent:event];
slouken@2765
   246
}
slouken@2765
   247
kees@6003
   248
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   249
{
slime73@9487
   250
    for (UITouch *touch in touches) {
slime73@12404
   251
        SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
slime73@12404
   252
        SDL_TouchID touchId = [self touchIdForType:touchType];
slime73@9861
   253
        float pressure = [self pressureForTouch:touch];
slime73@9861
   254
slime73@12404
   255
        if (SDL_AddTouch(touchId, touchType, "") < 0) {
slime73@12404
   256
            continue;
slime73@12404
   257
        }
slime73@12404
   258
slime73@9532
   259
        if (touch == firstFingerDown) {
slouken@6597
   260
            CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
slouken@4488
   261
slouken@6597
   262
            /* send moved event */
slime73@9529
   263
            SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
slouken@6597
   264
        }
jim@4660
   265
slouken@6436
   266
        CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
slime73@9501
   267
        SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
slime73@9861
   268
                            locationInView.x, locationInView.y, pressure);
slouken@5131
   269
    }
slouken@2765
   270
}
slouken@2765
   271
slime73@10340
   272
#if TARGET_OS_TV || defined(__IPHONE_9_1)
slime73@10340
   273
- (SDL_Scancode)scancodeFromPressType:(UIPressType)presstype
slime73@10340
   274
{
slime73@10340
   275
    switch (presstype) {
slime73@10340
   276
    case UIPressTypeUpArrow:
slime73@10340
   277
        return SDL_SCANCODE_UP;
slime73@10340
   278
    case UIPressTypeDownArrow:
slime73@10340
   279
        return SDL_SCANCODE_DOWN;
slime73@10340
   280
    case UIPressTypeLeftArrow:
slime73@10340
   281
        return SDL_SCANCODE_LEFT;
slime73@10340
   282
    case UIPressTypeRightArrow:
slime73@10340
   283
        return SDL_SCANCODE_RIGHT;
slime73@10340
   284
    case UIPressTypeSelect:
slime73@10340
   285
        /* HIG says: "primary button behavior" */
slouken@11845
   286
        return SDL_SCANCODE_RETURN;
slime73@10340
   287
    case UIPressTypeMenu:
slime73@10340
   288
        /* HIG says: "returns to previous screen" */
slouken@11845
   289
        return SDL_SCANCODE_ESCAPE;
slime73@10340
   290
    case UIPressTypePlayPause:
slime73@10340
   291
        /* HIG says: "secondary button behavior" */
slime73@10340
   292
        return SDL_SCANCODE_PAUSE;
slime73@10340
   293
    default:
slime73@10340
   294
        return SDL_SCANCODE_UNKNOWN;
slime73@10340
   295
    }
slime73@10340
   296
}
slime73@10340
   297
slime73@10340
   298
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
slime73@10340
   299
{
slouken@12201
   300
    if (!SDL_AppleTVRemoteOpenedAsJoystick) {
slouken@12201
   301
        for (UIPress *press in presses) {
slouken@12201
   302
            SDL_Scancode scancode = [self scancodeFromPressType:press.type];
slouken@12201
   303
            SDL_SendKeyboardKey(SDL_PRESSED, scancode);
slouken@12201
   304
        }
slouken@12201
   305
    }
slime73@10340
   306
    [super pressesBegan:presses withEvent:event];
slime73@10340
   307
}
slime73@10340
   308
slime73@10340
   309
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
slime73@10340
   310
{
slouken@12201
   311
    if (!SDL_AppleTVRemoteOpenedAsJoystick) {
slouken@12201
   312
        for (UIPress *press in presses) {
slouken@12201
   313
            SDL_Scancode scancode = [self scancodeFromPressType:press.type];
slouken@12201
   314
            SDL_SendKeyboardKey(SDL_RELEASED, scancode);
slouken@12201
   315
        }
slouken@12201
   316
    }
slime73@10340
   317
    [super pressesEnded:presses withEvent:event];
slime73@10340
   318
}
slime73@10340
   319
slime73@10340
   320
- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
slime73@10340
   321
{
slouken@12201
   322
    if (!SDL_AppleTVRemoteOpenedAsJoystick) {
slouken@12201
   323
        for (UIPress *press in presses) {
slouken@12201
   324
            SDL_Scancode scancode = [self scancodeFromPressType:press.type];
slouken@12201
   325
            SDL_SendKeyboardKey(SDL_RELEASED, scancode);
slouken@12201
   326
        }
slouken@12201
   327
    }
slime73@10340
   328
    [super pressesCancelled:presses withEvent:event];
slime73@10340
   329
}
slime73@10340
   330
slime73@10340
   331
- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
slime73@10340
   332
{
slime73@10340
   333
    /* This is only called when the force of a press changes. */
slime73@10340
   334
    [super pressesChanged:presses withEvent:event];
slime73@10340
   335
}
slime73@10340
   336
#endif /* TARGET_OS_TV || defined(__IPHONE_9_1) */
slime73@10340
   337
slime73@10710
   338
#if TARGET_OS_TV
slime73@10710
   339
-(void)swipeGesture:(UISwipeGestureRecognizer *)gesture
slime73@10710
   340
{
slime73@10710
   341
    /* Swipe gestures don't trigger begin states. */
slime73@10710
   342
    if (gesture.state == UIGestureRecognizerStateEnded) {
slouken@11846
   343
        if (!SDL_AppleTVRemoteOpenedAsJoystick) {
slouken@11846
   344
            /* Send arrow key presses for now, as we don't have an external API
slouken@11846
   345
             * which better maps to swipe gestures. */
slouken@11846
   346
            switch (gesture.direction) {
slouken@11846
   347
            case UISwipeGestureRecognizerDirectionUp:
slouken@11846
   348
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_UP);
slouken@11846
   349
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_UP);
slouken@11846
   350
                break;
slouken@11846
   351
            case UISwipeGestureRecognizerDirectionDown:
slouken@11846
   352
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_DOWN);
slouken@11846
   353
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_DOWN);
slouken@11846
   354
                break;
slouken@11846
   355
            case UISwipeGestureRecognizerDirectionLeft:
slouken@11846
   356
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LEFT);
slouken@11846
   357
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LEFT);
slouken@11846
   358
                break;
slouken@11846
   359
            case UISwipeGestureRecognizerDirectionRight:
slouken@11846
   360
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RIGHT);
slouken@11846
   361
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RIGHT);
slouken@11846
   362
                break;
slouken@11846
   363
            }
slime73@10710
   364
        }
slime73@10710
   365
    }
slime73@10710
   366
}
slime73@10710
   367
#endif /* TARGET_OS_TV */
slime73@10710
   368
slouken@2765
   369
@end
slouken@2765
   370
slouken@6044
   371
#endif /* SDL_VIDEO_DRIVER_UIKIT */
slouken@6044
   372
slouken@5132
   373
/* vi: set ts=4 sw=4 expandtab: */