2006-07-23 09:11:10 +00:00
|
|
|
/*
|
|
|
|
SDL - Simple DirectMedia Layer
|
2010-07-08 00:03:39 -07:00
|
|
|
Copyright (C) 1997-2010 Sam Lantinga
|
2006-07-23 09:11:10 +00:00
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
License along with this library; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
|
|
|
Sam Lantinga
|
|
|
|
slouken@libsdl.org
|
|
|
|
*/
|
|
|
|
#include "SDL_config.h"
|
|
|
|
|
|
|
|
#include "SDL_cocoavideo.h"
|
|
|
|
|
2010-12-01 12:23:16 -08:00
|
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
|
2006-08-06 08:55:37 +00:00
|
|
|
/*
|
|
|
|
Add methods to get at private members of NSScreen.
|
|
|
|
Since there is a bug in Apple's screen switching code
|
|
|
|
that does not update this variable when switching
|
|
|
|
to fullscreen, we'll set it manually (but only for the
|
|
|
|
main screen).
|
|
|
|
*/
|
|
|
|
@interface NSScreen (NSScreenAccess)
|
|
|
|
- (void) setFrame:(NSRect)frame;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSScreen (NSScreenAccess)
|
|
|
|
- (void) setFrame:(NSRect)frame;
|
|
|
|
{
|
|
|
|
_frame = frame;
|
|
|
|
}
|
|
|
|
@end
|
2009-09-05 09:54:45 +00:00
|
|
|
#endif
|
2006-08-06 08:55:37 +00:00
|
|
|
|
2006-07-24 07:21:16 +00:00
|
|
|
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);
|
|
|
|
|
2006-08-06 00:09:04 +00:00
|
|
|
mode->format = SDL_PIXELFORMAT_UNKNOWN;
|
2006-07-24 07:21:16 +00:00
|
|
|
switch (bpp) {
|
|
|
|
case 8:
|
2011-02-01 21:23:43 -08:00
|
|
|
/* We don't support palettized modes now */
|
|
|
|
return SDL_FALSE;
|
2006-07-24 07:21:16 +00:00
|
|
|
case 16:
|
2007-08-13 02:38:59 +00:00
|
|
|
mode->format = SDL_PIXELFORMAT_ARGB1555;
|
2006-07-24 07:21:16 +00:00
|
|
|
break;
|
|
|
|
case 32:
|
2007-08-13 02:38:59 +00:00
|
|
|
mode->format = SDL_PIXELFORMAT_ARGB8888;
|
2006-07-24 07:21:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
mode->w = width;
|
|
|
|
mode->h = height;
|
|
|
|
mode->refresh_rate = refreshRate;
|
|
|
|
mode->driverdata = data;
|
|
|
|
return SDL_TRUE;
|
|
|
|
}
|
2006-07-23 09:11:10 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
Cocoa_InitModes(_THIS)
|
|
|
|
{
|
2006-07-24 07:21:16 +00:00
|
|
|
CGDisplayErr result;
|
|
|
|
CGDirectDisplayID *displays;
|
|
|
|
CGDisplayCount numDisplays;
|
2009-12-01 10:41:58 +00:00
|
|
|
int pass, i;
|
2006-07-24 07:21:16 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-12-01 10:41:58 +00:00
|
|
|
/* Pick up the primary display in the first pass, then get the rest */
|
|
|
|
for (pass = 0; pass < 2; ++pass) {
|
|
|
|
for (i = 0; i < numDisplays; ++i) {
|
|
|
|
SDL_VideoDisplay display;
|
|
|
|
SDL_DisplayData *displaydata;
|
|
|
|
SDL_DisplayMode mode;
|
|
|
|
CFDictionaryRef moderef;
|
|
|
|
|
|
|
|
if (pass == 0) {
|
|
|
|
if (!CGDisplayIsMain(displays[i])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (CGDisplayIsMain(displays[i])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay) {
|
|
|
|
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);
|
2006-07-24 07:21:16 +00:00
|
|
|
}
|
|
|
|
}
|
2006-08-06 08:55:37 +00:00
|
|
|
SDL_stack_free(displays);
|
2006-07-24 07:21:16 +00:00
|
|
|
}
|
|
|
|
|
2009-12-06 08:03:38 +00:00
|
|
|
int
|
|
|
|
Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
|
2009-12-05 19:57:49 +00:00
|
|
|
{
|
2009-12-06 08:03:38 +00:00
|
|
|
SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
|
2009-12-05 19:57:49 +00:00
|
|
|
CGRect cgrect;
|
|
|
|
|
2009-12-06 08:03:38 +00:00
|
|
|
cgrect = CGDisplayBounds(displaydata->display);
|
|
|
|
rect->x = (int)cgrect.origin.x;
|
|
|
|
rect->y = (int)cgrect.origin.y;
|
|
|
|
rect->w = (int)cgrect.size.width;
|
|
|
|
rect->h = (int)cgrect.size.height;
|
|
|
|
return 0;
|
2009-12-05 19:57:49 +00:00
|
|
|
}
|
|
|
|
|
2006-07-24 07:21:16 +00:00
|
|
|
static void
|
|
|
|
AddDisplayMode(const void *moderef, void *context)
|
|
|
|
{
|
2009-12-01 05:57:15 +00:00
|
|
|
SDL_VideoDisplay *display = (SDL_VideoDisplay *) context;
|
2006-07-23 09:11:10 +00:00
|
|
|
SDL_DisplayMode mode;
|
|
|
|
|
2006-07-24 07:21:16 +00:00
|
|
|
if (GetDisplayMode(moderef, &mode)) {
|
2009-12-01 05:57:15 +00:00
|
|
|
SDL_AddDisplayMode(display, &mode);
|
2006-07-24 07:21:16 +00:00
|
|
|
}
|
2006-07-23 09:11:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-12-01 05:57:15 +00:00
|
|
|
Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
|
2006-07-23 09:11:10 +00:00
|
|
|
{
|
2009-12-01 05:57:15 +00:00
|
|
|
SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
|
2006-07-24 07:21:16 +00:00
|
|
|
CFArrayRef modes;
|
|
|
|
CFRange range;
|
|
|
|
|
|
|
|
modes = CGDisplayAvailableModes(data->display);
|
|
|
|
if (!modes) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
range.location = 0;
|
|
|
|
range.length = CFArrayGetCount(modes);
|
2009-12-01 05:57:15 +00:00
|
|
|
CFArrayApplyFunction(modes, range, AddDisplayMode, display);
|
2006-07-23 09:11:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-12-01 05:57:15 +00:00
|
|
|
Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
|
2006-07-23 09:11:10 +00:00
|
|
|
{
|
2009-12-01 05:57:15 +00:00
|
|
|
SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
|
2006-07-24 07:21:16 +00:00
|
|
|
SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
|
|
|
|
CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
|
|
|
|
CGError result;
|
2011-01-01 16:41:29 -08:00
|
|
|
|
2006-07-24 07:21:16 +00:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2009-12-02 07:55:23 +00:00
|
|
|
if (data == display->desktop_mode.driverdata) {
|
|
|
|
/* Restoring desktop mode */
|
2011-01-01 16:41:29 -08:00
|
|
|
CGDisplaySwitchToMode(displaydata->display, data->moderef);
|
|
|
|
|
2009-12-02 07:55:23 +00:00
|
|
|
CGDisplayRelease(displaydata->display);
|
|
|
|
|
|
|
|
if (CGDisplayIsMain(displaydata->display)) {
|
|
|
|
ShowMenuBar();
|
|
|
|
}
|
|
|
|
} else {
|
2011-01-01 16:41:29 -08:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2009-12-02 07:55:23 +00:00
|
|
|
/* Do the physical switch */
|
|
|
|
result = CGDisplaySwitchToMode(displaydata->display, data->moderef);
|
|
|
|
if (result != kCGErrorSuccess) {
|
|
|
|
CG_SetError("CGDisplaySwitchToMode()", result);
|
|
|
|
goto ERR_NO_SWITCH;
|
|
|
|
}
|
2006-07-24 07:21:16 +00:00
|
|
|
|
2009-12-02 07:55:23 +00:00
|
|
|
/* Hide the menu bar so it doesn't intercept events */
|
|
|
|
if (CGDisplayIsMain(displaydata->display)) {
|
|
|
|
HideMenuBar();
|
|
|
|
}
|
2009-12-02 07:28:58 +00:00
|
|
|
}
|
2006-08-06 08:55:37 +00:00
|
|
|
|
2006-07-24 07:21:16 +00:00
|
|
|
/* 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);
|
|
|
|
}
|
2006-08-06 08:55:37 +00:00
|
|
|
|
2009-09-19 13:29:40 +00:00
|
|
|
[[NSApp mainWindow] makeKeyAndOrderFront: nil];
|
|
|
|
|
2010-12-01 12:23:16 -08:00
|
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
|
2006-08-06 08:55:37 +00:00
|
|
|
/*
|
|
|
|
There is a bug in Cocoa where NSScreen doesn't synchronize
|
|
|
|
with CGDirectDisplay, so the main screen's frame is wrong.
|
|
|
|
As a result, coordinate translation produces incorrect results.
|
|
|
|
We can hack around this bug by setting the screen rect
|
|
|
|
ourselves. This hack should be removed if/when the bug is fixed.
|
|
|
|
*/
|
|
|
|
[[NSScreen mainScreen] setFrame:NSMakeRect(0,0,mode->w,mode->h)];
|
2009-09-05 09:54:45 +00:00
|
|
|
#endif
|
2006-08-06 08:55:37 +00:00
|
|
|
|
2006-07-24 07:21:16 +00:00
|
|
|
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);
|
|
|
|
}
|
2006-07-23 09:11:10 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Cocoa_QuitModes(_THIS)
|
|
|
|
{
|
2009-12-01 05:57:15 +00:00
|
|
|
int i;
|
2006-07-24 07:21:16 +00:00
|
|
|
|
|
|
|
for (i = 0; i < _this->num_displays; ++i) {
|
|
|
|
SDL_VideoDisplay *display = &_this->displays[i];
|
|
|
|
|
|
|
|
if (display->current_mode.driverdata != display->desktop_mode.driverdata) {
|
2009-12-01 05:57:15 +00:00
|
|
|
Cocoa_SetDisplayMode(_this, display, &display->desktop_mode);
|
2006-07-24 07:21:16 +00:00
|
|
|
}
|
|
|
|
}
|
2006-08-06 08:55:37 +00:00
|
|
|
ShowMenuBar();
|
2006-07-23 09:11:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* vi: set ts=4 sw=4 expandtab: */
|