#include "../../SDL_internal.h"
#if SDL_VIDEO_DRIVER_UIKIT
#include "SDL_hints.h"
#include "SDL_mouse.h"
#include "SDL_system.h"
#include "SDL_syswm.h"
#include "SDL_video.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"
#import "SDL_uikitview.h"
#import "SDL_uikitopenglview.h"
#include <Foundation/Foundation.h>
@implementation SDL_WindowData
@synthesize uiwindow;
@synthesize viewcontroller;
@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
{
if (!UIKit_IsSystemVersionAtLeast(9.0)) {
self.frame = self.screen.bounds;
}
[super layoutSubviews];
}
@end
static int
SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bool created)
{
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
SDL_DisplayData *displaydata = (__bridge SDL_DisplayData *) display->driverdata;
SDL_uikitview *view;
CGRect frame = UIKit_ComputeViewFrame(window, displaydata.uiscreen);
int width = (int) frame.size.width;
int height = (int) frame.size.height;
SDL_WindowData *data = [[SDL_WindowData alloc] init];
if (!data) {
return SDL_OutOfMemory();
}
window->driverdata = (void *) CFBridgingRetain(data);
data.uiwindow = uiwindow;
window->flags &= ~SDL_WINDOW_HIDDEN;
if (displaydata.uiscreen != [UIScreen mainScreen]) {
window->flags &= ~SDL_WINDOW_RESIZABLE;
window->flags &= ~SDL_WINDOW_INPUT_FOCUS;
window->flags |= SDL_WINDOW_BORDERLESS;
}
#if !TARGET_OS_TV
if (displaydata.uiscreen == [UIScreen mainScreen]) {
window->w = window->windowed.w;
window->h = window->windowed.h;
NSUInteger orients = UIKit_GetSupportedOrientations(window);
BOOL supportsLandscape = (orients & UIInterfaceOrientationMaskLandscape) != 0;
BOOL supportsPortrait = (orients & (UIInterfaceOrientationMaskPortrait|UIInterfaceOrientationMaskPortraitUpsideDown)) != 0;
if ((width > height && !supportsLandscape) || (height > width && !supportsPortrait)) {
int temp = width;
width = height;
height = temp;
}
}
#endif
#if 0#endif
window->w = width;
window->h = height;
data.viewcontroller = [[SDL_uikitviewcontroller alloc] initWithSDLWindow:window];
view = [[SDL_uikitview alloc] initWithFrame:frame];
[view setSDLWindow:window];
return 0;
}
int
UIKit_CreateWindow(_THIS, SDL_Window *window)
{
@autoreleasepool {
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata;
SDL_Window *other;
for (other = _this->windows; other; other = other->next) {
if (other != window && SDL_GetDisplayForWindow(other) == display) {
return SDL_SetError("Only one window allowed per display.");
}
}
#if !TARGET_OS_TV
const CGSize origsize = data.uiscreen.currentMode.size;
if ((origsize.width == 0.0f) && (origsize.height == 0.0f)) {
if (display->num_display_modes == 0) {
_this->GetDisplayModes(_this, display);
}
int i;
const SDL_DisplayMode *bestmode = NULL;
for (i = display->num_display_modes; i >= 0; i--) {
const SDL_DisplayMode *mode = &display->display_modes[i];
if ((mode->w >= window->w) && (mode->h >= window->h)) {
bestmode = mode;
}
}
if (bestmode) {
SDL_DisplayModeData *modedata = (__bridge SDL_DisplayModeData *)bestmode->driverdata;
[data.uiscreen setCurrentMode:modedata.uiscreenmode];
display->current_mode = *bestmode;
}
}
if (data.uiscreen == [UIScreen mainScreen]) {
if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) {
[UIApplication sharedApplication].statusBarHidden = YES;
} else {
[UIApplication sharedApplication].statusBarHidden = NO;
}
}
#endif
UIWindow *uiwindow = [[SDL_uikitwindow alloc] initWithFrame:data.uiscreen.bounds];
if (data.uiscreen != [UIScreen mainScreen]) {
[uiwindow setScreen:data.uiscreen];
}
if (SetupWindowData(_this, window, uiwindow, SDL_TRUE) < 0) {
return -1;
}
}
return 1;
}
void
UIKit_SetWindowTitle(_THIS, SDL_Window * window)
{
@autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
data.viewcontroller.title = @(window->title);
}
}
void
UIKit_ShowWindow(_THIS, SDL_Window * window)
{
@autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
[data.uiwindow makeKeyAndVisible];
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
SDL_DisplayData *displaydata = (__bridge SDL_DisplayData *) display->driverdata;
if (displaydata.uiscreen == [UIScreen mainScreen]) {
SDL_SetMouseFocus(window);
SDL_SetKeyboardFocus(window);
}
}
}
void
UIKit_HideWindow(_THIS, SDL_Window * window)
{
@autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
data.uiwindow.hidden = YES;
}
}
void
UIKit_RaiseWindow(_THIS, SDL_Window * window)
{
_this->GL_MakeCurrent(_this, _this->current_glwin, _this->current_glctx);
}
static void
UIKit_UpdateWindowBorder(_THIS, SDL_Window * window)
{
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
SDL_uikitviewcontroller *viewcontroller = data.viewcontroller;
#if !TARGET_OS_TV
if (data.uiwindow.screen == [UIScreen mainScreen]) {
if (window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS)) {
[UIApplication sharedApplication].statusBarHidden = YES;
} else {
[UIApplication sharedApplication].statusBarHidden = NO;
}
[viewcontroller setNeedsStatusBarAppearanceUpdate];
}
viewcontroller.view.frame = UIKit_ComputeViewFrame(window, data.uiwindow.screen);
#endif
#ifdef SDL_IPHONE_KEYBOARD
[viewcontroller updateKeyboard];
#endif
[viewcontroller.view setNeedsLayout];
[viewcontroller.view layoutIfNeeded];
}
void
UIKit_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
{
@autoreleasepool {
UIKit_UpdateWindowBorder(_this, window);
}
}
void
UIKit_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
{
@autoreleasepool {
UIKit_UpdateWindowBorder(_this, window);
}
}
void
UIKit_SetWindowMouseGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
{
}
void
UIKit_UpdatePointerLock(_THIS, SDL_Window * window)
{
#if !TARGET_OS_TV
#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
@autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
SDL_uikitviewcontroller *viewcontroller = data.viewcontroller;
if (@available(iOS 14.0, *)) {
[viewcontroller setNeedsUpdateOfPrefersPointerLocked];
}
}
#endif
#endif
}
void
UIKit_DestroyWindow(_THIS, SDL_Window * window)
{
@autoreleasepool {
if (window->driverdata != NULL) {
SDL_WindowData *data = (SDL_WindowData *) CFBridgingRelease(window->driverdata);
NSArray *views = nil;
[data.viewcontroller stopAnimation];
views = [data.views copy];
for (SDL_uikitview *view in views) {
[view setSDLWindow:NULL];
}
data.uiwindow.rootViewController = nil;
data.uiwindow.hidden = YES;
}
}
window->driverdata = NULL;
}
void
UIKit_GetWindowSizeInPixels(_THIS, SDL_Window * window, int *w, int *h)
{ @autoreleasepool
{
SDL_WindowData *windata = (__bridge SDL_WindowData *) window->driverdata;
UIView *view = windata.viewcontroller.view;
CGSize size = view.bounds.size;
CGFloat scale = 1.0;
if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
scale = windata.uiwindow.screen.nativeScale;
}
*w = size.width * scale;
*h = size.height * scale;
}}
SDL_bool
UIKit_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
{
@autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
if (info->version.major <= SDL_MAJOR_VERSION) {
int versionnum = SDL_VERSIONNUM(info->version.major, info->version.minor, info->version.patch);
info->subsystem = SDL_SYSWM_UIKIT;
info->info.uikit.window = data.uiwindow;
if (versionnum >= SDL_VERSIONNUM(2,0,4)) {
#if SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
if ([data.viewcontroller.view isKindOfClass:[SDL_uikitopenglview class]]) {
SDL_uikitopenglview *glview = (SDL_uikitopenglview *)data.viewcontroller.view;
info->info.uikit.framebuffer = glview.drawableFramebuffer;
info->info.uikit.colorbuffer = glview.drawableRenderbuffer;
info->info.uikit.resolveFramebuffer = glview.msaaResolveFramebuffer;
} else {
#else
{
#endif
info->info.uikit.framebuffer = 0;
info->info.uikit.colorbuffer = 0;
info->info.uikit.resolveFramebuffer = 0;
}
}
return SDL_TRUE;
} else {
SDL_SetError("Application not compiled with SDL %d",
SDL_MAJOR_VERSION);
return SDL_FALSE;
}
}
}
#if !TARGET_OS_TV
NSUInteger
UIKit_GetSupportedOrientations(SDL_Window * window)
{
const char *hint = SDL_GetHint(SDL_HINT_ORIENTATIONS);
NSUInteger validOrientations = UIInterfaceOrientationMaskAll;
NSUInteger orientationMask = 0;
@autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
UIApplication *app = [UIApplication sharedApplication];
if ([app.delegate respondsToSelector:@selector(application:supportedInterfaceOrientationsForWindow:)]) {
validOrientations = [app.delegate application:app supportedInterfaceOrientationsForWindow:data.uiwindow];
} else {
validOrientations = [app supportedInterfaceOrientationsForWindow:data.uiwindow];
}
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)) {
orientationMask = UIInterfaceOrientationMaskAll;
}
if (orientationMask == 0) {
if (window->w >= window->h) {
orientationMask |= UIInterfaceOrientationMaskLandscape;
}
if (window->h >= window->w) {
orientationMask |= (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
}
}
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
orientationMask &= ~UIInterfaceOrientationMaskPortraitUpsideDown;
}
if ((validOrientations & orientationMask) == 0) {
orientationMask = validOrientations;
}
}
return orientationMask;
}
#endif
int
SDL_iPhoneSetAnimationCallback(SDL_Window * window, int interval, void (*callback)(void*), void *callbackParam)
{
if (!window || !window->driverdata) {
return SDL_SetError("Invalid window");
}
@autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
[data.viewcontroller setAnimationCallback:interval
callback:callback
callbackParam:callbackParam];
}
return 0;
}
#endif