src/video/cocoa/SDL_cocoamodes.m
author Sam Lantinga
Sat, 19 Sep 2009 13:29:40 +0000
changeset 3280 00cace2d9080
parent 3248 cde30895105d
child 3500 4b594623401b
permissions -rw-r--r--
Merged a cleaned up version of Jiang's code changes from Google Summer of Code 2009
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #include "SDL_cocoavideo.h"
    25 
    26 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1050
    27 /* 
    28     Add methods to get at private members of NSScreen. 
    29     Since there is a bug in Apple's screen switching code
    30     that does not update this variable when switching
    31     to fullscreen, we'll set it manually (but only for the
    32     main screen).
    33 */
    34 @interface NSScreen (NSScreenAccess)
    35 - (void) setFrame:(NSRect)frame;
    36 @end
    37 
    38 @implementation NSScreen (NSScreenAccess)
    39 - (void) setFrame:(NSRect)frame;
    40 {
    41     _frame = frame;
    42 }
    43 @end
    44 #endif
    45 
    46 static void
    47 CG_SetError(const char *prefix, CGDisplayErr result)
    48 {
    49     const char *error;
    50 
    51     switch (result) {
    52     case kCGErrorFailure:
    53         error = "kCGErrorFailure";
    54         break;
    55     case kCGErrorIllegalArgument:
    56         error = "kCGErrorIllegalArgument";
    57         break;
    58     case kCGErrorInvalidConnection:
    59         error = "kCGErrorInvalidConnection";
    60         break;
    61     case kCGErrorInvalidContext:
    62         error = "kCGErrorInvalidContext";
    63         break;
    64     case kCGErrorCannotComplete:
    65         error = "kCGErrorCannotComplete";
    66         break;
    67     case kCGErrorNameTooLong:
    68         error = "kCGErrorNameTooLong";
    69         break;
    70     case kCGErrorNotImplemented:
    71         error = "kCGErrorNotImplemented";
    72         break;
    73     case kCGErrorRangeCheck:
    74         error = "kCGErrorRangeCheck";
    75         break;
    76     case kCGErrorTypeCheck:
    77         error = "kCGErrorTypeCheck";
    78         break;
    79     case kCGErrorNoCurrentPoint:
    80         error = "kCGErrorNoCurrentPoint";
    81         break;
    82     case kCGErrorInvalidOperation:
    83         error = "kCGErrorInvalidOperation";
    84         break;
    85     case kCGErrorNoneAvailable:
    86         error = "kCGErrorNoneAvailable";
    87         break;
    88     default:
    89         error = "Unknown Error";
    90         break;
    91     }
    92     SDL_SetError("%s: %s", prefix, error);
    93 }
    94 
    95 static SDL_bool
    96 GetDisplayMode(CFDictionaryRef moderef, SDL_DisplayMode *mode)
    97 {
    98     SDL_DisplayModeData *data;
    99     CFNumberRef number;
   100     long width, height, bpp, refreshRate;
   101 
   102     data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data));
   103     if (!data) {
   104         return SDL_FALSE;
   105     }
   106     data->moderef = moderef;
   107 
   108     number = CFDictionaryGetValue(moderef, kCGDisplayWidth);
   109     CFNumberGetValue(number, kCFNumberLongType, &width);
   110     number = CFDictionaryGetValue(moderef, kCGDisplayHeight);
   111     CFNumberGetValue(number, kCFNumberLongType, &height);
   112     number = CFDictionaryGetValue(moderef, kCGDisplayBitsPerPixel);
   113     CFNumberGetValue(number, kCFNumberLongType, &bpp);
   114     number = CFDictionaryGetValue(moderef, kCGDisplayRefreshRate);
   115     CFNumberGetValue(number, kCFNumberLongType, &refreshRate);
   116 
   117     mode->format = SDL_PIXELFORMAT_UNKNOWN;
   118     switch (bpp) {
   119     case 8:
   120         mode->format = SDL_PIXELFORMAT_INDEX8;
   121         break;
   122     case 16:
   123         mode->format = SDL_PIXELFORMAT_ARGB1555;
   124         break;
   125     case 32:
   126         mode->format = SDL_PIXELFORMAT_ARGB8888;
   127         break;
   128     }
   129     mode->w = width;
   130     mode->h = height;
   131     mode->refresh_rate = refreshRate;
   132     mode->driverdata = data;
   133     return SDL_TRUE;
   134 }
   135 
   136 void
   137 Cocoa_InitModes(_THIS)
   138 {
   139     CGDisplayErr result;
   140     CGDirectDisplayID *displays;
   141     CGDisplayCount numDisplays;
   142     int i;
   143 
   144     result = CGGetOnlineDisplayList(0, NULL, &numDisplays);
   145     if (result != kCGErrorSuccess) {
   146         CG_SetError("CGGetOnlineDisplayList()", result);
   147         return;
   148     }
   149     displays = SDL_stack_alloc(CGDirectDisplayID, numDisplays);
   150     result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays);
   151     if (result != kCGErrorSuccess) {
   152         CG_SetError("CGGetOnlineDisplayList()", result);
   153         SDL_stack_free(displays);
   154         return;
   155     }
   156 
   157     for (i = 0; i < numDisplays; ++i) {
   158         SDL_VideoDisplay display;
   159         SDL_DisplayData *displaydata;
   160         SDL_DisplayMode mode;
   161         CFDictionaryRef moderef;
   162 
   163         if (CGDisplayIsInMirrorSet(displays[i])) {
   164             continue;
   165         }
   166         moderef = CGDisplayCurrentMode(displays[i]);
   167         if (!moderef) {
   168             continue;
   169         }
   170 
   171         displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
   172         if (!displaydata) {
   173             continue;
   174         }
   175         displaydata->display = displays[i];
   176 
   177         SDL_zero(display);
   178         if (!GetDisplayMode (moderef, &mode)) {
   179             SDL_free(displaydata);
   180             continue;
   181         }
   182         display.desktop_mode = mode;
   183         display.current_mode = mode;
   184         display.driverdata = displaydata;
   185         SDL_AddVideoDisplay(&display);
   186     }
   187     SDL_stack_free(displays);
   188 }
   189 
   190 static void
   191 AddDisplayMode(const void *moderef, void *context)
   192 {
   193     SDL_VideoDevice *_this = (SDL_VideoDevice *) context;
   194     SDL_DisplayMode mode;
   195 
   196     if (GetDisplayMode(moderef, &mode)) {
   197         SDL_AddDisplayMode(_this->current_display, &mode);
   198     }
   199 }
   200 
   201 void
   202 Cocoa_GetDisplayModes(_THIS)
   203 {
   204     SDL_DisplayData *data = (SDL_DisplayData *) SDL_CurrentDisplay.driverdata;
   205     CFArrayRef modes;
   206     CFRange range;
   207 
   208     modes = CGDisplayAvailableModes(data->display);
   209     if (!modes) {
   210         return;
   211     }
   212     range.location = 0;
   213     range.length = CFArrayGetCount(modes);
   214     CFArrayApplyFunction(modes, range, AddDisplayMode, _this);
   215 }
   216 
   217 int
   218 Cocoa_SetDisplayMode(_THIS, SDL_DisplayMode * mode)
   219 {
   220     SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_CurrentDisplay.driverdata;
   221     SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
   222     CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
   223     CGError result;
   224     
   225     /* Fade to black to hide resolution-switching flicker */
   226     if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) {
   227         CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
   228     }
   229 
   230     /* Put up the blanking window (a window above all other windows) */
   231     result = CGDisplayCapture(displaydata->display);
   232     if (result != kCGErrorSuccess) {
   233         CG_SetError("CGDisplayCapture()", result);
   234         goto ERR_NO_CAPTURE;
   235     }
   236 
   237     /* Do the physical switch */
   238     result = CGDisplaySwitchToMode(displaydata->display, data->moderef);
   239     if (result != kCGErrorSuccess) {
   240         CG_SetError("CGDisplaySwitchToMode()", result);
   241         goto ERR_NO_SWITCH;
   242     }
   243 
   244     /* Hide the menu bar so it doesn't intercept events */
   245     HideMenuBar();
   246 
   247     /* Fade in again (asynchronously) */
   248     if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   249         CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   250         CGReleaseDisplayFadeReservation(fade_token);
   251     }
   252 
   253     [[NSApp mainWindow] makeKeyAndOrderFront: nil];
   254 
   255 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1050
   256     /* 
   257         There is a bug in Cocoa where NSScreen doesn't synchronize
   258         with CGDirectDisplay, so the main screen's frame is wrong.
   259         As a result, coordinate translation produces incorrect results.
   260         We can hack around this bug by setting the screen rect
   261         ourselves. This hack should be removed if/when the bug is fixed.
   262     */
   263     [[NSScreen mainScreen] setFrame:NSMakeRect(0,0,mode->w,mode->h)]; 
   264 #endif
   265 
   266     return 0;
   267 
   268     /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
   269 ERR_NO_SWITCH:
   270     CGDisplayRelease(displaydata->display);
   271 ERR_NO_CAPTURE:
   272     if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   273         CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   274         CGReleaseDisplayFadeReservation(fade_token);
   275     }
   276     return -1;
   277 }
   278 
   279 void
   280 Cocoa_QuitModes(_THIS)
   281 {
   282     int i, saved_display;
   283 
   284     saved_display = _this->current_display;
   285     for (i = 0; i < _this->num_displays; ++i) {
   286         SDL_VideoDisplay *display = &_this->displays[i];
   287 
   288         if (display->current_mode.driverdata != display->desktop_mode.driverdata) {
   289             _this->current_display = i;
   290             Cocoa_SetDisplayMode(_this, &display->desktop_mode);
   291         }
   292     }
   293     CGReleaseAllDisplays();
   294     ShowMenuBar();
   295 
   296     _this->current_display = saved_display;
   297 }
   298 
   299 /* vi: set ts=4 sw=4 expandtab: */