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