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