Add SDL_TouchDeviceType enum and SDL_GetTouchDeviceType(SDL_TouchID id).
Touch device types include SDL_TOUCH_DEVICE_DIRECT (a touch screen with window-relative coordinates for touches), SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE (a trackpad-style device with absolute device coordinates), and SDL_TOUCH_DEVICE_INDIRECT_RELATIVE (a trackpad-style device with screen cursor-relative coordinates).
Phone screens are an example of a direct device type. Mac trackpads are the indirect-absolute touch device type. The Apple TV remote is an indirect-relative touch device type.
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../../SDL_internal.h"
23 #if SDL_VIDEO_DRIVER_UIKIT
25 #include "SDL_uikitview.h"
27 #include "SDL_hints.h"
28 #include "../../events/SDL_mouse_c.h"
29 #include "../../events/SDL_touch_c.h"
30 #include "../../events/SDL_events_c.h"
32 #import "SDL_uikitappdelegate.h"
33 #import "SDL_uikitmodes.h"
34 #import "SDL_uikitwindow.h"
36 /* This is defined in SDL_sysjoystick.m */
37 extern int SDL_AppleTVRemoteOpenedAsJoystick;
39 @implementation SDL_uikitview {
40 SDL_Window *sdlwindow;
42 SDL_TouchID directTouchId;
43 SDL_TouchID indirectTouchId;
45 UITouch * __weak firstFingerDown;
48 - (instancetype)initWithFrame:(CGRect)frame
50 if ((self = [super initWithFrame:frame])) {
52 /* Apple TV Remote touchpad swipe gestures. */
53 UISwipeGestureRecognizer *swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
54 swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
55 [self addGestureRecognizer:swipeUp];
57 UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
58 swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
59 [self addGestureRecognizer:swipeDown];
61 UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
62 swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
63 [self addGestureRecognizer:swipeLeft];
65 UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
66 swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
67 [self addGestureRecognizer:swipeRight];
70 self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
71 self.autoresizesSubviews = YES;
77 self.multipleTouchEnabled = YES;
78 SDL_AddTouch(directTouchId, SDL_TOUCH_DEVICE_DIRECT, "");
85 - (void)setSDLWindow:(SDL_Window *)window
87 SDL_WindowData *data = nil;
89 if (window == sdlwindow) {
93 /* Remove ourself from the old window. */
95 SDL_uikitview *view = nil;
96 data = (__bridge SDL_WindowData *) sdlwindow->driverdata;
98 [data.views removeObject:self];
100 [self removeFromSuperview];
102 /* Restore the next-oldest view in the old window. */
103 view = data.views.lastObject;
105 data.viewcontroller.view = view;
107 data.uiwindow.rootViewController = nil;
108 data.uiwindow.rootViewController = data.viewcontroller;
110 [data.uiwindow layoutIfNeeded];
113 /* Add ourself to the new window. */
115 data = (__bridge SDL_WindowData *) window->driverdata;
117 /* Make sure the SDL window has a strong reference to this view. */
118 [data.views addObject:self];
120 /* Replace the view controller's old view with this one. */
121 [data.viewcontroller.view removeFromSuperview];
122 data.viewcontroller.view = self;
124 /* The root view controller handles rotation and the status bar.
125 * Assigning it also adds the controller's view to the window. We
126 * explicitly re-set it to make sure the view is properly attached to
127 * the window. Just adding the sub-view if the root view controller is
128 * already correct causes orientation issues on iOS 7 and below. */
129 data.uiwindow.rootViewController = nil;
130 data.uiwindow.rootViewController = data.viewcontroller;
132 /* The view's bounds may not be correct until the next event cycle. That
133 * might happen after the current dimensions are queried, so we force a
134 * layout now to immediately update the bounds. */
135 [data.uiwindow layoutIfNeeded];
141 - (SDL_TouchDeviceType)touchTypeForTouch:(UITouch *)touch
144 if ([touch respondsToSelector:@selector((type))]) {
145 if (touch.type == UITouchTypeIndirect) {
146 return SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
151 return SDL_TOUCH_DEVICE_DIRECT;
154 - (SDL_TouchID)touchIdForType:(SDL_TouchDeviceType)type
157 case SDL_TOUCH_DEVICE_DIRECT:
159 return directTouchId;
160 case SDL_TOUCH_DEVICE_INDIRECT_RELATIVE:
161 return indirectTouchId;
165 - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
167 CGPoint point = [touch locationInView:self];
170 CGRect bounds = self.bounds;
171 point.x /= bounds.size.width;
172 point.y /= bounds.size.height;
178 - (float)pressureForTouch:(UITouch *)touch
181 if ([touch respondsToSelector:@selector(force)]) {
182 return (float) touch.force;
189 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
191 for (UITouch *touch in touches) {
192 SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
193 SDL_TouchID touchId = [self touchIdForType:touchType];
194 float pressure = [self pressureForTouch:touch];
196 if (SDL_AddTouch(touchId, touchType, "") < 0) {
200 if (!firstFingerDown) {
201 CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
202 int clicks = (int) touch.tapCount;
204 /* send mouse moved event */
205 SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
207 /* send mouse down event */
208 SDL_SendMouseButtonClicks(sdlwindow, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT, clicks);
210 firstFingerDown = touch;
213 CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
214 SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
215 SDL_TRUE, locationInView.x, locationInView.y, pressure);
219 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
221 for (UITouch *touch in touches) {
222 SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
223 SDL_TouchID touchId = [self touchIdForType:touchType];
224 float pressure = [self pressureForTouch:touch];
226 if (SDL_AddTouch(touchId, touchType, "") < 0) {
230 if (touch == firstFingerDown) {
232 int clicks = (int) touch.tapCount;
233 SDL_SendMouseButtonClicks(sdlwindow, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT, clicks);
234 firstFingerDown = nil;
237 CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
238 SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
239 SDL_FALSE, locationInView.x, locationInView.y, pressure);
243 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
245 [self touchesEnded:touches withEvent:event];
248 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
250 for (UITouch *touch in touches) {
251 SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
252 SDL_TouchID touchId = [self touchIdForType:touchType];
253 float pressure = [self pressureForTouch:touch];
255 if (SDL_AddTouch(touchId, touchType, "") < 0) {
259 if (touch == firstFingerDown) {
260 CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
262 /* send moved event */
263 SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
266 CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
267 SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
268 locationInView.x, locationInView.y, pressure);
272 #if TARGET_OS_TV || defined(__IPHONE_9_1)
273 - (SDL_Scancode)scancodeFromPressType:(UIPressType)presstype
276 case UIPressTypeUpArrow:
277 return SDL_SCANCODE_UP;
278 case UIPressTypeDownArrow:
279 return SDL_SCANCODE_DOWN;
280 case UIPressTypeLeftArrow:
281 return SDL_SCANCODE_LEFT;
282 case UIPressTypeRightArrow:
283 return SDL_SCANCODE_RIGHT;
284 case UIPressTypeSelect:
285 /* HIG says: "primary button behavior" */
286 return SDL_SCANCODE_RETURN;
287 case UIPressTypeMenu:
288 /* HIG says: "returns to previous screen" */
289 return SDL_SCANCODE_ESCAPE;
290 case UIPressTypePlayPause:
291 /* HIG says: "secondary button behavior" */
292 return SDL_SCANCODE_PAUSE;
294 return SDL_SCANCODE_UNKNOWN;
298 - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
300 if (!SDL_AppleTVRemoteOpenedAsJoystick) {
301 for (UIPress *press in presses) {
302 SDL_Scancode scancode = [self scancodeFromPressType:press.type];
303 SDL_SendKeyboardKey(SDL_PRESSED, scancode);
306 [super pressesBegan:presses withEvent:event];
309 - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
311 if (!SDL_AppleTVRemoteOpenedAsJoystick) {
312 for (UIPress *press in presses) {
313 SDL_Scancode scancode = [self scancodeFromPressType:press.type];
314 SDL_SendKeyboardKey(SDL_RELEASED, scancode);
317 [super pressesEnded:presses withEvent:event];
320 - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
322 if (!SDL_AppleTVRemoteOpenedAsJoystick) {
323 for (UIPress *press in presses) {
324 SDL_Scancode scancode = [self scancodeFromPressType:press.type];
325 SDL_SendKeyboardKey(SDL_RELEASED, scancode);
328 [super pressesCancelled:presses withEvent:event];
331 - (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
333 /* This is only called when the force of a press changes. */
334 [super pressesChanged:presses withEvent:event];
336 #endif /* TARGET_OS_TV || defined(__IPHONE_9_1) */
339 -(void)swipeGesture:(UISwipeGestureRecognizer *)gesture
341 /* Swipe gestures don't trigger begin states. */
342 if (gesture.state == UIGestureRecognizerStateEnded) {
343 if (!SDL_AppleTVRemoteOpenedAsJoystick) {
344 /* Send arrow key presses for now, as we don't have an external API
345 * which better maps to swipe gestures. */
346 switch (gesture.direction) {
347 case UISwipeGestureRecognizerDirectionUp:
348 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_UP);
349 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_UP);
351 case UISwipeGestureRecognizerDirectionDown:
352 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_DOWN);
353 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_DOWN);
355 case UISwipeGestureRecognizerDirectionLeft:
356 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LEFT);
357 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LEFT);
359 case UISwipeGestureRecognizerDirectionRight:
360 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RIGHT);
361 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RIGHT);
367 #endif /* TARGET_OS_TV */
371 #endif /* SDL_VIDEO_DRIVER_UIKIT */
373 /* vi: set ts=4 sw=4 expandtab: */