aboutsummaryrefslogtreecommitdiffstats
path: root/distrib/sdl-1.2.15/src/video/quartz/SDL_QuartzVideo.m
diff options
context:
space:
mode:
Diffstat (limited to 'distrib/sdl-1.2.15/src/video/quartz/SDL_QuartzVideo.m')
-rw-r--r--distrib/sdl-1.2.15/src/video/quartz/SDL_QuartzVideo.m1695
1 files changed, 1695 insertions, 0 deletions
diff --git a/distrib/sdl-1.2.15/src/video/quartz/SDL_QuartzVideo.m b/distrib/sdl-1.2.15/src/video/quartz/SDL_QuartzVideo.m
new file mode 100644
index 0000000..bb20172
--- /dev/null
+++ b/distrib/sdl-1.2.15/src/video/quartz/SDL_QuartzVideo.m
@@ -0,0 +1,1695 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+#include "SDL_QuartzVideo.h"
+#include "SDL_QuartzWindow.h"
+#include "SDL_QuartzWM.h"
+
+/* These APIs aren't just deprecated; they're gone from the headers in the
+ 10.7 SDK. If we're using a >= 10.7 SDK, but targeting < 10.7, then we
+ force these function declarations. */
+#if ((MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && (MAC_OS_X_VERSION_MAX_ALLOWED >= 1070))
+CG_EXTERN void *CGDisplayBaseAddress(CGDirectDisplayID display)
+ CG_AVAILABLE_BUT_DEPRECATED(__MAC_10_0, __MAC_10_6,
+ __IPHONE_NA, __IPHONE_NA);
+CG_EXTERN size_t CGDisplayBytesPerRow(CGDirectDisplayID display)
+ CG_AVAILABLE_BUT_DEPRECATED(__MAC_10_0, __MAC_10_6,
+ __IPHONE_NA, __IPHONE_NA);
+#endif
+
+
+static inline BOOL IS_LION_OR_LATER(_THIS)
+{
+ return (system_version >= 0x1070);
+}
+
+static inline BOOL IS_SNOW_LEOPARD_OR_LATER(_THIS)
+{
+ return (system_version >= 0x1060);
+}
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED < 1060) && !defined(__LP64__) /* Fixed in Snow Leopard */
+/*
+ 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
+static inline void QZ_SetFrame(_THIS, NSScreen *nsscreen, NSRect frame)
+{
+ if (!IS_SNOW_LEOPARD_OR_LATER(this)) {
+ [nsscreen setFrame:frame];
+ }
+}
+#else
+static inline void QZ_SetFrame(_THIS, NSScreen *nsscreen, NSRect frame)
+{
+}
+#endif
+
+@interface SDLTranslatorResponder : NSTextView
+{
+}
+- (void) doCommandBySelector:(SEL)myselector;
+@end
+
+@implementation SDLTranslatorResponder
+- (void) doCommandBySelector:(SEL) myselector {}
+@end
+
+/* absent in 10.3.9. */
+CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
+
+/* Bootstrap functions */
+static int QZ_Available ();
+static SDL_VideoDevice* QZ_CreateDevice (int device_index);
+static void QZ_DeleteDevice (SDL_VideoDevice *device);
+
+/* Initialization, Query, Setup, and Redrawing functions */
+static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format);
+
+static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format,
+ Uint32 flags);
+static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop, BOOL save_gl);
+
+static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current,
+ int width, int height, int bpp,
+ Uint32 flags);
+static int QZ_ToggleFullScreen (_THIS, int on);
+static int QZ_SetColors (_THIS, int first_color,
+ int num_colors, SDL_Color *colors);
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
+static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface);
+static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface);
+static int QZ_ThreadFlip (_THIS);
+static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface);
+static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects);
+static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects);
+#endif
+
+static void QZ_UpdateRects (_THIS, int num_rects, SDL_Rect *rects);
+static void QZ_VideoQuit (_THIS);
+
+static int QZ_LockHWSurface(_THIS, SDL_Surface *surface);
+static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface);
+static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface);
+static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface);
+
+/* Bootstrap binding, enables entry point into the driver */
+VideoBootStrap QZ_bootstrap = {
+ "Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice
+};
+
+/* Disable compiler warnings we can't avoid. */
+#if (defined(__GNUC__) && (__GNUC__ >= 4))
+# if (MAC_OS_X_VERSION_MAX_ALLOWED <= 1070)
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+# endif
+#endif
+
+static void QZ_ReleaseDisplayMode(_THIS, const void *moderef)
+{
+ /* we only own these references in the 10.6+ API. */
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060)
+ if (use_new_mode_apis) {
+ CGDisplayModeRelease((CGDisplayModeRef) moderef);
+ }
+#endif
+}
+
+static void QZ_ReleaseDisplayModeList(_THIS, CFArrayRef mode_list)
+{
+ /* we only own these references in the 10.6+ API. */
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060)
+ if (use_new_mode_apis) {
+ CFRelease(mode_list);
+ }
+#endif
+}
+
+
+/* Bootstrap functions */
+static int QZ_Available ()
+{
+ return 1;
+}
+
+static SDL_VideoDevice* QZ_CreateDevice (int device_index)
+{
+#pragma unused (device_index)
+
+ SDL_VideoDevice *device;
+ SDL_PrivateVideoData *hidden;
+
+ device = (SDL_VideoDevice*) SDL_malloc (sizeof (*device) );
+ hidden = (SDL_PrivateVideoData*) SDL_malloc (sizeof (*hidden) );
+
+ if (device == NULL || hidden == NULL)
+ SDL_OutOfMemory ();
+
+ SDL_memset (device, 0, sizeof (*device) );
+ SDL_memset (hidden, 0, sizeof (*hidden) );
+
+ device->hidden = hidden;
+
+ device->VideoInit = QZ_VideoInit;
+ device->ListModes = QZ_ListModes;
+ device->SetVideoMode = QZ_SetVideoMode;
+ device->ToggleFullScreen = QZ_ToggleFullScreen;
+ device->UpdateMouse = QZ_UpdateMouse;
+ device->SetColors = QZ_SetColors;
+ /* device->UpdateRects = QZ_UpdateRects; this is determined by SetVideoMode() */
+ device->VideoQuit = QZ_VideoQuit;
+
+ device->LockHWSurface = QZ_LockHWSurface;
+ device->UnlockHWSurface = QZ_UnlockHWSurface;
+ device->AllocHWSurface = QZ_AllocHWSurface;
+ device->FreeHWSurface = QZ_FreeHWSurface;
+
+ device->SetGamma = QZ_SetGamma;
+ device->GetGamma = QZ_GetGamma;
+ device->SetGammaRamp = QZ_SetGammaRamp;
+ device->GetGammaRamp = QZ_GetGammaRamp;
+
+ device->GL_GetProcAddress = QZ_GL_GetProcAddress;
+ device->GL_GetAttribute = QZ_GL_GetAttribute;
+ device->GL_MakeCurrent = QZ_GL_MakeCurrent;
+ device->GL_SwapBuffers = QZ_GL_SwapBuffers;
+ device->GL_LoadLibrary = QZ_GL_LoadLibrary;
+
+ device->FreeWMCursor = QZ_FreeWMCursor;
+ device->CreateWMCursor = QZ_CreateWMCursor;
+ device->ShowWMCursor = QZ_ShowWMCursor;
+ device->WarpWMCursor = QZ_WarpWMCursor;
+ device->MoveWMCursor = QZ_MoveWMCursor;
+ device->CheckMouseMode = QZ_CheckMouseMode;
+ device->InitOSKeymap = QZ_InitOSKeymap;
+ device->PumpEvents = QZ_PumpEvents;
+
+ device->SetCaption = QZ_SetCaption;
+ device->SetIcon = QZ_SetIcon;
+ device->IconifyWindow = QZ_IconifyWindow;
+ device->SetWindowPos = QZ_SetWindowPos;
+ device->GetWindowPos = QZ_GetWindowPos;
+ device->IsWindowVisible = QZ_IsWindowVisible;
+ device->GetMonitorDPI = QZ_GetMonitorDPI;
+ device->GetMonitorRect = QZ_GetMonitorRect;
+ device->GetWMInfo = QZ_GetWMInfo;
+ device->GrabInput = QZ_GrabInput;
+
+ /*
+ * This is a big hassle, needing QuickDraw and QuickTime on older
+ * systems, and god knows what on 10.6, so we immediately fail here,
+ * which causes SDL to make an RGB surface and manage the YUV overlay
+ * in software. Sorry. Use SDL 1.3 if you want YUV rendering in a pixel
+ * shader. :)
+ */
+ /*device->CreateYUVOverlay = QZ_CreateYUVOverlay;*/
+
+ device->free = QZ_DeleteDevice;
+
+ return device;
+}
+
+static void QZ_DeleteDevice (SDL_VideoDevice *device)
+{
+ _THIS = device;
+ QZ_ReleaseDisplayMode(this, save_mode);
+ QZ_ReleaseDisplayMode(this, mode);
+ SDL_free (device->hidden);
+ SDL_free (device);
+}
+
+static void QZ_GetModeInfo(_THIS, const void *_mode, Uint32 *w, Uint32 *h, Uint32 *bpp)
+{
+ *w = *h = *bpp = 0;
+ if (_mode == NULL) {
+ return;
+ }
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060)
+ if (use_new_mode_apis) {
+ CGDisplayModeRef vidmode = (CGDisplayModeRef) _mode;
+ CFStringRef fmt = CGDisplayModeCopyPixelEncoding(vidmode);
+
+ *w = (Uint32) CGDisplayModeGetWidth(vidmode);
+ *h = (Uint32) CGDisplayModeGetHeight(vidmode);
+
+ /* we only care about the 32-bit modes... */
+ if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels),
+ kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
+ *bpp = 32;
+ }
+
+ CFRelease(fmt);
+ }
+#endif
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1060)
+ if (!use_new_mode_apis) {
+ CFDictionaryRef vidmode = (CFDictionaryRef) _mode;
+ CFNumberGetValue (
+ CFDictionaryGetValue (vidmode, kCGDisplayBitsPerPixel),
+ kCFNumberSInt32Type, bpp);
+
+ CFNumberGetValue (
+ CFDictionaryGetValue (vidmode, kCGDisplayWidth),
+ kCFNumberSInt32Type, w);
+
+ CFNumberGetValue (
+ CFDictionaryGetValue (vidmode, kCGDisplayHeight),
+ kCFNumberSInt32Type, h);
+ }
+#endif
+
+ /* we only care about the 32-bit modes... */
+ if (*bpp != 32) {
+ *bpp = 0;
+ }
+}
+
+static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format)
+{
+ NSRect r = NSMakeRect(0.0, 0.0, 0.0, 0.0);
+ const char *env = NULL;
+
+ if ( Gestalt(gestaltSystemVersion, &system_version) != noErr )
+ system_version = 0;
+
+ use_new_mode_apis = NO;
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060)
+ use_new_mode_apis = IS_SNOW_LEOPARD_OR_LATER(this);
+#endif
+
+ /* Initialize the video settings; this data persists between mode switches */
+ display_id = kCGDirectMainDisplay;
+
+#if 0 /* The mouse event code needs to take this into account... */
+ env = getenv("SDL_VIDEO_FULLSCREEN_DISPLAY");
+ if ( env ) {
+ int monitor = SDL_atoi(env);
+ CGDirectDisplayID activeDspys [3];
+ CGDisplayCount dspyCnt;
+ CGGetActiveDisplayList (3, activeDspys, &dspyCnt);
+ if ( monitor >= 0 && monitor < dspyCnt ) {
+ display_id = activeDspys[monitor];
+ }
+ }
+#endif
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060)
+ if (use_new_mode_apis) {
+ save_mode = CGDisplayCopyDisplayMode(display_id);
+ }
+#endif
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1060)
+ if (!use_new_mode_apis) {
+ save_mode = CGDisplayCurrentMode(display_id);
+ }
+#endif
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
+ if (!IS_LION_OR_LATER(this)) {
+ palette = CGPaletteCreateDefaultColorPalette();
+ }
+#endif
+
+ if (save_mode == NULL) {
+ SDL_SetError("Couldn't figure out current display mode.");
+ return -1;
+ }
+
+ /* Allow environment override of screensaver disable. */
+ env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER");
+ if ( env ) {
+ allow_screensaver = SDL_atoi(env);
+ } else {
+#ifdef SDL_VIDEO_DISABLE_SCREENSAVER
+ allow_screensaver = 0;
+#else
+ allow_screensaver = 1;
+#endif
+ }
+
+ /* Gather some information that is useful to know about the display */
+ QZ_GetModeInfo(this, save_mode, &device_width, &device_height, &device_bpp);
+ if (device_bpp == 0) {
+ QZ_ReleaseDisplayMode(this, save_mode);
+ save_mode = NULL;
+ SDL_SetError("Unsupported display mode");
+ return -1;
+ }
+
+ /* Determine the current screen size */
+ this->info.current_w = device_width;
+ this->info.current_h = device_height;
+
+ /* Determine the default screen depth */
+ video_format->BitsPerPixel = device_bpp;
+
+ /* Set misc globals */
+ current_grab_mode = SDL_GRAB_OFF;
+ cursor_should_be_visible = YES;
+ cursor_visible = YES;
+ current_mods = 0;
+ field_edit = [[SDLTranslatorResponder alloc] initWithFrame:r];
+
+ /* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */
+ QZ_RegisterForSleepNotifications (this);
+
+ /* Fill in some window manager capabilities */
+ this->info.wm_available = 1;
+
+ return 0;
+}
+
+static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags)
+{
+ CFArrayRef mode_list = NULL; /* list of available fullscreen modes */
+ CFIndex num_modes;
+ CFIndex i;
+
+ int list_size = 0;
+
+ /* Any windowed mode is acceptable */
+ if ( (flags & SDL_FULLSCREEN) == 0 )
+ return (SDL_Rect**)-1;
+
+ /* Free memory from previous call, if any */
+ if ( client_mode_list != NULL ) {
+ int i;
+
+ for (i = 0; client_mode_list[i] != NULL; i++)
+ SDL_free (client_mode_list[i]);
+
+ SDL_free (client_mode_list);
+ client_mode_list = NULL;
+ }
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060)
+ if (use_new_mode_apis) {
+ mode_list = CGDisplayCopyAllDisplayModes(display_id, NULL);
+ }
+#endif
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1060)
+ if (!use_new_mode_apis) {
+ mode_list = CGDisplayAvailableModes(display_id);
+ }
+#endif
+
+ num_modes = CFArrayGetCount (mode_list);
+
+ /* Build list of modes with the requested bpp */
+ for (i = 0; i < num_modes; i++) {
+ Uint32 width, height, bpp;
+ const void *onemode = CFArrayGetValueAtIndex(mode_list, i);
+
+ QZ_GetModeInfo(this, onemode, &width, &height, &bpp);
+
+ if (bpp && (bpp == format->BitsPerPixel)) {
+ int hasMode = SDL_FALSE;
+ int i;
+
+ /* Check if mode is already in the list */
+ for (i = 0; i < list_size; i++) {
+ if (client_mode_list[i]->w == width &&
+ client_mode_list[i]->h == height) {
+ hasMode = SDL_TRUE;
+ break;
+ }
+ }
+
+ /* Grow the list and add mode to the list */
+ if ( ! hasMode ) {
+ SDL_Rect *rect;
+
+ list_size++;
+
+ if (client_mode_list == NULL)
+ client_mode_list = (SDL_Rect**)
+ SDL_malloc (sizeof(*client_mode_list) * (list_size+1) );
+ else {
+ /* !!! FIXME: this leaks memory if SDL_realloc() fails! */
+ client_mode_list = (SDL_Rect**)
+ SDL_realloc (client_mode_list, sizeof(*client_mode_list) * (list_size+1));
+ }
+
+ rect = (SDL_Rect*) SDL_malloc (sizeof(**client_mode_list));
+
+ if (client_mode_list == NULL || rect == NULL) {
+ QZ_ReleaseDisplayModeList(this, mode_list);
+ SDL_OutOfMemory ();
+ return NULL;
+ }
+
+ rect->x = rect->y = 0;
+ rect->w = width;
+ rect->h = height;
+
+ client_mode_list[list_size-1] = rect;
+ client_mode_list[list_size] = NULL;
+ }
+ }
+ }
+
+ QZ_ReleaseDisplayModeList(this, mode_list);
+
+ /* Sort list largest to smallest (by area) */
+ {
+ int i, j;
+ for (i = 0; i < list_size; i++) {
+ for (j = 0; j < list_size-1; j++) {
+
+ int area1, area2;
+ area1 = client_mode_list[j]->w * client_mode_list[j]->h;
+ area2 = client_mode_list[j+1]->w * client_mode_list[j+1]->h;
+
+ if (area1 < area2) {
+ SDL_Rect *tmp = client_mode_list[j];
+ client_mode_list[j] = client_mode_list[j+1];
+ client_mode_list[j+1] = tmp;
+ }
+ }
+ }
+ }
+
+ return client_mode_list;
+}
+
+static SDL_bool QZ_WindowPosition(_THIS, int *x, int *y)
+{
+ const char *window = getenv("SDL_VIDEO_WINDOW_POS");
+ if ( window ) {
+ if ( sscanf(window, "%d,%d", x, y) == 2 ) {
+ return SDL_TRUE;
+ }
+ }
+ return SDL_FALSE;
+}
+
+static CGError QZ_SetDisplayMode(_THIS, const void *vidmode)
+{
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060)
+ if (use_new_mode_apis) {
+ return CGDisplaySetDisplayMode(display_id, (CGDisplayModeRef) vidmode, NULL);
+ }
+#endif
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1060)
+ if (!use_new_mode_apis) {
+ return CGDisplaySwitchToMode(display_id, (CFDictionaryRef) vidmode);
+ }
+#endif
+
+ return kCGErrorFailure;
+}
+
+static inline CGError QZ_RestoreDisplayMode(_THIS)
+{
+ return QZ_SetDisplayMode(this, save_mode);
+}
+
+static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop, BOOL save_gl)
+{
+ /* Reset values that may change between switches */
+ this->info.blit_fill = 0;
+ this->FillHWRect = NULL;
+ this->UpdateRects = NULL;
+ this->LockHWSurface = NULL;
+ this->UnlockHWSurface = NULL;
+
+ if (cg_context) {
+ CGContextFlush (cg_context);
+ CGContextRelease (cg_context);
+ cg_context = nil;
+ }
+
+ /* Release fullscreen resources */
+ if ( mode_flags & SDL_FULLSCREEN ) {
+
+ NSRect screen_rect;
+
+ /* Release double buffer stuff */
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
+ if ( !IS_LION_OR_LATER(this) && (mode_flags & SDL_DOUBLEBUF) ) {
+ quit_thread = YES;
+ SDL_SemPost (sem1);
+ SDL_WaitThread (thread, NULL);
+ SDL_DestroySemaphore (sem1);
+ SDL_DestroySemaphore (sem2);
+ SDL_free (sw_buffers[0]);
+ }
+#endif
+
+ /* If we still have a valid window, close it. */
+ if ( qz_window ) {
+ NSCAssert([ qz_window delegate ] == nil, @"full screen window shouldn't have a delegate"); /* if that should ever change, we'd have to release it here */
+ [ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */
+ qz_window = nil;
+ window_view = nil;
+ }
+ /*
+ Release the OpenGL context
+ Do this first to avoid trash on the display before fade
+ */
+ if ( mode_flags & SDL_OPENGL ) {
+ if (!save_gl) {
+ QZ_TearDownOpenGL (this);
+ }
+
+ #ifdef __powerpc__ /* we only use this for pre-10.3 compatibility. */
+ CGLSetFullScreen (NULL);
+ #endif
+ }
+ if (to_desktop) {
+ /* !!! FIXME: keep an eye on this.
+ * This API is officially unavailable for 64-bit binaries.
+ * It happens to work, as of 10.7, but we're going to see if
+ * we can just simply do without it on newer OSes...
+ */
+ #if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && !defined(__LP64__)
+ if ( !IS_LION_OR_LATER(this) ) {
+ ShowMenuBar ();
+ }
+ #endif
+
+ /* Restore original screen resolution/bpp */
+ QZ_RestoreDisplayMode (this);
+ CGReleaseAllDisplays ();
+ /*
+ Reset the main screen's rectangle
+ See comment in QZ_SetVideoFullscreen for why we do this
+ */
+ screen_rect = NSMakeRect(0,0,device_width,device_height);
+ QZ_SetFrame(this, [ NSScreen mainScreen ], screen_rect);
+ }
+ }
+ /* Release window mode resources */
+ else {
+ id delegate = [ qz_window delegate ];
+ [ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */
+ if (delegate != nil) [ delegate release ];
+ qz_window = nil;
+ window_view = nil;
+
+ /* Release the OpenGL context */
+ if ( mode_flags & SDL_OPENGL ) {
+ if (!save_gl) {
+ QZ_TearDownOpenGL (this);
+ }
+ }
+ }
+
+ /* Signal successful teardown */
+ video_set = SDL_FALSE;
+}
+
+static const void *QZ_BestMode(_THIS, const int bpp, const int w, const int h)
+{
+ const void *best = NULL;
+
+ if (bpp == 0) {
+ return NULL;
+ }
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060)
+ if (use_new_mode_apis) {
+ /* apparently, we have to roll our own now. :/ */
+ CFArrayRef mode_list = CGDisplayCopyAllDisplayModes(display_id, NULL);
+ if (mode_list != NULL) {
+ const CFIndex num_modes = CFArrayGetCount(mode_list);
+ CFIndex i;
+ for (i = 0; i < num_modes; i++) {
+ const void *vidmode = CFArrayGetValueAtIndex(mode_list, i);
+ Uint32 thisw, thish, thisbpp;
+ QZ_GetModeInfo(this, vidmode, &thisw, &thish, &thisbpp);
+
+ /* We only care about exact matches, apparently. */
+ if ((thisbpp == bpp) && (thisw == w) && (thish == h)) {
+ best = vidmode;
+ break; /* got it! */
+ }
+ }
+ CGDisplayModeRetain((CGDisplayModeRef) best); /* NULL is ok */
+ CFRelease(mode_list);
+ }
+ }
+#endif
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1060)
+ if (!use_new_mode_apis) {
+ boolean_t exact = 0;
+ best = CGDisplayBestModeForParameters(display_id, bpp, w, h, &exact);
+ if (!exact) {
+ best = NULL;
+ }
+ }
+#endif
+
+ return best;
+}
+
+static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int width,
+ int height, int bpp, Uint32 flags,
+ const BOOL save_gl)
+{
+ const BOOL isLion = IS_LION_OR_LATER(this);
+ NSRect screen_rect;
+ CGError error;
+ NSRect contentRect;
+ CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
+
+ current->flags = SDL_FULLSCREEN;
+ current->w = width;
+ current->h = height;
+
+ contentRect = NSMakeRect (0, 0, width, height);
+
+ /* Fade to black to hide resolution-switching flicker (and garbage
+ that is displayed by a destroyed OpenGL context, if applicable) */
+ if ( CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess ) {
+ CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
+ }
+
+ /* Destroy any previous mode */
+ if (video_set == SDL_TRUE)
+ QZ_UnsetVideoMode (this, FALSE, save_gl);
+
+ /* Sorry, QuickDraw was ripped out. */
+ if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) {
+ SDL_SetError ("Embedded QuickDraw windows are no longer supported");
+ goto ERR_NO_MATCH;
+ }
+
+ QZ_ReleaseDisplayMode(this, mode); /* NULL is okay. */
+
+ /* See if requested mode exists */
+ mode = QZ_BestMode(this, bpp, width, height);
+
+ /* Require an exact match to the requested mode */
+ if ( mode == NULL ) {
+ SDL_SetError ("Failed to find display resolution: %dx%dx%d", width, height, bpp);
+ goto ERR_NO_MATCH;
+ }
+
+ /* Put up the blanking window (a window above all other windows) */
+ if (getenv ("SDL_SINGLEDISPLAY"))
+ error = CGDisplayCapture (display_id);
+ else
+ error = CGCaptureAllDisplays ();
+
+ if ( CGDisplayNoErr != error ) {
+ SDL_SetError ("Failed capturing display");
+ goto ERR_NO_CAPTURE;
+ }
+
+ /* Do the physical switch */
+ if ( CGDisplayNoErr != QZ_SetDisplayMode(this, mode) ) {
+ SDL_SetError ("Failed switching display resolution");
+ goto ERR_NO_SWITCH;
+ }
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
+ if ( !isLion ) {
+ current->pixels = (Uint32*) CGDisplayBaseAddress (display_id);
+ current->pitch = CGDisplayBytesPerRow (display_id);
+
+ current->flags |= SDL_HWSURFACE;
+ current->flags |= SDL_PREALLOC;
+ /* current->hwdata = (void *) CGDisplayGetDrawingContext (display_id); */
+
+ this->UpdateRects = QZ_DirectUpdate;
+ this->LockHWSurface = QZ_LockHWSurface;
+ this->UnlockHWSurface = QZ_UnlockHWSurface;
+
+ /* Setup double-buffer emulation */
+ if ( flags & SDL_DOUBLEBUF ) {
+
+ /*
+ Setup a software backing store for reasonable results when
+ double buffering is requested (since a single-buffered hardware
+ surface looks hideous).
+
+ The actual screen blit occurs in a separate thread to allow
+ other blitting while waiting on the VBL (and hence results in higher framerates).
+ */
+ this->LockHWSurface = NULL;
+ this->UnlockHWSurface = NULL;
+ this->UpdateRects = NULL;
+
+ current->flags |= (SDL_HWSURFACE|SDL_DOUBLEBUF);
+ this->UpdateRects = QZ_DoubleBufferUpdate;
+ this->LockHWSurface = QZ_LockDoubleBuffer;
+ this->UnlockHWSurface = QZ_UnlockDoubleBuffer;
+ this->FlipHWSurface = QZ_FlipDoubleBuffer;
+
+ current->pixels = SDL_malloc (current->pitch * current->h * 2);
+ if (current->pixels == NULL) {
+ SDL_OutOfMemory ();
+ goto ERR_DOUBLEBUF;
+ }
+
+ sw_buffers[0] = current->pixels;
+ sw_buffers[1] = (Uint8*)current->pixels + current->pitch * current->h;
+
+ quit_thread = NO;
+ sem1 = SDL_CreateSemaphore (0);
+ sem2 = SDL_CreateSemaphore (1);
+ thread = SDL_CreateThread ((int (*)(void *))QZ_ThreadFlip, this);
+ }
+
+ if ( CGDisplayCanSetPalette (display_id) )
+ current->flags |= SDL_HWPALETTE;
+ }
+#endif
+
+ /* Check if we should recreate the window */
+ if (qz_window == nil) {
+ /* Manually create a window, avoids having a nib file resource */
+ qz_window = [ [ SDL_QuartzWindow alloc ]
+ initWithContentRect:contentRect
+ styleMask:(isLion ? NSBorderlessWindowMask : 0)
+ backing:NSBackingStoreBuffered
+ defer:NO ];
+
+ if (qz_window != nil) {
+ [ qz_window setAcceptsMouseMovedEvents:YES ];
+ [ qz_window setViewsNeedDisplay:NO ];
+ if (isLion) {
+ [ qz_window setContentView: [ [ [ SDL_QuartzView alloc ] init ] autorelease ] ];
+ }
+ }
+ }
+ /* We already have a window, just change its size */
+ else {
+ [ qz_window setContentSize:contentRect.size ];
+ current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags;
+ [ window_view setFrameSize:contentRect.size ];
+ }
+
+ /* Setup OpenGL for a fullscreen context */
+ if (flags & SDL_OPENGL) {
+
+ if ( ! save_gl ) {
+ if ( ! QZ_SetupOpenGL (this, bpp, flags) ) {
+ goto ERR_NO_GL;
+ }
+ }
+
+ /* Initialize the NSView and add it to our window. The presence of a valid window and
+ view allow the cursor to be changed whilst in fullscreen.*/
+ window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
+
+ if ( isLion ) {
+ [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
+ }
+
+ [ [ qz_window contentView ] addSubview:window_view ];
+
+ /* Apparently Lion checks some version flag set by the linker
+ and changes API behavior. Annoying. */
+ if ( isLion ) {
+ [ qz_window setLevel:CGShieldingWindowLevel() ];
+ [ gl_context setView: window_view ];
+ //[ gl_context setFullScreen ];
+ [ gl_context update ];
+ }
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
+ if ( !isLion ) {
+ CGLError err;
+ CGLContextObj ctx;
+
+ [ qz_window setLevel:NSNormalWindowLevel ];
+ ctx = QZ_GetCGLContextObj (gl_context);
+ err = CGLSetFullScreen (ctx);
+
+ if (err) {
+ SDL_SetError ("Error setting OpenGL fullscreen: %s", CGLErrorString(err));
+ goto ERR_NO_GL;
+ }
+ }
+#endif
+
+ [ window_view release ];
+ [ gl_context makeCurrentContext];
+
+ glClear (GL_COLOR_BUFFER_BIT);
+
+ [ gl_context flushBuffer ];
+
+ current->flags |= SDL_OPENGL;
+ } else if (isLion) { /* For 2D, we build a CGBitmapContext */
+ CGColorSpaceRef cgColorspace;
+
+ /* Only recreate the view if it doesn't already exist */
+ if (window_view == nil) {
+ window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
+ [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
+ [ [ qz_window contentView ] addSubview:window_view ];
+ [ window_view release ];
+ }
+
+ cgColorspace = CGColorSpaceCreateDeviceRGB();
+ current->pitch = 4 * current->w;
+ current->pixels = SDL_malloc (current->h * current->pitch);
+
+ cg_context = CGBitmapContextCreate (current->pixels, current->w, current->h,
+ 8, current->pitch, cgColorspace,
+ kCGImageAlphaNoneSkipFirst);
+ CGColorSpaceRelease (cgColorspace);
+
+ current->flags |= SDL_SWSURFACE;
+ current->flags |= SDL_ASYNCBLIT;
+ current->hwdata = (void *) cg_context;
+
+ /* Force this window to draw above _everything_. */
+ [ qz_window setLevel:CGShieldingWindowLevel() ];
+
+ this->UpdateRects = QZ_UpdateRects;
+ this->LockHWSurface = QZ_LockHWSurface;
+ this->UnlockHWSurface = QZ_UnlockHWSurface;
+ }
+
+ if (isLion) {
+ [ qz_window setHasShadow:NO];
+ [ qz_window setOpaque:YES];
+ [ qz_window makeKeyAndOrderFront:nil ];
+ }
+
+ /* !!! FIXME: keep an eye on this.
+ * This API is officially unavailable for 64-bit binaries.
+ * It happens to work, as of 10.7, but we're going to see if
+ * we can just simply do without it on newer OSes...
+ */
+ #if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && !defined(__LP64__)
+ if ( !isLion ) {
+ /* If we don't hide menu bar, it will get events and interrupt the program */
+ HideMenuBar ();
+ }
+ #endif
+
+ /* 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);
+ }
+
+ /*
+ 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.
+ */
+ screen_rect = NSMakeRect(0,0,width,height);
+ QZ_SetFrame(this, [ NSScreen mainScreen ], screen_rect);
+
+ /* Save the flags to ensure correct tear-down */
+ mode_flags = current->flags;
+
+ /* Set app state, hide cursor if necessary, ... */
+ QZ_DoActivate(this);
+
+ return current;
+
+ /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
+ERR_NO_GL: goto ERR_DOUBLEBUF; /* this goto is to stop a compiler warning on newer SDKs. */
+ERR_DOUBLEBUF: QZ_RestoreDisplayMode(this);
+ERR_NO_SWITCH: CGReleaseAllDisplays ();
+ERR_NO_CAPTURE:
+ERR_NO_MATCH: if ( fade_token != kCGDisplayFadeReservationInvalidToken ) {
+ CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
+ CGReleaseDisplayFadeReservation (fade_token);
+ }
+ return NULL;
+}
+
+static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width,
+ int height, int *bpp, Uint32 flags,
+ const BOOL save_gl)
+{
+ unsigned int style;
+ NSRect contentRect;
+ int center_window = 1;
+ int origin_x, origin_y;
+ CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
+
+ current->flags = 0;
+ current->w = width;
+ current->h = height;
+
+ contentRect = NSMakeRect (0, 0, width, height);
+
+ /*
+ Check if we should completely destroy the previous mode
+ - If it is fullscreen
+ - If it has different noframe or resizable attribute
+ - If it is OpenGL (since gl attributes could be different)
+ - If new mode is OpenGL, but previous mode wasn't
+ */
+ if (video_set == SDL_TRUE) {
+ if (mode_flags & SDL_FULLSCREEN) {
+ /* Fade to black to hide resolution-switching flicker (and garbage
+ that is displayed by a destroyed OpenGL context, if applicable) */
+ if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) {
+ CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
+ }
+ QZ_UnsetVideoMode (this, TRUE, save_gl);
+ }
+ else if ( ((mode_flags ^ flags) & (SDL_NOFRAME|SDL_RESIZABLE)) ||
+ (mode_flags & SDL_OPENGL) ||
+ (flags & SDL_OPENGL) ) {
+ QZ_UnsetVideoMode (this, TRUE, save_gl);
+ }
+ }
+
+ /* Sorry, QuickDraw was ripped out. */
+ if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) {
+ SDL_SetError ("Embedded QuickDraw windows are no longer supported");
+ if (fade_token != kCGDisplayFadeReservationInvalidToken) {
+ CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
+ CGReleaseDisplayFadeReservation (fade_token);
+ }
+ return NULL;
+ }
+
+ /* Check if we should recreate the window */
+ if (qz_window == nil) {
+
+ /* Set the window style based on input flags */
+ if ( flags & SDL_NOFRAME ) {
+ style = NSBorderlessWindowMask;
+ current->flags |= SDL_NOFRAME;
+ } else {
+ style = NSTitledWindowMask;
+ style |= (NSMiniaturizableWindowMask | NSClosableWindowMask);
+ if ( flags & SDL_RESIZABLE ) {
+ style |= NSResizableWindowMask;
+ current->flags |= SDL_RESIZABLE;
+ }
+ }
+
+ /* Manually create a window, avoids having a nib file resource */
+ qz_window = [ [ SDL_QuartzWindow alloc ]
+ initWithContentRect:contentRect
+ styleMask:style
+ backing:NSBackingStoreBuffered
+ defer:NO ];
+
+ if (qz_window == nil) {
+ SDL_SetError ("Could not create the Cocoa window");
+ if (fade_token != kCGDisplayFadeReservationInvalidToken) {
+ CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
+ CGReleaseDisplayFadeReservation (fade_token);
+ }
+ return NULL;
+ }
+
+ /*[ qz_window setReleasedWhenClosed:YES ];*/ /* no need to set this as it's the default for NSWindows */
+ QZ_SetCaption(this, this->wm_title, this->wm_icon);
+ [ qz_window setAcceptsMouseMovedEvents:YES ];
+ [ qz_window setViewsNeedDisplay:NO ];
+
+ if ( QZ_WindowPosition(this, &origin_x, &origin_y) ) {
+ /* have to flip the Y value (NSPoint is lower left corner origin) */
+ [ qz_window setFrameTopLeftPoint:NSMakePoint((float) origin_x, (float) (this->info.current_h - origin_y))];
+ center_window = 0;
+ } else if ( center_window ) {
+ [ qz_window center ];
+ }
+
+ [ qz_window setDelegate:
+ [ [ SDL_QuartzWindowDelegate alloc ] init ] ];
+ [ qz_window setContentView: [ [ [ SDL_QuartzView alloc ] init ] autorelease ] ];
+ }
+ /* We already have a window, just change its size */
+ else {
+ [ qz_window setContentSize:contentRect.size ];
+ current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags;
+ [ window_view setFrameSize:contentRect.size ];
+ }
+
+ /* For OpenGL, we bind the context to a subview */
+ if ( flags & SDL_OPENGL ) {
+
+ if ( ! save_gl ) {
+ if ( ! QZ_SetupOpenGL (this, *bpp, flags) ) {
+ if (fade_token != kCGDisplayFadeReservationInvalidToken) {
+ CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
+ CGReleaseDisplayFadeReservation (fade_token);
+ }
+ return NULL;
+ }
+ }
+
+ window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
+ [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
+ [ [ qz_window contentView ] addSubview:window_view ];
+ [ gl_context setView: window_view ];
+ [ window_view release ];
+ [ gl_context makeCurrentContext];
+ [ qz_window makeKeyAndOrderFront:nil ];
+ current->flags |= SDL_OPENGL;
+ }
+ /* For 2D, we build a CGBitmapContext */
+ else {
+ CGColorSpaceRef cgColorspace;
+
+ /* Only recreate the view if it doesn't already exist */
+ if (window_view == nil) {
+
+ window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
+ [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
+ [ [ qz_window contentView ] addSubview:window_view ];
+ [ window_view release ];
+ [ qz_window makeKeyAndOrderFront:nil ];
+ }
+
+ cgColorspace = CGColorSpaceCreateDeviceRGB();
+ current->pitch = 4 * current->w;
+ current->pixels = SDL_malloc (current->h * current->pitch);
+
+ cg_context = CGBitmapContextCreate (current->pixels, current->w, current->h,
+ 8, current->pitch, cgColorspace,
+ kCGImageAlphaNoneSkipFirst);
+ CGColorSpaceRelease (cgColorspace);
+
+ current->flags |= SDL_SWSURFACE;
+ current->flags |= SDL_ASYNCBLIT;
+ current->hwdata = (void *) cg_context;
+
+ this->UpdateRects = QZ_UpdateRects;
+ this->LockHWSurface = QZ_LockHWSurface;
+ this->UnlockHWSurface = QZ_UnlockHWSurface;
+ }
+
+ /* Save flags to ensure correct teardown */
+ mode_flags = current->flags;
+
+ /* Fade in again (asynchronously) if we came from a fullscreen mode and faded to black */
+ if (fade_token != kCGDisplayFadeReservationInvalidToken) {
+ CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
+ CGReleaseDisplayFadeReservation (fade_token);
+ }
+
+ return current;
+}
+
+
+static SDL_Surface* QZ_SetVideoModeInternal (_THIS, SDL_Surface *current,
+ int width, int height, int bpp,
+ Uint32 flags, BOOL save_gl)
+{
+ const BOOL isLion = IS_LION_OR_LATER(this);
+
+ current->flags = 0;
+ current->pixels = NULL;
+
+ /* Setup full screen video */
+ if ( flags & SDL_FULLSCREEN ) {
+ if ( isLion ) {
+ bpp = 32;
+ }
+ current = QZ_SetVideoFullScreen (this, current, width, height, bpp, flags, save_gl );
+ if (current == NULL)
+ return NULL;
+ }
+ /* Setup windowed video */
+ else {
+ /* Force bpp to 32 */
+ bpp = 32;
+ current = QZ_SetVideoWindowed (this, current, width, height, &bpp, flags, save_gl );
+ if (current == NULL)
+ return NULL;
+ }
+
+ if (qz_window != nil) {
+ nsgfx_context = [NSGraphicsContext graphicsContextWithWindow:qz_window];
+ [NSGraphicsContext setCurrentContext:nsgfx_context];
+ }
+
+ /* Setup the new pixel format */
+ {
+ int amask = 0,
+ rmask = 0,
+ gmask = 0,
+ bmask = 0;
+
+ switch (bpp) {
+ case 16: /* (1)-5-5-5 RGB */
+ amask = 0;
+ rmask = 0x7C00;
+ gmask = 0x03E0;
+ bmask = 0x001F;
+ break;
+ case 24:
+ SDL_SetError ("24bpp is not available");
+ return NULL;
+ case 32: /* (8)-8-8-8 ARGB */
+ amask = 0x00000000;
+ if ( (!isLion) && (flags & SDL_FULLSCREEN) ) {
+ rmask = 0x00FF0000;
+ gmask = 0x0000FF00;
+ bmask = 0x000000FF;
+ } else {
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+ rmask = 0x0000FF00;
+ gmask = 0x00FF0000;
+ bmask = 0xFF000000;
+#else
+ rmask = 0x00FF0000;
+ gmask = 0x0000FF00;
+ bmask = 0x000000FF;
+#endif
+ break;
+ }
+ }
+
+ if ( ! SDL_ReallocFormat (current, bpp,
+ rmask, gmask, bmask, amask ) ) {
+ SDL_SetError ("Couldn't reallocate pixel format");
+ return NULL;
+ }
+ }
+
+ /* Signal successful completion (used internally) */
+ video_set = SDL_TRUE;
+
+ return current;
+}
+
+static SDL_Surface* QZ_SetVideoMode(_THIS, SDL_Surface *current,
+ int width, int height, int bpp,
+ Uint32 flags)
+{
+ /* Don't throw away the GL context if we can just resize the current one. */
+#if 0 /* !!! FIXME: half-finished side project. Reenable this if you ever debug the corner cases. */
+ const BOOL save_gl = ( (video_set == SDL_TRUE) && ((flags & SDL_OPENGL) == (current->flags & SDL_OPENGL)) && (bpp == current->format->BitsPerPixel) );
+#else
+ const BOOL save_gl = NO;
+#endif
+
+ NSOpenGLContext *glctx = gl_context;
+ SDL_Surface* retval = NULL;
+
+ if (save_gl) {
+ [glctx retain]; /* just so we don't lose this when killing old views, etc */
+ }
+
+ retval = QZ_SetVideoModeInternal (this, current, width, height, bpp, flags, save_gl);
+
+ if (save_gl) {
+ [glctx release]; /* something else should own this now, or we legitimately release it. */
+ }
+
+ return retval;
+}
+
+
+static int QZ_ToggleFullScreen (_THIS, int on)
+{
+ return 0;
+}
+
+static int QZ_SetColors (_THIS, int first_color, int num_colors,
+ SDL_Color *colors)
+{
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
+ /* we shouldn't have an 8-bit mode on Lion! */
+ if (!IS_LION_OR_LATER(this)) {
+ CGTableCount index;
+ CGDeviceColor color;
+
+ for (index = first_color; index < first_color+num_colors; index++) {
+
+ /* Clamp colors between 0.0 and 1.0 */
+ color.red = colors->r / 255.0;
+ color.blue = colors->b / 255.0;
+ color.green = colors->g / 255.0;
+
+ colors++;
+
+ CGPaletteSetColorAtIndex (palette, color, index);
+ }
+
+ return ( CGDisplayNoErr == CGDisplaySetPalette (display_id, palette) );
+ }
+#endif
+
+ return 0;
+}
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
+static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface)
+{
+ return 1;
+}
+
+static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface)
+{
+}
+
+/* The VBL delay is based on code by Ian R Ollmann's RezLib <iano@cco.caltech.edu> */
+static AbsoluteTime QZ_SecondsToAbsolute ( double seconds )
+{
+ union
+ {
+ UInt64 i;
+ Nanoseconds ns;
+ } temp;
+
+ temp.i = seconds * 1000000000.0;
+
+ return NanosecondsToAbsolute ( temp.ns );
+}
+
+static int QZ_ThreadFlip (_THIS)
+{
+ Uint8 *src, *dst;
+ int skip, len, h;
+
+ /*
+ Give this thread the highest scheduling priority possible,
+ in the hopes that it will immediately run after the VBL delay
+ */
+ {
+ pthread_t current_thread;
+ int policy;
+ struct sched_param param;
+
+ current_thread = pthread_self ();
+ pthread_getschedparam (current_thread, &policy, &param);
+ policy = SCHED_RR;
+ param.sched_priority = sched_get_priority_max (policy);
+ pthread_setschedparam (current_thread, policy, &param);
+ }
+
+ while (1) {
+
+ SDL_SemWait (sem1);
+ if (quit_thread)
+ return 0;
+
+ /*
+ * We have to add SDL_VideoSurface->offset here, since we might be a
+ * smaller surface in the center of the framebuffer (you asked for
+ * a fullscreen resolution smaller than the hardware could supply
+ * so SDL is centering it in a bigger resolution)...
+ */
+ dst = ((Uint8 *)((size_t)CGDisplayBaseAddress (display_id))) + SDL_VideoSurface->offset;
+ src = current_buffer + SDL_VideoSurface->offset;
+ len = SDL_VideoSurface->w * SDL_VideoSurface->format->BytesPerPixel;
+ h = SDL_VideoSurface->h;
+ skip = SDL_VideoSurface->pitch;
+
+ /* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */
+ {
+
+ /* The VBL delay is based on Ian Ollmann's RezLib <iano@cco.caltech.edu> */
+ double refreshRate;
+ double linesPerSecond;
+ double target;
+ double position;
+ double adjustment;
+ AbsoluteTime nextTime;
+ CFNumberRef refreshRateCFNumber;
+
+ refreshRateCFNumber = CFDictionaryGetValue (mode, kCGDisplayRefreshRate);
+ if ( NULL == refreshRateCFNumber ) {
+ SDL_SetError ("Mode has no refresh rate");
+ goto ERROR;
+ }
+
+ if ( 0 == CFNumberGetValue (refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) ) {
+ SDL_SetError ("Error getting refresh rate");
+ goto ERROR;
+ }
+
+ if ( 0 == refreshRate ) {
+
+ SDL_SetError ("Display has no refresh rate, using 60hz");
+
+ /* ok, for LCD's we'll emulate a 60hz refresh, which may or may not look right */
+ refreshRate = 60.0;
+ }
+
+ linesPerSecond = refreshRate * h;
+ target = h;
+
+ /* Figure out the first delay so we start off about right */
+ position = CGDisplayBeamPosition (display_id);
+ if (position > target)
+ position = 0;
+
+ adjustment = (target - position) / linesPerSecond;
+
+ nextTime = AddAbsoluteToAbsolute (UpTime (), QZ_SecondsToAbsolute (adjustment));
+
+ MPDelayUntil (&nextTime);
+ }
+
+
+ /* On error, skip VBL delay */
+ ERROR:
+
+ /* TODO: use CGContextDrawImage here too! Create two CGContextRefs the same way we
+ create two buffers, replace current_buffer with current_context and set it
+ appropriately in QZ_FlipDoubleBuffer. */
+ while ( h-- ) {
+
+ SDL_memcpy (dst, src, len);
+ src += skip;
+ dst += skip;
+ }
+
+ /* signal flip completion */
+ SDL_SemPost (sem2);
+ }
+
+ return 0;
+}
+
+static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface)
+{
+ /* wait for previous flip to complete */
+ SDL_SemWait (sem2);
+
+ current_buffer = surface->pixels;
+
+ if (surface->pixels == sw_buffers[0])
+ surface->pixels = sw_buffers[1];
+ else
+ surface->pixels = sw_buffers[0];
+
+ /* signal worker thread to do the flip */
+ SDL_SemPost (sem1);
+
+ return 0;
+}
+
+static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects)
+{
+ /* perform a flip if someone calls updaterects on a doublebuferred surface */
+ this->FlipHWSurface (this, SDL_VideoSurface);
+}
+
+static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects)
+{
+#pragma unused(this,num_rects,rects)
+}
+#endif
+
+/* Resize icon, BMP format */
+static const unsigned char QZ_ResizeIcon[] = {
+ 0x42,0x4d,0x31,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,
+ 0x00,0x00,0x0d,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,
+ 0x00,0x00,0xfb,0x01,0x00,0x00,0x13,0x0b,0x00,0x00,0x13,0x0b,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
+ 0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,
+ 0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,
+ 0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xda,0xda,0xda,0x87,
+ 0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,
+ 0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd5,0xd5,0xd5,0x87,0x87,0x87,0xe8,0xe8,0xe8,
+ 0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,
+ 0xda,0xda,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,
+ 0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,
+ 0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,
+ 0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,
+ 0xe8,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xd9,0xd9,0xd9,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xdc,
+ 0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,
+ 0xdb,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,0xdb,0x87,0x87,0x87,0xe8,
+ 0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdc,
+ 0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b
+};
+
+static void QZ_DrawResizeIcon (_THIS)
+{
+ /* Check if we should draw the resize icon */
+ if (SDL_VideoSurface->flags & SDL_RESIZABLE) {
+
+ SDL_Rect icon_rect;
+
+ /* Create the icon image */
+ if (resize_icon == NULL) {
+
+ SDL_RWops *rw;
+ SDL_Surface *tmp;
+
+ rw = SDL_RWFromConstMem (QZ_ResizeIcon, sizeof(QZ_ResizeIcon));
+ tmp = SDL_LoadBMP_RW (rw, SDL_TRUE);
+
+ resize_icon = SDL_ConvertSurface (tmp, SDL_VideoSurface->format, SDL_SRCCOLORKEY);
+ SDL_SetColorKey (resize_icon, SDL_SRCCOLORKEY, 0xFFFFFF);
+
+ SDL_FreeSurface (tmp);
+ }
+
+ icon_rect.x = SDL_VideoSurface->w - 13;
+ icon_rect.y = SDL_VideoSurface->h - 13;
+ icon_rect.w = 13;
+ icon_rect.h = 13;
+
+ SDL_BlitSurface (resize_icon, NULL, SDL_VideoSurface, &icon_rect);
+ }
+}
+
+static void QZ_UpdateRects (_THIS, int numRects, SDL_Rect *rects)
+{
+ if (SDL_VideoSurface->flags & SDL_OPENGLBLIT) {
+ QZ_GL_SwapBuffers (this);
+ }
+ else if ( [ qz_window isMiniaturized ] ) {
+
+ /* Do nothing if miniaturized */
+ }
+
+ else {
+ NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+ if (ctx != nsgfx_context) { /* uhoh, you might be rendering from another thread... */
+ [NSGraphicsContext setCurrentContext:nsgfx_context];
+ ctx = nsgfx_context;
+ }
+ CGContextRef cgc = (CGContextRef) [ctx graphicsPort];
+ QZ_DrawResizeIcon (this);
+ CGContextFlush (cg_context);
+ CGImageRef image = CGBitmapContextCreateImage (cg_context);
+ CGRect rectangle = CGRectMake (0,0,[window_view frame].size.width,[window_view frame].size.height);
+
+ CGContextDrawImage (cgc, rectangle, image);
+ CGImageRelease(image);
+ CGContextFlush (cgc);
+ }
+}
+
+static void QZ_VideoQuit (_THIS)
+{
+ CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
+
+ /* Restore gamma settings */
+ CGDisplayRestoreColorSyncSettings ();
+
+ /* Ensure the cursor will be visible and working when we quit */
+ CGDisplayShowCursor (display_id);
+ CGAssociateMouseAndMouseCursorPosition (1);
+
+ if (mode_flags & SDL_FULLSCREEN) {
+ /* Fade to black to hide resolution-switching flicker (and garbage
+ that is displayed by a destroyed OpenGL context, if applicable) */
+ if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) {
+ CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
+ }
+ QZ_UnsetVideoMode (this, TRUE, FALSE);
+ if (fade_token != kCGDisplayFadeReservationInvalidToken) {
+ CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
+ CGReleaseDisplayFadeReservation (fade_token);
+ }
+ }
+ else
+ QZ_UnsetVideoMode (this, TRUE, FALSE);
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
+ if (!IS_LION_OR_LATER(this)) {
+ CGPaletteRelease(palette);
+ }
+#endif
+
+ if (opengl_library) {
+ SDL_UnloadObject(opengl_library);
+ opengl_library = NULL;
+ }
+ this->gl_config.driver_loaded = 0;
+
+ if (field_edit) {
+ [field_edit release];
+ field_edit = NULL;
+ }
+}
+
+static int QZ_LockHWSurface(_THIS, SDL_Surface *surface)
+{
+ return 1;
+}
+
+static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface)
+{
+}
+
+static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface)
+{
+ return(-1); /* unallowed (no HWSURFACE support here). */
+}
+
+static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface)
+{
+}
+
+/* Gamma functions */
+int QZ_SetGamma (_THIS, float red, float green, float blue)
+{
+ const CGGammaValue min = 0.0, max = 1.0;
+
+ if (red == 0.0)
+ red = FLT_MAX;
+ else
+ red = 1.0 / red;
+
+ if (green == 0.0)
+ green = FLT_MAX;
+ else
+ green = 1.0 / green;
+
+ if (blue == 0.0)
+ blue = FLT_MAX;
+ else
+ blue = 1.0 / blue;
+
+ if ( CGDisplayNoErr == CGSetDisplayTransferByFormula
+ (display_id, min, max, red, min, max, green, min, max, blue) ) {
+
+ return 0;
+ }
+ else {
+
+ return -1;
+ }
+}
+
+int QZ_GetGamma (_THIS, float *red, float *green, float *blue)
+{
+ CGGammaValue dummy;
+ if ( CGDisplayNoErr == CGGetDisplayTransferByFormula
+ (display_id, &dummy, &dummy, red,
+ &dummy, &dummy, green, &dummy, &dummy, blue) )
+
+ return 0;
+ else
+ return -1;
+}
+
+int QZ_SetGammaRamp (_THIS, Uint16 *ramp)
+{
+ const uint32_t tableSize = 255;
+ CGGammaValue redTable[tableSize];
+ CGGammaValue greenTable[tableSize];
+ CGGammaValue blueTable[tableSize];
+
+ int i;
+
+ /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
+ for (i = 0; i < 256; i++)
+ redTable[i % 256] = ramp[i] / 65535.0;
+
+ for (i=256; i < 512; i++)
+ greenTable[i % 256] = ramp[i] / 65535.0;
+
+ for (i=512; i < 768; i++)
+ blueTable[i % 256] = ramp[i] / 65535.0;
+
+ if ( CGDisplayNoErr == CGSetDisplayTransferByTable
+ (display_id, tableSize, redTable, greenTable, blueTable) )
+ return 0;
+ else
+ return -1;
+}
+
+int QZ_GetGammaRamp (_THIS, Uint16 *ramp)
+{
+ const uint32_t tableSize = 255;
+ CGGammaValue redTable[tableSize];
+ CGGammaValue greenTable[tableSize];
+ CGGammaValue blueTable[tableSize];
+ uint32_t actual;
+ int i;
+
+ if ( CGDisplayNoErr != CGGetDisplayTransferByTable
+ (display_id, tableSize, redTable, greenTable, blueTable, &actual) ||
+ actual != tableSize)
+
+ return -1;
+
+ /* Pack tables into one array, with values from 0 to 65535 */
+ for (i = 0; i < 256; i++)
+ ramp[i] = redTable[i % 256] * 65535.0;
+
+ for (i=256; i < 512; i++)
+ ramp[i] = greenTable[i % 256] * 65535.0;
+
+ for (i=512; i < 768; i++)
+ ramp[i] = blueTable[i % 256] * 65535.0;
+
+ return 0;
+}
+