src/video/cocoa/SDL_cocoamodes.m
author Ryan C. Gordon <icculus@icculus.org>
Fri, 02 Nov 2012 16:48:47 -0400
changeset 6639 c7e81fae23c4
parent 6388 470bc1333b09
child 6787 95a4c5a5464c
permissions -rw-r--r--
Cocoa_ShowMessageBox() should call Cocoa_RegisterApp().

Otherwise, message boxes can fail if SDL_Init(SDL_INIT_VIDEO) wasn't completed.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 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_config.h"
    22 
    23 #if SDL_VIDEO_DRIVER_COCOA
    24 
    25 #include "SDL_cocoavideo.h"
    26 
    27 /* we need this for ShowMenuBar() and HideMenuBar(). */
    28 #include <Carbon/Carbon.h>
    29 
    30 static inline void Cocoa_ToggleMenuBar(const BOOL show)
    31 {
    32     /* !!! FIXME: keep an eye on this.
    33      * ShowMenuBar/HideMenuBar is officially unavailable for 64-bit binaries.
    34      *  It happens to work, as of 10.7, but we're going to see if
    35      *  we can just simply do without it on newer OSes...
    36      */
    37 #if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && !defined(__LP64__)
    38     if (show)
    39         ShowMenuBar();
    40     else
    41         HideMenuBar();
    42 #endif
    43 }
    44 
    45 
    46 /* !!! FIXME: clean out the pre-10.6 code when it makes sense to do so. */
    47 #define FORCE_OLD_API 0 || (MAC_OS_X_VERSION_MAX_ALLOWED < 1060)
    48 
    49 #if FORCE_OLD_API
    50 #undef MAC_OS_X_VERSION_MIN_REQUIRED
    51 #define MAC_OS_X_VERSION_MIN_REQUIRED 1050
    52 #endif
    53 
    54 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1050
    55 /* 
    56     Add methods to get at private members of NSScreen. 
    57     Since there is a bug in Apple's screen switching code
    58     that does not update this variable when switching
    59     to fullscreen, we'll set it manually (but only for the
    60     main screen).
    61 */
    62 @interface NSScreen (NSScreenAccess)
    63 - (void) setFrame:(NSRect)frame;
    64 @end
    65 
    66 @implementation NSScreen (NSScreenAccess)
    67 - (void) setFrame:(NSRect)frame;
    68 {
    69     _frame = frame;
    70 }
    71 @end
    72 #endif
    73 
    74 static inline BOOL
    75 IS_SNOW_LEOPARD_OR_LATER(_THIS)
    76 {
    77 #if FORCE_OLD_API
    78     return NO;
    79 #else
    80     return ((((SDL_VideoData *) _this->driverdata))->osversion >= 0x1060);
    81 #endif
    82 }
    83 
    84 static void
    85 CG_SetError(const char *prefix, CGDisplayErr result)
    86 {
    87     const char *error;
    88 
    89     switch (result) {
    90     case kCGErrorFailure:
    91         error = "kCGErrorFailure";
    92         break;
    93     case kCGErrorIllegalArgument:
    94         error = "kCGErrorIllegalArgument";
    95         break;
    96     case kCGErrorInvalidConnection:
    97         error = "kCGErrorInvalidConnection";
    98         break;
    99     case kCGErrorInvalidContext:
   100         error = "kCGErrorInvalidContext";
   101         break;
   102     case kCGErrorCannotComplete:
   103         error = "kCGErrorCannotComplete";
   104         break;
   105     case kCGErrorNotImplemented:
   106         error = "kCGErrorNotImplemented";
   107         break;
   108     case kCGErrorRangeCheck:
   109         error = "kCGErrorRangeCheck";
   110         break;
   111     case kCGErrorTypeCheck:
   112         error = "kCGErrorTypeCheck";
   113         break;
   114     case kCGErrorInvalidOperation:
   115         error = "kCGErrorInvalidOperation";
   116         break;
   117     case kCGErrorNoneAvailable:
   118         error = "kCGErrorNoneAvailable";
   119         break;
   120     default:
   121         error = "Unknown Error";
   122         break;
   123     }
   124     SDL_SetError("%s: %s", prefix, error);
   125 }
   126 
   127 static SDL_bool
   128 GetDisplayMode(_THIS, const void *moderef, SDL_DisplayMode *mode)
   129 {
   130     SDL_DisplayModeData *data;
   131     long width = 0;
   132     long height = 0;
   133     long bpp = 0;
   134     long refreshRate = 0;
   135 
   136     data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data));
   137     if (!data) {
   138         return SDL_FALSE;
   139     }
   140     data->moderef = moderef;
   141 
   142     #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   143     if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   144         CGDisplayModeRef vidmode = (CGDisplayModeRef) moderef;
   145         CFStringRef fmt = CGDisplayModeCopyPixelEncoding(vidmode);
   146         width = (long) CGDisplayModeGetWidth(vidmode);
   147         height = (long) CGDisplayModeGetHeight(vidmode);
   148         refreshRate = (long) CGDisplayModeGetRefreshRate(vidmode);
   149 
   150         if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels),
   151                             kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
   152             bpp = 32;
   153         } else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels),
   154                             kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
   155             bpp = 16;
   156         } else {
   157             bpp = 0;  /* ignore 8-bit and such for now. */
   158         }
   159 
   160         CFRelease(fmt);
   161     }
   162     #endif
   163 
   164     #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   165     if (!IS_SNOW_LEOPARD_OR_LATER(_this)) {
   166         CFNumberRef number;
   167         CFDictionaryRef vidmode = (CFDictionaryRef) moderef;
   168         number = CFDictionaryGetValue(vidmode, kCGDisplayWidth);
   169         CFNumberGetValue(number, kCFNumberLongType, &width);
   170         number = CFDictionaryGetValue(vidmode, kCGDisplayHeight);
   171         CFNumberGetValue(number, kCFNumberLongType, &height);
   172         number = CFDictionaryGetValue(vidmode, kCGDisplayBitsPerPixel);
   173         CFNumberGetValue(number, kCFNumberLongType, &bpp);
   174         number = CFDictionaryGetValue(vidmode, kCGDisplayRefreshRate);
   175         CFNumberGetValue(number, kCFNumberLongType, &refreshRate);
   176     }
   177     #endif
   178 
   179     mode->format = SDL_PIXELFORMAT_UNKNOWN;
   180     switch (bpp) {
   181     case 16:
   182         mode->format = SDL_PIXELFORMAT_ARGB1555;
   183         break;
   184     case 32:
   185         mode->format = SDL_PIXELFORMAT_ARGB8888;
   186         break;
   187     case 8: /* We don't support palettized modes now */
   188     default: /* Totally unrecognizable bit depth. */
   189         return SDL_FALSE;
   190     }
   191     mode->w = width;
   192     mode->h = height;
   193     mode->refresh_rate = refreshRate;
   194     mode->driverdata = data;
   195     return SDL_TRUE;
   196 }
   197 
   198 static inline void
   199 Cocoa_ReleaseDisplayMode(_THIS, const void *moderef)
   200 {
   201     /* We don't own moderef unless we use the 10.6+ APIs. */
   202     #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   203     if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   204         CGDisplayModeRelease((CGDisplayModeRef) moderef);  /* NULL is ok */
   205     }
   206     #endif
   207 }
   208 
   209 static inline void
   210 Cocoa_ReleaseDisplayModeList(_THIS, CFArrayRef modelist)
   211 {
   212     /* We don't own modelis unless we use the 10.6+ APIs. */
   213     #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   214     if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   215         CFRelease(modelist);  /* NULL is ok */
   216     }
   217     #endif
   218 }
   219 
   220 void
   221 Cocoa_InitModes(_THIS)
   222 {
   223     CGDisplayErr result;
   224     CGDirectDisplayID *displays;
   225     CGDisplayCount numDisplays;
   226     int pass, i;
   227 
   228     result = CGGetOnlineDisplayList(0, NULL, &numDisplays);
   229     if (result != kCGErrorSuccess) {
   230         CG_SetError("CGGetOnlineDisplayList()", result);
   231         return;
   232     }
   233     displays = SDL_stack_alloc(CGDirectDisplayID, numDisplays);
   234     result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays);
   235     if (result != kCGErrorSuccess) {
   236         CG_SetError("CGGetOnlineDisplayList()", result);
   237         SDL_stack_free(displays);
   238         return;
   239     }
   240 
   241     /* Pick up the primary display in the first pass, then get the rest */
   242     for (pass = 0; pass < 2; ++pass) {
   243         for (i = 0; i < numDisplays; ++i) {
   244             SDL_VideoDisplay display;
   245             SDL_DisplayData *displaydata;
   246             SDL_DisplayMode mode;
   247             const void *moderef = NULL;
   248 
   249             if (pass == 0) {
   250                 if (!CGDisplayIsMain(displays[i])) {
   251                     continue;
   252                 }
   253             } else {
   254                 if (CGDisplayIsMain(displays[i])) {
   255                     continue;
   256                 }
   257             }
   258 
   259             if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay) {
   260                 continue;
   261             }
   262 
   263             #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   264             if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   265                 moderef = CGDisplayCopyDisplayMode(displays[i]);
   266             }
   267             #endif
   268 
   269             #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   270             if (!IS_SNOW_LEOPARD_OR_LATER(_this)) {
   271                 moderef = CGDisplayCurrentMode(displays[i]);
   272             }
   273             #endif
   274 
   275             if (!moderef) {
   276                 continue;
   277             }
   278 
   279             displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
   280             if (!displaydata) {
   281                 Cocoa_ReleaseDisplayMode(_this, moderef);
   282                 continue;
   283             }
   284             displaydata->display = displays[i];
   285 
   286             SDL_zero(display);
   287             if (!GetDisplayMode (_this, moderef, &mode)) {
   288                 Cocoa_ReleaseDisplayMode(_this, moderef);
   289                 SDL_free(displaydata);
   290                 continue;
   291             }
   292 
   293             display.desktop_mode = mode;
   294             display.current_mode = mode;
   295             display.driverdata = displaydata;
   296             SDL_AddVideoDisplay(&display);
   297         }
   298     }
   299     SDL_stack_free(displays);
   300 }
   301 
   302 int
   303 Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
   304 {
   305     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
   306     CGRect cgrect;
   307 
   308     cgrect = CGDisplayBounds(displaydata->display);
   309     rect->x = (int)cgrect.origin.x;
   310     rect->y = (int)cgrect.origin.y;
   311     rect->w = (int)cgrect.size.width;
   312     rect->h = (int)cgrect.size.height;
   313     return 0;
   314 }
   315 
   316 void
   317 Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
   318 {
   319     SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
   320     CFArrayRef modes = NULL;
   321 
   322     #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   323     if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   324         modes = CGDisplayCopyAllDisplayModes(data->display, NULL);
   325     }
   326     #endif
   327 
   328     #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   329     if (!IS_SNOW_LEOPARD_OR_LATER(_this)) {
   330         modes = CGDisplayAvailableModes(data->display);
   331     }
   332     #endif
   333 
   334     if (modes) {
   335         const CFIndex count = CFArrayGetCount(modes);
   336         CFIndex i;
   337 
   338         for (i = 0; i < count; i++) {
   339             const void *moderef = CFArrayGetValueAtIndex(modes, i);
   340             SDL_DisplayMode mode;
   341             if (GetDisplayMode(_this, moderef, &mode)) {
   342                 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   343                 if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   344                     CGDisplayModeRetain((CGDisplayModeRef) moderef);
   345                 }
   346                 #endif
   347                 SDL_AddDisplayMode(display, &mode);
   348             }
   349         }
   350 
   351         Cocoa_ReleaseDisplayModeList(_this, modes);
   352     }
   353 }
   354 
   355 static CGError
   356 Cocoa_SwitchMode(_THIS, CGDirectDisplayID display, const void *mode)
   357 {
   358     #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   359     if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   360         return CGDisplaySetDisplayMode(display, (CGDisplayModeRef) mode, NULL);
   361     }
   362     #endif
   363 
   364     #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   365     if (!IS_SNOW_LEOPARD_OR_LATER(_this)) {
   366         return CGDisplaySwitchToMode(display, (CFDictionaryRef) mode);
   367     }
   368     #endif
   369 
   370     return kCGErrorFailure;
   371 }
   372 
   373 int
   374 Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   375 {
   376     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
   377     SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
   378     CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
   379     CGError result;
   380 
   381     /* Fade to black to hide resolution-switching flicker */
   382     if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) {
   383         CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
   384     }
   385 
   386     if (data == display->desktop_mode.driverdata) {
   387         /* Restoring desktop mode */
   388         Cocoa_SwitchMode(_this, displaydata->display, data->moderef);
   389 
   390         if (CGDisplayIsMain(displaydata->display)) {
   391             CGReleaseAllDisplays();
   392         } else {
   393             CGDisplayRelease(displaydata->display);
   394         }
   395 
   396         if (CGDisplayIsMain(displaydata->display)) {
   397             Cocoa_ToggleMenuBar(YES);
   398         }
   399     } else {
   400         /* Put up the blanking window (a window above all other windows) */
   401         if (CGDisplayIsMain(displaydata->display)) {
   402             /* If we don't capture all displays, Cocoa tries to rearrange windows... *sigh* */
   403             result = CGCaptureAllDisplays();
   404         } else {
   405             result = CGDisplayCapture(displaydata->display);
   406         }
   407         if (result != kCGErrorSuccess) {
   408             CG_SetError("CGDisplayCapture()", result);
   409             goto ERR_NO_CAPTURE;
   410         }
   411 
   412         /* Do the physical switch */
   413         result = Cocoa_SwitchMode(_this, displaydata->display, data->moderef);
   414         if (result != kCGErrorSuccess) {
   415             CG_SetError("CGDisplaySwitchToMode()", result);
   416             goto ERR_NO_SWITCH;
   417         }
   418 
   419         /* Hide the menu bar so it doesn't intercept events */
   420         if (CGDisplayIsMain(displaydata->display)) {
   421             Cocoa_ToggleMenuBar(NO);
   422         }
   423     }
   424 
   425     /* Fade in again (asynchronously) */
   426     if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   427         CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   428         CGReleaseDisplayFadeReservation(fade_token);
   429     }
   430 
   431     return 0;
   432 
   433     /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
   434 ERR_NO_SWITCH:
   435     CGDisplayRelease(displaydata->display);
   436 ERR_NO_CAPTURE:
   437     if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   438         CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   439         CGReleaseDisplayFadeReservation(fade_token);
   440     }
   441     return -1;
   442 }
   443 
   444 void
   445 Cocoa_QuitModes(_THIS)
   446 {
   447     int i, j;
   448 
   449     for (i = 0; i < _this->num_displays; ++i) {
   450         SDL_VideoDisplay *display = &_this->displays[i];
   451         SDL_DisplayModeData *mode;
   452 
   453         if (display->current_mode.driverdata != display->desktop_mode.driverdata) {
   454             Cocoa_SetDisplayMode(_this, display, &display->desktop_mode);
   455         }
   456 
   457         mode = (SDL_DisplayModeData *) display->desktop_mode.driverdata;
   458         Cocoa_ReleaseDisplayMode(_this, mode->moderef);
   459 
   460         for (j = 0; j < display->num_display_modes; j++) {
   461             mode = (SDL_DisplayModeData*) display->display_modes[j].driverdata;
   462             Cocoa_ReleaseDisplayMode(_this, mode->moderef);
   463         }
   464 
   465     }
   466     Cocoa_ToggleMenuBar(YES);
   467 }
   468 
   469 #endif /* SDL_VIDEO_DRIVER_COCOA */
   470 
   471 /* vi: set ts=4 sw=4 expandtab: */