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