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