Implemented Mac OS X video mode selection.
authorSam Lantinga <slouken@libsdl.org>
Mon, 24 Jul 2006 07:21:16 +0000
changeset 1934 70139af5ac27
parent 1933 7ee5297340f7
child 1935 8a9b367a80f3
Implemented Mac OS X video mode selection.
src/video/cocoa/SDL_cocoamodes.h
src/video/cocoa/SDL_cocoamodes.m
src/video/cocoa/SDL_cocoavideo.h
--- a/src/video/cocoa/SDL_cocoamodes.h	Mon Jul 24 05:03:02 2006 +0000
+++ b/src/video/cocoa/SDL_cocoamodes.h	Mon Jul 24 07:21:16 2006 +0000
@@ -24,6 +24,16 @@
 #ifndef _SDL_cocoamodes_h
 #define _SDL_cocoamodes_h
 
+typedef struct
+{
+    CGDirectDisplayID display;
+} SDL_DisplayData;
+
+typedef struct
+{
+    CFDictionaryRef moderef;
+} SDL_DisplayModeData;
+
 extern void Cocoa_InitModes(_THIS);
 extern void Cocoa_GetDisplayModes(_THIS);
 extern int Cocoa_SetDisplayMode(_THIS, SDL_DisplayMode * mode);
--- a/src/video/cocoa/SDL_cocoamodes.m	Mon Jul 24 05:03:02 2006 +0000
+++ b/src/video/cocoa/SDL_cocoamodes.m	Mon Jul 24 07:21:16 2006 +0000
@@ -23,34 +23,237 @@
 
 #include "SDL_cocoavideo.h"
 
