src/video/uikit/SDL_uikitview.m
author Sam Lantinga <slouken@libsdl.org>
Tue, 12 Mar 2019 14:45:04 -0700
changeset 12626 c8dae6bd7ce5
parent 12503 806492103856
child 12682 aec854f934db
permissions -rw-r--r--
[iOS DAC] Fix touch events getting from SDL2 to source2.
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@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
slouken@12626
    85
- (void)layoutSubviews
slouken@12626
    86
{
slouken@12626
    87
	// Fix for touch ios.
slouken@12626
    88
#if TARGET_OS_IOS
slouken@12626
    89
	// on ios, a metal view gets added to our parent, and covers this for touch events.
slouken@12626
    90
	// So set ourselves to user interact, and siblings false. johna
slouken@12626
    91
	NSArray<UIView*>* subviews = [self.superview subviews];
slouken@12626
    92
	for (int i=0; i<[subviews count]; i++)
slouken@12626
    93
	{
slouken@12626
    94
		UIView *view = [subviews objectAtIndex:i];
slouken@12626
    95
		if (view == self) {
slouken@12626
    96
			[view setUserInteractionEnabled:YES];  // set our user interaction to true.
slouken@12626
    97
		} else {
slouken@12626
    98
			[view setUserInteractionEnabled:NO];  // siblings to false.
slouken@12626
    99
		}
slouken@12626
   100
	}
slouken@12626
   101
#endif
slouken@12626
   102
    [super layoutSubviews];
slouken@12626
   103
}
slouken@12626
   104
slouken@12626
   105
slime73@9532
   106
- (void)setSDLWindow:(SDL_Window *)window
slime73@9527
   107
{
slime73@9532
   108
    SDL_WindowData *data = nil;
slime73@9532
   109
slime73@9532
   110
    if (window == sdlwindow) {
slime73@9532
   111
        return;
slime73@9532
   112
    }
slime73@9532
   113
slime73@9727
   114
    /* Remove ourself from the old window. */
slime73@9532
   115
    if (sdlwindow) {
slime73@9532
   116
        SDL_uikitview *view = nil;
slime73@9532
   117
        data = (__bridge SDL_WindowData *) sdlwindow->driverdata;
slime73@9532
   118
slime73@9532
   119
        [data.views removeObject:self];
slime73@9532
   120
slime73@9532
   121
        [self removeFromSuperview];
slime73@9532
   122
slime73@9532
   123
        /* Restore the next-oldest view in the old window. */
slime73@9727
   124
        view = data.views.lastObject;
slime73@9532
   125
slime73@9532
   126
        data.viewcontroller.view = view;
slime73@9532
   127
slime73@9551
   128
        data.uiwindow.rootViewController = nil;
slime73@9551
   129
        data.uiwindow.rootViewController = data.viewcontroller;
slime73@9532
   130
slime73@9532
   131
        [data.uiwindow layoutIfNeeded];
slime73@9532
   132
    }
slime73@9532
   133
slime73@9727
   134
    /* Add ourself to the new window. */
slime73@9532
   135
    if (window) {
slime73@9532
   136
        data = (__bridge SDL_WindowData *) window->driverdata;
slime73@9532
   137
slime73@9532
   138
        /* Make sure the SDL window has a strong reference to this view. */
slime73@9532
   139
        [data.views addObject:self];
slime73@9532
   140
slime73@9532
   141
        /* Replace the view controller's old view with this one. */
slime73@9532
   142
        [data.viewcontroller.view removeFromSuperview];
slime73@9532
   143
        data.viewcontroller.view = self;
slime73@9532
   144
slime73@9551
   145
        /* The root view controller handles rotation and the status bar.
slime73@9551
   146
         * Assigning it also adds the controller's view to the window. We
slime73@9551
   147
         * explicitly re-set it to make sure the view is properly attached to
slime73@9551
   148
         * the window. Just adding the sub-view if the root view controller is
slime73@9551
   149
         * already correct causes orientation issues on iOS 7 and below. */
slime73@9551
   150
        data.uiwindow.rootViewController = nil;
slime73@9551
   151
        data.uiwindow.rootViewController = data.viewcontroller;
slime73@9532
   152
slime73@9532
   153
        /* The view's bounds may not be correct until the next event cycle. That
slime73@9532
   154
         * might happen after the current dimensions are queried, so we force a
slime73@9532
   155
         * layout now to immediately update the bounds. */
slime73@9532
   156
        [data.uiwindow layoutIfNeeded];
slime73@9532
   157
    }
slime73@9532
   158
slime73@9532
   159
    sdlwindow = window;
slime73@9527
   160
}
slime73@9527
   161
