/
SDL_uikitwindow.m
394 lines (321 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#if SDL_VIDEO_DRIVER_UIKIT
#include "SDL_syswm.h"
#include "SDL_video.h"
#include "SDL_mouse.h"
#include "SDL_assert.h"
#include "SDL_hints.h"
#include "../SDL_sysvideo.h"
#include "../SDL_pixels_c.h"
#include "../../events/SDL_events_c.h"
#include "SDL_uikitvideo.h"
#include "SDL_uikitevents.h"
#include "SDL_uikitmodes.h"
#include "SDL_uikitwindow.h"
#import "SDL_uikitappdelegate.h"
40
#import "SDL_uikitview.h"
41
42
43
#include <Foundation/Foundation.h>
44
45
46
47
@implementation SDL_WindowData
@synthesize uiwindow;
@synthesize viewcontroller;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
@synthesize views;
- (instancetype)init
{
if ((self = [super init])) {
views = [NSMutableArray new];
}
return self;
}
@end
@interface SDL_uikitwindow : UIWindow
- (void)layoutSubviews;
@end
@implementation SDL_uikitwindow
- (void)layoutSubviews
{
/* Workaround to fix window orientation issues in iOS 8+. */
self.frame = self.screen.bounds;
[super layoutSubviews];
}
75
76
77
@end
78
79
static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bool created)
80
81
{
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
82
SDL_DisplayData *displaydata = (__bridge SDL_DisplayData *) display->driverdata;
83
84
85
86
87
SDL_uikitview *view;
CGRect frame = UIKit_ComputeViewFrame(window, displaydata.uiscreen);
int width = (int) frame.size.width;
int height = (int) frame.size.height;
88
89
SDL_WindowData *data = [[SDL_WindowData alloc] init];
90
91
92
if (!data) {
return SDL_OutOfMemory();
}
93
94
95
window->driverdata = (void *) CFBridgingRetain(data);
96
data.uiwindow = uiwindow;
97
98
99
/* only one window on iOS, always shown */
window->flags &= ~SDL_WINDOW_HIDDEN;
100
101
102
103
104
105
106
107
108
109
110
111
112
if (displaydata.uiscreen == [UIScreen mainScreen]) {
window->flags |= SDL_WINDOW_INPUT_FOCUS; /* always has input focus */
} else {
window->flags &= ~SDL_WINDOW_RESIZABLE; /* window is NEVER resizable */
window->flags &= ~SDL_WINDOW_INPUT_FOCUS; /* never has input focus */
window->flags |= SDL_WINDOW_BORDERLESS; /* never has a status bar. */
}
if (displaydata.uiscreen == [UIScreen mainScreen]) {
NSUInteger orients = UIKit_GetSupportedOrientations(window);
BOOL supportsLandscape = (orients & UIInterfaceOrientationMaskLandscape) != 0;
BOOL supportsPortrait = (orients & (UIInterfaceOrientationMaskPortrait|UIInterfaceOrientationMaskPortraitUpsideDown)) != 0;
113
114
/* Make sure the width/height are oriented correctly */
115
if ((width > height && !supportsLandscape) || (height > width && !supportsPortrait)) {
116
117
118
119
120
121
int temp = width;
width = height;
height = temp;
}
}
122
123
124
125
window->x = 0;
window->y = 0;
window->w = width;
window->h = height;
126
127
128
129
/* The View Controller will handle rotating the view when the device
* orientation changes. This will trigger resize events, if appropriate. */
data.viewcontroller = [[SDL_uikitviewcontroller alloc] initWithSDLWindow:window];
130
131
132
133
/* The window will initially contain a generic view so resizes, touch events,
* etc. can be handled without an active OpenGL view/context. */
view = [[SDL_uikitview alloc] initWithFrame:frame];
134
135
136
137
/* Sets this view as the controller's view, and adds the view to the window
* heirarchy. */
[view setSDLWindow:window];
138
139
140
141
142
143
/* Make this window the current mouse focus for touch input */
if (displaydata.uiscreen == [UIScreen mainScreen]) {
SDL_SetMouseFocus(window);
SDL_SetKeyboardFocus(window);
}
144
145
146
147
148
149
150
return 0;
}
int
UIKit_CreateWindow(_THIS, SDL_Window *window)
{
151
152
@autoreleasepool {
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
153
SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata;
154
const CGSize origsize = data.uiscreen.currentMode.size;
155
156
157
158
159
160
161
/* SDL currently puts this window at the start of display's linked list. We rely on this. */
SDL_assert(_this->windows == window);
/* We currently only handle a single window per display on iOS */
if (window->next != NULL) {
return SDL_SetError("Only one window allowed per display.");
162
}
163
164
165
/* If monitor has a resolution of 0x0 (hasn't been explicitly set by the
* user, so it's in standby), try to force the display to a resolution
166
* that most closely matches the desired window size. */
167
168
169
170
if ((origsize.width == 0.0f) && (origsize.height == 0.0f)) {
if (display->num_display_modes == 0) {
_this->GetDisplayModes(_this, display);
}
171
172
173
174
175
int i;
const SDL_DisplayMode *bestmode = NULL;
for (i = display->num_display_modes; i >= 0; i--) {
const SDL_DisplayMode *mode = &display->display_modes[i];
176
if ((mode->w >= window->w) && (mode->h >= window->h)) {
177
bestmode = mode;
178
}
179
}
180
181
if (bestmode) {
182
183
SDL_DisplayModeData *modedata = (__bridge SDL_DisplayModeData *)bestmode->driverdata;
[data.uiscreen setCurrentMode:modedata.uiscreenmode];
184
185
186
/* desktop_mode doesn't change here (the higher level will
* use it to set all the screens back to their defaults
187
* upon window destruction, SDL_Quit(), etc. */
188
189
display->current_mode = *bestmode;
}
190
191
}
192
if (data.uiscreen == [UIScreen mainScreen]) {
193
194
195
NSUInteger orientations = UIKit_GetSupportedOrientations(window);
UIApplication *app = [UIApplication sharedApplication];
196
if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) {
197
app.statusBarHidden = YES;
198
} else {
199
app.statusBarHidden = NO;
200
}
201
}
202
203
204
/* ignore the size user requested, and make a fullscreen window */
/* !!! FIXME: can we have a smaller view? */
205
206
207
208
UIWindow *uiwindow = [[SDL_uikitwindow alloc] initWithFrame:data.uiscreen.bounds];
/* put the window on an external display if appropriate. */
if (data.uiscreen != [UIScreen mainScreen]) {
209
[uiwindow setScreen:data.uiscreen];
210
}
211
212
213
214
if (SetupWindowData(_this, window, uiwindow, SDL_TRUE) < 0) {
return -1;
}
215
216
217
218
219
}
return 1;
}
220
221
222
223
void
UIKit_SetWindowTitle(_THIS, SDL_Window * window)
{
@autoreleasepool {
224
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
225
data.viewcontroller.title = @(window->title);
226
227
228
}
}
229
230
231
void
UIKit_ShowWindow(_THIS, SDL_Window * window)
{
232
@autoreleasepool {
233
234
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
[data.uiwindow makeKeyAndVisible];
235
}
236
237
238
239
240
}
void
UIKit_HideWindow(_THIS, SDL_Window * window)
{
241
@autoreleasepool {
242
243
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
data.uiwindow.hidden = YES;
244
}
245
246
247
248
249
250
}
void
UIKit_RaiseWindow(_THIS, SDL_Window * window)
{
/* We don't currently offer a concept of "raising" the SDL window, since
251
* we only allow one per display, in the iOS fashion.
252
* However, we use this entry point to rebind the context to the view
253
* during OnWindowRestored processing. */
254
255
256
_this->GL_MakeCurrent(_this, _this->current_glwin, _this->current_glctx);
}
257
258
static void
UIKit_UpdateWindowBorder(_THIS, SDL_Window * window)
259
{
260
261
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
SDL_uikitviewcontroller *viewcontroller = data.viewcontroller;
262
263
if (data.uiwindow.screen == [UIScreen mainScreen]) {
264
265
266
267
268
if (window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS)) {
[UIApplication sharedApplication].statusBarHidden = YES;
} else {
[UIApplication sharedApplication].statusBarHidden = NO;
}
269
270
271
272
273
/* iOS 7+ won't update the status bar until we tell it to. */
if ([viewcontroller respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
[viewcontroller setNeedsStatusBarAppearanceUpdate];
}
274
275
}
276
/* Update the view's frame to account for the status bar change. */
277
278
279
viewcontroller.view.frame = UIKit_ComputeViewFrame(window, data.uiwindow.screen);
[viewcontroller.view setNeedsLayout];
[viewcontroller.view layoutIfNeeded];
280
281
}
282
283
284
void
UIKit_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
{
285
286
287
@autoreleasepool {
UIKit_UpdateWindowBorder(_this, window);
}
288
289
290
291
292
}
void
UIKit_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
{
293
294
295
@autoreleasepool {
UIKit_UpdateWindowBorder(_this, window);
}
296
297
}
298
299
300
void
UIKit_DestroyWindow(_THIS, SDL_Window * window)
{
301
@autoreleasepool {
302
if (window->driverdata != NULL) {
303
304
SDL_WindowData *data = (SDL_WindowData *) CFBridgingRelease(window->driverdata);
[data.viewcontroller stopAnimation];
305
}
306
}
307
window->driverdata = NULL;
308
309
310
311
312
}
SDL_bool
UIKit_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
{
313
@autoreleasepool {
314
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
315
316
317
if (info->version.major <= SDL_MAJOR_VERSION) {
info->subsystem = SDL_SYSWM_UIKIT;
318
info->info.uikit.window = data.uiwindow;
319
320
321
322
323
324
return SDL_TRUE;
} else {
SDL_SetError("Application not compiled with SDL %d.%d\n",
SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
return SDL_FALSE;
}
325
326
327
}
}
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
NSUInteger
UIKit_GetSupportedOrientations(SDL_Window * window)
{
const char *hint = SDL_GetHint(SDL_HINT_ORIENTATIONS);
NSUInteger orientationMask = 0;
@autoreleasepool {
if (hint != NULL) {
NSArray *orientations = [@(hint) componentsSeparatedByString:@" "];
if ([orientations containsObject:@"LandscapeLeft"]) {
orientationMask |= UIInterfaceOrientationMaskLandscapeLeft;
}
if ([orientations containsObject:@"LandscapeRight"]) {
orientationMask |= UIInterfaceOrientationMaskLandscapeRight;
}
if ([orientations containsObject:@"Portrait"]) {
orientationMask |= UIInterfaceOrientationMaskPortrait;
}
if ([orientations containsObject:@"PortraitUpsideDown"]) {
orientationMask |= UIInterfaceOrientationMaskPortraitUpsideDown;
}
}
if (orientationMask == 0 && (window->flags & SDL_WINDOW_RESIZABLE)) {
/* any orientation is okay. */
orientationMask = UIInterfaceOrientationMaskAll;
}
if (orientationMask == 0) {
if (window->w >= window->h) {
orientationMask |= UIInterfaceOrientationMaskLandscape;
}
if (window->h >= window->w) {
orientationMask |= (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
}
}
/* Don't allow upside-down orientation on the phone, so answering calls is in the natural orientation */
367
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
368
369
370
371
372
373
374
orientationMask &= ~UIInterfaceOrientationMaskPortraitUpsideDown;
}
}
return orientationMask;
}
375
376
377
int
SDL_iPhoneSetAnimationCallback(SDL_Window * window, int interval, void (*callback)(void*), void *callbackParam)
{
378
379
380
if (!window || !window->driverdata) {
return SDL_SetError("Invalid window");
}
381
382
383
384
385
386
@autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
[data.viewcontroller setAnimationCallback:interval
callback:callback
callbackParam:callbackParam];
387
388
}
389
390
391
392
393
394
return 0;
}
#endif /* SDL_VIDEO_DRIVER_UIKIT */
/* vi: set ts=4 sw=4 expandtab: */