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 int clicks = (int) touch.tapCount; 147 148 /* send mouse moved event */ 149 SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y); 150 151 /* send mouse down event */ 152 SDL_SendMouseButtonClicks(sdlwindow, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT, clicks); 153 154 firstFingerDown = touch; 155 } 156 157 CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; 158 SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch), 159 SDL_TRUE, locationInView.x, locationInView.y, pressure); 160 } 161} 162 163- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 164{ 165 for (UITouch *touch in touches) { 166 float pressure = [self pressureForTouch:touch]; 167 168 if (touch == firstFingerDown) { 169 /* send mouse up */ 170 int clicks = (int) touch.tapCount; 171 SDL_SendMouseButtonClicks(sdlwindow, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT, clicks); 172 firstFingerDown = nil; 173 } 174 175 CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; 176 SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch), 177 SDL_FALSE, locationInView.x, locationInView.y, pressure); 178 } 179} 180 181- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 182{ 183 [self touchesEnded:touches withEvent:event]; 184} 185 186- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 187{ 188 for (UITouch *touch in touches) { 189 float pressure = [self pressureForTouch:touch]; 190 191 if (touch == firstFingerDown) { 192 CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO]; 193 194 /* send moved event */ 195 SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y); 196 } 197 198 CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; 199 SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch), 200 locationInView.x, locationInView.y, pressure); 201 } 202} 203 204#if TARGET_OS_TV || defined(__IPHONE_9_1) 205- (SDL_Scancode)scancodeFromPressType:(UIPressType)presstype 206{ 207 switch (presstype) { 208 case UIPressTypeUpArrow: 209 return SDL_SCANCODE_UP; 210 case UIPressTypeDownArrow: 211 return SDL_SCANCODE_DOWN; 212 case UIPressTypeLeftArrow: 213 return SDL_SCANCODE_LEFT; 214 case UIPressTypeRightArrow: 215 return SDL_SCANCODE_RIGHT; 216 case UIPressTypeSelect: 217 /* HIG says: "primary button behavior" */ 218 return SDL_SCANCODE_SELECT; 219 case UIPressTypeMenu: 220 /* HIG says: "returns to previous screen" */ 221 return SDL_SCANCODE_MENU; 222 case UIPressTypePlayPause: 223 /* HIG says: "secondary button behavior" */ 224 return SDL_SCANCODE_PAUSE; 225 default: 226 return SDL_SCANCODE_UNKNOWN; 227 } 228} 229 230- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event 231{ 232 for (UIPress *press in presses) { 233 SDL_Scancode scancode = [self scancodeFromPressType:press.type]; 234 SDL_SendKeyboardKey(SDL_PRESSED, scancode); 235 } 236 237 [super pressesBegan:presses withEvent:event]; 238} 239 240- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event 241{ 242 for (UIPress *press in presses) { 243 SDL_Scancode scancode = [self scancodeFromPressType:press.type]; 244 SDL_SendKeyboardKey(SDL_RELEASED, scancode); 245 } 246 247 [super pressesEnded:presses withEvent:event]; 248} 249 250- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event 251{ 252 for (UIPress *press in presses) { 253 SDL_Scancode scancode = [self scancodeFromPressType:press.type]; 254 SDL_SendKeyboardKey(SDL_RELEASED, scancode); 255 } 256 257 [super pressesCancelled:presses withEvent:event]; 258} 259 260- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event 261{ 262 /* This is only called when the force of a press changes. */ 263 [super pressesChanged:presses withEvent:event]; 264} 265#endif /* TARGET_OS_TV || defined(__IPHONE_9_1) */ 266 267@end 268 269#endif /* SDL_VIDEO_DRIVER_UIKIT */ 270 271/* vi: set ts=4 sw=4 expandtab: */ 272