SDL  2.0
SDL_uikitview.m
Go to the documentation of this file.
1  /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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 "SDL_hints.h"
28 #include "../../events/SDL_mouse_c.h"
29 #include "../../events/SDL_touch_c.h"
30 #include "../../events/SDL_events_c.h"
31 
32 #import "SDL_uikitappdelegate.h"
33 #import "SDL_uikitmodes.h"
34 #import "SDL_uikitwindow.h"
35 
36 /* This is defined in SDL_sysjoystick.m */
38 
39 @implementation SDL_uikitview {
40  SDL_Window *sdlwindow;
41 
42  SDL_TouchID touchId;
43  UITouch * __weak firstFingerDown;
44 }
45 
46 - (instancetype)initWithFrame:(CGRect)frame
47 {
48  if ((self = [super initWithFrame:frame])) {
49 #if TARGET_OS_TV
50  /* Apple TV Remote touchpad swipe gestures. */
51  UISwipeGestureRecognizer *swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
52  swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
53  [self addGestureRecognizer:swipeUp];
54 
55  UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
56  swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
57  [self addGestureRecognizer:swipeDown];
58 
59  UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
60  swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
61  [self addGestureRecognizer:swipeLeft];
62 
63  UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
64  swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
65  [self addGestureRecognizer:swipeRight];
66 #endif
67 
68  self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
69  self.autoresizesSubviews = YES;
70 
71 #if !TARGET_OS_TV
72  self.multipleTouchEnabled = YES;
73 #endif
74 
75  touchId = 1;
76  SDL_AddTouch(touchId, "");
77  }
78 
79  return self;
80 }
81 
82 - (void)setSDLWindow:(SDL_Window *)window
83 {
84  SDL_WindowData *data = nil;
85 
86  if (window == sdlwindow) {
87  return;
88  }
89 
90  /* Remove ourself from the old window. */
91  if (sdlwindow) {
92  SDL_uikitview *view = nil;
93  data = (__bridge SDL_WindowData *) sdlwindow->driverdata;
94 
95  [data.views removeObject:self];
96 
97  [self removeFromSuperview];
98 
99  /* Restore the next-oldest view in the old window. */
100  view = data.views.lastObject;
101 
102  data.viewcontroller.view = view;
103 
104  data.uiwindow.rootViewController = nil;
105  data.uiwindow.rootViewController = data.viewcontroller;
106 
107  [data.uiwindow layoutIfNeeded];
108  }
109 
110  /* Add ourself to the new window. */
111  if (window) {
112  data = (__bridge SDL_WindowData *) window->driverdata;
113 
114  /* Make sure the SDL window has a strong reference to this view. */
115  [data.views addObject:self];
116 
117  /* Replace the view controller's old view with this one. */
118  [data.viewcontroller.view removeFromSuperview];
119  data.viewcontroller.view = self;
120 
121  /* The root view controller handles rotation and the status bar.
122  * Assigning it also adds the controller's view to the window. We
123  * explicitly re-set it to make sure the view is properly attached to
124  * the window. Just adding the sub-view if the root view controller is
125  * already correct causes orientation issues on iOS 7 and below. */
126  data.uiwindow.rootViewController = nil;
127  data.uiwindow.rootViewController = data.viewcontroller;
128 
129  /* The view's bounds may not be correct until the next event cycle. That
130  * might happen after the current dimensions are queried, so we force a
131  * layout now to immediately update the bounds. */
132  [data.uiwindow layoutIfNeeded];
133  }
134 
135  sdlwindow = window;
136 }
137 
138 - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
139 {
140  CGPoint point = [touch locationInView:self];
141 
142  if (normalize) {
143  CGRect bounds = self.bounds;
144  point.x /= bounds.size.width;
145  point.y /= bounds.size.height;
146  }
147 
148  return point;
149 }
150 
151 - (float)pressureForTouch:(UITouch *)touch
152 {
153 #ifdef __IPHONE_9_0
154  if ([touch respondsToSelector:@selector(force)]) {
155  return (float) touch.force;
156  }
157 #endif
158 
159  return 1.0f;
160 }
161 
162 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
163 {
164  for (UITouch *touch in touches) {
165  float pressure = [self pressureForTouch:touch];
166 
167  if (!firstFingerDown) {
168  CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
169  int clicks = (int) touch.tapCount;
170 
171  /* send mouse moved event */
172  SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
173 
174  /* send mouse down event */
176 
177  firstFingerDown = touch;
178  }
179 
180  CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
181  SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
182  SDL_TRUE, locationInView.x, locationInView.y, pressure);
183  }
184 }
185 
186 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
187 {
188  for (UITouch *touch in touches) {
189  float pressure = [self pressureForTouch:touch];
190 
191  if (touch == firstFingerDown) {
192  /* send mouse up */
193  int clicks = (int) touch.tapCount;
195  firstFingerDown = nil;
196  }
197 
198  CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
199  SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
200  SDL_FALSE, locationInView.x, locationInView.y, pressure);
201  }
202 }
203 
204 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
205 {
206  [self touchesEnded:touches withEvent:event];
207 }
208 
209 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
210 {
211  for (UITouch *touch in touches) {
212  float pressure = [self pressureForTouch:touch];
213 
214  if (touch == firstFingerDown) {
215  CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
216 
217  /* send moved event */
218  SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
219  }
220 
221  CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
222  SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
223  locationInView.x, locationInView.y, pressure);
224  }
225 }
226 
227 #if TARGET_OS_TV || defined(__IPHONE_9_1)
228 - (SDL_Scancode)scancodeFromPressType:(UIPressType)presstype
229 {
230  switch (presstype) {
231  case UIPressTypeUpArrow:
232  return SDL_SCANCODE_UP;
233  case UIPressTypeDownArrow:
234  return SDL_SCANCODE_DOWN;
235  case UIPressTypeLeftArrow:
236  return SDL_SCANCODE_LEFT;
237  case UIPressTypeRightArrow:
238  return SDL_SCANCODE_RIGHT;
239  case UIPressTypeSelect:
240  /* HIG says: "primary button behavior" */
241  return SDL_SCANCODE_RETURN;
242  case UIPressTypeMenu:
243  /* HIG says: "returns to previous screen" */
244  return SDL_SCANCODE_ESCAPE;
245  case UIPressTypePlayPause:
246  /* HIG says: "secondary button behavior" */
247  return SDL_SCANCODE_PAUSE;
248  default:
249  return SDL_SCANCODE_UNKNOWN;
250  }
251 }
252 
253 - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
254 {
256  for (UIPress *press in presses) {
257  SDL_Scancode scancode = [self scancodeFromPressType:press.type];
258  SDL_SendKeyboardKey(SDL_PRESSED, scancode);
259  }
260  }
261  [super pressesBegan:presses withEvent:event];
262 }
263 
264 - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
265 {
267  for (UIPress *press in presses) {
268  SDL_Scancode scancode = [self scancodeFromPressType:press.type];
270  }
271  }
272  [super pressesEnded:presses withEvent:event];
273 }
274 
275 - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
276 {
278  for (UIPress *press in presses) {
279  SDL_Scancode scancode = [self scancodeFromPressType:press.type];
281  }
282  }
283  [super pressesCancelled:presses withEvent:event];
284 }
285 
286 - (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
287 {
288  /* This is only called when the force of a press changes. */
289  [super pressesChanged:presses withEvent:event];
290 }
291 #endif /* TARGET_OS_TV || defined(__IPHONE_9_1) */
292 
293 #if TARGET_OS_TV
294 -(void)swipeGesture:(UISwipeGestureRecognizer *)gesture
295 {
296  /* Swipe gestures don't trigger begin states. */
297  if (gesture.state == UIGestureRecognizerStateEnded) {
299  /* Send arrow key presses for now, as we don't have an external API
300  * which better maps to swipe gestures. */
301  switch (gesture.direction) {
302  case UISwipeGestureRecognizerDirectionUp:
305  break;
306  case UISwipeGestureRecognizerDirectionDown:
309  break;
310  case UISwipeGestureRecognizerDirectionLeft:
313  break;
314  case UISwipeGestureRecognizerDirectionRight:
317  break;
318  }
319  }
320  }
321 }
322 #endif /* TARGET_OS_TV */
323 
324 @end
325 
326 #endif /* SDL_VIDEO_DRIVER_UIKIT */
327 
328 /* vi: set ts=4 sw=4 expandtab: */
Sint64 SDL_FingerID
Definition: SDL_touch.h:42
SDL_uikitviewcontroller * viewcontroller
int SDL_SendMouseButtonClicks(SDL_Window *window, SDL_MouseID mouseID, Uint8 state, Uint8 button, int clicks)
Definition: SDL_mouse.c:499
int SDL_SendTouch(SDL_TouchID id, SDL_FingerID fingerid, SDL_bool down, float x, float y, float pressure)
Definition: SDL_touch.c:222
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
#define SDL_TOUCH_MOUSEID
Definition: SDL_touch.h:53
int SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode)
Definition: SDL_keyboard.c:679
int SDL_SendTouchMotion(SDL_TouchID id, SDL_FingerID fingerid, float x, float y, float pressure)
Definition: SDL_touch.c:284
NSMutableArray * views
int SDL_SendMouseMotion(SDL_Window *window, SDL_MouseID mouseID, int relative, int x, int y)
Definition: SDL_mouse.c:237
#define SDL_BUTTON_LEFT
Definition: SDL_mouse.h:282
int frame
Definition: teststreaming.c:60
Sint64 SDL_TouchID
Definition: SDL_touch.h:41
int SDL_AddTouch(SDL_TouchID touchID, const char *name)
Definition: SDL_touch.c:136
UIWindow * uiwindow
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
The type used to identify a window.
Definition: SDL_sysvideo.h:73
int SDL_AppleTVRemoteOpenedAsJoystick
void * driverdata
Definition: SDL_sysvideo.h:111
#define SDL_PRESSED
Definition: SDL_events.h:50
#define SDL_RELEASED
Definition: SDL_events.h:49
GLuint in
SDL_Scancode
The SDL keyboard scancode representation.
Definition: SDL_scancode.h:43