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