From a855d6202536ff28e5aae4f22a0f31d8f5b325d0 Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Sat, 21 Jan 2017 15:18:31 -0500 Subject: Port of Duke Nukem 3D This ports Fabien Sanglard's Chocolate Duke to run on a version of SDL for Rockbox. Change-Id: I8f2c4c78af19de10c1633ed7bb7a997b43256dd9 --- .../plugins/sdl/src/video/quartz/SDL_QuartzVideo.m | 1689 ++++++++++++++++++++ 1 file changed, 1689 insertions(+) create mode 100644 apps/plugins/sdl/src/video/quartz/SDL_QuartzVideo.m (limited to 'apps/plugins/sdl/src/video/quartz/SDL_QuartzVideo.m') diff --git a/apps/plugins/sdl/src/video/quartz/SDL_QuartzVideo.m b/apps/plugins/sdl/src/video/quartz/SDL_QuartzVideo.m new file mode 100644 index 0000000000..fa04e9d107 --- /dev/null +++ b/apps/plugins/sdl/src/video/quartz/SDL_QuartzVideo.m @@ -0,0 +1,1689 @@ +/* + 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" + +/* 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->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 */ +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, ¶m); + policy = SCHED_RR; + param.sched_priority = sched_get_priority_max (policy); + pthread_setschedparam (current_thread, policy, ¶m); + } + + 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 */ + 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; +} + -- cgit v1.2.3