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