src/video/quartz/SDL_QuartzVideo.m
author Sam Lantinga
Sat, 10 Oct 2009 15:10:06 +0000
branchSDL-1.2
changeset 4319 edefeb52a627
parent 4317 719faf118c38
child 4340 0deabd35275b
permissions -rw-r--r--
Added support for SDL_VIDEO_FULLSCREEN_DISPLAY, but mouse events need to be fixed up.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009  Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #include "SDL_QuartzVideo.h"
    25 #include "SDL_QuartzWindow.h"
    26 
    27 #ifdef __powerpc__  /* I'm gambling they fixed this by 10.4. --ryan. */
    28 /*
    29     Add methods to get at private members of NSScreen. 
    30     Since there is a bug in Apple's screen switching code
    31     that does not update this variable when switching
    32     to fullscreen, we'll set it manually (but only for the
    33     main screen).
    34 */
    35 @interface NSScreen (NSScreenAccess)
    36 - (void) setFrame:(NSRect)frame;
    37 @end
    38 
    39 @implementation NSScreen (NSScreenAccess)
    40 - (void) setFrame:(NSRect)frame;
    41 {
    42     _frame = frame;
    43 }
    44 @end
    45 static inline void QZ_SetFrame(NSScreen *nsscreen, NSRect frame)
    46 {
    47     [nsscreen setFrame:frame];
    48 }
    49 #else
    50 static inline void QZ_SetFrame(NSScreen *nsscreen, NSRect frame)
    51 {
    52 }
    53 #endif
    54 
    55 @interface SDLTranslatorResponder : NSTextView
    56 {
    57 }
    58 - (void) doCommandBySelector:(SEL)myselector;
    59 @end
    60 
    61 @implementation SDLTranslatorResponder
    62 - (void) doCommandBySelector:(SEL) myselector {}
    63 @end
    64 
    65 /* absent in 10.3.9.  */
    66 CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
    67 
    68 /* Bootstrap functions */
    69 static int              QZ_Available ();
    70 static SDL_VideoDevice* QZ_CreateDevice (int device_index);
    71 static void             QZ_DeleteDevice (SDL_VideoDevice *device);
    72 
    73 /* Initialization, Query, Setup, and Redrawing functions */
    74 static int          QZ_VideoInit        (_THIS, SDL_PixelFormat *video_format);
    75 
    76 static SDL_Rect**   QZ_ListModes        (_THIS, SDL_PixelFormat *format,
    77                                          Uint32 flags);
    78 static void         QZ_UnsetVideoMode   (_THIS, BOOL to_desktop);
    79 
    80 static SDL_Surface* QZ_SetVideoMode     (_THIS, SDL_Surface *current,
    81                                          int width, int height, int bpp,
    82                                          Uint32 flags);
    83 static int          QZ_ToggleFullScreen (_THIS, int on);
    84 static int          QZ_SetColors        (_THIS, int first_color,
    85                                          int num_colors, SDL_Color *colors);
    86 
    87 static int          QZ_LockDoubleBuffer   (_THIS, SDL_Surface *surface);
    88 static void         QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface);
    89 static int          QZ_ThreadFlip         (_THIS);
    90 static int          QZ_FlipDoubleBuffer   (_THIS, SDL_Surface *surface);
    91 static void         QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects);
    92 
    93 static void         QZ_DirectUpdate     (_THIS, int num_rects, SDL_Rect *rects);
    94 static void         QZ_UpdateRects      (_THIS, int num_rects, SDL_Rect *rects);
    95 static void         QZ_VideoQuit        (_THIS);
    96 
    97 /* Hardware surface functions (for fullscreen mode only) */
    98 #if 0 /* Not used (apparently, it's really slow) */
    99 static int  QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color);
   100 #endif
   101 static int  QZ_LockHWSurface(_THIS, SDL_Surface *surface);
   102 static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface);
   103 static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface);
   104 static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface);
   105 /* static int  QZ_FlipHWSurface (_THIS, SDL_Surface *surface); */
   106 
   107 /* Bootstrap binding, enables entry point into the driver */
   108 VideoBootStrap QZ_bootstrap = {
   109     "Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice
   110 };
   111 
   112 
   113 /* Bootstrap functions */
   114 static int QZ_Available ()
   115 {
   116     return 1;
   117 }
   118 
   119 static SDL_VideoDevice* QZ_CreateDevice (int device_index)
   120 {
   121 #pragma unused (device_index)
   122 
   123     SDL_VideoDevice *device;
   124     SDL_PrivateVideoData *hidden;
   125 
   126     device = (SDL_VideoDevice*) SDL_malloc (sizeof (*device) );
   127     hidden = (SDL_PrivateVideoData*) SDL_malloc (sizeof (*hidden) );
   128 
   129     if (device == NULL || hidden == NULL)
   130         SDL_OutOfMemory ();
   131 
   132     SDL_memset (device, 0, sizeof (*device) );
   133     SDL_memset (hidden, 0, sizeof (*hidden) );
   134 
   135     device->hidden = hidden;
   136 
   137     device->VideoInit        = QZ_VideoInit;
   138     device->ListModes        = QZ_ListModes;
   139     device->SetVideoMode     = QZ_SetVideoMode;
   140     device->ToggleFullScreen = QZ_ToggleFullScreen;
   141     device->UpdateMouse      = QZ_UpdateMouse;
   142     device->SetColors        = QZ_SetColors;
   143     /* device->UpdateRects      = QZ_UpdateRects; this is determined by SetVideoMode() */
   144     device->VideoQuit        = QZ_VideoQuit;
   145 
   146     device->LockHWSurface   = QZ_LockHWSurface;
   147     device->UnlockHWSurface = QZ_UnlockHWSurface;
   148     device->AllocHWSurface   = QZ_AllocHWSurface;
   149     device->FreeHWSurface   = QZ_FreeHWSurface;
   150     /* device->FlipHWSurface   = QZ_FlipHWSurface */;
   151 
   152     device->SetGamma     = QZ_SetGamma;
   153     device->GetGamma     = QZ_GetGamma;
   154     device->SetGammaRamp = QZ_SetGammaRamp;
   155     device->GetGammaRamp = QZ_GetGammaRamp;
   156 
   157     device->GL_GetProcAddress = QZ_GL_GetProcAddress;
   158     device->GL_GetAttribute   = QZ_GL_GetAttribute;
   159     device->GL_MakeCurrent    = QZ_GL_MakeCurrent;
   160     device->GL_SwapBuffers    = QZ_GL_SwapBuffers;
   161     device->GL_LoadLibrary    = QZ_GL_LoadLibrary;
   162 
   163     device->FreeWMCursor   = QZ_FreeWMCursor;
   164     device->CreateWMCursor = QZ_CreateWMCursor;
   165     device->ShowWMCursor   = QZ_ShowWMCursor;
   166     device->WarpWMCursor   = QZ_WarpWMCursor;
   167     device->MoveWMCursor   = QZ_MoveWMCursor;
   168     device->CheckMouseMode = QZ_CheckMouseMode;
   169     device->InitOSKeymap   = QZ_InitOSKeymap;
   170     device->PumpEvents     = QZ_PumpEvents;
   171 
   172     device->SetCaption    = QZ_SetCaption;
   173     device->SetIcon       = QZ_SetIcon;
   174     device->IconifyWindow = QZ_IconifyWindow;
   175     /*device->GetWMInfo     = QZ_GetWMInfo;*/
   176     device->GrabInput     = QZ_GrabInput;
   177 
   178     /*
   179      * This is a big hassle, needing QuickDraw and QuickTime on older
   180      *  systems, and god knows what on 10.6, so we immediately fail here,
   181      *  which causes SDL to make an RGB surface and manage the YUV overlay
   182      *  in software. Sorry. Use SDL 1.3 if you want YUV rendering in a pixel
   183      *  shader.  :)
   184      */
   185     /*device->CreateYUVOverlay = QZ_CreateYUVOverlay;*/
   186 
   187     device->free             = QZ_DeleteDevice;
   188 
   189     return device;
   190 }
   191 
   192 static void QZ_DeleteDevice (SDL_VideoDevice *device)
   193 {
   194     SDL_free (device->hidden);
   195     SDL_free (device);
   196 }
   197 
   198 static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format)
   199 {
   200     NSRect r = NSMakeRect(0.0, 0.0, 0.0, 0.0);
   201     const char *env = NULL;
   202 	
   203     /* Initialize the video settings; this data persists between mode switches */
   204     display_id = kCGDirectMainDisplay;
   205 
   206 #if 0 /* The mouse event code needs to take this into account... */
   207     env = getenv("SDL_VIDEO_FULLSCREEN_DISPLAY");
   208     if ( env ) {
   209         int monitor = SDL_atoi(env);
   210     	CGDirectDisplayID activeDspys [3];
   211     	CGDisplayCount dspyCnt;
   212     	CGGetActiveDisplayList (3, activeDspys, &dspyCnt);
   213         if ( monitor >= 0 && monitor < dspyCnt ) {
   214     	    display_id = activeDspys[monitor];
   215         }
   216     }
   217 #endif
   218 
   219     save_mode  = CGDisplayCurrentMode    (display_id);
   220     mode_list  = CGDisplayAvailableModes (display_id);
   221     palette    = CGPaletteCreateDefaultColorPalette ();
   222 
   223     /* Allow environment override of screensaver disable. */
   224     env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER");
   225     if ( env ) {
   226         allow_screensaver = SDL_atoi(env);
   227     } else {
   228 #ifdef SDL_VIDEO_DISABLE_SCREENSAVER
   229         allow_screensaver = 0;
   230 #else
   231         allow_screensaver = 1;
   232 #endif
   233     }
   234 
   235     /* Gather some information that is useful to know about the display */
   236     CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayBitsPerPixel),
   237                       kCFNumberSInt32Type, &device_bpp);
   238 
   239     CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayWidth),
   240                       kCFNumberSInt32Type, &device_width);
   241 
   242     CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayHeight),
   243                       kCFNumberSInt32Type, &device_height);
   244 
   245     /* Determine the current screen size */
   246     this->info.current_w = device_width;
   247     this->info.current_h = device_height;
   248 
   249     /* Determine the default screen depth */
   250     video_format->BitsPerPixel = device_bpp;
   251 
   252     /* Set misc globals */
   253     current_grab_mode = SDL_GRAB_OFF;
   254     cursor_should_be_visible    = YES;
   255     cursor_visible              = YES;
   256     current_mods = 0;
   257     field_edit =  [[SDLTranslatorResponder alloc] initWithFrame:r];
   258     
   259     if ( Gestalt(gestaltSystemVersion, &system_version) != noErr )
   260         system_version = 0;
   261     
   262     /* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */
   263     QZ_RegisterForSleepNotifications (this);
   264     
   265     /* Fill in some window manager capabilities */
   266     this->info.wm_available = 1;
   267 
   268     return 0;
   269 }
   270 
   271 static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags)
   272 {
   273     CFIndex num_modes;
   274     CFIndex i;
   275 
   276     int list_size = 0;
   277 
   278     /* Any windowed mode is acceptable */
   279     if ( (flags & SDL_FULLSCREEN) == 0 )
   280         return (SDL_Rect**)-1;
   281 
   282     /* Free memory from previous call, if any */
   283     if ( client_mode_list != NULL ) {
   284 
   285         int i;
   286 
   287         for (i = 0; client_mode_list[i] != NULL; i++)
   288             SDL_free (client_mode_list[i]);
   289 
   290         SDL_free (client_mode_list);
   291         client_mode_list = NULL;
   292     }
   293 
   294     num_modes = CFArrayGetCount (mode_list);
   295 
   296     /* Build list of modes with the requested bpp */
   297     for (i = 0; i < num_modes; i++) {
   298 
   299         CFDictionaryRef onemode;
   300         CFNumberRef     number;
   301         int bpp;
   302 
   303         onemode = CFArrayGetValueAtIndex (mode_list, i);
   304         number = CFDictionaryGetValue (onemode, kCGDisplayBitsPerPixel);
   305         CFNumberGetValue (number, kCFNumberSInt32Type, &bpp);
   306 
   307         if (bpp == format->BitsPerPixel) {
   308 
   309             int intvalue;
   310             int hasMode;
   311             int width, height;
   312 
   313             number = CFDictionaryGetValue (onemode, kCGDisplayWidth);
   314             CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
   315             width = (Uint16) intvalue;
   316 
   317             number = CFDictionaryGetValue (onemode, kCGDisplayHeight);
   318             CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
   319             height = (Uint16) intvalue;
   320 
   321             /* Check if mode is already in the list */
   322             {
   323                 int i;
   324                 hasMode = SDL_FALSE;
   325                 for (i = 0; i < list_size; i++) {
   326                     if (client_mode_list[i]->w == width && 
   327                         client_mode_list[i]->h == height) {
   328                         hasMode = SDL_TRUE;
   329                         break;
   330                     }
   331                 }
   332             }
   333 
   334             /* Grow the list and add mode to the list */
   335             if ( ! hasMode ) {
   336 
   337                 SDL_Rect *rect;
   338 
   339                 list_size++;
   340 
   341                 if (client_mode_list == NULL)
   342                     client_mode_list = (SDL_Rect**) 
   343                         SDL_malloc (sizeof(*client_mode_list) * (list_size+1) );
   344                 else
   345                     client_mode_list = (SDL_Rect**) 
   346                         SDL_realloc (client_mode_list, sizeof(*client_mode_list) * (list_size+1));
   347 
   348                 rect = (SDL_Rect*) SDL_malloc (sizeof(**client_mode_list));
   349 
   350                 if (client_mode_list == NULL || rect == NULL) {
   351                     SDL_OutOfMemory ();
   352                     return NULL;
   353                 }
   354 
   355                 rect->x = rect->y = 0;
   356                 rect->w = width;
   357                 rect->h = height;
   358 
   359                 client_mode_list[list_size-1] = rect;
   360                 client_mode_list[list_size]   = NULL;
   361             }
   362         }
   363     }
   364 
   365     /* Sort list largest to smallest (by area) */
   366     {
   367         int i, j;
   368         for (i = 0; i < list_size; i++) {
   369             for (j = 0; j < list_size-1; j++) {
   370 
   371                 int area1, area2;
   372                 area1 = client_mode_list[j]->w * client_mode_list[j]->h;
   373                 area2 = client_mode_list[j+1]->w * client_mode_list[j+1]->h;
   374 
   375                 if (area1 < area2) {
   376                     SDL_Rect *tmp = client_mode_list[j];
   377                     client_mode_list[j] = client_mode_list[j+1];
   378                     client_mode_list[j+1] = tmp;
   379                 }
   380             }
   381         }
   382     }
   383     return client_mode_list;
   384 }
   385 
   386 static SDL_bool QZ_WindowPosition(_THIS, int *x, int *y)
   387 {
   388     const char *window = getenv("SDL_VIDEO_WINDOW_POS");
   389     if ( window ) {
   390         if ( sscanf(window, "%d,%d", x, y) == 2 ) {
   391             return SDL_TRUE;
   392         }
   393     }
   394     return SDL_FALSE;
   395 }
   396 
   397 static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop)
   398 {
   399     /* Reset values that may change between switches */
   400     this->info.blit_fill  = 0;
   401     this->FillHWRect      = NULL;
   402     this->UpdateRects     = NULL;
   403     this->LockHWSurface   = NULL;
   404     this->UnlockHWSurface = NULL;
   405     
   406     if (cg_context) {
   407         CGContextFlush (cg_context);
   408         CGContextRelease (cg_context);
   409         cg_context = nil;
   410     }
   411     
   412     /* Release fullscreen resources */
   413     if ( mode_flags & SDL_FULLSCREEN ) {
   414 
   415         NSRect screen_rect;
   416         
   417         /*  Release double buffer stuff */
   418         if ( mode_flags & SDL_DOUBLEBUF) {
   419             quit_thread = YES;
   420             SDL_SemPost (sem1);
   421             SDL_WaitThread (thread, NULL);
   422             SDL_DestroySemaphore (sem1);
   423             SDL_DestroySemaphore (sem2);
   424             SDL_free (sw_buffers[0]);
   425         }
   426         
   427         /* If we still have a valid window, close it. */
   428         if ( qz_window ) {
   429             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 */
   430             [ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */
   431             qz_window = nil;
   432             window_view = nil;
   433         }
   434         /* 
   435             Release the OpenGL context
   436             Do this first to avoid trash on the display before fade
   437         */
   438         if ( mode_flags & SDL_OPENGL ) {
   439         
   440             QZ_TearDownOpenGL (this);
   441             CGLSetFullScreen (NULL);
   442         }
   443         if (to_desktop) {
   444             ShowMenuBar ();
   445             /* Restore original screen resolution/bpp */
   446             CGDisplaySwitchToMode (display_id, save_mode);
   447             CGReleaseAllDisplays ();
   448             /* 
   449                 Reset the main screen's rectangle
   450                 See comment in QZ_SetVideoFullscreen for why we do this
   451             */
   452             screen_rect = NSMakeRect(0,0,device_width,device_height);
   453             QZ_SetFrame([ NSScreen mainScreen ], screen_rect);
   454         }
   455     }
   456     /* Release window mode resources */
   457     else {
   458         id delegate = [ qz_window delegate ];
   459         [ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */
   460         if (delegate != nil) [ delegate release ];
   461         qz_window = nil;
   462         window_view = nil;
   463 
   464         /* Release the OpenGL context */
   465         if ( mode_flags & SDL_OPENGL )
   466             QZ_TearDownOpenGL (this);
   467     }
   468 
   469     /* Signal successful teardown */
   470     video_set = SDL_FALSE;
   471 }
   472 
   473 static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int width,
   474                                            int height, int bpp, Uint32 flags)
   475 {
   476     boolean_t exact_match = 0;
   477     NSRect screen_rect;
   478     CGError error;
   479     NSRect contentRect;
   480     CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
   481 
   482     /* Fade to black to hide resolution-switching flicker (and garbage
   483        that is displayed by a destroyed OpenGL context, if applicable) */
   484     if ( CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess ) {
   485         CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
   486     }
   487     
   488     /* Destroy any previous mode */
   489     if (video_set == SDL_TRUE)
   490         QZ_UnsetVideoMode (this, FALSE);
   491 
   492     /* Sorry, QuickDraw was ripped out. */
   493     if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) {
   494         SDL_SetError ("Embedded QuickDraw windows are no longer supported");
   495         goto ERR_NO_MATCH;
   496     }
   497 
   498     /* See if requested mode exists */
   499     mode = CGDisplayBestModeForParameters (display_id, bpp, width,
   500                                            height, &exact_match);
   501 
   502     /* Require an exact match to the requested mode */
   503     if ( ! exact_match ) {
   504         SDL_SetError ("Failed to find display resolution: %dx%dx%d", width, height, bpp);
   505         goto ERR_NO_MATCH;
   506     }
   507 
   508     /* Put up the blanking window (a window above all other windows) */
   509     if (getenv ("SDL_SINGLEDISPLAY"))
   510         error = CGDisplayCapture (display_id);
   511     else
   512         error = CGCaptureAllDisplays ();
   513         
   514     if ( CGDisplayNoErr != error ) {
   515         SDL_SetError ("Failed capturing display");
   516         goto ERR_NO_CAPTURE;
   517     }
   518 
   519     /* Do the physical switch */
   520     if ( CGDisplayNoErr != CGDisplaySwitchToMode (display_id, mode) ) {
   521         SDL_SetError ("Failed switching display resolution");
   522         goto ERR_NO_SWITCH;
   523     }
   524 
   525     current->pixels = (Uint32*) CGDisplayBaseAddress (display_id);
   526     current->pitch  = CGDisplayBytesPerRow (display_id);
   527 
   528     current->flags = 0;
   529     current->w = width;
   530     current->h = height;
   531     current->flags |= SDL_FULLSCREEN;
   532     current->flags |= SDL_HWSURFACE;
   533     current->flags |= SDL_PREALLOC;
   534     /* current->hwdata = (void *) CGDisplayGetDrawingContext (display_id); */
   535 
   536     this->UpdateRects     = QZ_DirectUpdate;
   537     this->LockHWSurface   = QZ_LockHWSurface;
   538     this->UnlockHWSurface = QZ_UnlockHWSurface;
   539 
   540     /* Setup double-buffer emulation */
   541     if ( flags & SDL_DOUBLEBUF ) {
   542         
   543         /*
   544             Setup a software backing store for reasonable results when
   545             double buffering is requested (since a single-buffered hardware
   546             surface looks hideous).
   547             
   548             The actual screen blit occurs in a separate thread to allow 
   549             other blitting while waiting on the VBL (and hence results in higher framerates).
   550         */
   551         this->LockHWSurface = NULL;
   552         this->UnlockHWSurface = NULL;
   553         this->UpdateRects = NULL;
   554         
   555         current->flags |= (SDL_HWSURFACE|SDL_DOUBLEBUF);
   556         this->UpdateRects = QZ_DoubleBufferUpdate;
   557         this->LockHWSurface = QZ_LockDoubleBuffer;
   558         this->UnlockHWSurface = QZ_UnlockDoubleBuffer;
   559         this->FlipHWSurface = QZ_FlipDoubleBuffer;
   560 
   561         current->pixels = SDL_malloc (current->pitch * current->h * 2);
   562         if (current->pixels == NULL) {
   563             SDL_OutOfMemory ();
   564             goto ERR_DOUBLEBUF;
   565         }
   566         
   567         sw_buffers[0] = current->pixels;
   568         sw_buffers[1] = (Uint8*)current->pixels + current->pitch * current->h;
   569         
   570         quit_thread = NO;
   571         sem1 = SDL_CreateSemaphore (0);
   572         sem2 = SDL_CreateSemaphore (1);
   573         thread = SDL_CreateThread ((int (*)(void *))QZ_ThreadFlip, this);
   574     }
   575 
   576     if ( CGDisplayCanSetPalette (display_id) )
   577         current->flags |= SDL_HWPALETTE;
   578 
   579     /* Check if we should recreate the window */
   580     if (qz_window == nil) {
   581         /* Manually create a window, avoids having a nib file resource */
   582         qz_window = [ [ SDL_QuartzWindow alloc ] 
   583             initWithContentRect:contentRect
   584                 styleMask:0
   585                     backing:NSBackingStoreBuffered
   586                         defer:NO ];
   587 
   588         if (qz_window != nil) {
   589             [ qz_window setAcceptsMouseMovedEvents:YES ];
   590             [ qz_window setViewsNeedDisplay:NO ];
   591         }
   592     }
   593     /* We already have a window, just change its size */
   594     else {
   595         [ qz_window setContentSize:contentRect.size ];
   596         current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags;
   597         [ window_view setFrameSize:contentRect.size ];
   598     }
   599 
   600     /* Setup OpenGL for a fullscreen context */
   601     if (flags & SDL_OPENGL) {
   602 
   603         CGLError err;
   604         CGLContextObj ctx;
   605 
   606         if ( ! QZ_SetupOpenGL (this, bpp, flags) ) {
   607             goto ERR_NO_GL;
   608         }
   609 
   610         /* Initialize the NSView and add it to our window.  The presence of a valid window and
   611            view allow the cursor to be changed whilst in fullscreen.*/
   612         window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
   613         [ [ qz_window contentView ] addSubview:window_view ];	
   614         [ window_view release ];
   615 
   616         ctx = QZ_GetCGLContextObj (gl_context);
   617         err = CGLSetFullScreen (ctx);
   618 
   619         if (err) {
   620             SDL_SetError ("Error setting OpenGL fullscreen: %s", CGLErrorString(err));
   621             goto ERR_NO_GL;
   622         }
   623 
   624         [ gl_context makeCurrentContext];
   625 
   626         glClear (GL_COLOR_BUFFER_BIT);
   627 
   628         [ gl_context flushBuffer ];
   629 
   630         current->flags |= SDL_OPENGL;
   631     }
   632 
   633     /* If we don't hide menu bar, it will get events and interrupt the program */
   634     HideMenuBar ();
   635 
   636     /* Fade in again (asynchronously) */
   637     if ( fade_token != kCGDisplayFadeReservationInvalidToken ) {
   638         CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   639         CGReleaseDisplayFadeReservation(fade_token);
   640     }
   641 
   642     /* 
   643         There is a bug in Cocoa where NSScreen doesn't synchronize
   644         with CGDirectDisplay, so the main screen's frame is wrong.
   645         As a result, coordinate translation produces incorrect results.
   646         We can hack around this bug by setting the screen rect
   647         ourselves. This hack should be removed if/when the bug is fixed.
   648     */
   649     screen_rect = NSMakeRect(0,0,width,height);
   650     QZ_SetFrame([ NSScreen mainScreen ], screen_rect);
   651 
   652     /* Save the flags to ensure correct tear-down */
   653     mode_flags = current->flags;
   654 
   655     /* Set app state, hide cursor if necessary, ... */
   656     QZ_DoActivate(this);
   657 
   658     return current;
   659 
   660     /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
   661 ERR_NO_GL:      
   662 ERR_DOUBLEBUF:  CGDisplaySwitchToMode (display_id, save_mode);
   663 ERR_NO_SWITCH:  CGReleaseAllDisplays ();
   664 ERR_NO_CAPTURE:
   665 ERR_NO_MATCH:   if ( fade_token != kCGDisplayFadeReservationInvalidToken ) {
   666                     CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   667                     CGReleaseDisplayFadeReservation (fade_token);
   668                 }
   669                 return NULL;
   670 }
   671 
   672 static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width,
   673                                          int height, int *bpp, Uint32 flags)
   674 {
   675     unsigned int style;
   676     NSRect contentRect;
   677     int center_window = 1;
   678     int origin_x, origin_y;
   679     CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
   680 
   681     current->flags = 0;
   682     current->w = width;
   683     current->h = height;
   684     
   685     contentRect = NSMakeRect (0, 0, width, height);
   686 
   687     /*
   688         Check if we should completely destroy the previous mode 
   689         - If it is fullscreen
   690         - If it has different noframe or resizable attribute
   691         - If it is OpenGL (since gl attributes could be different)
   692         - If new mode is OpenGL, but previous mode wasn't
   693     */
   694     if (video_set == SDL_TRUE) {
   695         if (mode_flags & SDL_FULLSCREEN) {
   696             /* Fade to black to hide resolution-switching flicker (and garbage
   697                that is displayed by a destroyed OpenGL context, if applicable) */
   698             if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) {
   699                 CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
   700             }
   701             QZ_UnsetVideoMode (this, TRUE);
   702         }
   703         else if ( ((mode_flags ^ flags) & (SDL_NOFRAME|SDL_RESIZABLE)) ||
   704                   (mode_flags & SDL_OPENGL) || 
   705                   (flags & SDL_OPENGL) ) {
   706             QZ_UnsetVideoMode (this, TRUE);
   707         }
   708     }
   709     
   710     /* Sorry, QuickDraw was ripped out. */
   711     if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) {
   712         SDL_SetError ("Embedded QuickDraw windows are no longer supported");
   713         if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   714             CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   715             CGReleaseDisplayFadeReservation (fade_token);
   716         }
   717         return NULL;
   718     }
   719 
   720     /* Check if we should recreate the window */
   721     if (qz_window == nil) {
   722     
   723         /* Set the window style based on input flags */
   724         if ( flags & SDL_NOFRAME ) {
   725             style = NSBorderlessWindowMask;
   726             current->flags |= SDL_NOFRAME;
   727         } else {
   728             style = NSTitledWindowMask;
   729             style |= (NSMiniaturizableWindowMask | NSClosableWindowMask);
   730             if ( flags & SDL_RESIZABLE ) {
   731                 style |= NSResizableWindowMask;
   732                 current->flags |= SDL_RESIZABLE;
   733             }
   734         }
   735 
   736         /* Manually create a window, avoids having a nib file resource */
   737         qz_window = [ [ SDL_QuartzWindow alloc ] 
   738             initWithContentRect:contentRect
   739                 styleMask:style 
   740                     backing:NSBackingStoreBuffered
   741                         defer:NO ];
   742                           
   743         if (qz_window == nil) {
   744             SDL_SetError ("Could not create the Cocoa window");
   745             if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   746                 CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   747                 CGReleaseDisplayFadeReservation (fade_token);
   748             }
   749             return NULL;
   750         }
   751 
   752         /*[ qz_window setReleasedWhenClosed:YES ];*/ /* no need to set this as it's the default for NSWindows */
   753         QZ_SetCaption(this, this->wm_title, this->wm_icon);
   754         [ qz_window setAcceptsMouseMovedEvents:YES ];
   755         [ qz_window setViewsNeedDisplay:NO ];
   756 
   757         if ( QZ_WindowPosition(this, &origin_x, &origin_y) ) {
   758             /* have to flip the Y value (NSPoint is lower left corner origin) */
   759             [ qz_window setFrameTopLeftPoint:NSMakePoint((float) origin_x, (float) (this->info.current_h - origin_y))];
   760             center_window = 0;
   761         } else if ( center_window ) {
   762             [ qz_window center ];
   763         }
   764 
   765         [ qz_window setDelegate:
   766             [ [ SDL_QuartzWindowDelegate alloc ] init ] ];
   767         [ qz_window setContentView: [ [ [ SDL_QuartzView alloc ] init ] autorelease ] ];
   768     }
   769     /* We already have a window, just change its size */
   770     else {
   771         [ qz_window setContentSize:contentRect.size ];
   772         current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags;
   773         [ window_view setFrameSize:contentRect.size ];
   774     }
   775 
   776     /* For OpenGL, we bind the context to a subview */
   777     if ( flags & SDL_OPENGL ) {
   778 
   779         if ( ! QZ_SetupOpenGL (this, *bpp, flags) ) {
   780             if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   781                 CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   782                 CGReleaseDisplayFadeReservation (fade_token);
   783             }
   784             return NULL;
   785         }
   786 
   787         window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
   788         [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
   789         [ [ qz_window contentView ] addSubview:window_view ];
   790         [ gl_context setView: window_view ];
   791         [ window_view release ];
   792         [ gl_context makeCurrentContext];
   793         [ qz_window makeKeyAndOrderFront:nil ];
   794         current->flags |= SDL_OPENGL;
   795     }
   796     /* For 2D, we build a CGBitmapContext */
   797     else {
   798         CGColorSpaceRef cgColorspace;
   799 
   800         /* Only recreate the view if it doesn't already exist */
   801         if (window_view == nil) {
   802         
   803             window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
   804             [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
   805             [ [ qz_window contentView ] addSubview:window_view ];
   806             [ window_view release ];
   807             [ qz_window makeKeyAndOrderFront:nil ];
   808         }
   809         
   810         cgColorspace = CGColorSpaceCreateDeviceRGB();
   811         current->pitch = 4 * current->w;
   812         current->pixels = SDL_malloc (current->h * current->pitch);
   813         
   814         cg_context = CGBitmapContextCreate (current->pixels, current->w, current->h,
   815                         8, current->pitch, cgColorspace,
   816                         kCGImageAlphaNoneSkipFirst);
   817         CGColorSpaceRelease (cgColorspace);
   818         
   819         current->flags |= SDL_SWSURFACE;
   820         current->flags |= SDL_ASYNCBLIT;
   821         current->hwdata = (void *) cg_context;
   822         
   823         this->UpdateRects     = QZ_UpdateRects;
   824         this->LockHWSurface   = QZ_LockHWSurface;
   825         this->UnlockHWSurface = QZ_UnlockHWSurface;
   826     }
   827 
   828     /* Save flags to ensure correct teardown */
   829     mode_flags = current->flags;
   830 
   831     /* Fade in again (asynchronously) if we came from a fullscreen mode and faded to black */
   832     if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   833         CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   834         CGReleaseDisplayFadeReservation (fade_token);
   835     }
   836 
   837     return current;
   838 }
   839 
   840 static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current, int width,
   841                                      int height, int bpp, Uint32 flags)
   842 {
   843     current->flags = 0;
   844     current->pixels = NULL;
   845 
   846     /* Setup full screen video */
   847     if ( flags & SDL_FULLSCREEN ) {
   848         current = QZ_SetVideoFullScreen (this, current, width, height, bpp, flags );
   849         if (current == NULL)
   850             return NULL;
   851     }
   852     /* Setup windowed video */
   853     else {
   854         /* Force bpp to 32 */
   855         bpp = 32;
   856         current = QZ_SetVideoWindowed (this, current, width, height, &bpp, flags);
   857         if (current == NULL)
   858             return NULL;
   859     }
   860 
   861     /* Setup the new pixel format */
   862     {
   863         int amask = 0,
   864         rmask = 0,
   865         gmask = 0,
   866         bmask = 0;
   867 
   868         switch (bpp) {
   869             case 16:   /* (1)-5-5-5 RGB */
   870                 amask = 0;
   871                 rmask = 0x7C00;
   872                 gmask = 0x03E0;
   873                 bmask = 0x001F;
   874                 break;
   875             case 24:
   876                 SDL_SetError ("24bpp is not available");
   877                 return NULL;
   878             case 32:   /* (8)-8-8-8 ARGB */
   879                 amask = 0x00000000;
   880 		if ( flags & SDL_FULLSCREEN )
   881 		{
   882 			rmask = 0x00FF0000;
   883 			gmask = 0x0000FF00;
   884 			bmask = 0x000000FF;
   885 		}
   886 		else
   887 		{
   888 #ifdef __LITTLE_ENDIAN__
   889 			rmask = 0x0000FF00;
   890 			gmask = 0x00FF0000;
   891 			bmask = 0xFF000000;
   892 #else
   893 			rmask = 0x00FF0000;
   894 			gmask = 0x0000FF00;
   895 			bmask = 0x000000FF;
   896 #endif
   897 		}
   898                 break;
   899         }
   900 
   901         if ( ! SDL_ReallocFormat (current, bpp,
   902                                   rmask, gmask, bmask, amask ) ) {
   903             SDL_SetError ("Couldn't reallocate pixel format");
   904             return NULL;
   905         }
   906     }
   907 
   908     /* Signal successful completion (used internally) */
   909     video_set = SDL_TRUE;
   910 
   911     return current;
   912 }
   913 
   914 static int QZ_ToggleFullScreen (_THIS, int on)
   915 {
   916     return 0;
   917 }
   918 
   919 static int QZ_SetColors (_THIS, int first_color, int num_colors,
   920                          SDL_Color *colors)
   921 {
   922     CGTableCount  index;
   923     CGDeviceColor color;
   924 
   925     for (index = first_color; index < first_color+num_colors; index++) {
   926 
   927         /* Clamp colors between 0.0 and 1.0 */
   928         color.red   = colors->r / 255.0;
   929         color.blue  = colors->b / 255.0;
   930         color.green = colors->g / 255.0;
   931 
   932         colors++;
   933 
   934         CGPaletteSetColorAtIndex (palette, color, index);
   935     }
   936 
   937     if ( CGDisplayNoErr != CGDisplaySetPalette (display_id, palette) )
   938         return 0;
   939 
   940     return 1;
   941 }
   942 
   943 static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface)
   944 {
   945     return 1;
   946 }
   947 
   948 static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface)
   949 {
   950 }
   951 
   952 /* The VBL delay is based on code by Ian R Ollmann's RezLib <iano@cco.caltech.edu> */
   953 static AbsoluteTime QZ_SecondsToAbsolute ( double seconds )
   954 {
   955     union
   956     {
   957         UInt64 i;
   958         Nanoseconds ns;
   959     } temp;
   960         
   961     temp.i = seconds * 1000000000.0;
   962     
   963     return NanosecondsToAbsolute ( temp.ns );
   964 }
   965 
   966 static int QZ_ThreadFlip (_THIS)
   967 {
   968     Uint8 *src, *dst;
   969     int skip, len, h;
   970     
   971     /*
   972         Give this thread the highest scheduling priority possible,
   973         in the hopes that it will immediately run after the VBL delay
   974     */
   975     {
   976         pthread_t current_thread;
   977         int policy;
   978         struct sched_param param;
   979         
   980         current_thread = pthread_self ();
   981         pthread_getschedparam (current_thread, &policy, &param);
   982         policy = SCHED_RR;
   983         param.sched_priority = sched_get_priority_max (policy);
   984         pthread_setschedparam (current_thread, policy, &param);
   985     }
   986     
   987     while (1) {
   988     
   989         SDL_SemWait (sem1);
   990         if (quit_thread)
   991             return 0;
   992                 
   993         /*
   994          * We have to add SDL_VideoSurface->offset here, since we might be a
   995          *  smaller surface in the center of the framebuffer (you asked for
   996          *  a fullscreen resolution smaller than the hardware could supply
   997          *  so SDL is centering it in a bigger resolution)...
   998          */
   999         dst = (Uint8 *)CGDisplayBaseAddress (display_id) + SDL_VideoSurface->offset;
  1000         src = current_buffer + SDL_VideoSurface->offset;
  1001         len = SDL_VideoSurface->w * SDL_VideoSurface->format->BytesPerPixel;
  1002         h = SDL_VideoSurface->h;
  1003         skip = SDL_VideoSurface->pitch;
  1004     
  1005         /* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */
  1006         {
  1007             
  1008             /* The VBL delay is based on Ian Ollmann's RezLib <iano@cco.caltech.edu> */
  1009             double refreshRate;
  1010             double linesPerSecond;
  1011             double target;
  1012             double position;
  1013             double adjustment;
  1014             AbsoluteTime nextTime;        
  1015             CFNumberRef refreshRateCFNumber;
  1016             
  1017             refreshRateCFNumber = CFDictionaryGetValue (mode, kCGDisplayRefreshRate);
  1018             if ( NULL == refreshRateCFNumber ) {
  1019                 SDL_SetError ("Mode has no refresh rate");
  1020                 goto ERROR;
  1021             }
  1022             
  1023             if ( 0 == CFNumberGetValue (refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) ) {
  1024                 SDL_SetError ("Error getting refresh rate");
  1025                 goto ERROR;
  1026             }
  1027             
  1028             if ( 0 == refreshRate ) {
  1029                
  1030                SDL_SetError ("Display has no refresh rate, using 60hz");
  1031                 
  1032                 /* ok, for LCD's we'll emulate a 60hz refresh, which may or may not look right */
  1033                 refreshRate = 60.0;
  1034             }
  1035             
  1036             linesPerSecond = refreshRate * h;
  1037             target = h;
  1038         
  1039             /* Figure out the first delay so we start off about right */
  1040             position = CGDisplayBeamPosition (display_id);
  1041             if (position > target)
  1042                 position = 0;
  1043             
  1044             adjustment = (target - position) / linesPerSecond; 
  1045             
  1046             nextTime = AddAbsoluteToAbsolute (UpTime (), QZ_SecondsToAbsolute (adjustment));
  1047         
  1048             MPDelayUntil (&nextTime);
  1049         }
  1050         
  1051         
  1052         /* On error, skip VBL delay */
  1053         ERROR:
  1054         
  1055         /* TODO: use CGContextDrawImage here too!  Create two CGContextRefs the same way we
  1056            create two buffers, replace current_buffer with current_context and set it
  1057            appropriately in QZ_FlipDoubleBuffer.  */
  1058         while ( h-- ) {
  1059         
  1060             SDL_memcpy (dst, src, len);
  1061             src += skip;
  1062             dst += skip;
  1063         }
  1064         
  1065         /* signal flip completion */
  1066         SDL_SemPost (sem2);
  1067     }
  1068     
  1069     return 0;
  1070 }
  1071         
  1072 static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface)
  1073 {
  1074     /* wait for previous flip to complete */
  1075     SDL_SemWait (sem2);
  1076     
  1077     current_buffer = surface->pixels;
  1078         
  1079     if (surface->pixels == sw_buffers[0])
  1080         surface->pixels = sw_buffers[1];
  1081     else
  1082         surface->pixels = sw_buffers[0];
  1083     
  1084     /* signal worker thread to do the flip */
  1085     SDL_SemPost (sem1);
  1086     
  1087     return 0;
  1088 }
  1089 
  1090 static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects)
  1091 {
  1092     /* perform a flip if someone calls updaterects on a doublebuferred surface */
  1093     this->FlipHWSurface (this, SDL_VideoSurface);
  1094 }
  1095 
  1096 static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects)
  1097 {
  1098 #pragma unused(this,num_rects,rects)
  1099 }
  1100 
  1101 
  1102 /* Resize icon, BMP format */
  1103 static const unsigned char QZ_ResizeIcon[] = {
  1104     0x42,0x4d,0x31,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,
  1105     0x00,0x00,0x0d,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,
  1106     0x00,0x00,0xfb,0x01,0x00,0x00,0x13,0x0b,0x00,0x00,0x13,0x0b,0x00,0x00,0x00,0x00,
  1107     0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  1108     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  1109     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
  1110     0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,
  1111     0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,
  1112     0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xda,0xda,0xda,0x87,
  1113     0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,
  1114     0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
  1115     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd5,0xd5,0xd5,0x87,0x87,0x87,0xe8,0xe8,0xe8,
  1116     0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,
  1117     0xda,0xda,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  1118     0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,
  1119     0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
  1120     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,
  1121     0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,
  1122     0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  1123     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,
  1124     0xe8,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
  1125     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  1126     0xff,0xff,0xff,0xd9,0xd9,0xd9,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xdc,
  1127     0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  1128     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,
  1129     0xdb,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
  1130     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  1131     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,0xdb,0x87,0x87,0x87,0xe8,
  1132     0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  1133     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  1134     0xff,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
  1135     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  1136     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdc,
  1137     0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  1138     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  1139     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b
  1140 };
  1141 
  1142 static void QZ_DrawResizeIcon (_THIS)
  1143 {
  1144     /* Check if we should draw the resize icon */
  1145     if (SDL_VideoSurface->flags & SDL_RESIZABLE) {
  1146     
  1147         SDL_Rect icon_rect;
  1148         
  1149         /* Create the icon image */
  1150         if (resize_icon == NULL) {
  1151         
  1152             SDL_RWops *rw;
  1153             SDL_Surface *tmp;
  1154             
  1155             rw = SDL_RWFromConstMem (QZ_ResizeIcon, sizeof(QZ_ResizeIcon));
  1156             tmp = SDL_LoadBMP_RW (rw, SDL_TRUE);
  1157                                                             
  1158             resize_icon = SDL_ConvertSurface (tmp, SDL_VideoSurface->format, SDL_SRCCOLORKEY);
  1159             SDL_SetColorKey (resize_icon, SDL_SRCCOLORKEY, 0xFFFFFF);
  1160             
  1161             SDL_FreeSurface (tmp);
  1162         }
  1163             
  1164         icon_rect.x = SDL_VideoSurface->w - 13;
  1165         icon_rect.y = SDL_VideoSurface->h - 13;
  1166         icon_rect.w = 13;
  1167         icon_rect.h = 13;
  1168             
  1169         SDL_BlitSurface (resize_icon, NULL, SDL_VideoSurface, &icon_rect);
  1170     }
  1171 }
  1172 
  1173 static void QZ_UpdateRects (_THIS, int numRects, SDL_Rect *rects)
  1174 {
  1175     if (SDL_VideoSurface->flags & SDL_OPENGLBLIT) {
  1176         QZ_GL_SwapBuffers (this);
  1177     }
  1178     else if ( [ qz_window isMiniaturized ] ) {
  1179     
  1180         /* Do nothing if miniaturized */
  1181     }
  1182     
  1183     else {
  1184         CGContextRef cgc = (CGContextRef)
  1185             [[NSGraphicsContext graphicsContextWithWindow: qz_window]
  1186                 graphicsPort];
  1187         QZ_DrawResizeIcon (this);
  1188         CGContextFlush (cg_context);
  1189         CGImageRef image = CGBitmapContextCreateImage (cg_context);
  1190         CGRect rectangle = CGRectMake (0,0,[window_view frame].size.width,[window_view frame].size.height);
  1191         
  1192         CGContextDrawImage (cgc, rectangle, image);
  1193         CGImageRelease(image);
  1194         CGContextFlush (cgc);
  1195         CGContextRelease (cgc);
  1196     }
  1197 }
  1198 
  1199 static void QZ_VideoQuit (_THIS)
  1200 {
  1201     CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
  1202 
  1203     /* Restore gamma settings */
  1204     CGDisplayRestoreColorSyncSettings ();
  1205 
  1206     /* Ensure the cursor will be visible and working when we quit */
  1207     CGDisplayShowCursor (display_id);
  1208     CGAssociateMouseAndMouseCursorPosition (1);
  1209     
  1210     if (mode_flags & SDL_FULLSCREEN) {
  1211         /* Fade to black to hide resolution-switching flicker (and garbage
  1212            that is displayed by a destroyed OpenGL context, if applicable) */
  1213         if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) {
  1214             CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
  1215         }
  1216         QZ_UnsetVideoMode (this, TRUE);
  1217         if (fade_token != kCGDisplayFadeReservationInvalidToken) {
  1218             CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
  1219             CGReleaseDisplayFadeReservation (fade_token);
  1220         }
  1221     }
  1222     else
  1223         QZ_UnsetVideoMode (this, TRUE);
  1224     
  1225     CGPaletteRelease (palette);
  1226 
  1227     if (opengl_library) {
  1228         SDL_UnloadObject(opengl_library);
  1229         opengl_library = NULL;
  1230     }
  1231     this->gl_config.driver_loaded = 0;
  1232 
  1233     if (field_edit) {
  1234         [field_edit release];
  1235         field_edit = NULL;
  1236     }
  1237 }
  1238 
  1239 #if 0 /* Not used (apparently, it's really slow) */
  1240 static int  QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color)
  1241 {
  1242     CGSDisplayHWFill (display_id, rect->x, rect->y, rect->w, rect->h, color);
  1243 
  1244     return 0;
  1245 }
  1246 #endif
  1247 
  1248 static int  QZ_LockHWSurface(_THIS, SDL_Surface *surface)
  1249 {
  1250     return 1;
  1251 }
  1252 
  1253 static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface)
  1254 {
  1255 }
  1256 
  1257 static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface)
  1258 {
  1259     return(-1); /* unallowed (no HWSURFACE support here). */
  1260 }
  1261 
  1262 static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface)
  1263 {
  1264 }
  1265 
  1266 /*
  1267  int QZ_FlipHWSurface (_THIS, SDL_Surface *surface) {
  1268      return 0;
  1269  }
  1270  */
  1271 
  1272 /* Gamma functions */
  1273 int QZ_SetGamma (_THIS, float red, float green, float blue)
  1274 {
  1275     const CGGammaValue min = 0.0, max = 1.0;
  1276 
  1277     if (red == 0.0)
  1278         red = FLT_MAX;
  1279     else
  1280         red = 1.0 / red;
  1281 
  1282     if (green == 0.0)
  1283         green = FLT_MAX;
  1284     else
  1285         green = 1.0 / green;
  1286 
  1287     if (blue == 0.0)
  1288         blue = FLT_MAX;
  1289     else
  1290         blue  = 1.0 / blue;
  1291 
  1292     if ( CGDisplayNoErr == CGSetDisplayTransferByFormula
  1293          (display_id, min, max, red, min, max, green, min, max, blue) ) {
  1294 
  1295         return 0;
  1296     }
  1297     else {
  1298 
  1299         return -1;
  1300     }
  1301 }
  1302 
  1303 int QZ_GetGamma (_THIS, float *red, float *green, float *blue)
  1304 {
  1305     CGGammaValue dummy;
  1306     if ( CGDisplayNoErr == CGGetDisplayTransferByFormula
  1307          (display_id, &dummy, &dummy, red,
  1308           &dummy, &dummy, green, &dummy, &dummy, blue) )
  1309 
  1310         return 0;
  1311     else
  1312         return -1;
  1313 }
  1314 
  1315 int QZ_SetGammaRamp (_THIS, Uint16 *ramp)
  1316 {
  1317     const CGTableCount tableSize = 255;
  1318     CGGammaValue redTable[tableSize];
  1319     CGGammaValue greenTable[tableSize];
  1320     CGGammaValue blueTable[tableSize];
  1321 
  1322     int i;
  1323 
  1324     /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
  1325     for (i = 0; i < 256; i++)
  1326         redTable[i % 256] = ramp[i] / 65535.0;
  1327 
  1328     for (i=256; i < 512; i++)
  1329         greenTable[i % 256] = ramp[i] / 65535.0;
  1330 
  1331     for (i=512; i < 768; i++)
  1332         blueTable[i % 256] = ramp[i] / 65535.0;
  1333 
  1334     if ( CGDisplayNoErr == CGSetDisplayTransferByTable
  1335          (display_id, tableSize, redTable, greenTable, blueTable) )
  1336         return 0;
  1337     else
  1338         return -1;
  1339 }
  1340 
  1341 int QZ_GetGammaRamp (_THIS, Uint16 *ramp)
  1342 {
  1343     const CGTableCount tableSize = 255;
  1344     CGGammaValue redTable[tableSize];
  1345     CGGammaValue greenTable[tableSize];
  1346     CGGammaValue blueTable[tableSize];
  1347     CGTableCount actual;
  1348     int i;
  1349 
  1350     if ( CGDisplayNoErr != CGGetDisplayTransferByTable
  1351          (display_id, tableSize, redTable, greenTable, blueTable, &actual) ||
  1352          actual != tableSize)
  1353 
  1354         return -1;
  1355 
  1356     /* Pack tables into one array, with values from 0 to 65535 */
  1357     for (i = 0; i < 256; i++)
  1358         ramp[i] = redTable[i % 256] * 65535.0;
  1359 
  1360     for (i=256; i < 512; i++)
  1361         ramp[i] = greenTable[i % 256] * 65535.0;
  1362 
  1363     for (i=512; i < 768; i++)
  1364         ramp[i] = blueTable[i % 256] * 65535.0;
  1365 
  1366     return 0;
  1367 }
  1368