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