src/video/quartz/SDL_QuartzVideo.m
author Ryan C. Gordon
Mon, 02 Jan 2012 21:25:34 -0800
branchSDL-1.2
changeset 6157 8a1bc41db6bb
parent 6146 06ae169343e4
child 6158 6f3a2bb301d6
permissions -rw-r--r--
Quartz: Restore 1.2.14 behaviour of letting apps draw from background thread.

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