src/video/quartz/SDL_QuartzVideo.m
author Ryan C. Gordon <icculus@icculus.org>
Fri, 06 Apr 2007 20:30:41 +0000
branchSDL-1.2
changeset 3936 c5c3c772f5aa
parent 3877 81f66f258d77
child 4049 60f677630282
permissions -rw-r--r--
Let app set SDL_VIDEO_ALLOW_SCREENSAVER environment variable to override SDL's
attempt to disable screen savers. Works for Quartz (Mac OS X) and X11.

Need a formal API for this in 1.3, still.

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