1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 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#import <UIKit/UIKit.h> 26 27#include "SDL_video.h" 28#include "SDL_mouse.h" 29#include "SDL_hints.h" 30#include "../SDL_sysvideo.h" 31#include "../SDL_pixels_c.h" 32#include "../../events/SDL_events_c.h" 33 34#include "SDL_uikitvideo.h" 35#include "SDL_uikitevents.h" 36#include "SDL_uikitmodes.h" 37#include "SDL_uikitwindow.h" 38#include "SDL_uikitopengles.h" 39#include "SDL_uikitclipboard.h" 40#include "SDL_uikitvulkan.h" 41#include "SDL_uikitmetalview.h" 42 43#define UIKITVID_DRIVER_NAME "uikit" 44 45@implementation SDL_VideoData 46 47@end 48 49/* Initialization/Query functions */ 50static int UIKit_VideoInit(_THIS); 51static void UIKit_VideoQuit(_THIS); 52 53/* DUMMY driver bootstrap functions */ 54 55static int 56UIKit_Available(void) 57{ 58 return 1; 59} 60 61static void UIKit_DeleteDevice(SDL_VideoDevice * device) 62{ 63 @autoreleasepool { 64 CFRelease(device->driverdata); 65 SDL_free(device); 66 } 67} 68 69static SDL_VideoDevice * 70UIKit_CreateDevice(int devindex) 71{ 72 @autoreleasepool { 73 SDL_VideoDevice *device; 74 SDL_VideoData *data; 75 76 /* Initialize all variables that we clean on shutdown */ 77 device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice)); 78 if (device) { 79 data = [SDL_VideoData new]; 80 } else { 81 SDL_free(device); 82 SDL_OutOfMemory(); 83 return (0); 84 } 85 86 device->driverdata = (void *) CFBridgingRetain(data); 87 88 /* Set the function pointers */ 89 device->VideoInit = UIKit_VideoInit; 90 device->VideoQuit = UIKit_VideoQuit; 91 device->GetDisplayModes = UIKit_GetDisplayModes; 92 device->SetDisplayMode = UIKit_SetDisplayMode; 93 device->PumpEvents = UIKit_PumpEvents; 94 device->SuspendScreenSaver = UIKit_SuspendScreenSaver; 95 device->CreateSDLWindow = UIKit_CreateWindow; 96 device->SetWindowTitle = UIKit_SetWindowTitle; 97 device->ShowWindow = UIKit_ShowWindow; 98 device->HideWindow = UIKit_HideWindow; 99 device->RaiseWindow = UIKit_RaiseWindow; 100 device->SetWindowBordered = UIKit_SetWindowBordered; 101 device->SetWindowFullscreen = UIKit_SetWindowFullscreen; 102 device->DestroyWindow = UIKit_DestroyWindow; 103 device->GetWindowWMInfo = UIKit_GetWindowWMInfo; 104 device->GetDisplayUsableBounds = UIKit_GetDisplayUsableBounds; 105 device->GetDisplayDPI = UIKit_GetDisplayDPI; 106 107#if SDL_IPHONE_KEYBOARD 108 device->HasScreenKeyboardSupport = UIKit_HasScreenKeyboardSupport; 109 device->ShowScreenKeyboard = UIKit_ShowScreenKeyboard; 110 device->HideScreenKeyboard = UIKit_HideScreenKeyboard; 111 device->IsScreenKeyboardShown = UIKit_IsScreenKeyboardShown; 112 device->SetTextInputRect = UIKit_SetTextInputRect; 113#endif 114 115 device->SetClipboardText = UIKit_SetClipboardText; 116 device->GetClipboardText = UIKit_GetClipboardText; 117 device->HasClipboardText = UIKit_HasClipboardText; 118 119 /* OpenGL (ES) functions */ 120#if SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2 121 device->GL_MakeCurrent = UIKit_GL_MakeCurrent; 122 device->GL_GetDrawableSize = UIKit_GL_GetDrawableSize; 123 device->GL_SwapWindow = UIKit_GL_SwapWindow; 124 device->GL_CreateContext = UIKit_GL_CreateContext; 125 device->GL_DeleteContext = UIKit_GL_DeleteContext; 126 device->GL_GetProcAddress = UIKit_GL_GetProcAddress; 127 device->GL_LoadLibrary = UIKit_GL_LoadLibrary; 128#endif 129 device->free = UIKit_DeleteDevice; 130 131#if SDL_VIDEO_VULKAN 132 device->Vulkan_LoadLibrary = UIKit_Vulkan_LoadLibrary; 133 device->Vulkan_UnloadLibrary = UIKit_Vulkan_UnloadLibrary; 134 device->Vulkan_GetInstanceExtensions 135 = UIKit_Vulkan_GetInstanceExtensions; 136 device->Vulkan_CreateSurface = UIKit_Vulkan_CreateSurface; 137 device->Vulkan_GetDrawableSize = UIKit_Vulkan_GetDrawableSize; 138#endif 139 140#if SDL_VIDEO_METAL 141 device->Metal_CreateView = UIKit_Metal_CreateView; 142 device->Metal_DestroyView = UIKit_Metal_DestroyView; 143 device->Metal_GetLayer = UIKit_Metal_GetLayer; 144 device->Metal_GetDrawableSize = UIKit_Metal_GetDrawableSize; 145#endif 146 147 device->gl_config.accelerated = 1; 148 149 return device; 150 } 151} 152 153VideoBootStrap UIKIT_bootstrap = { 154 UIKITVID_DRIVER_NAME, "SDL UIKit video driver", 155 UIKit_Available, UIKit_CreateDevice 156}; 157 158 159int 160UIKit_VideoInit(_THIS) 161{ 162 _this->gl_config.driver_loaded = 1; 163 164 if (UIKit_InitModes(_this) < 0) { 165 return -1; 166 } 167 return 0; 168} 169 170void 171UIKit_VideoQuit(_THIS) 172{ 173 UIKit_QuitModes(_this); 174} 175 176void 177UIKit_SuspendScreenSaver(_THIS) 178{ 179 @autoreleasepool { 180 /* Ignore ScreenSaver API calls if the idle timer hint has been set. */ 181 /* FIXME: The idle timer hint should be deprecated for SDL 2.1. */ 182 if (!SDL_GetHintBoolean(SDL_HINT_IDLE_TIMER_DISABLED, SDL_FALSE)) { 183 UIApplication *app = [UIApplication sharedApplication]; 184 185 /* Prevent the display from dimming and going to sleep. */ 186 app.idleTimerDisabled = (_this->suspend_screensaver != SDL_FALSE); 187 } 188 } 189} 190 191SDL_bool 192UIKit_IsSystemVersionAtLeast(double version) 193{ 194 return [[UIDevice currentDevice].systemVersion doubleValue] >= version; 195} 196 197CGRect 198UIKit_ComputeViewFrame(SDL_Window *window, UIScreen *screen) 199{ 200 SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; 201 CGRect frame = screen.bounds; 202 203 /* Use the UIWindow bounds instead of the UIScreen bounds, when possible. 204 * The uiwindow bounds may be smaller than the screen bounds when Split View 205 * is used on an iPad. */ 206 if (data != nil && data.uiwindow != nil) { 207 frame = data.uiwindow.bounds; 208 } 209 210#if !TARGET_OS_TV && (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0) 211 BOOL hasiOS7 = UIKit_IsSystemVersionAtLeast(7.0); 212 213 /* The view should always show behind the status bar in iOS 7+. */ 214 if (!hasiOS7 && !(window->flags & (SDL_WINDOW_BORDERLESS|SDL_WINDOW_FULLSCREEN))) { 215 frame = screen.applicationFrame; 216 } 217#endif 218 219#if !TARGET_OS_TV 220 /* iOS 10 seems to have a bug where, in certain conditions, putting the 221 * device to sleep with the a landscape-only app open, re-orienting the 222 * device to portrait, and turning it back on will result in the screen 223 * bounds returning portrait orientation despite the app being in landscape. 224 * This is a workaround until a better solution can be found. 225 * https://bugzilla.libsdl.org/show_bug.cgi?id=3505 226 * https://bugzilla.libsdl.org/show_bug.cgi?id=3465 227 * https://forums.developer.apple.com/thread/65337 */ 228 if (UIKit_IsSystemVersionAtLeast(8.0)) { 229 UIInterfaceOrientation orient = [UIApplication sharedApplication].statusBarOrientation; 230 BOOL landscape = UIInterfaceOrientationIsLandscape(orient); 231 BOOL fullscreen = CGRectEqualToRect(screen.bounds, frame); 232 233 /* The orientation flip doesn't make sense when the window is smaller 234 * than the screen (iPad Split View, for example). */ 235 if (fullscreen && (landscape != (frame.size.width > frame.size.height))) { 236 float height = frame.size.width; 237 frame.size.width = frame.size.height; 238 frame.size.height = height; 239 } 240 } 241#endif 242 243 return frame; 244} 245 246void 247UIKit_ForceUpdateHomeIndicator() 248{ 249#if !TARGET_OS_TV 250 /* Force the main SDL window to re-evaluate home indicator state */ 251 SDL_Window *focus = SDL_GetFocusWindow(); 252 if (focus) { 253 SDL_WindowData *data = (__bridge SDL_WindowData *) focus->driverdata; 254 if (data != nil) { 255#pragma clang diagnostic push 256#pragma clang diagnostic ignored "-Wunguarded-availability-new" 257 if ([data.viewcontroller respondsToSelector:@selector(setNeedsUpdateOfHomeIndicatorAutoHidden)]) { 258 [data.viewcontroller performSelectorOnMainThread:@selector(setNeedsUpdateOfHomeIndicatorAutoHidden) withObject:nil waitUntilDone:NO]; 259 [data.viewcontroller performSelectorOnMainThread:@selector(setNeedsUpdateOfScreenEdgesDeferringSystemGestures) withObject:nil waitUntilDone:NO]; 260 } 261#pragma clang diagnostic pop 262 } 263 } 264#endif /* !TARGET_OS_TV */ 265} 266 267/* 268 * iOS log support. 269 * 270 * This doesn't really have aything to do with the interfaces of the SDL video 271 * subsystem, but we need to stuff this into an Objective-C source code file. 272 * 273 * NOTE: This is copypasted from src/video/cocoa/SDL_cocoavideo.m! Thus, if 274 * Cocoa is supported, we use that one instead. Be sure both versions remain 275 * identical! 276 */ 277 278#if !defined(SDL_VIDEO_DRIVER_COCOA) 279void SDL_NSLog(const char *text) 280{ 281 NSLog(@"%s", text); 282} 283#endif /* SDL_VIDEO_DRIVER_COCOA */ 284 285/* 286 * iOS Tablet detection 287 * 288 * This doesn't really have aything to do with the interfaces of the SDL video 289 * subsystem, but we need to stuff this into an Objective-C source code file. 290 */ 291SDL_bool SDL_IsIPad(void) 292{ 293 return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad); 294} 295 296#endif /* SDL_VIDEO_DRIVER_UIKIT */ 297 298/* vi: set ts=4 sw=4 expandtab: */ 299