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