src/video/quartz/SDL_QuartzVideo.m
author Sam Lantinga <slouken@libsdl.org>
Sun, 23 Sep 2001 22:10:03 +0000
changeset 197 ef78524e5f59
parent 172 37e3ca9254c7
child 252 e8157fcb3114
permissions -rw-r--r--
*** empty log message ***

/*
    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 char QZ_Error[255]; /* Global error buffer to temporarily store more informative error messages */

/* Include files into one compile unit...break apart eventually */
#include "SDL_QuartzWM.m"
#include "SDL_QuartzEvents.m"
#include "SDL_QuartzWindow.m"

/* 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;
  
  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;

      for (i = 0; list[i] != NULL; i++)
	free (list[i]);

      free (list);
      list = NULL;
    }
    
    /* Build list of modes with the requested bpp */
    for (i = 0; i < num_modes; i++) {
   
        CFDictionaryRef onemode;
        CFNumberRef     number;
	int bpp;
	
        onemode = CFArrayGetValueAtIndex (mode_list, i); 
	number = CFDictionaryGetValue (onemode, kCGDisplayBitsPerPixel);
	CFNumberGetValue (number, kCFNumberSInt32Type, &bpp);

	if (bpp == format->BitsPerPixel) {
	  
	  int intvalue;
          int hasMode;
          int width, height;
          
	  number = CFDictionaryGetValue (onemode, kCGDisplayWidth);
	  CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
	  width = (Uint16) intvalue;
	  
	  number = CFDictionaryGetValue (onemode, kCGDisplayHeight);
	  CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
	  height = (Uint16) intvalue;
          
          /* Check if mode is already in the list */
          {
            int i;
            hasMode = SDL_FALSE;
            for (i = 0; i < list_size; i++) {
                if (list[i]->w == width && 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 ( 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;
        }
      }
    }
    
    /* 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 = list[j]->w * list[j]->h;
                area2 = list[j+1]->w * list[j+1]->h;
                
                if (area1 < area2) {
                    SDL_Rect *tmp = list[j];
                    list[j] = list[j+1];
                    list[j+1] = tmp;
                }
            }
        }
    }
    return list;
}

static void QZ_UnsetVideoMode (_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 ) {
        if (mode_flags & SDL_OPENGL)
            CGLSetFullScreen(NULL);
            
        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  ];
        }
        [ qz_window setContentView:nil ];
        [ qz_window setDelegate:nil ];
        [ qz_window close ];
    }
    
    /* Set pixels to null (so other code doesn't try to free it) */
    if (this->screen != NULL)
        this->screen->pixels = NULL;
        
    /* Release the OpenGL context */
    if ( mode_flags & SDL_OPENGL )
        QZ_TearDownOpenGL (this);
        
    /* Ensure the cursor will be visible and working when we quit */
    CGDisplayShowCursor (display_id);
    CGAssociateMouseAndMouseCursorPosition (1);
    
    /* Signal successful teardown */
    video_set = SDL_FALSE;
}

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->flags = 0;
    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) {    
    unsigned int style;
    NSRect rect;    
    rect = NSMakeRect (0, 0, width, height);

#if 1 // FIXME - the resize button doesn't show?  Also need resize events...
    flags &= ~SDL_RESIZABLE;
#endif
    /* Set the window style based on input flags */
    if ( flags & SDL_NOFRAME ) {
        style = NSBorderlessWindowMask;
    } else {
        style = NSTitledWindowMask;
        style |= (NSMiniaturizableWindowMask | NSClosableWindowMask);
        if ( flags & SDL_RESIZABLE )
            style |= NSResizableWindowMask;
    }

    /* Manually create a window, avoids having a nib file resource */
    qz_window = [ [ SDL_QuartzWindow alloc ] initWithContentRect:rect 
        styleMask:style backing:NSBackingStoreBuffered defer:NO ];
    if (qz_window == nil) {
        SDL_SetError ("Could not create the Cocoa window");
        return NULL;
    }
    
    current->flags = 0;
    current->w = width;
    current->h = height;
    
    [ qz_window setReleasedWhenClosed:YES ];
    QZ_SetCaption(this, this->wm_title, this->wm_icon);
    [ qz_window setAcceptsMouseMovedEvents:YES ];
    [ qz_window setViewsNeedDisplay:NO ];
    [ qz_window center ];
    [ qz_window setDelegate:
        [ [ [ SDL_QuartzWindowDelegate alloc ] init ] autorelease ] ];
    
    /* 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: [ qz_window contentView ] ];
        [ gl_context makeCurrentContext];
        [ qz_window makeKeyAndOrderFront:nil ];
        current->flags |= SDL_OPENGL;
    }
    /* For 2D, we set the content view to a NSQuickDrawView */
    else {
    
        windowView = [ [ NSQuickDrawView alloc ] init ];
        [ qz_window setContentView:windowView ];
        [ qz_window makeKeyAndOrderFront:nil ];    
        
        LockPortBits ( [ windowView qdPort ] );
        current->pixels = GetPixBaseAddr ( GetPortPixMap ( [ windowView qdPort ] ) );
        current->pitch  = GetPixRowBytes ( GetPortPixMap ( [ windowView qdPort ] ) );
            
        current->flags |= SDL_SWSURFACE;
        current->flags |= SDL_PREALLOC;
	if ( flags & SDL_NOFRAME )
        	current->flags |= SDL_NOFRAME;
	if ( flags & SDL_RESIZABLE )
        	current->flags |= SDL_RESIZABLE;

        /* Offset 22 pixels down to fill the full content region */
	if ( ! (current->flags & SDL_NOFRAME) ) {
        	current->pixels += 22 * current->pitch;
	}

        this->UpdateRects = QZ_UpdateRects;
    }
    return current;
}

static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current, int width, 
				     int height, int bpp, Uint32 flags) {

    if (video_set == SDL_TRUE)
        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 = 0x03E0;
                bmask = 0x001F;
                break;
            case 24:
                SDL_SetError ("24bpp is not available");
                return NULL;
            case 32:   /* (8)-8-8-8 ARGB */
                amask = 0x00000000;
                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);
    
    /* Signal successful completion */
    video_set = SDL_TRUE;
    
    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) {
    this->gl_config.driver_loaded = 1;
    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 ];
}