Atlas - SDL_uikitmodes.m

Home / ext / SDL / src / video / uikit Lines: 1 | Size: 16934 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#include "SDL_uikitmodes.h" 26 27#include "../../events/SDL_events_c.h" 28 29#import <sys/utsname.h> 30 31@implementation SDL_UIKitDisplayData 32 33#ifndef SDL_PLATFORM_VISIONOS 34- (instancetype)initWithScreen:(UIScreen *)screen 35{ 36 if (self = [super init]) { 37 self.uiscreen = screen; 38 } 39 return self; 40} 41@synthesize uiscreen; 42#endif 43 44@end 45 46@implementation SDL_UIKitDisplayModeData 47 48#ifndef SDL_PLATFORM_VISIONOS 49@synthesize uiscreenmode; 50#endif 51 52@end 53 54@interface SDL_DisplayWatch : NSObject 55@end 56 57#ifndef SDL_PLATFORM_VISIONOS 58@implementation SDL_DisplayWatch 59 60+ (void)start 61{ 62 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 63 64 [center addObserver:self 65 selector:@selector(screenConnected:) 66 name:UIScreenDidConnectNotification 67 object:nil]; 68 [center addObserver:self 69 selector:@selector(screenDisconnected:) 70 name:UIScreenDidDisconnectNotification 71 object:nil]; 72} 73 74+ (void)stop 75{ 76 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 77 78 [center removeObserver:self 79 name:UIScreenDidConnectNotification 80 object:nil]; 81 [center removeObserver:self 82 name:UIScreenDidDisconnectNotification 83 object:nil]; 84} 85 86+ (void)screenConnected:(NSNotification *)notification 87{ 88 UIScreen *uiscreen = [notification object]; 89 UIKit_AddDisplay(uiscreen, true); 90} 91 92+ (void)screenDisconnected:(NSNotification *)notification 93{ 94 UIScreen *uiscreen = [notification object]; 95 UIKit_DelDisplay(uiscreen, true); 96} 97 98@end 99#endif 100 101#ifndef SDL_PLATFORM_VISIONOS 102static bool UIKit_AllocateDisplayModeData(SDL_DisplayMode *mode, 103 UIScreenMode *uiscreenmode) 104{ 105 SDL_UIKitDisplayModeData *data = nil; 106 107 if (uiscreenmode != nil) { 108 // Allocate the display mode data 109 data = [[SDL_UIKitDisplayModeData alloc] init]; 110 if (!data) { 111 return SDL_OutOfMemory(); 112 } 113 114 data.uiscreenmode = uiscreenmode; 115 } 116 117 mode->internal = (void *)CFBridgingRetain(data); 118 119 return true; 120} 121#endif 122 123static void UIKit_FreeDisplayModeData(SDL_DisplayMode *mode) 124{ 125 if (mode->internal != NULL) { 126 CFRelease(mode->internal); 127 mode->internal = NULL; 128 } 129} 130 131#ifndef SDL_PLATFORM_VISIONOS 132static float UIKit_GetDisplayModeRefreshRate(UIScreen *uiscreen) 133{ 134 return (float)uiscreen.maximumFramesPerSecond; 135} 136 137static bool UIKit_AddSingleDisplayMode(SDL_VideoDisplay *display, int w, int h, 138 UIScreen *uiscreen, UIScreenMode *uiscreenmode) 139{ 140 SDL_DisplayMode mode; 141 142 SDL_zero(mode); 143 if (!UIKit_AllocateDisplayModeData(&mode, uiscreenmode)) { 144 return false; 145 } 146 147 mode.w = w; 148 mode.h = h; 149 mode.pixel_density = uiscreen.nativeScale; 150 mode.refresh_rate = UIKit_GetDisplayModeRefreshRate(uiscreen); 151 mode.format = SDL_PIXELFORMAT_ABGR8888; 152 153 if (SDL_AddFullscreenDisplayMode(display, &mode)) { 154 return true; 155 } else { 156 UIKit_FreeDisplayModeData(&mode); 157 return false; 158 } 159} 160 161static bool UIKit_AddDisplayMode(SDL_VideoDisplay *display, int w, int h, 162 UIScreen *uiscreen, UIScreenMode *uiscreenmode, bool addRotation) 163{ 164 if (!UIKit_AddSingleDisplayMode(display, w, h, uiscreen, uiscreenmode)) { 165 return false; 166 } 167 168 if (addRotation) { 169 // Add the rotated version 170 if (!UIKit_AddSingleDisplayMode(display, h, w, uiscreen, uiscreenmode)) { 171 return false; 172 } 173 } 174 175 return true; 176} 177 178static CGSize GetUIScreenModeSize(UIScreen *uiscreen, UIScreenMode *mode) 179{ 180 /* For devices such as iPhone 6/7/8 Plus, the UIScreenMode reported by iOS 181 * isn't the physical pixels of the display, but rather the point size times 182 * the scale. For example, on iOS 12.2 on iPhone 8 Plus the physical pixel 183 * resolution is 1080x1920, the size reported by mode.size is 1242x2208, 184 * the size in points is 414x736, the scale property is 3.0, and the 185 * nativeScale property is ~2.6087 (ie 1920.0 / 736.0). 186 * 187 * What we want for the mode size is the point size, and the pixel density 188 * is the native scale. 189 * 190 * Note that the iOS Simulator doesn't have this behavior for those devices. 191 * https://github.com/libsdl-org/SDL/issues/3220 192 */ 193 CGSize size = mode.size; 194 195 size.width = SDL_round(size.width / uiscreen.scale); 196 size.height = SDL_round(size.height / uiscreen.scale); 197 198 return size; 199} 200 201bool UIKit_AddDisplay(UIScreen *uiscreen, bool send_event) 202{ 203 UIScreenMode *uiscreenmode = uiscreen.currentMode; 204 CGSize size = GetUIScreenModeSize(uiscreen, uiscreenmode); 205 SDL_VideoDisplay display; 206 SDL_DisplayMode mode; 207 208 // Make sure the width/height are oriented correctly 209 if (UIKit_IsDisplayLandscape(uiscreen) != (size.width > size.height)) { 210 CGFloat height = size.width; 211 size.width = size.height; 212 size.height = height; 213 } 214 215 SDL_zero(mode); 216 mode.w = (int)size.width; 217 mode.h = (int)size.height; 218 mode.pixel_density = uiscreen.nativeScale; 219 mode.format = SDL_PIXELFORMAT_ABGR8888; 220 mode.refresh_rate = UIKit_GetDisplayModeRefreshRate(uiscreen); 221 222 if (!UIKit_AllocateDisplayModeData(&mode, uiscreenmode)) { 223 return false; 224 } 225 226 SDL_zero(display); 227#ifndef SDL_PLATFORM_TVOS 228 if (uiscreen == [UIScreen mainScreen]) { 229 // The natural orientation (used by sensors) is portrait 230 display.natural_orientation = SDL_ORIENTATION_PORTRAIT; 231 } else 232#endif 233 if (UIKit_IsDisplayLandscape(uiscreen)) { 234 display.natural_orientation = SDL_ORIENTATION_LANDSCAPE; 235 } else { 236 display.natural_orientation = SDL_ORIENTATION_PORTRAIT; 237 } 238 display.desktop_mode = mode; 239 240 display.HDR.SDR_white_level = 1.0f; 241 display.HDR.HDR_headroom = 1.0f; 242 243#ifndef SDL_PLATFORM_TVOS 244 if (@available(iOS 16.0, *)) { 245 if (uiscreen.currentEDRHeadroom > 1.0f) { 246 display.HDR.HDR_headroom = uiscreen.currentEDRHeadroom; 247 } else { 248 display.HDR.HDR_headroom = uiscreen.potentialEDRHeadroom; 249 } 250 } 251#endif // !SDL_PLATFORM_TVOS 252 253 // Allocate the display data 254#ifdef SDL_PLATFORM_VISIONOS 255 SDL_UIKitDisplayData *data = [[SDL_UIKitDisplayData alloc] init]; 256#else 257 SDL_UIKitDisplayData *data = [[SDL_UIKitDisplayData alloc] initWithScreen:uiscreen]; 258#endif 259 if (!data) { 260 UIKit_FreeDisplayModeData(&display.desktop_mode); 261 return SDL_OutOfMemory(); 262 } 263 264 display.internal = (SDL_DisplayData *)CFBridgingRetain(data); 265 if (SDL_AddVideoDisplay(&display, send_event) == 0) { 266 return false; 267 } 268 return true; 269} 270#endif 271 272#ifdef SDL_PLATFORM_VISIONOS 273bool UIKit_AddDisplay(bool send_event) 274{ 275 CGSize size = CGSizeMake(SDL_XR_SCREENWIDTH, SDL_XR_SCREENHEIGHT); 276 SDL_VideoDisplay display; 277 SDL_DisplayMode mode; 278 279 SDL_zero(mode); 280 mode.w = (int)size.width; 281 mode.h = (int)size.height; 282 mode.pixel_density = 2; 283 mode.format = SDL_PIXELFORMAT_ABGR8888; 284 mode.refresh_rate = 90.0f; 285 286 display.natural_orientation = SDL_ORIENTATION_LANDSCAPE; 287 288 display.desktop_mode = mode; 289 290 SDL_UIKitDisplayData *data = [[SDL_UIKitDisplayData alloc] init]; 291 292 if (!data) { 293 UIKit_FreeDisplayModeData(&display.desktop_mode); 294 return SDL_OutOfMemory(); 295 } 296 297 display.internal = (SDL_DisplayData *)CFBridgingRetain(data); 298 if (SDL_AddVideoDisplay(&display, send_event) == 0) { 299 return false; 300 } 301 return true; 302} 303#endif 304 305#ifndef SDL_PLATFORM_VISIONOS 306 307void UIKit_DelDisplay(UIScreen *uiscreen, bool send_event) 308{ 309 SDL_DisplayID *displays; 310 int i; 311 312 displays = SDL_GetDisplays(NULL); 313 if (displays) { 314 for (i = 0; displays[i]; ++i) { 315 SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]); 316 SDL_UIKitDisplayData *data = (__bridge SDL_UIKitDisplayData *)display->internal; 317 318 if (data && data.uiscreen == uiscreen) { 319 CFRelease(display->internal); 320 display->internal = NULL; 321 SDL_DelVideoDisplay(displays[i], send_event); 322 break; 323 } 324 } 325 SDL_free(displays); 326 } 327} 328 329bool UIKit_IsDisplayLandscape(UIScreen *uiscreen) 330{ 331#ifndef SDL_PLATFORM_TVOS 332 if (uiscreen == [UIScreen mainScreen]) { 333#pragma clang diagnostic push 334#pragma clang diagnostic ignored "-Wdeprecated-declarations" 335 return UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation); 336#pragma clang diagnostic pop 337 } else 338#endif // !SDL_PLATFORM_TVOS 339 { 340 CGSize size = uiscreen.bounds.size; 341 return (size.width > size.height); 342 } 343} 344#endif 345bool UIKit_InitModes(SDL_VideoDevice *_this) 346{ 347 @autoreleasepool { 348#ifdef SDL_PLATFORM_VISIONOS 349 UIKit_AddDisplay(false); 350#else 351 for (UIScreen *uiscreen in [UIScreen screens]) { 352 if (!UIKit_AddDisplay(uiscreen, false)) { 353 return false; 354 } 355 } 356#endif 357 358#if !defined(SDL_PLATFORM_TVOS) && !defined(SDL_PLATFORM_VISIONOS) 359 SDL_OnApplicationDidChangeStatusBarOrientation(); 360#endif 361 362#ifndef SDL_PLATFORM_VISIONOS 363 [SDL_DisplayWatch start]; 364#endif 365 } 366 367 return true; 368} 369 370bool UIKit_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display) 371{ 372#ifndef SDL_PLATFORM_VISIONOS 373 @autoreleasepool { 374 SDL_UIKitDisplayData *data = (__bridge SDL_UIKitDisplayData *)display->internal; 375 376 bool isLandscape = UIKit_IsDisplayLandscape(data.uiscreen); 377 bool addRotation = (data.uiscreen == [UIScreen mainScreen]); 378 NSArray *availableModes = nil; 379 380#ifdef SDL_PLATFORM_TVOS 381 addRotation = false; 382 availableModes = @[ data.uiscreen.currentMode ]; 383#else 384 availableModes = data.uiscreen.availableModes; 385#endif 386 387 for (UIScreenMode *uimode in availableModes) { 388 CGSize size = GetUIScreenModeSize(data.uiscreen, uimode); 389 int w = (int)size.width; 390 int h = (int)size.height; 391 392 // Make sure the width/height are oriented correctly 393 if (isLandscape != (w > h)) { 394 int tmp = w; 395 w = h; 396 h = tmp; 397 } 398 399 UIKit_AddDisplayMode(display, w, h, data.uiscreen, uimode, addRotation); 400 } 401 } 402#endif 403 return true; 404} 405 406bool UIKit_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode) 407{ 408#ifndef SDL_PLATFORM_VISIONOS 409 @autoreleasepool { 410 SDL_UIKitDisplayData *data = (__bridge SDL_UIKitDisplayData *)display->internal; 411 412#ifndef SDL_PLATFORM_TVOS 413 SDL_UIKitDisplayModeData *modedata = (__bridge SDL_UIKitDisplayModeData *)mode->internal; 414 [data.uiscreen setCurrentMode:modedata.uiscreenmode]; 415#endif 416 417 if (data.uiscreen == [UIScreen mainScreen]) { 418 /* [UIApplication setStatusBarOrientation:] no longer works reliably 419 * in recent iOS versions, so we can't rotate the screen when setting 420 * the display mode. */ 421 if (mode->w > mode->h) { 422 if (!UIKit_IsDisplayLandscape(data.uiscreen)) { 423 return SDL_SetError("Screen orientation does not match display mode size"); 424 } 425 } else if (mode->w < mode->h) { 426 if (UIKit_IsDisplayLandscape(data.uiscreen)) { 427 return SDL_SetError("Screen orientation does not match display mode size"); 428 } 429 } 430 } 431 } 432#endif 433 return true; 434} 435 436bool UIKit_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect) 437{ 438 @autoreleasepool { 439 SDL_UIKitDisplayData *data = (__bridge SDL_UIKitDisplayData *)display->internal; 440#ifdef SDL_PLATFORM_VISIONOS 441 CGRect frame = CGRectMake(0, 0, SDL_XR_SCREENWIDTH, SDL_XR_SCREENHEIGHT); 442#else 443 CGRect frame = data.uiscreen.bounds; 444#endif 445 446 /* the default function iterates displays to make a fake offset, 447 as if all the displays were side-by-side, which is fine for iOS. */ 448 if (!SDL_GetDisplayBounds(display->id, rect)) { 449 return false; 450 } 451 452 rect->x += (int)frame.origin.x; 453 rect->y += (int)frame.origin.y; 454 rect->w = (int)frame.size.width; 455 rect->h = (int)frame.size.height; 456 } 457 458 return true; 459} 460 461void UIKit_QuitModes(SDL_VideoDevice *_this) 462{ 463#ifndef SDL_PLATFORM_VISIONOS 464 [SDL_DisplayWatch stop]; 465#endif 466 467 // Release Objective-C objects, so higher level doesn't free() them. 468 int i, j; 469 @autoreleasepool { 470 for (i = 0; i < _this->num_displays; i++) { 471 SDL_VideoDisplay *display = _this->displays[i]; 472 473 UIKit_FreeDisplayModeData(&display->desktop_mode); 474 for (j = 0; j < display->num_fullscreen_modes; j++) { 475 SDL_DisplayMode *mode = &display->fullscreen_modes[j]; 476 UIKit_FreeDisplayModeData(mode); 477 } 478 479 if (display->internal != NULL) { 480 CFRelease(display->internal); 481 display->internal = NULL; 482 } 483 } 484 } 485} 486 487#if !defined(SDL_PLATFORM_TVOS) && !defined(SDL_PLATFORM_VISIONOS) 488void SDL_OnApplicationDidChangeStatusBarOrientation(void) 489{ 490#pragma clang diagnostic push 491#pragma clang diagnostic ignored "-Wdeprecated-declarations" 492 BOOL isLandscape = UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation); 493#pragma clang diagnostic pop 494 SDL_VideoDisplay *display = SDL_GetVideoDisplay(SDL_GetPrimaryDisplay()); 495 496 if (display) { 497 SDL_DisplayMode *mode = &display->desktop_mode; 498 SDL_DisplayOrientation orientation = SDL_ORIENTATION_UNKNOWN; 499 int i; 500 501 /* The desktop display mode should be kept in sync with the screen 502 * orientation so that updating a window's fullscreen state to 503 * fullscreen desktop keeps the window dimensions in the 504 * correct orientation. */ 505 if (isLandscape != (mode->w > mode->h)) { 506 SDL_DisplayMode new_mode; 507 SDL_copyp(&new_mode, mode); 508 new_mode.w = mode->h; 509 new_mode.h = mode->w; 510 511 // Make sure we don't free the current display mode data 512 mode->internal = NULL; 513 514 SDL_SetDesktopDisplayMode(display, &new_mode); 515 } 516 517 // Same deal with the fullscreen modes 518 for (i = 0; i < display->num_fullscreen_modes; ++i) { 519 mode = &display->fullscreen_modes[i]; 520 if (isLandscape != (mode->w > mode->h)) { 521 int height = mode->w; 522 mode->w = mode->h; 523 mode->h = height; 524 } 525 } 526 527#pragma clang diagnostic push 528#pragma clang diagnostic ignored "-Wdeprecated-declarations" 529 switch ([UIApplication sharedApplication].statusBarOrientation) { 530#pragma clang diagnostic pop 531 case UIInterfaceOrientationPortrait: 532 orientation = SDL_ORIENTATION_PORTRAIT; 533 break; 534 case UIInterfaceOrientationPortraitUpsideDown: 535 orientation = SDL_ORIENTATION_PORTRAIT_FLIPPED; 536 break; 537 case UIInterfaceOrientationLandscapeLeft: 538 // Bug: UIInterfaceOrientationLandscapeLeft/Right are reversed - http://openradar.appspot.com/7216046 539 orientation = SDL_ORIENTATION_LANDSCAPE_FLIPPED; 540 break; 541 case UIInterfaceOrientationLandscapeRight: 542 // Bug: UIInterfaceOrientationLandscapeLeft/Right are reversed - http://openradar.appspot.com/7216046 543 orientation = SDL_ORIENTATION_LANDSCAPE; 544 break; 545 default: 546 break; 547 } 548 SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_ORIENTATION, orientation, 0); 549 } 550} 551#endif // !SDL_PLATFORM_TVOS 552 553#endif // SDL_VIDEO_DRIVER_UIKIT 554
[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.