Atlas - SDL_uikitvideo.m
Home / ext / SDL / src / video / uikit Lines: 1 | Size: 13633 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 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#ifdef SDL_VIDEO_DRIVER_UIKIT 24 25#import <UIKit/UIKit.h> 26#import <GameController/GameController.h> 27 28#include "../SDL_sysvideo.h" 29#include "../SDL_pixels_c.h" 30#include "../../events/SDL_events_c.h" 31 32#include "SDL_uikitvideo.h" 33#include "SDL_uikitevents.h" 34#include "SDL_uikitmodes.h" 35#include "SDL_uikitwindow.h" 36#include "SDL_uikitopengles.h" 37#include "SDL_uikitclipboard.h" 38#include "SDL_uikitvulkan.h" 39#include "SDL_uikitmetalview.h" 40#include "SDL_uikitmessagebox.h" 41#include "SDL_uikitpen.h" 42 43#define UIKITVID_DRIVER_NAME "uikit" 44 45@implementation SDL_UIKitVideoData 46 47@end 48 49// Initialization/Query functions 50static bool UIKit_VideoInit(SDL_VideoDevice *_this); 51static void UIKit_VideoQuit(SDL_VideoDevice *_this); 52 53// DUMMY driver bootstrap functions 54 55static void UIKit_DeleteDevice(SDL_VideoDevice *device) 56{ 57 @autoreleasepool { 58 if (device->internal){ 59 CFRelease(device->internal); 60 } 61 SDL_free(device); 62 } 63} 64 65static SDL_VideoDevice *UIKit_CreateDevice(void) 66{ 67 @autoreleasepool { 68 SDL_VideoDevice *device; 69 SDL_UIKitVideoData *data; 70 71 // Initialize all variables that we clean on shutdown 72 device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice)); 73 if (!device) { 74 return NULL; 75 } 76 77 data = [SDL_UIKitVideoData new]; 78 79 device->internal = (SDL_VideoData *)CFBridgingRetain(data); 80 device->system_theme = UIKit_GetSystemTheme(); 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->SetWindowSize = UIKit_SetWindowSize; 92 device->ShowWindow = UIKit_ShowWindow; 93 device->HideWindow = UIKit_HideWindow; 94 device->RaiseWindow = UIKit_RaiseWindow; 95 device->SetWindowBordered = UIKit_SetWindowBordered; 96 device->SetWindowFullscreen = UIKit_SetWindowFullscreen; 97 device->DestroyWindow = UIKit_DestroyWindow; 98 device->GetDisplayUsableBounds = UIKit_GetDisplayUsableBounds; 99 device->GetWindowSizeInPixels = UIKit_GetWindowSizeInPixels; 100 101#ifdef SDL_IPHONE_KEYBOARD 102 device->HasScreenKeyboardSupport = UIKit_HasScreenKeyboardSupport; 103 device->StartTextInput = UIKit_StartTextInput; 104 device->StopTextInput = UIKit_StopTextInput; 105 device->SetTextInputProperties = UIKit_SetTextInputProperties; 106 device->UpdateTextInputArea = UIKit_UpdateTextInputArea; 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 defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) 115 device->GL_MakeCurrent = UIKit_GL_MakeCurrent; 116 device->GL_SwapWindow = UIKit_GL_SwapWindow; 117 device->GL_CreateContext = UIKit_GL_CreateContext; 118 device->GL_DestroyContext = UIKit_GL_DestroyContext; 119 device->GL_GetProcAddress = UIKit_GL_GetProcAddress; 120 device->GL_LoadLibrary = UIKit_GL_LoadLibrary; 121#endif 122 device->free = UIKit_DeleteDevice; 123 124#ifdef SDL_VIDEO_VULKAN 125 device->Vulkan_LoadLibrary = UIKit_Vulkan_LoadLibrary; 126 device->Vulkan_UnloadLibrary = UIKit_Vulkan_UnloadLibrary; 127 device->Vulkan_GetInstanceExtensions = UIKit_Vulkan_GetInstanceExtensions; 128 device->Vulkan_CreateSurface = UIKit_Vulkan_CreateSurface; 129 device->Vulkan_DestroySurface = UIKit_Vulkan_DestroySurface; 130#endif 131 132#ifdef SDL_VIDEO_METAL 133 device->Metal_CreateView = UIKit_Metal_CreateView; 134 device->Metal_DestroyView = UIKit_Metal_DestroyView; 135 device->Metal_GetLayer = UIKit_Metal_GetLayer; 136#endif 137 138 device->device_caps = VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS; 139 140 device->gl_config.accelerated = 1; 141 142 return device; 143 } 144} 145 146VideoBootStrap UIKIT_bootstrap = { 147 UIKITVID_DRIVER_NAME, "SDL UIKit video driver", 148 UIKit_CreateDevice, 149 UIKit_ShowMessageBox, 150 false 151}; 152 153static bool UIKit_VideoInit(SDL_VideoDevice *_this) 154{ 155 _this->gl_config.driver_loaded = 1; 156 157 if (!UIKit_InitModes(_this)) { 158 return false; 159 } 160 161 SDL_InitGCKeyboard(); 162 SDL_InitGCMouse(); 163 164 UIKit_InitClipboard(_this); 165 166 return true; 167} 168 169static void UIKit_VideoQuit(SDL_VideoDevice *_this) 170{ 171 UIKit_QuitClipboard(_this); 172 173 SDL_QuitGCKeyboard(); 174 SDL_QuitGCMouse(); 175 UIKit_QuitPen(_this); 176 177 UIKit_QuitModes(_this); 178} 179 180bool UIKit_SuspendScreenSaver(SDL_VideoDevice *_this) 181{ 182 @autoreleasepool { 183 UIApplication *app = [UIApplication sharedApplication]; 184 185 // Prevent the display from dimming and going to sleep. 186 app.idleTimerDisabled = (_this->suspend_screensaver != false); 187 } 188 return true; 189} 190 191bool UIKit_IsSystemVersionAtLeast(double version) 192{ 193 return [[UIDevice currentDevice].systemVersion doubleValue] >= version; 194} 195 196SDL_SystemTheme UIKit_GetSystemTheme(void) 197{ 198#ifndef SDL_PLATFORM_VISIONOS 199 if (@available(iOS 12.0, tvOS 10.0, *)) { 200 switch ([UIScreen mainScreen].traitCollection.userInterfaceStyle) { 201 case UIUserInterfaceStyleDark: 202 return SDL_SYSTEM_THEME_DARK; 203 case UIUserInterfaceStyleLight: 204 return SDL_SYSTEM_THEME_LIGHT; 205 default: 206 break; 207 } 208 } 209#endif 210 return SDL_SYSTEM_THEME_UNKNOWN; 211} 212 213#ifdef SDL_PLATFORM_VISIONOS 214CGRect UIKit_ComputeViewFrame(SDL_Window *window) 215{ 216 // View origin is always (0,0) relative to the UIWindow. 217 // window->x/y are screen-level positions (often SDL_WINDOWPOS_UNDEFINED). 218 return CGRectMake(0, 0, window->w, window->h); 219} 220#else 221CGRect UIKit_ComputeViewFrame(SDL_Window *window, UIScreen *screen) 222{ 223 SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->internal; 224 CGRect frame = screen.bounds; 225 226 /* Use the UIWindow bounds instead of the UIScreen bounds, when possible. 227 * The uiwindow bounds may be smaller than the screen bounds when Split View 228 * is used on an iPad. */ 229 if (data != nil && data.uiwindow != nil) { 230 frame = data.uiwindow.bounds; 231 } 232 233#ifndef SDL_PLATFORM_TVOS 234 /* iOS 10 seems to have a bug where, in certain conditions, putting the 235 * device to sleep with the a landscape-only app open, re-orienting the 236 * device to portrait, and turning it back on will result in the screen 237 * bounds returning portrait orientation despite the app being in landscape. 238 * This is a workaround until a better solution can be found. 239 * https://bugzilla.libsdl.org/show_bug.cgi?id=3505 240 * https://bugzilla.libsdl.org/show_bug.cgi?id=3465 241 * https://forums.developer.apple.com/thread/65337 */ 242#pragma clang diagnostic push 243#pragma clang diagnostic ignored "-Wdeprecated-declarations" 244 UIInterfaceOrientation orient = [UIApplication sharedApplication].statusBarOrientation; 245#pragma clang diagnostic pop 246 BOOL landscape = UIInterfaceOrientationIsLandscape(orient) || 247 !(UIKit_GetSupportedOrientations(window) & (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown)); 248 BOOL fullscreen = CGRectEqualToRect(screen.bounds, frame); 249 250 /* The orientation flip doesn't make sense when the window is smaller 251 * than the screen (iPad Split View, for example). */ 252 if (fullscreen && (landscape != (frame.size.width > frame.size.height))) { 253 float height = frame.size.width; 254 frame.size.width = frame.size.height; 255 frame.size.height = height; 256 } 257#endif 258 259 return frame; 260} 261#endif // SDL_PLATFORM_VISIONOS 262 263UIWindowScene *UIKit_GetActiveWindowScene(void) 264{ 265 if (@available(iOS 13.0, tvOS 13.0, *)) { 266 NSSet<UIScene *> *connectedScenes = [UIApplication sharedApplication].connectedScenes; 267 268 // First, try to find an active foreground scene 269 for (UIScene *scene in connectedScenes) { 270 if ([scene isKindOfClass:[UIWindowScene class]]) { 271 UIWindowScene *windowScene = (UIWindowScene *)scene; 272 if (windowScene.activationState == UISceneActivationStateForegroundActive) { 273 return windowScene; 274 } 275 } 276 } 277 278 // If no active scene, return any foreground scene 279 for (UIScene *scene in connectedScenes) { 280 if ([scene isKindOfClass:[UIWindowScene class]]) { 281 UIWindowScene *windowScene = (UIWindowScene *)scene; 282 if (windowScene.activationState == UISceneActivationStateForegroundInactive) { 283 return windowScene; 284 } 285 } 286 } 287 288 // Last resort: return first window scene 289 for (UIScene *scene in connectedScenes) { 290 if ([scene isKindOfClass:[UIWindowScene class]]) { 291 return (UIWindowScene *)scene; 292 } 293 } 294 } 295 296 return nil; 297} 298 299void UIKit_SetGameControllerInteraction(bool enabled) 300{ 301 if (@available(iOS 13.0, tvOS 13.0, *)) { 302 NSSet<UIScene *> *connectedScenes = [UIApplication sharedApplication].connectedScenes; 303 for (UIScene *scene in connectedScenes) { 304 if (![scene isKindOfClass:[UIWindowScene class]]) { 305 continue; 306 } 307 308 UIWindowScene *windowScene = (UIWindowScene *)scene; 309 for (UIWindow *window in windowScene.windows) { 310 UIKit_SetViewGameControllerInteraction(window, enabled); 311 } 312 } 313 } 314} 315 316void UIKit_SetViewGameControllerInteraction(UIView *view, bool enabled) 317{ 318#if defined(SDL_PLATFORM_VISIONOS) || \ 319 (defined(SDL_PLATFORM_IOS) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000) 320 if (@available(iOS 18.0, visionOS 2.0, *)) { 321 if (enabled) { 322 GCEventInteraction *interaction = [[GCEventInteraction alloc] init]; 323 interaction.handledEventTypes = GCUIEventTypeGamepad; 324 [view addInteraction:interaction]; 325 } else { 326 for (id<UIInteraction> entry in view.interactions) { 327 if ([entry isKindOfClass:[GCEventInteraction class]]) { 328 GCEventInteraction *interaction = (GCEventInteraction *)entry; 329 if (interaction.handledEventTypes == GCUIEventTypeGamepad) { 330 [view removeInteraction:interaction]; 331 break; 332 } 333 } 334 } 335 } 336 } 337#endif // !SDL_PLATFORM_TVOS 338} 339 340void UIKit_ForceUpdateHomeIndicator(void) 341{ 342#ifndef SDL_PLATFORM_TVOS 343 // Force the main SDL window to re-evaluate home indicator state 344 SDL_Window *focus = SDL_GetKeyboardFocus(); 345 if (focus) { 346 SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)focus->internal; 347 if (data != nil) { 348 [data.viewcontroller performSelectorOnMainThread:@selector(setNeedsUpdateOfHomeIndicatorAutoHidden) withObject:nil waitUntilDone:NO]; 349 [data.viewcontroller performSelectorOnMainThread:@selector(setNeedsUpdateOfScreenEdgesDeferringSystemGestures) withObject:nil waitUntilDone:NO]; 350 } 351 } 352#endif // !SDL_PLATFORM_TVOS 353} 354 355/* 356 * iOS log support. 357 * 358 * This doesn't really have anything to do with the interfaces of the SDL video 359 * subsystem, but we need to stuff this into an Objective-C source code file. 360 * 361 * NOTE: This is copypasted from src/video/cocoa/SDL_cocoavideo.m! Thus, if 362 * Cocoa is supported, we use that one instead. Be sure both versions remain 363 * identical! 364 */ 365 366#ifndef SDL_VIDEO_DRIVER_COCOA 367void SDL_NSLog(const char *prefix, const char *text) 368{ 369 @autoreleasepool { 370 NSString *nsText = [NSString stringWithUTF8String:text]; 371 if (prefix && *prefix) { 372 NSString *nsPrefix = [NSString stringWithUTF8String:prefix]; 373 NSLog(@"%@%@", nsPrefix, nsText); 374 } else { 375 NSLog(@"%@", nsText); 376 } 377 } 378} 379#endif // SDL_VIDEO_DRIVER_COCOA 380 381/* 382 * iOS Tablet, etc, detection 383 * 384 * This doesn't really have anything to do with the interfaces of the SDL video 385 * subsystem, but we need to stuff this into an Objective-C source code file. 386 */ 387bool SDL_IsIPad(void) 388{ 389 return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad); 390} 391 392bool SDL_IsAppleTV(void) 393{ 394 return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomTV); 395} 396 397#endif // SDL_VIDEO_DRIVER_UIKIT 398[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.