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