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