src/video/cocoa/SDL_cocoamodes.m
author Ryan C. Gordon
Mon, 05 Dec 2011 16:45:32 -0500
changeset 6105 3b06b03d74f4
parent 6104 e49364c8a253
child 6138 4c64952a58fb
permissions -rw-r--r--
More work to clean up compiler warnings.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2011 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 kCGErrorNameTooLong:
   106         error = "kCGErrorNameTooLong";
   107         break;
   108     case kCGErrorNotImplemented:
   109         error = "kCGErrorNotImplemented";
   110         break;
   111     case kCGErrorRangeCheck:
   112         error = "kCGErrorRangeCheck";
   113         break;
   114     case kCGErrorTypeCheck:
   115         error = "kCGErrorTypeCheck";
   116         break;
   117     case kCGErrorNoCurrentPoint:
   118         error = "kCGErrorNoCurrentPoint";
   119         break;
   120     case kCGErrorInvalidOperation:
   121         error = "kCGErrorInvalidOperation";
   122         break;
   123     case kCGErrorNoneAvailable:
   124         error = "kCGErrorNoneAvailable";
   125         break;
   126     default:
   127         error = "Unknown Error";
   128         break;
   129     }
   130     SDL_SetError("%s: %s", prefix, error);
   131 }
   132 
   133 static SDL_bool
   134 GetDisplayMode(_THIS, const void *moderef, SDL_DisplayMode *mode)
   135 {
   136     SDL_DisplayModeData *data;
   137     long width = 0;
   138     long height = 0;
   139     long bpp = 0;
   140     long refreshRate = 0;
   141 
   142     data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data));
   143     if (!data) {
   144         return SDL_FALSE;
   145     }
   146     data->moderef = moderef;
   147 
   148     #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   149     if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   150         CGDisplayModeRef vidmode = (CGDisplayModeRef) moderef;
   151         CFStringRef fmt = CGDisplayModeCopyPixelEncoding(vidmode);
   152         width = (long) CGDisplayModeGetWidth(vidmode);
   153         height = (long) CGDisplayModeGetHeight(vidmode);
   154         refreshRate = (long) CGDisplayModeGetRefreshRate(vidmode);
   155 
   156         if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels),
   157                             kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
   158             bpp = 32;
   159         } else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels),
   160                             kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
   161             bpp = 16;
   162         } else {
   163             bpp = 0;  /* ignore 8-bit and such for now. */
   164         }
   165 
   166         CFRelease(fmt);
   167     }
   168     #endif
   169 
   170     #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   171     if (!IS_SNOW_LEOPARD_OR_LATER(_this)) {
   172         CFNumberRef number;
   173         CFDictionaryRef vidmode = (CFDictionaryRef) moderef;
   174         number = CFDictionaryGetValue(vidmode, kCGDisplayWidth);
   175         CFNumberGetValue(number, kCFNumberLongType, &width);
   176         number = CFDictionaryGetValue(vidmode, kCGDisplayHeight);
   177         CFNumberGetValue(number, kCFNumberLongType, &height);
   178         number = CFDictionaryGetValue(vidmode, kCGDisplayBitsPerPixel);
   179         CFNumberGetValue(number, kCFNumberLongType, &bpp);
   180         number = CFDictionaryGetValue(vidmode, kCGDisplayRefreshRate);
   181         CFNumberGetValue(number, kCFNumberLongType, &refreshRate);
   182     }
   183     #endif
   184 
   185     mode->format = SDL_PIXELFORMAT_UNKNOWN;
   186     switch (bpp) {
   187     case 16:
   188         mode->format = SDL_PIXELFORMAT_ARGB1555;
   189         break;
   190     case 32:
   191         mode->format = SDL_PIXELFORMAT_ARGB8888;
   192         break;
   193     case 8: /* We don't support palettized modes now */
   194     default: /* Totally unrecognizable bit depth. */
   195         return SDL_FALSE;
   196     }
   197     mode->w = width;
   198     mode->h = height;
   199     mode->refresh_rate = refreshRate;
   200     mode->driverdata = data;
   201     return SDL_TRUE;
   202 }
   203 
   204 static inline void
   205 Cocoa_ReleaseDisplayMode(_THIS, const void *moderef)
   206 {
   207     /* We don't own moderef unless we use the 10.6+ APIs. */
   208     #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   209     if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   210         CGDisplayModeRelease((CGDisplayModeRef) moderef);  /* NULL is ok */
   211     }
   212     #endif
   213 }
   214 
   215 static inline void
   216 Cocoa_ReleaseDisplayModeList(_THIS, CFArrayRef modelist)
   217 {
   218     /* We don't own modelis unless we use the 10.6+ APIs. */
   219     #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   220     if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   221         CFRelease(modelist);  /* NULL is ok */
   222     }
   223     #endif
   224 }
   225 
   226 void
   227 Cocoa_InitModes(_THIS)
   228 {
   229     CGDisplayErr result;
   230     CGDirectDisplayID *displays;
   231     CGDisplayCount numDisplays;
   232     int pass, i;
   233 
   234     result = CGGetOnlineDisplayList(0, NULL, &numDisplays);
   235     if (result != kCGErrorSuccess) {
   236         CG_SetError("CGGetOnlineDisplayList()", result);
   237         return;
   238     }
   239     displays = SDL_stack_alloc(CGDirectDisplayID, numDisplays);
   240     result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays);
   241     if (result != kCGErrorSuccess) {
   242         CG_SetError("CGGetOnlineDisplayList()", result);
   243         SDL_stack_free(displays);
   244         return;
   245     }
   246 
   247     /* Pick up the primary display in the first pass, then get the rest */
   248     for (pass = 0; pass < 2; ++pass) {
   249         for (i = 0; i < numDisplays; ++i) {
   250             SDL_VideoDisplay display;
   251             SDL_DisplayData *displaydata;
   252             SDL_DisplayMode mode;
   253             const void *moderef = NULL;
   254 
   255             if (pass == 0) {
   256                 if (!CGDisplayIsMain(displays[i])) {
   257                     continue;
   258                 }
   259             } else {
   260                 if (CGDisplayIsMain(displays[i])) {
   261                     continue;
   262                 }
   263             }
   264 
   265             if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay) {
   266                 continue;
   267             }
   268 
   269             #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   270             if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   271                 moderef = CGDisplayCopyDisplayMode(displays[i]);
   272             }
   273             #endif
   274 
   275             #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   276             if (!IS_SNOW_LEOPARD_OR_LATER(_this)) {
   277                 moderef = CGDisplayCurrentMode(displays[i]);
   278             }
   279             #endif
   280 
   281             if (!moderef) {
   282                 continue;
   283             }
   284 
   285             displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
   286             if (!displaydata) {
   287                 Cocoa_ReleaseDisplayMode(_this, moderef);
   288                 continue;
   289             }
   290             displaydata->display = displays[i];
   291 
   292             SDL_zero(display);
   293             if (!GetDisplayMode (_this, moderef, &mode)) {
   294                 Cocoa_ReleaseDisplayMode(_this, moderef);
   295                 SDL_free(displaydata);
   296                 continue;
   297             }
   298 
   299             display.desktop_mode = mode;
   300             display.current_mode = mode;
   301             display.driverdata = displaydata;
   302             SDL_AddVideoDisplay(&display);
   303         }
   304     }
   305     SDL_stack_free(displays);
   306 }
   307 
   308 int
   309 Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
   310 {
   311     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
   312     CGRect cgrect;
   313 
   314     cgrect = CGDisplayBounds(displaydata->display);
   315     rect->x = (int)cgrect.origin.x;
   316     rect->y = (int)cgrect.origin.y;
   317     rect->w = (int)cgrect.size.width;
   318     rect->h = (int)cgrect.size.height;
   319     return 0;
   320 }
   321 
   322 void
   323 Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
   324 {
   325     SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
   326     CFArrayRef modes = NULL;
   327 
   328     #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   329     if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   330         modes = CGDisplayCopyAllDisplayModes(data->display, NULL);
   331     }
   332     #endif
   333 
   334     #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   335     if (!IS_SNOW_LEOPARD_OR_LATER(_this)) {
   336         modes = CGDisplayAvailableModes(data->display);
   337     }
   338     #endif
   339 
   340     if (modes) {
   341         const CFIndex count = CFArrayGetCount(modes);
   342         CFIndex i;
   343 
   344         for (i = 0; i < count; i++) {
   345             const void *moderef = CFArrayGetValueAtIndex(modes, i);
   346             SDL_DisplayMode mode;
   347             if (GetDisplayMode(_this, moderef, &mode)) {
   348                 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   349                 if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   350                     CGDisplayModeRetain((CGDisplayModeRef) moderef);
   351                 }
   352                 #endif
   353                 SDL_AddDisplayMode(display, &mode);
   354             }
   355         }
   356 
   357         Cocoa_ReleaseDisplayModeList(_this, modes);
   358     }
   359 }
   360 
   361 static CGError
   362 Cocoa_SwitchMode(_THIS, CGDirectDisplayID display, const void *mode)
   363 {
   364     #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   365     if (IS_SNOW_LEOPARD_OR_LATER(_this)) {
   366         return CGDisplaySetDisplayMode(display, (CGDisplayModeRef) mode, NULL);
   367     }
   368     #endif
   369 
   370     #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   371     if (!IS_SNOW_LEOPARD_OR_LATER(_this)) {
   372         return CGDisplaySwitchToMode(display, (CFDictionaryRef) mode);
   373     }
   374     #endif
   375 
   376     return kCGErrorFailure;
   377 }
   378 
   379 int
   380 Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   381 {
   382     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
   383     SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
   384     CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
   385     CGError result;
   386 
   387     /* Fade to black to hide resolution-switching flicker */
   388     if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) {
   389         CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
   390     }
   391 
   392     if (data == display->desktop_mode.driverdata) {
   393         /* Restoring desktop mode */
   394         Cocoa_SwitchMode(_this, displaydata->display, data->moderef);
   395 
   396         if (CGDisplayIsMain(displaydata->display)) {
   397             CGReleaseAllDisplays();
   398         } else {
   399             CGDisplayRelease(displaydata->display);
   400         }
   401 
   402         if (CGDisplayIsMain(displaydata->display)) {
   403             Cocoa_ToggleMenuBar(YES);
   404         }
   405     } else {
   406         /* Put up the blanking window (a window above all other windows) */
   407         if (CGDisplayIsMain(displaydata->display)) {
   408             /* If we don't capture all displays, Cocoa tries to rearrange windows... *sigh* */
   409             result = CGCaptureAllDisplays();
   410         } else {
   411             result = CGDisplayCapture(displaydata->display);
   412         }
   413         if (result != kCGErrorSuccess) {
   414             CG_SetError("CGDisplayCapture()", result);
   415             goto ERR_NO_CAPTURE;
   416         }
   417 
   418         /* Do the physical switch */
   419         result = Cocoa_SwitchMode(_this, displaydata->display, data->moderef);
   420         if (result != kCGErrorSuccess) {
   421             CG_SetError("CGDisplaySwitchToMode()", result);
   422             goto ERR_NO_SWITCH;
   423         }
   424 
   425         /* Hide the menu bar so it doesn't intercept events */
   426         if (CGDisplayIsMain(displaydata->display)) {
   427             Cocoa_ToggleMenuBar(NO);
   428         }
   429     }
   430 
   431     /* Fade in again (asynchronously) */
   432     if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   433         CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   434         CGReleaseDisplayFadeReservation(fade_token);
   435     }
   436 
   437     return 0;
   438 
   439     /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
   440 ERR_NO_SWITCH:
   441     CGDisplayRelease(displaydata->display);
   442 ERR_NO_CAPTURE:
   443     if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   444         CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   445         CGReleaseDisplayFadeReservation(fade_token);
   446     }
   447     return -1;
   448 }
   449 
   450 void
   451 Cocoa_QuitModes(_THIS)
   452 {
   453     int i, j;
   454 
   455     for (i = 0; i < _this->num_displays; ++i) {
   456         SDL_VideoDisplay *display = &_this->displays[i];
   457         SDL_DisplayModeData *mode;
   458 
   459         if (display->current_mode.driverdata != display->desktop_mode.driverdata) {
   460             Cocoa_SetDisplayMode(_this, display, &display->desktop_mode);
   461         }
   462 
   463         mode = (SDL_DisplayModeData *) display->desktop_mode.driverdata;
   464         Cocoa_ReleaseDisplayMode(_this, mode->moderef);
   465 
   466         for (j = 0; j < display->num_display_modes; j++) {
   467             mode = (SDL_DisplayModeData*) display->display_modes[j].driverdata;
   468             Cocoa_ReleaseDisplayMode(_this, mode->moderef);
   469         }
   470 
   471     }
   472     Cocoa_ToggleMenuBar(YES);
   473 }
   474 
   475 #endif /* SDL_VIDEO_DRIVER_COCOA */
   476 
   477 /* vi: set ts=4 sw=4 expandtab: */