Mercurial > sdl-ios-xcode
diff src/video/quartz/SDL_QuartzVideo.m @ 47:45b1c4303f87
Added initial support for Quartz video (thanks Darrell!)
author | Sam Lantinga <slouken@lokigames.com> |
---|---|
date | Thu, 07 Jun 2001 14:28:11 +0000 |
parents | |
children | ce9cd2cf0d0d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/video/quartz/SDL_QuartzVideo.m Thu Jun 07 14:28:11 2001 +0000 @@ -0,0 +1,756 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001 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@devolution.com +*/ + +#include "SDL_QuartzVideo.h" + +/* Some variables to share among files, put in device structure eventually */ +static SDL_GrabMode currentGrabMode = SDL_GRAB_OFF; +static BOOL inForeground = YES; +static SDLKey keymap[256]; +static unsigned int currentMods = 0; /* Current keyboard modifiers, to track modifier state */ + +/* Include files into one compile unit...break apart eventually */ +#include "SDL_QuartzWM.m" +#include "SDL_QuartzEvents.m" +#include "SDL_QuartzWindow.m" + +char QZ_Error[255]; /* Global error buffer to temporarily store more informative error messages */ + +/* Bootstrap binding, enables entry point into the driver */ +VideoBootStrap QZ_bootstrap = { + "Quartz", "MacOS X CoreGraphics", QZ_Available, QZ_CreateDevice +}; + +/* 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*) malloc (sizeof (*device) ); + hidden = (SDL_PrivateVideoData*) malloc (sizeof (*hidden) ); + + if (device == NULL || hidden == NULL) + SDL_OutOfMemory (); + + memset (device, 0, sizeof (*device) ); + 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->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->FreeHWSurface = QZ_FreeHWSurface; + /* device->FlipHWSurface = QZ_FlipHWSurface */; + + 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; + + device->free = QZ_DeleteDevice; + + return device; +} + +static void QZ_DeleteDevice (SDL_VideoDevice *device) { + + free (device->hidden); + free (device); +} + +static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format) { + + /* Initialize the video settings; this data persists between mode switches */ + display_id = kCGDirectMainDisplay; + save_mode = CGDisplayCurrentMode (display_id); + mode_list = CGDisplayAvailableModes (display_id); + palette = CGPaletteCreateDefaultColorPalette (); + + /* Gather some information that is useful to know about the display */ + CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayBitsPerPixel), + kCFNumberSInt32Type, &device_bpp); + + CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayWidth), + kCFNumberSInt32Type, &device_width); + + CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayHeight), + kCFNumberSInt32Type, &device_height); + + video_format->BitsPerPixel = device_bpp; + windowTitle = @""; + + return 0; +} + +static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags) { + + CFIndex num_modes = CFArrayGetCount (mode_list); + CFIndex i; + + static SDL_Rect **list = NULL; + 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 ( list != NULL ) { + + int i = 0; + + for (i = 0; list[i] != NULL; i++) + free (list[i]); + + free (list); + list = NULL; + } + + /* Build list of modes with the requested bpp */ + for (i = num_modes-1; i >= 0; i--) { + + CFDictionaryRef onemode = CFArrayGetValueAtIndex (mode_list, i); + CFNumberRef number; + int bpp; + + number = CFDictionaryGetValue (onemode, kCGDisplayBitsPerPixel); + CFNumberGetValue (number, kCFNumberSInt32Type, &bpp); + + if (bpp == format->BitsPerPixel) { + + int intvalue; + SDL_Rect *rect; + int lastwidth = 0, lastheight = 0, width, height; + + number = CFDictionaryGetValue (onemode, kCGDisplayWidth); + CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue); + width = (Uint16) intvalue; + + number = CFDictionaryGetValue (onemode, kCGDisplayHeight); + CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue); + height = (Uint16) intvalue; + + /* We'll get a lot of modes with the same size, so ignore them */ + if ( width != lastwidth && height != lastheight ) { + + lastwidth = width; + lastheight = height; + + list_size++; + + if ( list == NULL) + list = (SDL_Rect**) malloc (sizeof(*list) * list_size+1); + else + list = (SDL_Rect**) realloc (list, sizeof(*list) * list_size+1); + + rect = (SDL_Rect*) malloc (sizeof(**list)); + + if (list == NULL || rect == NULL) + SDL_OutOfMemory (); + + rect->w = width; + rect->h = height; + + list[list_size-1] = rect; + list[list_size] = NULL; + } + } + } + + return list; +} + +static void QZ_UnsetVideoMode (_THIS) { + + if ( mode_flags & SDL_OPENGL ) + QZ_TearDownOpenGL (this); + + /* Reset values that may change between switches */ + this->info.blit_fill = 0; + this->FillHWRect = NULL; + this->UpdateRects = NULL; + + /* Restore gamma settings */ + CGDisplayRestoreColorSyncSettings (); + + /* Restore original screen resolution */ + if ( mode_flags & SDL_FULLSCREEN ) { + CGDisplaySwitchToMode (display_id, save_mode); + CGDisplayRelease (display_id); + } + /* Release window mode data structures */ + else { + if ( (mode_flags & SDL_OPENGL) == 0 ) { + UnlockPortBits ( [ windowView qdPort ] ); + [ windowView release ]; + } + [ window setContentView:nil ]; + [ window close ]; + [ window release ]; + } + + /* Ensure the cursor will be visible and working when we quit */ + CGDisplayShowCursor (display_id); + CGAssociateMouseAndMouseCursorPosition (1); +} + +static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int width, + int height, int bpp, Uint32 flags) { + int exact_match; + + /* See if requested mode exists */ + mode = CGDisplayBestModeForParameters (display_id, bpp, width, + height, &exact_match); + + /* Require an exact match to the requested mode */ + if ( ! exact_match ) { + sprintf (QZ_Error, "Failed to find display resolution: %dx%dx%d", width, height, bpp); + SDL_SetError (QZ_Error); + goto ERR_NO_MATCH; + } + + /* Put up the blanking window (a window above all other windows) */ + if ( CGDisplayNoErr != CGDisplayCapture (display_id) ) { + SDL_SetError ("Failed capturing display"); + goto ERR_NO_CAPTURE; + } + + /* Do the physical switch */ + if ( CGDisplayNoErr != CGDisplaySwitchToMode (display_id, mode) ) { + SDL_SetError ("Failed switching display resolution"); + goto ERR_NO_SWITCH; + } + + /* None of these methods seem to fix the fullscreen artifacts bug(s) */ +#if USE_GDHANDLE + SetGDevice(GetMainDevice()); + current->pitch = (**(** GetMainDevice() ).gdPMap).rowBytes & 0x3FFF; + current->pixels = (**(** GetMainDevice() ).gdPMap).baseAddr; +#elif USE_CREATEPORT + device_port = CreateNewPortForCGDisplayID((Uint32*)display_id); + SetPort (device_port); + LockPortBits ( device_port ); + current->pixels = GetPixBaseAddr ( GetPortPixMap ( device_port ) ); + current->pitch = GetPixRowBytes ( GetPortPixMap ( device_port ) ); + UnlockPortBits ( device_port ); +#else + current->pixels = (Uint32*) CGDisplayBaseAddress (display_id); + current->pitch = CGDisplayBytesPerRow (display_id); +#endif + + current->w = width; + current->h = height; + current->flags |= SDL_FULLSCREEN; + current->flags |= SDL_HWSURFACE; + + this->UpdateRects = QZ_DirectUpdate; + + /* Setup some mode-dependant info */ + if ( CGSDisplayCanHWFill (display_id) ) { + this->info.blit_fill = 1; + this->FillHWRect = QZ_FillHWRect; + } + + if ( CGDisplayCanSetPalette (display_id) ) + current->flags |= SDL_HWPALETTE; + + /* Setup OpenGL for a fullscreen context */ + if (flags & SDL_OPENGL) { + + CGLError err; + CGLContextObj ctx; + + if ( ! QZ_SetupOpenGL (this, bpp, flags) ) { + return NULL; + } + + ctx = [ gl_context cglContext ]; + err = CGLSetFullScreen (ctx); + + if (err) { + sprintf (QZ_Error, "Error setting OpenGL fullscreen: %s", CGLErrorString(err)); + SDL_SetError (QZ_Error); + goto ERR_NO_GL; + } + + [ gl_context makeCurrentContext]; + + current->flags |= SDL_OPENGL; + } + + /* If we don't hide menu bar, it will get events and interrupt the program */ + HideMenuBar (); + + /* Save the flags to ensure correct tear-down */ + mode_flags = current->flags; + + return current; + + /* Since the blanking window covers *all* windows (even force quit) correct recovery is crutial */ + ERR_NO_GL: CGDisplaySwitchToMode (display_id, save_mode); + ERR_NO_SWITCH: CGDisplayRelease (display_id); + ERR_NO_CAPTURE: + ERR_NO_MATCH: return NULL; +} + +static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width, + int height, int bpp, Uint32 flags) { + NSRect rect; + rect = NSMakeRect (0, 0, width, height); + + /* Manually create a window, avoids having a nib file resource */ + window = [ [ SDL_QuartzWindow alloc ] initWithContentRect:rect + styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask) + backing: //NSBackingStoreBuffered + NSBackingStoreRetained + defer:NO ]; + + if (window == nil) { + SDL_SetError ("Could not create the Cocoa window"); + return NULL; + } + + current->w = width; + current->h = height; + + [ window setTitle:windowTitle ]; + [ window setAcceptsMouseMovedEvents:YES ]; + [ window center ]; + + /* For OpenGL, we set the content view to a NSOpenGLView */ + if ( flags & SDL_OPENGL ) { + + if ( ! QZ_SetupOpenGL (this, bpp, flags) ) { + return NULL; + } + + [ gl_context setView: [ window contentView ] ]; + [ gl_context makeCurrentContext]; + [ window orderFront:nil ]; + current->flags |= SDL_OPENGL; + } + /* For 2D, we set the content view to a NSQuickDrawView */ + else { + + windowView = [ [ NSQuickDrawView alloc ] init ]; + [ window setContentView:windowView ]; + [ window orderFront:nil ]; + + LockPortBits ( [ windowView qdPort ] ); + current->pixels = GetPixBaseAddr ( GetPortPixMap ( [ windowView qdPort ] ) ); + current->pitch = GetPixRowBytes ( GetPortPixMap ( [ windowView qdPort ] ) ); + + /* Offset 22 pixels down to fill the full content region */ + current->pixels += 22 * current->pitch; + + current->flags |= SDL_SWSURFACE; + current->flags |= SDL_PREALLOC; + + this->UpdateRects = QZ_UpdateRects; + } + return current; +} + +static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current, int width, + int height, int bpp, Uint32 flags) { + + if (SDL_VideoSurface != NULL) + QZ_UnsetVideoMode (this); + + current->flags = 0; + + /* Setup full screen video */ + if ( flags & SDL_FULLSCREEN ) { + current = QZ_SetVideoFullScreen (this, current, width, height, bpp, flags ); + if (current == NULL) + return NULL; + } + /* Setup windowed video */ + else { + /* Force bpp to the device's bpp */ + bpp = current->format->BitsPerPixel; + current = QZ_SetVideoWindowed (this, current, width, height, bpp, flags); + if (current == NULL) + return NULL; + } + + /* 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 = 0x3e0; + bmask = 0x1f; + break; + case 24: + SDL_SetError ("24bpp is not available"); + return NULL; + case 32: /* (8)-8-8-8 ARGB */ + amask = 0xFF000000; + rmask = 0x00FF0000; + gmask = 0x0000FF00; + bmask = 0x000000FF; + break; + } + + if ( ! SDL_ReallocFormat (current, bpp, + rmask, gmask, bmask, amask ) ) { + SDL_SetError ("Couldn't reallocate pixel format"); + return NULL; + } + } + + /* Warp mouse to origin in order to get passive mouse motion events started correctly */ + QZ_PrivateWarpCursor (this, current->flags & SDL_FULLSCREEN, height, 0, 0); + + return current; +} + +static int QZ_ToggleFullScreen (_THIS, int on) { + return -1; +} + +static int QZ_SetColors (_THIS, int first_color, int num_colors, + SDL_Color *colors) { + + 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); + } + + if ( CGDisplayNoErr != CGDisplaySetPalette (display_id, palette) ) + return 0; + + return 1; +} + +static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects) { + #pragma unused(this,num_rects,rects) +} + +static void QZ_UpdateRects (_THIS, int numRects, SDL_Rect *rects) { + + if (SDL_VideoSurface->flags & SDL_OPENGLBLIT) { + QZ_GL_SwapBuffers (this); + } + else { + int i; + RgnHandle dirty = NewRgn (); + RgnHandle temp = NewRgn (); + + SetEmptyRgn (dirty); + + /* Build the region of dirty rectangles */ + for (i = 0; i < numRects; i++) { + + MacSetRectRgn (temp, rects[i].x, rects[i].y, + rects[i].x + rects[i].w, rects[i].y + rects[i].h); + MacUnionRgn (dirty, temp, dirty); + } + + /* Flush the dirty region */ + QDFlushPortBuffer ( [ windowView qdPort ], dirty ); + DisposeRgn (dirty); + DisposeRgn (temp); + } +} + +static void QZ_VideoQuit (_THIS) { + + QZ_UnsetVideoMode (this); + CGPaletteRelease (palette); +} + +static int QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color) { + + CGSDisplayHWFill (display_id, rect->x, rect->y, rect->w, rect->h, color); + return 0; +} + +static int QZ_LockHWSurface(_THIS, SDL_Surface *surface) { + + return 1; +} + +static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface) { +} + +static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface) { +} + +/* +int QZ_FlipHWSurface (_THIS, SDL_Surface *surface) { + return 0; +} +*/ + +/* Gamma functions */ +static int QZ_SetGamma (_THIS, float red, float green, float blue) { + + const CGGammaValue min = 0.0, max = 1.0; + + if ( CGDisplayNoErr != CGSetDisplayTransferByFormula + (display_id, min, max, red, min, max, green, min, max, blue) ) + return -1; + + return 0; +} + +static 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 -1; + + return 0; +} + +static int QZ_SetGammaRamp (_THIS, Uint16 *ramp) { + + const CGTableCount 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 -1; + + return 0; +} + +static int QZ_GetGammaRamp (_THIS, Uint16 *ramp) { + + const CGTableCount tableSize = 255; + CGGammaValue redTable[tableSize]; + CGGammaValue greenTable[tableSize]; + CGGammaValue blueTable[tableSize]; + CGTableCount 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; +} + +/* OpenGL helper functions */ +static int QZ_SetupOpenGL (_THIS, int bpp, Uint32 flags) { + + NSOpenGLPixelFormatAttribute attr[32]; + NSOpenGLPixelFormat *fmt; + int i = 0; + int colorBits = bpp; + + if ( flags & SDL_FULLSCREEN ) { + + attr[i++] = NSOpenGLPFAFullScreen; + } + /* In windowed mode, the OpenGL pixel depth must match device pixel depth */ + else if ( colorBits != device_bpp ) { + + colorBits = device_bpp; + } + + attr[i++] = NSOpenGLPFAColorSize; + attr[i++] = colorBits; + + attr[i++] = NSOpenGLPFADepthSize; + attr[i++] = this->gl_config.depth_size; + + if ( this->gl_config.double_buffer ) { + attr[i++] = NSOpenGLPFADoubleBuffer; + } + + if ( this->gl_config.stencil_size != 0 ) { + attr[i++] = NSOpenGLPFAStencilSize; + attr[i++] = this->gl_config.stencil_size; + } + + attr[i++] = NSOpenGLPFAScreenMask; + attr[i++] = CGDisplayIDToOpenGLDisplayMask (display_id); + attr[i] = 0; + + fmt = [ [ NSOpenGLPixelFormat alloc ] initWithAttributes:attr ]; + if (fmt == nil) { + SDL_SetError ("Failed creating OpenGL pixel format"); + return 0; + } + + gl_context = [ [ NSOpenGLContext alloc ] initWithFormat:fmt + shareContext:nil]; + + if (gl_context == nil) { + SDL_SetError ("Failed creating OpenGL context"); + return 0; + } + + /* Convince SDL that the GL "driver" is loaded */ + this->gl_config.driver_loaded = 1; + + [ fmt release ]; + + return 1; +} + +static void QZ_TearDownOpenGL (_THIS) { + + [ NSOpenGLContext clearCurrentContext ]; + [ gl_context clearDrawable ]; + [ gl_context release ]; +} + +/* SDL OpenGL functions */ + +static int QZ_GL_LoadLibrary (_THIS, const char *location) { + return 1; +} + +static void* QZ_GL_GetProcAddress (_THIS, const char *proc) { + + /* We may want to cache the bundleRef at some point */ + CFBundleRef bundle; + CFURLRef bundleURL = CFURLCreateWithFileSystemPath (kCFAllocatorDefault, + CFSTR("/System/Library/Frameworks/OpenGL.framework"), kCFURLPOSIXPathStyle, true); + + CFStringRef functionName = CFStringCreateWithCString + (kCFAllocatorDefault, proc, kCFStringEncodingASCII); + + void *function; + + bundle = CFBundleCreate (kCFAllocatorDefault, bundleURL); + assert (bundle != NULL); + + function = CFBundleGetFunctionPointerForName (bundle, functionName); + + CFRelease ( bundleURL ); + CFRelease ( functionName ); + CFRelease ( bundle ); + + return function; +} + +static int QZ_GL_GetAttribute (_THIS, SDL_GLattr attrib, int* value) { + +/* + CGLContextRef ctx = [ gl_context cglContext ]; + CGLContextParameter param; + + switch (attrib) { + case SDL_GL_RED_SIZE: param = CGLContextParameter break; + + } + + CGLGetParameter (ctx, param, (long*)value); +*/ + *value = -1; + return -1; +} + +static int QZ_GL_MakeCurrent (_THIS) { + [ gl_context makeCurrentContext ]; + return 0; +} + +static void QZ_GL_SwapBuffers (_THIS) { + [ gl_context flushBuffer ]; +}