src/video/quartz/SDL_QuartzVideo.m
author Sam Lantinga <slouken@libsdl.org>
Sat, 29 Dec 2007 21:31:26 +0000
branchSDL-1.2
changeset 4123 9d90d7765fa7
parent 4090 fedb379bedd0
child 4139 568c9b3c0167
permissions -rw-r--r--
Guillaume Borios fixed bug #508

When unicode translation is ON, pressing the escape key raise an NSBeep()
because the NSTextView interprets the key as a special command (in that case
impossible to interpret)... The NSTextView instance should replaced by
something subclassed so that doCommandBySelector: does nothing.

Example code :

@interface SDLTranslatorResponder : NSTextView
{
}
- (void) doCommandBySelector:(SEL)myselector;
@end

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