src/video/cocoa/SDL_cocoamodes.m
author Sam Lantinga
Tue, 08 Feb 2011 16:50:51 -0800
changeset 5229 c015d3e63631
parent 5149 be02be2ea897
child 5248 ff2564c24045
permissions -rw-r--r--
Fixed setting the texture unit, still doesn't work.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2010 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 < MAC_OS_X_VERSION_10_5
    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         /* We don't support palettized modes now */
   121         return SDL_FALSE;
   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 pass, 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     /* Pick up the primary display in the first pass, then get the rest */
   158     for (pass = 0; pass < 2; ++pass) {
   159         for (i = 0; i < numDisplays; ++i) {
   160             SDL_VideoDisplay display;
   161             SDL_DisplayData *displaydata;
   162             SDL_DisplayMode mode;
   163             CFDictionaryRef moderef;
   164 
   165             if (pass == 0) {
   166                 if (!CGDisplayIsMain(displays[i])) {
   167                     continue;
   168                 }
   169             } else {
   170                 if (CGDisplayIsMain(displays[i])) {
   171                     continue;
   172                 }
   173             }
   174 
   175             if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay) {
   176                 continue;
   177             }
   178             moderef = CGDisplayCurrentMode(displays[i]);
   179             if (!moderef) {
   180                 continue;
   181             }
   182 
   183             displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
   184             if (!displaydata) {
   185                 continue;
   186             }
   187             displaydata->display = displays[i];
   188 
   189             SDL_zero(display);
   190             if (!GetDisplayMode (moderef, &mode)) {
   191                 SDL_free(displaydata);
   192                 continue;
   193             }
   194             display.desktop_mode = mode;
   195             display.current_mode = mode;
   196             display.driverdata = displaydata;
   197             SDL_AddVideoDisplay(&display);
   198         }
   199     }
   200     SDL_stack_free(displays);
   201 }
   202 
   203 int
   204 Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
   205 {
   206     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
   207     CGRect cgrect;
   208 
   209     cgrect = CGDisplayBounds(displaydata->display);
   210     rect->x = (int)cgrect.origin.x;
   211     rect->y = (int)cgrect.origin.y;
   212     rect->w = (int)cgrect.size.width;
   213     rect->h = (int)cgrect.size.height;
   214     return 0;
   215 }
   216 
   217 static void
   218 AddDisplayMode(const void *moderef, void *context)
   219 {
   220     SDL_VideoDisplay *display = (SDL_VideoDisplay *) context;
   221     SDL_DisplayMode mode;
   222 
   223     if (GetDisplayMode(moderef, &mode)) {
   224         SDL_AddDisplayMode(display, &mode);
   225     }
   226 }
   227 
   228 void
   229 Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
   230 {
   231     SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
   232     CFArrayRef modes;
   233     CFRange range;
   234 
   235     modes = CGDisplayAvailableModes(data->display);
   236     if (!modes) {
   237         return;
   238     }
   239     range.location = 0;
   240     range.length = CFArrayGetCount(modes);
   241     CFArrayApplyFunction(modes, range, AddDisplayMode, display);
   242 }
   243 
   244 int
   245 Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   246 {
   247     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
   248     SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
   249     CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
   250     CGError result;
   251 
   252     /* Fade to black to hide resolution-switching flicker */
   253     if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) {
   254         CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
   255     }
   256 
   257     if (data == display->desktop_mode.driverdata) {
   258         /* Restoring desktop mode */
   259         CGDisplaySwitchToMode(displaydata->display, data->moderef);
   260 
   261         CGDisplayRelease(displaydata->display);
   262 
   263         if (CGDisplayIsMain(displaydata->display)) {
   264             ShowMenuBar();
   265         }
   266     } else {
   267         /* Put up the blanking window (a window above all other windows) */
   268         result = CGDisplayCapture(displaydata->display);
   269         if (result != kCGErrorSuccess) {
   270             CG_SetError("CGDisplayCapture()", result);
   271             goto ERR_NO_CAPTURE;
   272         }
   273 
   274         /* Do the physical switch */
   275         result = CGDisplaySwitchToMode(displaydata->display, data->moderef);
   276         if (result != kCGErrorSuccess) {
   277             CG_SetError("CGDisplaySwitchToMode()", result);
   278             goto ERR_NO_SWITCH;
   279         }
   280 
   281         /* Hide the menu bar so it doesn't intercept events */
   282         if (CGDisplayIsMain(displaydata->display)) {
   283             HideMenuBar();
   284         }
   285     }
   286 
   287     /* Fade in again (asynchronously) */
   288     if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   289         CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   290         CGReleaseDisplayFadeReservation(fade_token);
   291     }
   292 
   293     [[NSApp mainWindow] makeKeyAndOrderFront: nil];
   294 
   295 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
   296     /* 
   297         There is a bug in Cocoa where NSScreen doesn't synchronize
   298         with CGDirectDisplay, so the main screen's frame is wrong.
   299         As a result, coordinate translation produces incorrect results.
   300         We can hack around this bug by setting the screen rect
   301         ourselves. This hack should be removed if/when the bug is fixed.
   302     */
   303     [[NSScreen mainScreen] setFrame:NSMakeRect(0,0,mode->w,mode->h)]; 
   304 #endif
   305 
   306     return 0;
   307 
   308     /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
   309 ERR_NO_SWITCH:
   310     CGDisplayRelease(displaydata->display);
   311 ERR_NO_CAPTURE:
   312     if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   313         CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   314         CGReleaseDisplayFadeReservation(fade_token);
   315     }
   316     return -1;
   317 }
   318 
   319 void
   320 Cocoa_QuitModes(_THIS)
   321 {
   322     int i;
   323 
   324     for (i = 0; i < _this->num_displays; ++i) {
   325         SDL_VideoDisplay *display = &_this->displays[i];
   326 
   327         if (display->current_mode.driverdata != display->desktop_mode.driverdata) {
   328             Cocoa_SetDisplayMode(_this, display, &display->desktop_mode);
   329         }
   330     }
   331     ShowMenuBar();
   332 }
   333 
   334 /* vi: set ts=4 sw=4 expandtab: */