slime73@12404
   162
- (SDL_TouchDeviceType)touchTypeForTouch:(UITouch *)touch
slime73@12404
   163
{
slime73@12404
   164
#ifdef __IPHONE_9_0
slime73@12404
   165
    if ([touch respondsToSelector:@selector((type))]) {
slime73@12404
   166
        if (touch.type == UITouchTypeIndirect) {
slime73@12404
   167
            return SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
slime73@12404
   168
        }
slime73@12404
   169
    }
slime73@12404
   170
#endif
slime73@12404
   171
slime73@12404
   172
    return SDL_TOUCH_DEVICE_DIRECT;
slime73@12404
   173
}
slime73@12404
   174
slime73@12404
   175
- (SDL_TouchID)touchIdForType:(SDL_TouchDeviceType)type
slime73@12404
   176
{
slime73@12404
   177
    switch (type) {
slime73@12404
   178
        case SDL_TOUCH_DEVICE_DIRECT:
slime73@12404
   179
        default:
slime73@12404
   180
            return directTouchId;
slime73@12404
   181
        case SDL_TOUCH_DEVICE_INDIRECT_RELATIVE:
slime73@12404
   182
            return indirectTouchId;
slime73@12404
   183
    }
slime73@12404
   184
}
slime73@12404
   185
slouken@6436
   186
- (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
slouken@6079
   187
{
slime73@9524
   188
    CGPoint point = [touch locationInView:self];
slouken@6436
   189
slouken@6436
   190
    if (normalize) {
slime73@9501
   191
        CGRect bounds = self.bounds;
slouken@6439
   192
        point.x /= bounds.size.width;
slouken@6439
   193
        point.y /= bounds.size.height;
slouken@6436
   194
    }
slime73@9488
   195
slouken@6079
   196
    return point;
slouken@6079
   197
}
slouken@6079
   198
slime73@9861
   199
- (float)pressureForTouch:(UITouch *)touch
slime73@9861
   200
{
slime73@9861
   201
#ifdef __IPHONE_9_0
slime73@9861
   202
    if ([touch respondsToSelector:@selector(force)]) {
slime73@9861
   203
        return (float) touch.force;
slime73@9861
   204
    }
slime73@9861
   205
#endif
slime73@9861
   206
slime73@9861
   207
    return 1.0f;
slime73@9861
   208
}
slime73@9861
   209
kees@6003
   210
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   211
{
slime73@9487
   212
    for (UITouch *touch in touches) {
slime73@12404
   213
        SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
slime73@12404
   214
        SDL_TouchID touchId = [self touchIdForType:touchType];
slime73@9861
   215
        float pressure = [self pressureForTouch:touch];
slime73@9861
   216
slime73@12404
   217
        if (SDL_AddTouch(touchId, touchType, "") < 0) {
slime73@12404
   218
            continue;
slime73@12404
   219
        }
slime73@12404
   220
slime73@9532
   221
        if (!firstFingerDown) {
slouken@6597
   222
            CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
slime73@10366
   223
            int clicks = (int) touch.tapCount;
kees@6001
   224
slime73@9532
   225
            /* send mouse moved event */
slime73@9529
   226
            SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
slouken@4488
   227
slouken@6597
   228
            /* send mouse down event */
slime73@10366
   229
            SDL_SendMouseButtonClicks(sdlwindow, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT, clicks);
jim@4660
   230
slime73@9532
   231
            firstFingerDown = touch;
slouken@6597
   232
        }
slouken@6597
   233
slouken@6436
   234
        CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
icculus@7050
   235
        SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
slime73@9861
   236
                      SDL_TRUE, locationInView.x, locationInView.y, pressure);
slouken@5131
   237
    }
slouken@2765
   238
}
slouken@2765
   239
