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