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