src/video/quartz/SDL_QuartzVideo.m
author Ryan C. Gordon
Thu, 18 Aug 2005 06:18:30 +0000
changeset 1119 430d8d701f69
parent 967 cda407d627a3
child 1120 8e1fde455471
permissions -rw-r--r--
SDL_GetAppState() correction when toggling fullscreen on OSX.

Patch by me to address this comment on the SDL mailing list:

"There appears to be a serious bug with SDL_GetAppState on OS X (10.3.9). When
first running in windowed mode and then switching to full screen mode by
calling SDL_SetVideoMode, SDL_GetAppState returns 4 (SDL_APPACTIVE) instead of
7 (SDL_APPMOUSEFOCUS | SDL_APPINPUTFOCUS | SDL_APPACTIVE). However, the SDL
application clearly does have the keyboard focus, since it is able to receive
keyboard events."

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