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