• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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