+static void
+CG_SetError(const char *prefix, CGDisplayErr result)
+{
+    const char *error;
+
+    switch (result) {
+    case kCGErrorFailure:
+        error = "kCGErrorFailure";
+        break;
+    case kCGErrorIllegalArgument:
+        error = "kCGErrorIllegalArgument";
+        break;
+    case kCGErrorInvalidConnection:
+        error = "kCGErrorInvalidConnection";
+        break;
+    case kCGErrorInvalidContext:
+        error = "kCGErrorInvalidContext";
+        break;
+    case kCGErrorCannotComplete:
+        error = "kCGErrorCannotComplete";
+        break;
+    case kCGErrorNameTooLong:
+        error = "kCGErrorNameTooLong";
+        break;
+    case kCGErrorNotImplemented:
+        error = "kCGErrorNotImplemented";
+        break;
+    case kCGErrorRangeCheck:
+        error = "kCGErrorRangeCheck";
+        break;
+    case kCGErrorTypeCheck:
+        error = "kCGErrorTypeCheck";
+        break;
+    case kCGErrorNoCurrentPoint:
+        error = "kCGErrorNoCurrentPoint";
+        break;
+    case kCGErrorInvalidOperation:
+        error = "kCGErrorInvalidOperation";
+        break;
+    case kCGErrorNoneAvailable:
+        error = "kCGErrorNoneAvailable";
+        break;
+    default:
+        error = "Unknown Error";
+        break;
+    }
+    SDL_SetError("%s: %s", prefix, error);
+}
+
+static SDL_bool
+GetDisplayMode(CFDictionaryRef moderef, SDL_DisplayMode *mode)
+{
+    SDL_DisplayModeData *data;
+    CFNumberRef number;
+    long width, height, bpp, refreshRate;
+
+    data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data));
+    if (!data) {
+        return SDL_FALSE;
+    }
+    data->moderef = moderef;
+
+    number = CFDictionaryGetValue(moderef, kCGDisplayWidth);
+    CFNumberGetValue(number, kCFNumberLongType, &width);
+    number = CFDictionaryGetValue(moderef, kCGDisplayHeight);
+    CFNumberGetValue(number, kCFNumberLongType, &height);
+    number = CFDictionaryGetValue(moderef, kCGDisplayBitsPerPixel);
+    CFNumberGetValue(number, kCFNumberLongType, &bpp);
+    number = CFDictionaryGetValue(moderef, kCGDisplayRefreshRate);
+    CFNumberGetValue(number, kCFNumberLongType, &refreshRate);
+
+    mode->format = SDL_PixelFormat_Unknown;
+    switch (bpp) {
+    case 8:
+        mode->format = SDL_PixelFormat_Index8;
+        break;
+    case 16:
+        mode->format = SDL_PixelFormat_RGB555;
+        break;
+    case 32:
+        mode->format = SDL_PixelFormat_RGB888;
+        break;
+    }
+    mode->w = width;
+    mode->h = height;
+    mode->refresh_rate = refreshRate;
+    mode->driverdata = data;
+    return SDL_TRUE;
+}
 
 void
 Cocoa_InitModes(_THIS)
 {
-    SDL_VideoDisplay display;
+    CGDisplayErr result;
+    CGDirectDisplayID *displays;
+    CGDisplayCount numDisplays;
+    int i;
+
+    result = CGGetOnlineDisplayList(0, NULL, &numDisplays);
+    if (result != kCGErrorSuccess) {
+        CG_SetError("CGGetOnlineDisplayList()", result);
+        return;
+    }
+    displays = SDL_stack_alloc(CGDirectDisplayID, numDisplays);
+    result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays);
+    if (result != kCGErrorSuccess) {
+        CG_SetError("CGGetOnlineDisplayList()", result);
+        SDL_stack_free(displays);
+        return;
+    }
+
+    for (i = 0; i < numDisplays; ++i) {
+        SDL_VideoDisplay display;
+        SDL_DisplayData *displaydata;
+        SDL_DisplayMode mode;
+        CFDictionaryRef moderef;
+
+        if (CGDisplayIsInMirrorSet(displays[i])) {
+            continue;
+        }
+        moderef = CGDisplayCurrentMode(displays[i]);
+        if (!moderef) {
+            continue;
+        }
+
+        displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
+        if (!displaydata) {
+            continue;
+        }
+        displaydata->display = displays[i];
+
+        SDL_zero(display);
+        if (!GetDisplayMode (moderef, &mode)) {
+            SDL_free(displaydata);
+            continue;
+        }
+        display.desktop_mode = mode;
+        display.current_mode = mode;
+        display.driverdata = displaydata;
+        SDL_AddVideoDisplay(&display);
+    }
+}
+
+static void
+AddDisplayMode(const void *moderef, void *context)
+{
+    SDL_VideoDevice *_this = (SDL_VideoDevice *) context;
     SDL_DisplayMode mode;
 
-    SDL_zero(display);
-    SDL_zero(mode);
-    display.desktop_mode = mode;
-    display.current_mode = mode;
-    SDL_AddVideoDisplay(&display);
+    if (GetDisplayMode(moderef, &mode)) {
+        SDL_AddDisplayMode(_this->current_display, &mode);
+    }
 }
 
 void
 Cocoa_GetDisplayModes(_THIS)
 {
+    SDL_DisplayData *data = (SDL_DisplayData *) SDL_CurrentDisplay.driverdata;
+    CFArrayRef modes;
+    CFRange range;
+
+    modes = CGDisplayAvailableModes(data->display);
+    if (!modes) {
+        return;
+    }
+    range.location = 0;
+    range.length = CFArrayGetCount(modes);
+    CFArrayApplyFunction(modes, range, AddDisplayMode, _this);
 }
 
 int
 Cocoa_SetDisplayMode(_THIS, SDL_DisplayMode * mode)
 {
+    SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_CurrentDisplay.driverdata;
+    SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
+    CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
+    CGError result;
+    
+    /* Fade to black to hide resolution-switching flicker */
+    if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) {
+        CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
+    }
+
+    /* Put up the blanking window (a window above all other windows) */
+    result = CGDisplayCapture(displaydata->display);
+    if (result != kCGErrorSuccess) {
+        CG_SetError("CGDisplayCapture()", result);
+        goto ERR_NO_CAPTURE;
+    }
+
+    /* Do the physical switch */
+    result = CGDisplaySwitchToMode(displaydata->display, data->moderef);
+    if (result != kCGErrorSuccess) {
+        CG_SetError("CGDisplaySwitchToMode()", result);
+        goto ERR_NO_SWITCH;
+    }
+
+    /* Fade in again (asynchronously) */
+    if (fade_token != kCGDisplayFadeReservationInvalidToken) {
+        CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
+        CGReleaseDisplayFadeReservation(fade_token);
+    }
+    return 0;
+
+    /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
+ERR_NO_SWITCH:
+    CGDisplayRelease(displaydata->display);
+ERR_NO_CAPTURE:
+    if (fade_token != kCGDisplayFadeReservationInvalidToken) {
+        CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
+        CGReleaseDisplayFadeReservation(fade_token);
+    }
     return -1;
 }
 
 void
 Cocoa_QuitModes(_THIS)
 {
+    int i, saved_display;
+
+    saved_display = _this->current_display;
+    for (i = 0; i < _this->num_displays; ++i) {
+        SDL_VideoDisplay *display = &_this->displays[i];
+
+        if (display->current_mode.driverdata != display->desktop_mode.driverdata) {
+            _this->current_display = i;
+            Cocoa_SetDisplayMode(_this, &display->desktop_mode);
+        }
+    }
+    CGReleaseAllDisplays();
+    _this->current_display = saved_display;
 }
 
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/video/cocoa/SDL_cocoavideo.h	Mon Jul 24 05:03:02 2006 +0000
+++ b/src/video/cocoa/SDL_cocoavideo.h	Mon Jul 24 07:21:16 2006 +0000
@@ -24,6 +24,7 @@
 #ifndef _SDL_cocoavideo_h
 #define _SDL_cocoavideo_h
 
+#include <ApplicationServices/ApplicationServices.h>
 #include <Cocoa/Cocoa.h>
 
 #include "../SDL_sysvideo.h"