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