src/video/uikit/SDL_uikitview.m
author Alex Szpakowski <slime73@gmail.com>
Tue, 13 Sep 2016 22:18:06 -0300
changeset 10340 5724f5087acd
parent 9998 f67cf37e9cd4
child 10366 e1b52e69578b
permissions -rw-r--r--
Initial Apple TV / tvOS support.

The Apple TV remote is currently exposed as a joystick with its touch surface treated as two axes. Key presses are also generated when its buttons and touch surface are used.

A new hint has been added to help deal with deciding whether to background the app when the remote's menu button is pressed: SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS.
     1  /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_DRIVER_UIKIT
    24 
    25 #include "SDL_uikitview.h"
    26 
    27 #include "../../events/SDL_mouse_c.h"
    28 #include "../../events/SDL_touch_c.h"
    29 #include "../../events/SDL_events_c.h"
    30 
    31 #import "SDL_uikitappdelegate.h"
    32 #import "SDL_uikitmodes.h"
    33 #import "SDL_uikitwindow.h"
    34 
    35 @implementation SDL_uikitview {
    36     SDL_Window *sdlwindow;
    37 
    38     SDL_TouchID touchId;
    39     UITouch * __weak firstFingerDown;
    40 }
    41 
    42 - (instancetype)initWithFrame:(CGRect)frame
    43 {
    44     if ((self = [super initWithFrame:frame])) {
    45         self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    46         self.autoresizesSubviews = YES;
    47 
    48 #if !TARGET_OS_TV
    49         self.multipleTouchEnabled = YES;
    50 #endif
    51 
    52         touchId = 1;
    53         SDL_AddTouch(touchId, "");
    54     }
    55 
    56     return self;
    57 }
    58 
    59 - (void)setSDLWindow:(SDL_Window *)window
    60 {
    61     SDL_WindowData *data = nil;
    62 
    63     if (window == sdlwindow) {
    64         return;
    65     }
    66 
    67     /* Remove ourself from the old window. */
    68     if (sdlwindow) {
    69         SDL_uikitview *view = nil;
    70         data = (__bridge SDL_WindowData *) sdlwindow->driverdata;
    71 
    72         [data.views removeObject:self];
    73 
    74         [self removeFromSuperview];
    75 
    76         /* Restore the next-oldest view in the old window. */
    77         view = data.views.lastObject;
    78 
    79         data.viewcontroller.view = view;
    80 
    81         data.uiwindow.rootViewController = nil;
    82         data.uiwindow.rootViewController = data.viewcontroller;
    83 
    84         [data.uiwindow layoutIfNeeded];
    85     }
    86 
    87     /* Add ourself to the new window. */
    88     if (window) {
    89         data = (__bridge SDL_WindowData *) window->driverdata;
    90 
    91         /* Make sure the SDL window has a strong reference to this view. */
    92         [data.views addObject:self];
    93 
    94         /* Replace the view controller's old view with this one. */
    95         [data.viewcontroller.view removeFromSuperview];
    96         data.viewcontroller.view = self;
    97 
    98         /* The root view controller handles rotation and the status bar.
    99          * Assigning it also adds the controller's view to the window. We
   100          * explicitly re-set it to make sure the view is properly attached to
   101          * the window. Just adding the sub-view if the root view controller is
   102          * already correct causes orientation issues on iOS 7 and below. */
   103         data.uiwindow.rootViewController = nil;
   104         data.uiwindow.rootViewController = data.viewcontroller;
   105 
   106         /* The view's bounds may not be correct until the next event cycle. That
   107          * might happen after the current dimensions are queried, so we force a
   108          * layout now to immediately update the bounds. */
   109         [data.uiwindow layoutIfNeeded];
   110     }
   111 
   112     sdlwindow = window;
   113 }
   114 
   115 - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
   116 {
   117     CGPoint point = [touch locationInView:self];
   118 
   119     if (normalize) {
   120         CGRect bounds = self.bounds;
   121         point.x /= bounds.size.width;
   122         point.y /= bounds.size.height;
   123     }
   124 
   125     return point;
   126 }
   127 
   128 - (float)pressureForTouch:(UITouch *)touch
   129 {
   130 #ifdef __IPHONE_9_0
   131     if ([touch respondsToSelector:@selector(force)]) {
   132         return (float) touch.force;
   133     }
   134 #endif
   135 
   136     return 1.0f;
   137 }
   138 
   139 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
   140 {
   141     for (UITouch *touch in touches) {
   142         float pressure = [self pressureForTouch:touch];
   143 
   144         if (!firstFingerDown) {
   145             CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
   146 
   147             /* send mouse moved event */
   148             SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
   149 
   150             /* send mouse down event */
   151             SDL_SendMouseButton(sdlwindow, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT);
   152 
   153             firstFingerDown = touch;
   154         }
   155 
   156         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   157         SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
   158                       SDL_TRUE, locationInView.x, locationInView.y, pressure);
   159     }
   160 }
   161 
   162 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
   163 {
   164     for (UITouch *touch in touches) {
   165         float pressure = [self pressureForTouch:touch];
   166 
   167         if (touch == firstFingerDown) {
   168             /* send mouse up */
   169             SDL_SendMouseButton(sdlwindow, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT);
   170             firstFingerDown = nil;
   171         }
   172 
   173         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   174         SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
   175                       SDL_FALSE, locationInView.x, locationInView.y, pressure);
   176     }
   177 }
   178 
   179 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
   180 {
   181     [self touchesEnded:touches withEvent:event];
   182 }
   183 
   184 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
   185 {
   186     for (UITouch *touch in touches) {
   187         float pressure = [self pressureForTouch:touch];
   188 
   189         if (touch == firstFingerDown) {
   190             CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
   191 
   192             /* send moved event */
   193             SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
   194         }
   195 
   196         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   197         SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
   198                             locationInView.x, locationInView.y, pressure);
   199     }
   200 }
   201 
   202 #if TARGET_OS_TV || defined(__IPHONE_9_1)
   203 - (SDL_Scancode)scancodeFromPressType:(UIPressType)presstype
   204 {
   205     switch (presstype) {
   206     case UIPressTypeUpArrow:
   207         return SDL_SCANCODE_UP;
   208     case UIPressTypeDownArrow:
   209         return SDL_SCANCODE_DOWN;
   210     case UIPressTypeLeftArrow:
   211         return SDL_SCANCODE_LEFT;
   212     case UIPressTypeRightArrow:
   213         return SDL_SCANCODE_RIGHT;
   214     case UIPressTypeSelect:
   215         /* HIG says: "primary button behavior" */
   216         return SDL_SCANCODE_SELECT;
   217     case UIPressTypeMenu:
   218         /* HIG says: "returns to previous screen" */
   219         return SDL_SCANCODE_MENU;
   220     case UIPressTypePlayPause:
   221         /* HIG says: "secondary button behavior" */
   222         return SDL_SCANCODE_PAUSE;
   223     default:
   224         return SDL_SCANCODE_UNKNOWN;
   225     }
   226 }
   227 
   228 - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   229 {
   230     for (UIPress *press in presses) {
   231         SDL_Scancode scancode = [self scancodeFromPressType:press.type];
   232         SDL_SendKeyboardKey(SDL_PRESSED, scancode);
   233     }
   234 
   235     [super pressesBegan:presses withEvent:event];
   236 }
   237 
   238 - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   239 {
   240     for (UIPress *press in presses) {
   241         SDL_Scancode scancode = [self scancodeFromPressType:press.type];
   242         SDL_SendKeyboardKey(SDL_RELEASED, scancode);
   243     }
   244 
   245     [super pressesEnded:presses withEvent:event];
   246 }
   247 
   248 - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   249 {
   250     for (UIPress *press in presses) {
   251         SDL_Scancode scancode = [self scancodeFromPressType:press.type];
   252         SDL_SendKeyboardKey(SDL_RELEASED, scancode);
   253     }
   254 
   255     [super pressesCancelled:presses withEvent:event];
   256 }
   257 
   258 - (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
   259 {
   260     /* This is only called when the force of a press changes. */
   261     [super pressesChanged:presses withEvent:event];
   262 }
   263 #endif /* TARGET_OS_TV || defined(__IPHONE_9_1) */
   264 
   265 @end
   266 
   267 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   268 
   269 /* vi: set ts=4 sw=4 expandtab: */