SDL_cocoamodes.m (24980B)
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_COCOA 24 25 #include "SDL_cocoavideo.h" 26 27 /* We need this for IODisplayCreateInfoDictionary and kIODisplayOnlyPreferredName */ 28 #include <IOKit/graphics/IOGraphicsLib.h> 29 30 /* We need this for CVDisplayLinkGetNominalOutputVideoRefreshPeriod */ 31 #include <CoreVideo/CVBase.h> 32 #include <CoreVideo/CVDisplayLink.h> 33 34 /* we need this for ShowMenuBar() and HideMenuBar(). */ 35 #include <Carbon/Carbon.h> 36 37 /* This gets us MAC_OS_X_VERSION_MIN_REQUIRED... */ 38 #include <AvailabilityMacros.h> 39 40 #ifndef MAC_OS_X_VERSION_10_13 41 #define NSAppKitVersionNumber10_12 1504 42 #endif 43 44 45 static void 46 Cocoa_ToggleMenuBar(const BOOL show) 47 { 48 /* !!! FIXME: keep an eye on this. 49 * ShowMenuBar/HideMenuBar is officially unavailable for 64-bit binaries. 50 * It happens to work, as of 10.7, but we're going to see if 51 * we can just simply do without it on newer OSes... 52 */ 53 #if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && !defined(__LP64__) 54 if (show) { 55 ShowMenuBar(); 56 } else { 57 HideMenuBar(); 58 } 59 #endif 60 } 61 62 static int 63 CG_SetError(const char *prefix, CGDisplayErr result) 64 { 65 const char *error; 66 67 switch (result) { 68 case kCGErrorFailure: 69 error = "kCGErrorFailure"; 70 break; 71 case kCGErrorIllegalArgument: 72 error = "kCGErrorIllegalArgument"; 73 break; 74 case kCGErrorInvalidConnection: 75 error = "kCGErrorInvalidConnection"; 76 break; 77 case kCGErrorInvalidContext: 78 error = "kCGErrorInvalidContext"; 79 break; 80 case kCGErrorCannotComplete: 81 error = "kCGErrorCannotComplete"; 82 break; 83 case kCGErrorNotImplemented: 84 error = "kCGErrorNotImplemented"; 85 break; 86 case kCGErrorRangeCheck: 87 error = "kCGErrorRangeCheck"; 88 break; 89 case kCGErrorTypeCheck: 90 error = "kCGErrorTypeCheck"; 91 break; 92 case kCGErrorInvalidOperation: 93 error = "kCGErrorInvalidOperation"; 94 break; 95 case kCGErrorNoneAvailable: 96 error = "kCGErrorNoneAvailable"; 97 break; 98 default: 99 error = "Unknown Error"; 100 break; 101 } 102 return SDL_SetError("%s: %s", prefix, error); 103 } 104 105 static int 106 GetDisplayModeRefreshRate(CGDisplayModeRef vidmode, CVDisplayLinkRef link) 107 { 108 int refreshRate = (int) (CGDisplayModeGetRefreshRate(vidmode) + 0.5); 109 110 /* CGDisplayModeGetRefreshRate can return 0 (eg for built-in displays). */ 111 if (refreshRate == 0 && link != NULL) { 112 CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link); 113 if ((time.flags & kCVTimeIsIndefinite) == 0 && time.timeValue != 0) { 114 refreshRate = (int) ((time.timeScale / (double) time.timeValue) + 0.5); 115 } 116 } 117 118 return refreshRate; 119 } 120 121 static SDL_bool 122 HasValidDisplayModeFlags(CGDisplayModeRef vidmode) 123 { 124 uint32_t ioflags = CGDisplayModeGetIOFlags(vidmode); 125 126 /* Filter out modes which have flags that we don't want. */ 127 if (ioflags & (kDisplayModeNeverShowFlag | kDisplayModeNotGraphicsQualityFlag)) { 128 return SDL_FALSE; 129 } 130 131 /* Filter out modes which don't have flags that we want. */ 132 if (!(ioflags & kDisplayModeValidFlag) || !(ioflags & kDisplayModeSafeFlag)) { 133 return SDL_FALSE; 134 } 135 136 return SDL_TRUE; 137 } 138 139 static Uint32 140 GetDisplayModePixelFormat(CGDisplayModeRef vidmode) 141 { 142 /* This API is deprecated in 10.11 with no good replacement (as of 10.15). */ 143 CFStringRef fmt = CGDisplayModeCopyPixelEncoding(vidmode); 144 Uint32 pixelformat = SDL_PIXELFORMAT_UNKNOWN; 145 146 if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels), 147 kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 148 pixelformat = SDL_PIXELFORMAT_ARGB8888; 149 } else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels), 150 kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 151 pixelformat = SDL_PIXELFORMAT_ARGB1555; 152 } else if (CFStringCompare(fmt, CFSTR(kIO30BitDirectPixels), 153 kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 154 pixelformat = SDL_PIXELFORMAT_ARGB2101010; 155 } else { 156 /* ignore 8-bit and such for now. */ 157 } 158 159 CFRelease(fmt); 160 161 return pixelformat; 162 } 163 164 static SDL_bool 165 GetDisplayMode(_THIS, CGDisplayModeRef vidmode, CFArrayRef modelist, CVDisplayLinkRef link, SDL_DisplayMode *mode) 166 { 167 SDL_DisplayModeData *data; 168 bool usableForGUI = CGDisplayModeIsUsableForDesktopGUI(vidmode); 169 int width = (int) CGDisplayModeGetWidth(vidmode); 170 int height = (int) CGDisplayModeGetHeight(vidmode); 171 uint32_t ioflags = CGDisplayModeGetIOFlags(vidmode); 172 int refreshrate = GetDisplayModeRefreshRate(vidmode, link); 173 Uint32 format = GetDisplayModePixelFormat(vidmode); 174 bool interlaced = (ioflags & kDisplayModeInterlacedFlag) != 0; 175 CFMutableArrayRef modes; 176 177 if (format == SDL_PIXELFORMAT_UNKNOWN) { 178 return SDL_FALSE; 179 } 180 181 if (!HasValidDisplayModeFlags(vidmode)) { 182 return SDL_FALSE; 183 } 184 185 modes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 186 CFArrayAppendValue(modes, vidmode); 187 188 /* If a list of possible diplay modes is passed in, use it to filter out 189 * modes that have duplicate sizes. We don't just rely on SDL's higher level 190 * duplicate filtering because this code can choose what properties are 191 * prefered, and it can add CGDisplayModes to the DisplayModeData's list of 192 * modes to try (see comment below for why that's necessary). 193 * CGDisplayModeGetPixelWidth and friends are only available in 10.8+. */ 194 #ifdef MAC_OS_X_VERSION_10_8 195 if (modelist != NULL && floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_7) { 196 int pixelW = (int) CGDisplayModeGetPixelWidth(vidmode); 197 int pixelH = (int) CGDisplayModeGetPixelHeight(vidmode); 198 199 CFIndex modescount = CFArrayGetCount(modelist); 200 int i; 201 202 for (i = 0; i < modescount; i++) { 203 CGDisplayModeRef othermode = (CGDisplayModeRef) CFArrayGetValueAtIndex(modelist, i); 204 uint32_t otherioflags = CGDisplayModeGetIOFlags(othermode); 205 206 if (CFEqual(vidmode, othermode)) { 207 continue; 208 } 209 210 if (!HasValidDisplayModeFlags(othermode)) { 211 continue; 212 } 213 214 int otherW = (int) CGDisplayModeGetWidth(othermode); 215 int otherH = (int) CGDisplayModeGetHeight(othermode); 216 int otherpixelW = (int) CGDisplayModeGetPixelWidth(othermode); 217 int otherpixelH = (int) CGDisplayModeGetPixelHeight(othermode); 218 int otherrefresh = GetDisplayModeRefreshRate(othermode, link); 219 Uint32 otherformat = GetDisplayModePixelFormat(othermode); 220 bool otherGUI = CGDisplayModeIsUsableForDesktopGUI(othermode); 221 222 /* Ignore this mode if it's low-dpi (@1x) and we have a high-dpi 223 * mode in the list with the same size in points. 224 */ 225 if (width == pixelW && height == pixelH 226 && width == otherW && height == otherH 227 && refreshrate == otherrefresh && format == otherformat 228 && (otherpixelW != otherW || otherpixelH != otherH)) { 229 CFRelease(modes); 230 return SDL_FALSE; 231 } 232 233 /* Ignore this mode if it's interlaced and there's a non-interlaced 234 * mode in the list with the same properties. 235 */ 236 if (interlaced && ((otherioflags & kDisplayModeInterlacedFlag) == 0) 237 && width == otherW && height == otherH && pixelW == otherpixelW 238 && pixelH == otherpixelH && refreshrate == otherrefresh 239 && format == otherformat && usableForGUI == otherGUI) { 240 CFRelease(modes); 241 return SDL_FALSE; 242 } 243 244 /* Ignore this mode if it's not usable for desktop UI and its 245 * properties are equal to another GUI-capable mode in the list. 246 */ 247 if (width == otherW && height == otherH && pixelW == otherpixelW 248 && pixelH == otherpixelH && !usableForGUI && otherGUI 249 && refreshrate == otherrefresh && format == otherformat) { 250 CFRelease(modes); 251 return SDL_FALSE; 252 } 253 254 /* If multiple modes have the exact same properties, they'll all 255 * go in the list of modes to try when SetDisplayMode is called. 256 * This is needed because kCGDisplayShowDuplicateLowResolutionModes 257 * (which is used to expose highdpi display modes) can make the 258 * list of modes contain duplicates (according to their properties 259 * obtained via public APIs) which don't work with SetDisplayMode. 260 * Those duplicate non-functional modes *do* have different pixel 261 * formats according to their internal data structure viewed with 262 * NSLog, but currently no public API can detect that. 263 * https://bugzilla.libsdl.org/show_bug.cgi?id=4822 264 * 265 * As of macOS 10.15.0, those duplicates have the exact same 266 * properties via public APIs in every way (even their IO flags and 267 * CGDisplayModeGetIODisplayModeID is the same), so we could test 268 * those for equality here too, but I'm intentionally not doing that 269 * in case there are duplicate modes with different IO flags or IO 270 * display mode IDs in the future. In that case I think it's better 271 * to try them all in SetDisplayMode than to risk one of them being 272 * correct but it being filtered out by SDL_AddDisplayMode as being 273 * a duplicate. 274 */ 275 if (width == otherW && height == otherH && pixelW == otherpixelW 276 && pixelH == otherpixelH && usableForGUI == otherGUI 277 && refreshrate == otherrefresh && format == otherformat) { 278 CFArrayAppendValue(modes, othermode); 279 } 280 } 281 } 282 #endif 283 284 data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data)); 285 if (!data) { 286 CFRelease(modes); 287 return SDL_FALSE; 288 } 289 data->modes = modes; 290 mode->format = format; 291 mode->w = width; 292 mode->h = height; 293 mode->refresh_rate = refreshrate; 294 mode->driverdata = data; 295 return SDL_TRUE; 296 } 297 298 static const char * 299 Cocoa_GetDisplayName(CGDirectDisplayID displayID) 300 { 301 /* This API is deprecated in 10.9 with no good replacement (as of 10.15). */ 302 io_service_t servicePort = CGDisplayIOServicePort(displayID); 303 CFDictionaryRef deviceInfo = IODisplayCreateInfoDictionary(servicePort, kIODisplayOnlyPreferredName); 304 NSDictionary *localizedNames = [(NSDictionary *)deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]]; 305 const char* displayName = NULL; 306 307 if ([localizedNames count] > 0) { 308 displayName = SDL_strdup([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]); 309 } 310 CFRelease(deviceInfo); 311 return displayName; 312 } 313 314 void 315 Cocoa_InitModes(_THIS) 316 { @autoreleasepool 317 { 318 CGDisplayErr result; 319 CGDirectDisplayID *displays; 320 CGDisplayCount numDisplays; 321 SDL_bool isstack; 322 int pass, i; 323 324 result = CGGetOnlineDisplayList(0, NULL, &numDisplays); 325 if (result != kCGErrorSuccess) { 326 CG_SetError("CGGetOnlineDisplayList()", result); 327 return; 328 } 329 displays = SDL_small_alloc(CGDirectDisplayID, numDisplays, &isstack); 330 result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays); 331 if (result != kCGErrorSuccess) { 332 CG_SetError("CGGetOnlineDisplayList()", result); 333 SDL_small_free(displays, isstack); 334 return; 335 } 336 337 /* Pick up the primary display in the first pass, then get the rest */ 338 for (pass = 0; pass < 2; ++pass) { 339 for (i = 0; i < numDisplays; ++i) { 340 SDL_VideoDisplay display; 341 SDL_DisplayData *displaydata; 342 SDL_DisplayMode mode; 343 CGDisplayModeRef moderef = NULL; 344 CVDisplayLinkRef link = NULL; 345 346 if (pass == 0) { 347 if (!CGDisplayIsMain(displays[i])) { 348 continue; 349 } 350 } else { 351 if (CGDisplayIsMain(displays[i])) { 352 continue; 353 } 354 } 355 356 if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay) { 357 continue; 358 } 359 360 moderef = CGDisplayCopyDisplayMode(displays[i]); 361 362 if (!moderef) { 363 continue; 364 } 365 366 displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata)); 367 if (!displaydata) { 368 CGDisplayModeRelease(moderef); 369 continue; 370 } 371 displaydata->display = displays[i]; 372 373 CVDisplayLinkCreateWithCGDisplay(displays[i], &link); 374 375 SDL_zero(display); 376 /* this returns a stddup'ed string */ 377 display.name = (char *)Cocoa_GetDisplayName(displays[i]); 378 if (!GetDisplayMode(_this, moderef, NULL, link, &mode)) { 379 CVDisplayLinkRelease(link); 380 CGDisplayModeRelease(moderef); 381 SDL_free(display.name); 382 SDL_free(displaydata); 383 continue; 384 } 385 386 CVDisplayLinkRelease(link); 387 CGDisplayModeRelease(moderef); 388 389 display.desktop_mode = mode; 390 display.current_mode = mode; 391 display.driverdata = displaydata; 392 SDL_AddVideoDisplay(&display, SDL_FALSE); 393 SDL_free(display.name); 394 } 395 } 396 SDL_small_free(displays, isstack); 397 }} 398 399 int 400 Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) 401 { 402 SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; 403 CGRect cgrect; 404 405 cgrect = CGDisplayBounds(displaydata->display); 406 rect->x = (int)cgrect.origin.x; 407 rect->y = (int)cgrect.origin.y; 408 rect->w = (int)cgrect.size.width; 409 rect->h = (int)cgrect.size.height; 410 return 0; 411 } 412 413 int 414 Cocoa_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) 415 { 416 SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; 417 const CGDirectDisplayID cgdisplay = displaydata->display; 418 NSArray *screens = [NSScreen screens]; 419 NSScreen *screen = nil; 420 421 /* !!! FIXME: maybe track the NSScreen in SDL_DisplayData? */ 422 for (NSScreen *i in screens) { 423 const CGDirectDisplayID thisDisplay = (CGDirectDisplayID) [[[i deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue]; 424 if (thisDisplay == cgdisplay) { 425 screen = i; 426 break; 427 } 428 } 429 430 SDL_assert(screen != nil); /* didn't find it?! */ 431 if (screen == nil) { 432 return -1; 433 } 434 435 const NSRect frame = [screen visibleFrame]; 436 rect->x = (int)frame.origin.x; 437 rect->y = (int)(CGDisplayPixelsHigh(kCGDirectMainDisplay) - frame.origin.y - frame.size.height); 438 rect->w = (int)frame.size.width; 439 rect->h = (int)frame.size.height; 440 441 return 0; 442 } 443 444 int 445 Cocoa_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi) 446 { @autoreleasepool 447 { 448 const float MM_IN_INCH = 25.4f; 449 450 SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; 451 452 /* we need the backingScaleFactor for Retina displays, which is only exposed through NSScreen, not CGDisplay, afaik, so find our screen... */ 453 CGFloat scaleFactor = 1.0f; 454 NSArray *screens = [NSScreen screens]; 455 for (NSScreen *screen in screens) { 456 const CGDirectDisplayID dpyid = (const CGDirectDisplayID ) [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue]; 457 if (dpyid == data->display) { 458 if ([screen respondsToSelector:@selector(backingScaleFactor)]) { // Mac OS X 10.7 and later 459 scaleFactor = [screen backingScaleFactor]; 460 break; 461 } 462 } 463 } 464 465 const CGSize displaySize = CGDisplayScreenSize(data->display); 466 const int pixelWidth = (int) CGDisplayPixelsWide(data->display); 467 const int pixelHeight = (int) CGDisplayPixelsHigh(data->display); 468 469 if (ddpi) { 470 *ddpi = (SDL_ComputeDiagonalDPI(pixelWidth, pixelHeight, displaySize.width / MM_IN_INCH, displaySize.height / MM_IN_INCH)) * scaleFactor; 471 } 472 if (hdpi) { 473 *hdpi = (pixelWidth * MM_IN_INCH / displaySize.width) * scaleFactor; 474 } 475 if (vdpi) { 476 *vdpi = (pixelHeight * MM_IN_INCH / displaySize.height) * scaleFactor; 477 } 478 479 return 0; 480 }} 481 482 void 483 Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display) 484 { 485 SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; 486 CVDisplayLinkRef link = NULL; 487 CGDisplayModeRef desktopmoderef; 488 SDL_DisplayMode desktopmode; 489 CFArrayRef modes; 490 CFDictionaryRef dict = NULL; 491 492 CVDisplayLinkCreateWithCGDisplay(data->display, &link); 493 494 desktopmoderef = CGDisplayCopyDisplayMode(data->display); 495 496 /* CopyAllDisplayModes won't always contain the desktop display mode (if 497 * NULL is passed in) - for example on a retina 15" MBP, System Preferences 498 * allows choosing 1920x1200 but it's not in the list. AddDisplayMode makes 499 * sure there are no duplicates so it's safe to always add the desktop mode 500 * even in cases where it is in the CopyAllDisplayModes list. 501 */ 502 if (desktopmoderef && GetDisplayMode(_this, desktopmoderef, NULL, link, &desktopmode)) { 503 if (!SDL_AddDisplayMode(display, &desktopmode)) { 504 CFRelease(((SDL_DisplayModeData*)desktopmode.driverdata)->modes); 505 SDL_free(desktopmode.driverdata); 506 } 507 } 508 509 CGDisplayModeRelease(desktopmoderef); 510 511 /* By default, CGDisplayCopyAllDisplayModes will only get a subset of the 512 * system's available modes. For example on a 15" 2016 MBP, users can 513 * choose 1920x1080@2x in System Preferences but it won't show up here, 514 * unless we specify the option below. 515 * The display modes returned by CGDisplayCopyAllDisplayModes are also not 516 * high dpi-capable unless this option is set. 517 * macOS 10.15 also seems to have a bug where entering, exiting, and 518 * re-entering exclusive fullscreen with a low dpi display mode can cause 519 * the content of the screen to move up, which this setting avoids: 520 * https://bugzilla.libsdl.org/show_bug.cgi?id=4822 521 */ 522 #ifdef MAC_OS_X_VERSION_10_8 523 if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_7) { 524 const CFStringRef dictkeys[] = {kCGDisplayShowDuplicateLowResolutionModes}; 525 const CFBooleanRef dictvalues[] = {kCFBooleanTrue}; 526 dict = CFDictionaryCreate(NULL, 527 (const void **)dictkeys, 528 (const void **)dictvalues, 529 1, 530 &kCFCopyStringDictionaryKeyCallBacks, 531 &kCFTypeDictionaryValueCallBacks); 532 } 533 #endif 534 535 modes = CGDisplayCopyAllDisplayModes(data->display, dict); 536 537 if (dict) { 538 CFRelease(dict); 539 } 540 541 if (modes) { 542 CFIndex i; 543 const CFIndex count = CFArrayGetCount(modes); 544 545 for (i = 0; i < count; i++) { 546 CGDisplayModeRef moderef = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); 547 SDL_DisplayMode mode; 548 549 if (GetDisplayMode(_this, moderef, modes, link, &mode)) { 550 if (!SDL_AddDisplayMode(display, &mode)) { 551 CFRelease(((SDL_DisplayModeData*)mode.driverdata)->modes); 552 SDL_free(mode.driverdata); 553 } 554 } 555 } 556 557 CFRelease(modes); 558 } 559 560 CVDisplayLinkRelease(link); 561 } 562 563 static CGError 564 SetDisplayModeForDisplay(CGDirectDisplayID display, SDL_DisplayModeData *data) 565 { 566 /* SDL_DisplayModeData can contain multiple CGDisplayModes to try (with 567 * identical properties), some of which might not work. See GetDisplayMode. 568 */ 569 CGError result = kCGErrorFailure; 570 for (CFIndex i = 0; i < CFArrayGetCount(data->modes); i++) { 571 CGDisplayModeRef moderef = (CGDisplayModeRef)CFArrayGetValueAtIndex(data->modes, i); 572 result = CGDisplaySetDisplayMode(display, moderef, NULL); 573 if (result == kCGErrorSuccess) { 574 /* If this mode works, try it first next time. */ 575 CFArrayExchangeValuesAtIndices(data->modes, i, 0); 576 break; 577 } 578 } 579 return result; 580 } 581 582 int 583 Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) 584 { 585 SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; 586 SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata; 587 CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken; 588 CGError result; 589 590 /* Fade to black to hide resolution-switching flicker */ 591 if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) { 592 CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); 593 } 594 595 if (data == display->desktop_mode.driverdata) { 596 /* Restoring desktop mode */ 597 SetDisplayModeForDisplay(displaydata->display, data); 598 599 if (CGDisplayIsMain(displaydata->display)) { 600 CGReleaseAllDisplays(); 601 } else { 602 CGDisplayRelease(displaydata->display); 603 } 604 605 if (CGDisplayIsMain(displaydata->display)) { 606 Cocoa_ToggleMenuBar(YES); 607 } 608 } else { 609 /* Put up the blanking window (a window above all other windows) */ 610 if (CGDisplayIsMain(displaydata->display)) { 611 /* If we don't capture all displays, Cocoa tries to rearrange windows... *sigh* */ 612 result = CGCaptureAllDisplays(); 613 } else { 614 result = CGDisplayCapture(displaydata->display); 615 } 616 if (result != kCGErrorSuccess) { 617 CG_SetError("CGDisplayCapture()", result); 618 goto ERR_NO_CAPTURE; 619 } 620 621 /* Do the physical switch */ 622 result = SetDisplayModeForDisplay(displaydata->display, data); 623 if (result != kCGErrorSuccess) { 624 CG_SetError("CGDisplaySwitchToMode()", result); 625 goto ERR_NO_SWITCH; 626 } 627 628 /* Hide the menu bar so it doesn't intercept events */ 629 if (CGDisplayIsMain(displaydata->display)) { 630 Cocoa_ToggleMenuBar(NO); 631 } 632 } 633 634 /* Fade in again (asynchronously) */ 635 if (fade_token != kCGDisplayFadeReservationInvalidToken) { 636 CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); 637 CGReleaseDisplayFadeReservation(fade_token); 638 } 639 640 return 0; 641 642 /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */ 643 ERR_NO_SWITCH: 644 if (CGDisplayIsMain(displaydata->display)) { 645 CGReleaseAllDisplays(); 646 } else { 647 CGDisplayRelease(displaydata->display); 648 } 649 ERR_NO_CAPTURE: 650 if (fade_token != kCGDisplayFadeReservationInvalidToken) { 651 CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); 652 CGReleaseDisplayFadeReservation(fade_token); 653 } 654 return -1; 655 } 656 657 void 658 Cocoa_QuitModes(_THIS) 659 { 660 int i, j; 661 662 for (i = 0; i < _this->num_displays; ++i) { 663 SDL_VideoDisplay *display = &_this->displays[i]; 664 SDL_DisplayModeData *mode; 665 666 if (display->current_mode.driverdata != display->desktop_mode.driverdata) { 667 Cocoa_SetDisplayMode(_this, display, &display->desktop_mode); 668 } 669 670 mode = (SDL_DisplayModeData *) display->desktop_mode.driverdata; 671 CFRelease(mode->modes); 672 673 for (j = 0; j < display->num_display_modes; j++) { 674 mode = (SDL_DisplayModeData*) display->display_modes[j].driverdata; 675 CFRelease(mode->modes); 676 } 677 } 678 Cocoa_ToggleMenuBar(YES); 679 } 680 681 #endif /* SDL_VIDEO_DRIVER_COCOA */ 682 683 /* vi: set ts=4 sw=4 expandtab: */