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