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