kees@6003
   240
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   241
{
slime73@9487
   242
    for (UITouch *touch in touches) {
slime73@12404
   243
        SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
slime73@12404
   244
        SDL_TouchID touchId = [self touchIdForType:touchType];
slime73@9861
   245
        float pressure = [self pressureForTouch:touch];
slime73@9861
   246
slime73@12404
   247
        if (SDL_AddTouch(touchId, touchType, "") < 0) {
slime73@12404
   248
            continue;
slime73@12404
   249
        }
slime73@12404
   250
slime73@9532
   251
        if (touch == firstFingerDown) {
slouken@6597
   252
            /* send mouse up */
slime73@10366
   253
            int clicks = (int) touch.tapCount;
slime73@10366
   254
            SDL_SendMouseButtonClicks(sdlwindow, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT, clicks);
slime73@9532
   255
            firstFingerDown = nil;
slouken@6597
   256
        }
slouken@4661
   257
slouken@6436
   258
        CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
slime73@9501
   259
        SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
slime73@9861
   260
                      SDL_FALSE, locationInView.x, locationInView.y, pressure);
slouken@5131
   261
    }
slouken@2765
   262
}
slouken@2765
   263
kees@6003
   264
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   265
{
slime73@9524
   266
    [self touchesEnded:touches withEvent:event];
slouken@2765
   267
}
slouken@2765
   268
kees@6003
   269
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
kees@6003
   270
{
slime73@9487
   271
    for (UITouch *touch in touches) {
slime73@12404
   272
        SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
slime73@12404
   273
        SDL_TouchID touchId = [self touchIdForType:touchType];
slime73@9861
   274
        float pressure = [self pressureForTouch:touch];
slime73@9861
   275
slime73@12404
   276
        if (SDL_AddTouch(touchId, touchType, "") < 0) {
slime73@12404
   277
            continue;
slime73@12404
   278
        }
slime73@12404
   279
slime73@9532
   280
        if (touch == firstFingerDown) {
slouken@6597
   281
            CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
slouken@4488
   282
slouken@6597
   283
            /* send moved event */
slime73@9529
   284
            SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
slouken@6597
   285
        }
jim@4660
   286
slouken@6436
   287
        CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
slime73@9501
   288
        SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
slime73@9861
   289
                            locationInView.x, locationInView.y, pressure);
slouken@5131
   290
    }
slouken@2765
   291
}
slouken@2765
   292
slime73@10340
   293
#if TARGET_OS_TV || defined(__IPHONE_9_1)
slime73@10340
   294
- (SDL_Scancode)scancodeFromPressType:(UIPressType)presstype
slime73@10340
   295
{
slime73@10340
   296
    switch (presstype) {
slime73@10340
   297
    case UIPressTypeUpArrow:
slime73@10340
   298
        return SDL_SCANCODE_UP;
slime73@10340
   299
    case UIPressTypeDownArrow:
slime73@10340
   300
        return SDL_SCANCODE_DOWN;
slime73@10340
   301
    case UIPressTypeLeftArrow:
slime73@10340
   302
        return SDL_SCANCODE_LEFT;
slime73@10340
   303
    case UIPressTypeRightArrow:
slime73@10340
   304
        return SDL_SCANCODE_RIGHT;
slime73@10340
   305
    case UIPressTypeSelect:
slime73@10340
   306
        /* HIG says: "primary button behavior" */
slouken@11845
   307
        return SDL_SCANCODE_RETURN;
slime73@10340
   308
    case UIPressTypeMenu:
slime73@10340
   309
        /* HIG says: "returns to previous screen" */
slouken@11845
   310
        return SDL_SCANCODE_ESCAPE;
slime73@10340
   311
    case UIPressTypePlayPause:
slime73@10340
   312
        /* HIG says: "secondary button behavior" */
slime73@10340
   313
        return SDL_SCANCODE_PAUSE;
slime73@10340
   314
    default:
slime73@10340
   315
        return SDL_SCANCODE_UNKNOWN;
slime73@10340
   316
    }
slime73@10340
   317
}
slime73@10340
   318
