Atlas - SDL_cocoamodes.m

Home / ext / SDL2 / src / video / cocoa Lines: 1 | Size: 16002 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 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#include "SDL_assert.h" 23 24#if SDL_VIDEO_DRIVER_COCOA 25 26#include "SDL_cocoavideo.h" 27 28/* We need this for IODisplayCreateInfoDictionary and kIODisplayOnlyPreferredName */ 29#include <IOKit/graphics/IOGraphicsLib.h> 30 31/* We need this for CVDisplayLinkGetNominalOutputVideoRefreshPeriod */ 32#include <CoreVideo/CVBase.h> 33#include <CoreVideo/CVDisplayLink.h> 34 35/* we need this for ShowMenuBar() and HideMenuBar(). */ 36#include <Carbon/Carbon.h> 37 38/* This gets us MAC_OS_X_VERSION_MIN_REQUIRED... */ 39#include <AvailabilityMacros.h> 40 41 42static void 43Cocoa_ToggleMenuBar(const BOOL show) 44{ 45 /* !!! FIXME: keep an eye on this. 46 * ShowMenuBar/HideMenuBar is officially unavailable for 64-bit binaries. 47 * It happens to work, as of 10.7, but we're going to see if 48 * we can just simply do without it on newer OSes... 49 */ 50#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && !defined(__LP64__) 51 if (show) { 52 ShowMenuBar(); 53 } else { 54 HideMenuBar(); 55 } 56#endif 57} 58 59static int 60CG_SetError(const char *prefix, CGDisplayErr result) 61{ 62 const char *error; 63 64 switch (result) { 65 case kCGErrorFailure: 66 error = "kCGErrorFailure"; 67 break; 68 case kCGErrorIllegalArgument: 69 error = "kCGErrorIllegalArgument"; 70 break; 71 case kCGErrorInvalidConnection: 72 error = "kCGErrorInvalidConnection"; 73 break; 74 case kCGErrorInvalidContext: 75 error = "kCGErrorInvalidContext"; 76 break; 77 case kCGErrorCannotComplete: 78 error = "kCGErrorCannotComplete"; 79 break; 80 case kCGErrorNotImplemented: 81 error = "kCGErrorNotImplemented"; 82 break; 83 case kCGErrorRangeCheck: 84 error = "kCGErrorRangeCheck"; 85 break; 86 case kCGErrorTypeCheck: 87 error = "kCGErrorTypeCheck"; 88 break; 89 case kCGErrorInvalidOperation: 90 error = "kCGErrorInvalidOperation"; 91 break; 92 case kCGErrorNoneAvailable: 93 error = "kCGErrorNoneAvailable"; 94 break; 95 default: 96 error = "Unknown Error"; 97 break; 98 } 99 return SDL_SetError("%s: %s", prefix, error); 100} 101 102static SDL_bool 103GetDisplayMode(_THIS, CGDisplayModeRef vidmode, CVDisplayLinkRef link, SDL_DisplayMode *mode) 104{ 105 SDL_DisplayModeData *data; 106 int width = 0; 107 int height = 0; 108 int bpp = 0; 109 int refreshRate = 0; 110 CFStringRef fmt; 111 112 data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data)); 113 if (!data) { 114 return SDL_FALSE; 115 } 116 data->moderef = vidmode; 117 118 fmt = CGDisplayModeCopyPixelEncoding(vidmode); 119 width = (int) CGDisplayModeGetWidth(vidmode); 120 height = (int) CGDisplayModeGetHeight(vidmode); 121 refreshRate = (int) (CGDisplayModeGetRefreshRate(vidmode) + 0.5); 122 123 if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels), 124 kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 125 bpp = 32; 126 } else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels), 127 kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 128 bpp = 16; 129 } else if (CFStringCompare(fmt, CFSTR(kIO30BitDirectPixels), 130 kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 131 bpp = 30; 132 } else { 133 bpp = 0; /* ignore 8-bit and such for now. */ 134 } 135 136 CFRelease(fmt); 137 138 /* CGDisplayModeGetRefreshRate returns 0 for many non-CRT displays. */ 139 if (refreshRate == 0 && link != NULL) { 140 CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link); 141 if ((time.flags & kCVTimeIsIndefinite) == 0 && time.timeValue != 0) { 142 refreshRate = (int) ((time.timeScale / (double) time.timeValue) + 0.5); 143 } 144 } 145 146 mode->format = SDL_PIXELFORMAT_UNKNOWN; 147 switch (bpp) { 148 case 16: 149 mode->format = SDL_PIXELFORMAT_ARGB1555; 150 break; 151 case 30: 152 mode->format = SDL_PIXELFORMAT_ARGB2101010; 153 break; 154 case 32: 155 mode->format = SDL_PIXELFORMAT_ARGB8888; 156 break; 157 case 8: /* We don't support palettized modes now */ 158 default: /* Totally unrecognizable bit depth. */ 159 SDL_free(data); 160 return SDL_FALSE; 161 } 162 mode->w = width; 163 mode->h = height; 164 mode->refresh_rate = refreshRate; 165 mode->driverdata = data; 166 return SDL_TRUE; 167} 168 169static const char * 170Cocoa_GetDisplayName(CGDirectDisplayID displayID) 171{ 172 CFDictionaryRef deviceInfo = IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName); 173 NSDictionary *localizedNames = [(NSDictionary *)deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]]; 174 const char* displayName = NULL; 175 176 if ([localizedNames count] > 0) { 177 displayName = SDL_strdup([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]); 178 } 179 CFRelease(deviceInfo); 180 return displayName; 181} 182 183void 184Cocoa_InitModes(_THIS) 185{ @autoreleasepool 186{ 187 CGDisplayErr result; 188 CGDirectDisplayID *displays; 189 CGDisplayCount numDisplays; 190 int pass, i; 191 192 result = CGGetOnlineDisplayList(0, NULL, &numDisplays); 193 if (result != kCGErrorSuccess) { 194 CG_SetError("CGGetOnlineDisplayList()", result); 195 return; 196 } 197 displays = SDL_stack_alloc(CGDirectDisplayID, numDisplays); 198 result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays); 199 if (result != kCGErrorSuccess) { 200 CG_SetError("CGGetOnlineDisplayList()", result); 201 SDL_stack_free(displays); 202 return; 203 } 204 205 /* Pick up the primary display in the first pass, then get the rest */ 206 for (pass = 0; pass < 2; ++pass) { 207 for (i = 0; i < numDisplays; ++i) { 208 SDL_VideoDisplay display; 209 SDL_DisplayData *displaydata; 210 SDL_DisplayMode mode; 211 CGDisplayModeRef moderef = NULL; 212 CVDisplayLinkRef link = NULL; 213 214 if (pass == 0) { 215 if (!CGDisplayIsMain(displays[i])) { 216 continue; 217 } 218 } else { 219 if (CGDisplayIsMain(displays[i])) { 220 continue; 221 } 222 } 223 224 if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay) { 225 continue; 226 } 227 228 moderef = CGDisplayCopyDisplayMode(displays[i]); 229 230 if (!moderef) { 231 continue; 232 } 233 234 displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata)); 235 if (!displaydata) { 236 CGDisplayModeRelease(moderef); 237 continue; 238 } 239 displaydata->display = displays[i]; 240 241 CVDisplayLinkCreateWithCGDisplay(displays[i], &link); 242 243 SDL_zero(display); 244 /* this returns a stddup'ed string */ 245 display.name = (char *)Cocoa_GetDisplayName(displays[i]); 246 if (!GetDisplayMode(_this, moderef, link, &mode)) { 247 CVDisplayLinkRelease(link); 248 CGDisplayModeRelease(moderef); 249 SDL_free(display.name); 250 SDL_free(displaydata); 251 continue; 252 } 253 254 CVDisplayLinkRelease(link); 255 256 display.desktop_mode = mode; 257 display.current_mode = mode; 258 display.driverdata = displaydata; 259 SDL_AddVideoDisplay(&display); 260 SDL_free(display.name); 261 } 262 } 263 SDL_stack_free(displays); 264}} 265 266int 267Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) 268{ 269 SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; 270 CGRect cgrect; 271 272 cgrect = CGDisplayBounds(displaydata->display); 273 rect->x = (int)cgrect.origin.x; 274 rect->y = (int)cgrect.origin.y; 275 rect->w = (int)cgrect.size.width; 276 rect->h = (int)cgrect.size.height; 277 return 0; 278} 279 280int 281Cocoa_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) 282{ 283 SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; 284 const CGDirectDisplayID cgdisplay = displaydata->display; 285 NSArray *screens = [NSScreen screens]; 286 NSScreen *screen = nil; 287 288 /* !!! FIXME: maybe track the NSScreen in SDL_DisplayData? */ 289 for (NSScreen *i in screens) { 290 const CGDirectDisplayID thisDisplay = (CGDirectDisplayID) [[[i deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue]; 291 if (thisDisplay == cgdisplay) { 292 screen = i; 293 break; 294 } 295 } 296 297 SDL_assert(screen != nil); /* didn't find it?! */ 298 if (screen == nil) { 299 return -1; 300 } 301 302 const CGRect cgrect = CGDisplayBounds(cgdisplay); 303 const NSRect frame = [screen visibleFrame]; 304 305 // !!! FIXME: I assume -[NSScreen visibleFrame] is relative to the origin of the screen in question and not the whole desktop. 306 // !!! FIXME: The math vs CGDisplayBounds might be incorrect if that's not the case, though. Check this. 307 rect->x = (int)(cgrect.origin.x + frame.origin.x); 308 rect->y = (int)(cgrect.origin.y + frame.origin.y); 309 rect->w = (int)frame.size.width; 310 rect->h = (int)frame.size.height; 311 312 return 0; 313} 314 315int 316Cocoa_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi) 317{ 318 const float MM_IN_INCH = 25.4f; 319 320 SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; 321 322 CGSize displaySize = CGDisplayScreenSize(data->display); 323 int pixelWidth = (int) CGDisplayPixelsWide(data->display); 324 int pixelHeight = (int) CGDisplayPixelsHigh(data->display); 325 326 if (ddpi) { 327 *ddpi = SDL_ComputeDiagonalDPI(pixelWidth, pixelHeight, displaySize.width / MM_IN_INCH, displaySize.height / MM_IN_INCH); 328 } 329 if (hdpi) { 330 *hdpi = pixelWidth * MM_IN_INCH / displaySize.width; 331 } 332 if (vdpi) { 333 *vdpi = pixelHeight * MM_IN_INCH / displaySize.height; 334 } 335 336 return 0; 337} 338 339void 340Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display) 341{ 342 SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; 343 CVDisplayLinkRef link = NULL; 344 CGDisplayModeRef desktopmoderef; 345 SDL_DisplayMode desktopmode; 346 CFArrayRef modes; 347 348 CVDisplayLinkCreateWithCGDisplay(data->display, &link); 349 350 desktopmoderef = CGDisplayCopyDisplayMode(data->display); 351 352 /* CopyAllDisplayModes won't always contain the desktop display mode (if 353 * NULL is passed in) - for example on a retina 15" MBP, System Preferences 354 * allows choosing 1920x1200 but it's not in the list. AddDisplayMode makes 355 * sure there are no duplicates so it's safe to always add the desktop mode 356 * even in cases where it is in the CopyAllDisplayModes list. 357 */ 358 if (desktopmoderef && GetDisplayMode(_this, desktopmoderef, link, &desktopmode)) { 359 if (!SDL_AddDisplayMode(display, &desktopmode)) { 360 CGDisplayModeRelease(desktopmoderef); 361 SDL_free(desktopmode.driverdata); 362 } 363 } else { 364 CGDisplayModeRelease(desktopmoderef); 365 } 366 367 modes = CGDisplayCopyAllDisplayModes(data->display, NULL); 368 369 if (modes) { 370 CFIndex i; 371 const CFIndex count = CFArrayGetCount(modes); 372 373 for (i = 0; i < count; i++) { 374 CGDisplayModeRef moderef = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); 375 SDL_DisplayMode mode; 376 377 if (GetDisplayMode(_this, moderef, link, &mode)) { 378 if (SDL_AddDisplayMode(display, &mode)) { 379 CGDisplayModeRetain(moderef); 380 } else { 381 SDL_free(mode.driverdata); 382 } 383 } 384 } 385 386 CFRelease(modes); 387 } 388 389 CVDisplayLinkRelease(link); 390} 391 392int 393Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) 394{ 395 SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; 396 SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata; 397 CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken; 398 CGError result; 399 400 /* Fade to black to hide resolution-switching flicker */ 401 if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) { 402 CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); 403 } 404 405 if (data == display->desktop_mode.driverdata) { 406 /* Restoring desktop mode */ 407 CGDisplaySetDisplayMode(displaydata->display, data->moderef, NULL); 408 409 if (CGDisplayIsMain(displaydata->display)) { 410 CGReleaseAllDisplays(); 411 } else { 412 CGDisplayRelease(displaydata->display); 413 } 414 415 if (CGDisplayIsMain(displaydata->display)) { 416 Cocoa_ToggleMenuBar(YES); 417 } 418 } else { 419 /* Put up the blanking window (a window above all other windows) */ 420 if (CGDisplayIsMain(displaydata->display)) { 421 /* If we don't capture all displays, Cocoa tries to rearrange windows... *sigh* */ 422 result = CGCaptureAllDisplays(); 423 } else { 424 result = CGDisplayCapture(displaydata->display); 425 } 426 if (result != kCGErrorSuccess) { 427 CG_SetError("CGDisplayCapture()", result); 428 goto ERR_NO_CAPTURE; 429 } 430 431 /* Do the physical switch */ 432 result = CGDisplaySetDisplayMode(displaydata->display, data->moderef, NULL); 433 if (result != kCGErrorSuccess) { 434 CG_SetError("CGDisplaySwitchToMode()", result); 435 goto ERR_NO_SWITCH; 436 } 437 438 /* Hide the menu bar so it doesn't intercept events */ 439 if (CGDisplayIsMain(displaydata->display)) { 440 Cocoa_ToggleMenuBar(NO); 441 } 442 } 443 444 /* Fade in again (asynchronously) */ 445 if (fade_token != kCGDisplayFadeReservationInvalidToken) { 446 CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); 447 CGReleaseDisplayFadeReservation(fade_token); 448 } 449 450 return 0; 451 452 /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */ 453ERR_NO_SWITCH: 454 CGDisplayRelease(displaydata->display); 455ERR_NO_CAPTURE: 456 if (fade_token != kCGDisplayFadeReservationInvalidToken) { 457 CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); 458 CGReleaseDisplayFadeReservation(fade_token); 459 } 460 return -1; 461} 462 463void 464Cocoa_QuitModes(_THIS) 465{ 466 int i, j; 467 468 for (i = 0; i < _this->num_displays; ++i) { 469 SDL_VideoDisplay *display = &_this->displays[i]; 470 SDL_DisplayModeData *mode; 471 472 if (display->current_mode.driverdata != display->desktop_mode.driverdata) { 473 Cocoa_SetDisplayMode(_this, display, &display->desktop_mode); 474 } 475 476 mode = (SDL_DisplayModeData *) display->desktop_mode.driverdata; 477 CGDisplayModeRelease(mode->moderef); 478 479 for (j = 0; j < display->num_display_modes; j++) { 480 mode = (SDL_DisplayModeData*) display->display_modes[j].driverdata; 481 CGDisplayModeRelease(mode->moderef); 482 } 483 484 } 485 Cocoa_ToggleMenuBar(YES); 486} 487 488#endif /* SDL_VIDEO_DRIVER_COCOA */ 489 490/* vi: set ts=4 sw=4 expandtab: */ 491
[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.