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