slime73@10340
   319
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
slime73@10340
   320
{
slouken@12201
   321
    if (!SDL_AppleTVRemoteOpenedAsJoystick) {
slouken@12201
   322
        for (UIPress *press in presses) {
slouken@12201
   323
            SDL_Scancode scancode = [self scancodeFromPressType:press.type];
slouken@12201
   324
            SDL_SendKeyboardKey(SDL_PRESSED, scancode);
slouken@12201
   325
        }
slouken@12201
   326
    }
slime73@10340
   327
    [super pressesBegan:presses withEvent:event];
slime73@10340
   328
}
slime73@10340
   329
slime73@10340
   330
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
slime73@10340
   331
{
slouken@12201
   332
    if (!SDL_AppleTVRemoteOpenedAsJoystick) {
slouken@12201
   333
        for (UIPress *press in presses) {
slouken@12201
   334
            SDL_Scancode scancode = [self scancodeFromPressType:press.type];
slouken@12201
   335
            SDL_SendKeyboardKey(SDL_RELEASED, scancode);
slouken@12201
   336
        }
slouken@12201
   337
    }
slime73@10340
   338
    [super pressesEnded:presses withEvent:event];
slime73@10340
   339
}
slime73@10340
   340
slime73@10340
   341
- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
slime73@10340
   342
{
slouken@12201
   343
    if (!SDL_AppleTVRemoteOpenedAsJoystick) {
slouken@12201
   344
        for (UIPress *press in presses) {
slouken@12201
   345
            SDL_Scancode scancode = [self scancodeFromPressType:press.type];
slouken@12201
   346
            SDL_SendKeyboardKey(SDL_RELEASED, scancode);
slouken@12201
   347
        }
slouken@12201
   348
    }
slime73@10340
   349
    [super pressesCancelled:presses withEvent:event];
slime73@10340
   350
}
slime73@10340
   351
slime73@10340
   352
- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
slime73@10340
   353
{
slime73@10340
   354
    /* This is only called when the force of a press changes. */
slime73@10340
   355
    [super pressesChanged:presses withEvent:event];
slime73@10340
   356
}
slime73@10340
   357
#endif /* TARGET_OS_TV || defined(__IPHONE_9_1) */
slime73@10340
   358
slime73@10710
   359
#if TARGET_OS_TV
slime73@10710
   360
-(void)swipeGesture:(UISwipeGestureRecognizer *)gesture
slime73@10710
   361
{
slime73@10710
   362
    /* Swipe gestures don't trigger begin states. */
slime73@10710
   363
    if (gesture.state == UIGestureRecognizerStateEnded) {
slouken@11846
   364
        if (!SDL_AppleTVRemoteOpenedAsJoystick) {
slouken@11846
   365
            /* Send arrow key presses for now, as we don't have an external API
slouken@11846
   366
             * which better maps to swipe gestures. */
slouken@11846
   367
            switch (gesture.direction) {
slouken@11846
   368
            case UISwipeGestureRecognizerDirectionUp:
slouken@11846
   369
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_UP);
slouken@11846
   370
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_UP);
slouken@11846
   371
                break;
slouken@11846
   372
            case UISwipeGestureRecognizerDirectionDown:
slouken@11846
   373
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_DOWN);
slouken@11846
   374
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_DOWN);
slouken@11846
   375
                break;
slouken@11846
   376
            case UISwipeGestureRecognizerDirectionLeft:
slouken@11846
   377
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LEFT);
slouken@11846
   378
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LEFT);
slouken@11846
   379
                break;
slouken@11846
   380
            case UISwipeGestureRecognizerDirectionRight:
slouken@11846
   381
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RIGHT);
slouken@11846
   382
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RIGHT);
slouken@11846
   383
                break;
slouken@11846
   384
            }
slime73@10710
   385
        }
slime73@10710
   386
    }
slime73@10710
   387
}
slime73@10710
   388
#endif /* TARGET_OS_TV */
slime73@10710
   389
slouken@2765
   390
@end
slouken@2765
   391
slouken@6044
   392
#endif /* SDL_VIDEO_DRIVER_UIKIT */
slouken@6044
   393
slouken@5132
   394
/* vi: set ts=4 sw=4 expandtab: */