Atlas - SDL_uikitvideo.m

Home / ext / SDL / src / video / uikit Lines: 1 | Size: 13295 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 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 return CGRectMake(window->x, window->y, window->w, window->h); 217} 218#else 219CGRect UIKit_ComputeViewFrame(SDL_Window *window, UIScreen *screen) 220{ 221 SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->internal; 222 CGRect frame = screen.bounds; 223 224 /* Use the UIWindow bounds instead of the UIScreen bounds, when possible. 225 * The uiwindow bounds may be smaller than the screen bounds when Split View 226 * is used on an iPad. */ 227 if (data != nil && data.uiwindow != nil) { 228 frame = data.uiwindow.bounds; 229 } 230 231#ifndef SDL_PLATFORM_TVOS 232 /* iOS 10 seems to have a bug where, in certain conditions, putting the 233 * device to sleep with the a landscape-only app open, re-orienting the 234 * device to portrait, and turning it back on will result in the screen 235 * bounds returning portrait orientation despite the app being in landscape. 236 * This is a workaround until a better solution can be found. 237 * https://bugzilla.libsdl.org/show_bug.cgi?id=3505 238 * https://bugzilla.libsdl.org/show_bug.cgi?id=3465 239 * https://forums.developer.apple.com/thread/65337 */ 240 UIInterfaceOrientation orient = [UIApplication sharedApplication].statusBarOrientation; 241 BOOL landscape = UIInterfaceOrientationIsLandscape(orient) || 242 !(UIKit_GetSupportedOrientations(window) & (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown)); 243 BOOL fullscreen = CGRectEqualToRect(screen.bounds, frame); 244 245 /* The orientation flip doesn't make sense when the window is smaller 246 * than the screen (iPad Split View, for example). */ 247 if (fullscreen && (landscape != (frame.size.width > frame.size.height))) { 248 float height = frame.size.width; 249 frame.size.width = frame.size.height; 250 frame.size.height = height; 251 } 252#endif 253 254 return frame; 255} 256#endif // SDL_PLATFORM_VISIONOS 257 258UIWindowScene *UIKit_GetActiveWindowScene(void) 259{ 260 if (@available(iOS 13.0, tvOS 13.0, *)) { 261 NSSet<UIScene *> *connectedScenes = [UIApplication sharedApplication].connectedScenes; 262 263 // First, try to find an active foreground scene 264 for (UIScene *scene in connectedScenes) { 265 if ([scene isKindOfClass:[UIWindowScene class]]) { 266 UIWindowScene *windowScene = (UIWindowScene *)scene; 267 if (windowScene.activationState == UISceneActivationStateForegroundActive) { 268 return windowScene; 269 } 270 } 271 } 272 273 // If no active scene, return any foreground scene 274 for (UIScene *scene in connectedScenes) { 275 if ([scene isKindOfClass:[UIWindowScene class]]) { 276 UIWindowScene *windowScene = (UIWindowScene *)scene; 277 if (windowScene.activationState == UISceneActivationStateForegroundInactive) { 278 return windowScene; 279 } 280 } 281 } 282 283 // Last resort: return first window scene 284 for (UIScene *scene in connectedScenes) { 285 if ([scene isKindOfClass:[UIWindowScene class]]) { 286 return (UIWindowScene *)scene; 287 } 288 } 289 } 290 291 return nil; 292} 293 294void UIKit_SetGameControllerInteraction(bool enabled) 295{ 296 if (@available(iOS 13.0, tvOS 13.0, *)) { 297 NSSet<UIScene *> *connectedScenes = [UIApplication sharedApplication].connectedScenes; 298 for (UIScene *scene in connectedScenes) { 299 if (![scene isKindOfClass:[UIWindowScene class]]) { 300 continue; 301 } 302 303 UIWindowScene *windowScene = (UIWindowScene *)scene; 304 for (UIWindow *window in windowScene.windows) { 305 UIKit_SetViewGameControllerInteraction(window, enabled); 306 } 307 } 308 } 309} 310 311void UIKit_SetViewGameControllerInteraction(UIView *view, bool enabled) 312{ 313#ifndef SDL_PLATFORM_TVOS 314 if (@available(iOS 18.0, visionOS 2.0, *)) { 315 if (enabled) { 316 GCEventInteraction *interaction = [[GCEventInteraction alloc] init]; 317 interaction.handledEventTypes = GCUIEventTypeGamepad; 318 [view addInteraction:interaction]; 319 } else { 320 for (id<UIInteraction> entry in view.interactions) { 321 if ([entry isKindOfClass:[GCEventInteraction class]]) { 322 GCEventInteraction *interaction = (GCEventInteraction *)entry; 323 if (interaction.handledEventTypes == GCUIEventTypeGamepad) { 324 [view removeInteraction:interaction]; 325 break; 326 } 327 } 328 } 329 } 330 } 331#endif // !SDL_PLATFORM_TVOS 332} 333 334void UIKit_ForceUpdateHomeIndicator(void) 335{ 336#ifndef SDL_PLATFORM_TVOS 337 // Force the main SDL window to re-evaluate home indicator state 338 SDL_Window *focus = SDL_GetKeyboardFocus(); 339 if (focus) { 340 SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)focus->internal; 341 if (data != nil) { 342 [data.viewcontroller performSelectorOnMainThread:@selector(setNeedsUpdateOfHomeIndicatorAutoHidden) withObject:nil waitUntilDone:NO]; 343 [data.viewcontroller performSelectorOnMainThread:@selector(setNeedsUpdateOfScreenEdgesDeferringSystemGestures) withObject:nil waitUntilDone:NO]; 344 } 345 } 346#endif // !SDL_PLATFORM_TVOS 347} 348 349/* 350 * iOS log support. 351 * 352 * This doesn't really have anything to do with the interfaces of the SDL video 353 * subsystem, but we need to stuff this into an Objective-C source code file. 354 * 355 * NOTE: This is copypasted from src/video/cocoa/SDL_cocoavideo.m! Thus, if 356 * Cocoa is supported, we use that one instead. Be sure both versions remain 357 * identical! 358 */ 359 360#ifndef SDL_VIDEO_DRIVER_COCOA 361void SDL_NSLog(const char *prefix, const char *text) 362{ 363 @autoreleasepool { 364 NSString *nsText = [NSString stringWithUTF8String:text]; 365 if (prefix && *prefix) { 366 NSString *nsPrefix = [NSString stringWithUTF8String:prefix]; 367 NSLog(@"%@%@", nsPrefix, nsText); 368 } else { 369 NSLog(@"%@", nsText); 370 } 371 } 372} 373#endif // SDL_VIDEO_DRIVER_COCOA 374 375/* 376 * iOS Tablet, etc, detection 377 * 378 * This doesn't really have anything to do with the interfaces of the SDL video 379 * subsystem, but we need to stuff this into an Objective-C source code file. 380 */ 381bool SDL_IsIPad(void) 382{ 383 return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad); 384} 385 386bool SDL_IsAppleTV(void) 387{ 388 return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomTV); 389} 390 391#endif // SDL_VIDEO_DRIVER_UIKIT 392
[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.