diff src/video/quartz/SDL_QuartzVideo.m @ 272:d1447a846d80

Date: Sat, 19 Jan 2002 17:24:32 -0500 (EST) From: Darrell Walisser <dwaliss1@purdue.edu> Subject: SDL Quartz video update -better mouse motion events -fixed minification bugs (except OpenGL) -fixed QZ_SetGamma for correct semantics -fade/unfade display before/after rez switch -experimental obscured-check/blind-copy code The obscured code, while it speeds up window drawing substantially, isn't ready yet. The reason is that there doesn't (yet) seem to be a way to know when the window is dragged or when the window suddenly comes to the foreground. Since Carbon windows seem to allow detection of such things, I suspect it is possible through some window server API. Cocoa(NSWindow) has no functions for such things, AFAIK.
author Sam Lantinga <slouken@libsdl.org>
date Tue, 22 Jan 2002 18:46:28 +0000
parents e8157fcb3114
children f6ffac90895c
line wrap: on
line diff
--- a/src/video/quartz/SDL_QuartzVideo.m	Tue Jan 22 18:28:35 2002 +0000
+++ b/src/video/quartz/SDL_QuartzVideo.m	Tue Jan 22 18:46:28 2002 +0000
@@ -32,9 +32,10 @@
 #include "SDL_QuartzEvents.m"
 #include "SDL_QuartzWindow.m"
 
+
 /* Bootstrap binding, enables entry point into the driver */
 VideoBootStrap QZ_bootstrap = {
-    "Quartz", "MacOS X CoreGraphics", QZ_Available, QZ_CreateDevice
+    "Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice
 };
 
 /* Bootstrap functions */
@@ -44,7 +45,7 @@
 
 static SDL_VideoDevice* QZ_CreateDevice (int device_index) {
 
-   #pragma unused (device_index)
+#pragma unused (device_index)
 
     SDL_VideoDevice *device;
     SDL_PrivateVideoData *hidden;
@@ -57,7 +58,7 @@
 
     memset (device, 0, sizeof (*device) );
     memset (hidden, 0, sizeof (*hidden) );
-    
+
     device->hidden = hidden;
 
     device->VideoInit        = QZ_VideoInit;
@@ -67,7 +68,7 @@
     device->SetColors        = QZ_SetColors;
     /* device->UpdateRects      = QZ_UpdateRects; this is determined by SetVideoMode() */
     device->VideoQuit        = QZ_VideoQuit;
-    
+
     device->LockHWSurface   = QZ_LockHWSurface;
     device->UnlockHWSurface = QZ_UnlockHWSurface;
     device->FreeHWSurface   = QZ_FreeHWSurface;
@@ -83,7 +84,7 @@
     device->GL_MakeCurrent    = QZ_GL_MakeCurrent;
     device->GL_SwapBuffers    = QZ_GL_SwapBuffers;
     device->GL_LoadLibrary    = QZ_GL_LoadLibrary;
-    
+
     device->FreeWMCursor   = QZ_FreeWMCursor;
     device->CreateWMCursor = QZ_CreateWMCursor;
     device->ShowWMCursor   = QZ_ShowWMCursor;
@@ -98,9 +99,9 @@
     device->IconifyWindow = QZ_IconifyWindow;
     /*device->GetWMInfo     = QZ_GetWMInfo;*/
     device->GrabInput     = QZ_GrabInput;
-    
+
     device->free = QZ_DeleteDevice;
-    
+
     return device;
 }
 
@@ -112,30 +113,30 @@
 
 static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format) {
 
-  /* Initialize the video settings; this data persists between mode switches */
-  display_id = kCGDirectMainDisplay; 
-  save_mode  = CGDisplayCurrentMode    (display_id);
-  mode_list  = CGDisplayAvailableModes (display_id);
-  palette    = CGPaletteCreateDefaultColorPalette ();
-  
-  /* Gather some information that is useful to know about the display */
-  CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayBitsPerPixel), 
-		    kCFNumberSInt32Type, &device_bpp);
+    /* Initialize the video settings; this data persists between mode switches */
+    display_id = kCGDirectMainDisplay;
+    save_mode  = CGDisplayCurrentMode    (display_id);
+    mode_list  = CGDisplayAvailableModes (display_id);
+    palette    = CGPaletteCreateDefaultColorPalette ();
+
+    /* Gather some information that is useful to know about the display */
+    CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayBitsPerPixel),
+                      kCFNumberSInt32Type, &device_bpp);
 
-  CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayWidth),
-                    kCFNumberSInt32Type, &device_width);
-  
-  CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayHeight),
-                    kCFNumberSInt32Type, &device_height);
+    CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayWidth),
+                      kCFNumberSInt32Type, &device_width);
 
-  video_format->BitsPerPixel = device_bpp;
-  
-  return 0;
+    CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayHeight),
+                      kCFNumberSInt32Type, &device_height);
+
+    video_format->BitsPerPixel = device_bpp;
+
+    return 0;
 }
 
 static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags) {
     
-    CFIndex num_modes = CFArrayGetCount (mode_list);
+    CFIndex num_modes;
     CFIndex i;
 
     static SDL_Rect **list = NULL;
@@ -157,6 +158,8 @@
       list = NULL;
     }
     
+    num_modes = CFArrayGetCount (mode_list);
+
     /* Build list of modes with the requested bpp */
     for (i = 0; i < num_modes; i++) {
    
@@ -201,16 +204,18 @@
             
             list_size++;
 
-            if ( list == NULL)
-                list = (SDL_Rect**) malloc (sizeof(*list) * list_size+1);
+            if (list == NULL)
+                list = (SDL_Rect**) malloc (sizeof(*list) * (list_size+1) );
             else
-                list = (SDL_Rect**) realloc (list, sizeof(*list) * list_size+1);
+                list = (SDL_Rect**) realloc (list, sizeof(*list) * (list_size+1));
             
             rect = (SDL_Rect*) malloc (sizeof(**list));
             
-            if (list == NULL || rect == NULL)
+            if (list == NULL || rect == NULL) {
                 SDL_OutOfMemory ();
-    
+                return NULL;
+            }
+            
             rect->w = width;
             rect->h = height;
     
@@ -241,43 +246,146 @@
     return list;
 }
 
+/* Gamma functions to try to hide the flash from a rez switch */
+/* Fade the display from normal to black */
+/* Save gamma tables for fade back to normal */
+static UInt32 QZ_FadeGammaOut (_THIS, SDL_QuartzGammaTable *table) {
+
+    CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE],
+    greenTable[QZ_GAMMA_TABLE_SIZE],
+    blueTable[QZ_GAMMA_TABLE_SIZE];
+
+    float percent;
+    int j;
+    int actual;
+
+    if ( (CGDisplayNoErr != CGGetDisplayTransferByTable
+          (display_id, QZ_GAMMA_TABLE_SIZE,
+           table->red, table->green, table->blue, &actual)) ||
+         actual != QZ_GAMMA_TABLE_SIZE) {
+
+        return 1;
+    }
+
+    memcpy (redTable, table->red, sizeof(redTable));
+    memcpy (greenTable, table->green, sizeof(greenTable));
+    memcpy (blueTable, table->blue, sizeof(greenTable));
+
+    for (percent = 1.0; percent >= 0.0; percent -= 0.01) {
+
+        for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) {
+
+            redTable[j]   = redTable[j]   * percent;
+            greenTable[j] = greenTable[j] * percent;
+            blueTable[j]  = blueTable[j]  * percent;
+        }
+
+        if (CGDisplayNoErr != CGSetDisplayTransferByTable
+            (display_id, QZ_GAMMA_TABLE_SIZE,
+             redTable, greenTable, blueTable)) {
+
+            CGDisplayRestoreColorSyncSettings();
+            return 1;
+        }
+
+        SDL_Delay (10);
+    }
+
+    return 0;
+}
+
+/* Fade the display from black to normal */
+/* Restore previously saved gamma values */
+static UInt32 QZ_FadeGammaIn (_THIS, SDL_QuartzGammaTable *table) {
+
+    CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE],
+        greenTable[QZ_GAMMA_TABLE_SIZE],
+        blueTable[QZ_GAMMA_TABLE_SIZE];
+
+    float percent;
+    int j;
+
+    memset (redTable, 0, sizeof(redTable));
+    memset (greenTable, 0, sizeof(greenTable));
+    memset (blueTable, 0, sizeof(greenTable));
+    
+    for (percent = 0.0; percent <= 1.0; percent += 0.01) {
+
+        for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) {
+
+            redTable[j]   = table->red[j]   * percent;
+            greenTable[j] = table->green[j] * percent;
+            blueTable[j]  = table->blue[j]  * percent;
+        }
+
+        if (CGDisplayNoErr != CGSetDisplayTransferByTable
+            (display_id, QZ_GAMMA_TABLE_SIZE,
+             redTable, greenTable, blueTable)) {
+
+            CGDisplayRestoreColorSyncSettings();
+            return 1;
+        }
+
+        SDL_Delay (10);
+    }
+
+    return 0;
+}
+
 static void QZ_UnsetVideoMode (_THIS) {
 
     /* Reset values that may change between switches */
     this->info.blit_fill = 0;
     this->FillHWRect     = NULL;
     this->UpdateRects    = NULL;
-    
-    /* Restore gamma settings */
-    CGDisplayRestoreColorSyncSettings ();
    
-    /* Restore original screen resolution */
+    /* Release fullscreen resources */
     if ( mode_flags & SDL_FULLSCREEN ) {
+
+        SDL_QuartzGammaTable gamma_table;
+        int gamma_error;
+
+        gamma_error = QZ_FadeGammaOut (this, &gamma_table);
+
+        /* Release the OpenGL context */
+        /* Do this first to avoid trash on the display before fade */
+        if ( mode_flags & SDL_OPENGL )
+            QZ_TearDownOpenGL (this);
+        
         if (mode_flags & SDL_OPENGL)
             CGLSetFullScreen(NULL);
-            
+
+        /* Restore original screen resolution/bpp */
         CGDisplaySwitchToMode (display_id, save_mode);
         CGDisplayRelease (display_id);
+        ShowMenuBar ();
+        
+        if (! gamma_error)
+            QZ_FadeGammaIn (this, &gamma_table);
     }
-    /* Release window mode data structures */
+    /* Release window mode resources */
     else { 
         if ( (mode_flags & SDL_OPENGL) == 0 ) {
-            UnlockPortBits ( [ windowView qdPort ] );
-            [ windowView release  ];
+            UnlockPortBits ( [ window_view qdPort ] );
+            [ window_view release  ];
         }
         [ qz_window setContentView:nil ];
         [ qz_window setDelegate:nil ];
         [ qz_window close ];
+        [ qz_window release ];
+
+        /* Release the OpenGL context */
+        if ( mode_flags & SDL_OPENGL )
+            QZ_TearDownOpenGL (this);
     }
+
+    /* Restore gamma settings */
+    CGDisplayRestoreColorSyncSettings ();
     
     /* Set pixels to null (so other code doesn't try to free it) */
     if (this->screen != NULL)
         this->screen->pixels = NULL;
         
-    /* Release the OpenGL context */
-    if ( mode_flags & SDL_OPENGL )
-        QZ_TearDownOpenGL (this);
-        
     /* Ensure the cursor will be visible and working when we quit */
     CGDisplayShowCursor (display_id);
     CGAssociateMouseAndMouseCursorPosition (1);
@@ -289,7 +397,9 @@
 static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int width,
                                            int height, int bpp, Uint32 flags) {
     int exact_match;
-
+    int gamma_error;
+    SDL_QuartzGammaTable gamma_table;
+    
     /* See if requested mode exists */
     mode = CGDisplayBestModeForParameters (display_id, bpp, width, 
 					   height, &exact_match);
@@ -301,34 +411,24 @@
         goto ERR_NO_MATCH;
     }
 
+    /* Fade display to zero gamma */
+    gamma_error = QZ_FadeGammaOut (this, &gamma_table);
+
     /* Put up the blanking window (a window above all other windows) */
     if ( CGDisplayNoErr != CGDisplayCapture (display_id) ) {
         SDL_SetError ("Failed capturing display");
         goto ERR_NO_CAPTURE;
     }
+
     
     /* Do the physical switch */
     if ( CGDisplayNoErr != CGDisplaySwitchToMode (display_id, mode) ) {
         SDL_SetError ("Failed switching display resolution");
         goto ERR_NO_SWITCH;
     }
-    
-    /* None of these methods seem to fix the fullscreen artifacts bug(s) */
-#if USE_GDHANDLE
-    SetGDevice(GetMainDevice());
-    current->pitch = (**(**  GetMainDevice() ).gdPMap).rowBytes & 0x3FFF;
-    current->pixels = (**(** GetMainDevice() ).gdPMap).baseAddr;
-#elif USE_CREATEPORT
-    device_port = CreateNewPortForCGDisplayID((Uint32*)display_id);
-    SetPort (device_port);
-    LockPortBits ( device_port );
-    current->pixels = GetPixBaseAddr ( GetPortPixMap ( device_port ) );
-    current->pitch  = GetPixRowBytes ( GetPortPixMap ( device_port ) );
-    UnlockPortBits ( device_port );
-#else
+
     current->pixels = (Uint32*) CGDisplayBaseAddress (display_id);
     current->pitch  = CGDisplayBytesPerRow (display_id);
-#endif
 
     current->flags = 0;
     current->w = width;
@@ -354,7 +454,7 @@
         CGLContextObj ctx;
         
         if ( ! QZ_SetupOpenGL (this, bpp, flags) ) {
-            return NULL;
+            goto ERR_NO_GL;
         }
        
         ctx = [ gl_context cglContext ];
@@ -367,22 +467,30 @@
         }
          
         [ gl_context makeCurrentContext];
+
+        glClear (GL_COLOR_BUFFER_BIT);
+
+        [ gl_context flushBuffer ];
         
         current->flags |= SDL_OPENGL;
     }
 
     /* If we don't hide menu bar, it will get events and interrupt the program */
     HideMenuBar ();
+
+    /* Fade the display to original gamma */
+    if (! gamma_error )
+        QZ_FadeGammaIn (this, &gamma_table);
     
     /* Save the flags to ensure correct tear-down */
     mode_flags = current->flags;
     
     return current;
 
- /* Since the blanking window covers *all* windows (even force quit) correct recovery is crutial */
+ /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
  ERR_NO_GL:      CGDisplaySwitchToMode (display_id, save_mode);
  ERR_NO_SWITCH:  CGDisplayRelease (display_id);
- ERR_NO_CAPTURE:
+ ERR_NO_CAPTURE: if (!gamma_error) { QZ_FadeGammaIn (this, &gamma_table); }
  ERR_NO_MATCH:	return NULL;
 }
 
@@ -440,16 +548,17 @@
     /* For 2D, we set the content view to a NSQuickDrawView */
     else {
     
-        windowView = [ [ NSQuickDrawView alloc ] init ];
-        [ qz_window setContentView:windowView ];
+        window_view = [ [ SDL_QuartzWindowView alloc ] init ];
+        [ qz_window setContentView:window_view ];
         [ qz_window makeKeyAndOrderFront:nil ];    
         
-        LockPortBits ( [ windowView qdPort ] );
-        current->pixels = GetPixBaseAddr ( GetPortPixMap ( [ windowView qdPort ] ) );
-        current->pitch  = GetPixRowBytes ( GetPortPixMap ( [ windowView qdPort ] ) );
-            
+        LockPortBits ( [ window_view qdPort ] );
+        current->pixels = GetPixBaseAddr ( GetPortPixMap ( [ window_view qdPort ] ) );
+        current->pitch  = GetPixRowBytes ( GetPortPixMap ( [ window_view qdPort ] ) );
+        
         current->flags |= SDL_SWSURFACE;
         current->flags |= SDL_PREALLOC;
+        
 	if ( flags & SDL_NOFRAME )
         	current->flags |= SDL_NOFRAME;
 	if ( flags & SDL_RESIZABLE )
@@ -462,6 +571,10 @@
 
         this->UpdateRects = QZ_UpdateRects;
     }
+    
+    /* Save flags to ensure correct teardown */
+    mode_flags = current->flags;
+      
     return current;
 }
 
@@ -482,7 +595,7 @@
     /* Setup windowed video */
     else {
         /* Force bpp to the device's bpp */
-        bpp = current->format->BitsPerPixel;
+        bpp = device_bpp;
         current = QZ_SetVideoWindowed (this, current, width, height, bpp, flags);
         if (current == NULL)
             return NULL;
@@ -520,10 +633,7 @@
        	}
     }
     
-    /* Warp mouse to origin in order to get passive mouse motion events started correctly */
-    QZ_PrivateWarpCursor (this, current->flags & SDL_FULLSCREEN, height, 0, 0);
-    
-    /* Signal successful completion */
+    /* Signal successful completion (used internally) */
     video_set = SDL_TRUE;
     
     return current;
@@ -561,12 +671,293 @@
     #pragma unused(this,num_rects,rects)
 }
 
+/** 
+ *  The obscured code is based on work by Matt Slot fprefect@ambrosiasw.com,
+ *  who supplied sample code for Carbon.
+ **/
+static int QZ_IsWindowObscured (NSWindow *window) {
+
+//#define TEST_OBSCURED 1
+
+#if TEST_OBSCURED
+    
+    /*  In order to determine if a direct copy to the screen is possible,
+       we must figure out if there are any windows covering ours (including shadows).
+       This can be done by querying the window server about the on screen            
+       windows for their screen rectangle and window level.                          
+       The procedure used below is puts accuracy before speed; however, it aims to call 
+       the window server the fewest number of times possible to keep things reasonable.
+       In my testing on a 300mhz G3, this routine typically takes < 2 ms. -DW
+    
+        Notes:
+            -Calls into the Window Server involve IPC which is slow.
+            -Getting a rectangle seems slower than getting the window level
+            -The window list we get back is in sorted order, top to bottom
+            -On average, I suspect, most windows above ours are dock icon windows (hence optimization)
+            -Some windows above ours are always there, and cannot move or obscure us (menu bar)
+            
+        Bugs:
+            -no way (yet) to deactivate direct drawing when a window is dragged, 
+               or suddenly obscured, so drawing continues and can produce garbage
+               We need some kind of locking mechanism on window movement to prevent this
+               
+            -deactivated normal windows use activated normal 
+               window shadows (slight inaccuraccy)
+    */
+    
+    /* Cache the connection to the window server */
+    static CGSConnectionID	cgsConnection = (CGSConnectionID) -1;
+    
+    /* Cache the dock icon windows */
+    static CGSWindowID          dockIcons[kMaxWindows];
+    static int                  numCachedDockIcons = 0;
+    
+    CGSWindowID		        windows[kMaxWindows];
+    CGSWindowCount	        i, count;
+    CGSWindowLevel	        winLevel;
+    CGSRect			winRect;
+
+    CGSRect contentRect;
+    int     windowNumber;
+    //int     isMainWindow;
+    int     firstDockIcon;    
+    int     dockIconCacheMiss;
+    int     windowContentOffset;
+    
+    int     obscured = SDL_TRUE;
+        
+    if ( [ window isVisible ] ) {
+        
+        /*  walk the window list looking for windows over top of 
+                (or casting a shadow on) ours */
+       
+        /* Get a connection to the window server */
+        /* Should probably be moved out into SetVideoMode() or InitVideo() */
+        if (cgsConnection == (CGSConnectionID) -1) {
+            cgsConnection = (CGSConnectionID) 0;
+            cgsConnection = _CGSDefaultConnection ();
+        }
+        
+        if (cgsConnection) { 
+        
+            if ( ! [ window styleMask ] & NSBorderlessWindowMask )
+                windowContentOffset = 22;
+            else
+                windowContentOffset = 0;
+                
+            windowNumber = [ window windowNumber ];
+            //isMainWindow = [ window isMainWindow ];
+            
+            /* The window list is sorted according to order on the screen */
+            count = 0;
+            CGSGetOnScreenWindowList (cgsConnection, 0, kMaxWindows, windows, &count);
+            CGSGetScreenRectForWindow (cgsConnection, windowNumber, &contentRect);
+    
+            /* adjust rect for window title bar (if present) */
+            contentRect.origin.y    += windowContentOffset;
+            contentRect.size.height -= windowContentOffset;
+            
+            firstDockIcon = -1;
+            dockIconCacheMiss = SDL_FALSE;
+            
+            /* The first window is always an empty window with level kCGSWindowLevelTop 
+               so start at index 1 */
+            for (i = 1; i < count; i++) {
+                
+                /* If we reach our window in the list, it cannot be obscured */
+                if (windows[i] == windowNumber) {
+                    
+                    obscured = SDL_FALSE;
+                    break;
+                }
+                else {
+                    
+                    float shadowSide;
+                    float shadowTop;
+                    float shadowBottom;
+
+                    CGSGetWindowLevel (cgsConnection, windows[i], &winLevel);
+                    
+                    if (winLevel == kCGSWindowLevelDockIcon) {
+                    
+                        int j;
+                        
+                        if (firstDockIcon < 0) {
+                            
+                            firstDockIcon = i;
+                        
+                            if (numCachedDockIcons > 0) {
+                            
+                                for (j = 0; j < numCachedDockIcons; j++) {
+                            
+                                    if (windows[i] == dockIcons[j])
+                                        i++;
+                                    else
+                                        break;
+                                }
+                            
+                                if (j != 0) {
+                                 
+                                    i--;
+                                                                    
+                                    if (j < numCachedDockIcons) {
+                                        
+                                        dockIconCacheMiss = SDL_TRUE;
+                                    }
+                                }
+
+                            }
+                        }
+                                               
+                        continue;
+                    }
+                    else if (winLevel == kCGSWindowLevelMenuIgnore
+                             /* winLevel == kCGSWindowLevelTop */) {
+                     
+                        continue; /* cannot obscure window */
+                    }
+                    else if (winLevel == kCGSWindowLevelDockMenu ||
+                             winLevel == kCGSWindowLevelMenu) {
+                     
+                        shadowSide = 18;
+                        shadowTop = 4;
+                        shadowBottom = 22;   
+                    }
+                    else if (winLevel == kCGSWindowLevelUtility) {
+                    
+                        shadowSide = 8;
+                        shadowTop = 4;
+                        shadowBottom = 12;
+                    }
+                    else if (winLevel == kCGSWindowLevelNormal) {
+                    
+                        /* These numbers are for foreground windows,
+                           they are too big (but will work) for background windows */
+                        shadowSide = 20;
+                        shadowTop = 10;
+                        shadowBottom = 24;
+                    }
+                    else if (winLevel == kCGSWindowLevelDock) {
+                    
+                        /* Create dock icon cache */
+                        if (numCachedDockIcons != (i-firstDockIcon) ||
+                            dockIconCacheMiss) {
+                        
+                            numCachedDockIcons = i - firstDockIcon;
+                            memcpy (dockIcons, &(windows[firstDockIcon]), 
+                                    numCachedDockIcons * sizeof(*windows));
+                        }
+                        
+                        /* no shadow */
+                        shadowSide = 0;
+                        shadowTop = 0;
+                        shadowBottom = 0;
+                    }
+                    else {
+                    
+                        /*   kCGSWindowLevelDockLabel,
+                             kCGSWindowLevelDock,
+                             kOther??? */
+                        
+                        /* no shadow */
+                        shadowSide = 0;
+                        shadowTop = 0;
+                        shadowBottom = 0;
+                    }
+                    
+                    CGSGetScreenRectForWindow (cgsConnection, windows[i], &winRect);
+                    
+                    winRect.origin.x -= shadowSide;
+                    winRect.origin.y -= shadowTop;
+                    winRect.size.width += shadowSide;
+                    winRect.size.height += shadowBottom;
+                    
+                    if (NSIntersectsRect (contentRect, winRect)) {
+                    
+                        obscured = SDL_TRUE;
+                        break;
+                    }
+               
+               } /* window was not our window */
+            
+            } /* iterate over windows */
+            
+        } /* get cgsConnection */
+    
+    } /* window is visible */
+    
+    return obscured;
+#else
+    return SDL_TRUE;
+#endif
+}
+
 static void QZ_UpdateRects (_THIS, int numRects, SDL_Rect *rects) { 
-    
+
     if (SDL_VideoSurface->flags & SDL_OPENGLBLIT) {
         QZ_GL_SwapBuffers (this);
     }
+    else if ( [ qz_window isMiniaturized ] && 
+              ! (SDL_VideoSurface->flags & SDL_OPENGL)) {
+    
+        /** 
+         * Set port alpha opaque so deminiaturize looks right
+         * This isn't so nice, but there is no 
+         * initial deminatureize notification (before demini starts)
+         **/
+        QZ_SetPortAlphaOpaque ([ [ qz_window contentView ] qdPort],
+                                [ qz_window styleMask ] & NSBorderlessWindowMask);
+    }
+    else if ( ! QZ_IsWindowObscured (qz_window) ) {
+        
+        /* Use direct copy to flush contents to the display */
+        CGrafPtr savePort;
+        CGrafPtr dstPort, srcPort;
+        const BitMap  *dstBits, *srcBits;
+        Rect     dstRect, srcRect;      
+        Point    offset;
+        int i;
+        
+        GetPort (&savePort);
+        
+        dstPort = CreateNewPortForCGDisplayID ((UInt32)display_id);
+        srcPort = [ window_view qdPort ];
+        
+        offset.h = 0;
+        offset.v = 0;
+        SetPort (srcPort);
+        LocalToGlobal (&offset);
+        
+        SetPort (dstPort);
+        
+        LockPortBits (dstPort);
+        LockPortBits (srcPort);
+        
+        dstBits = GetPortBitMapForCopyBits (dstPort);
+        srcBits = GetPortBitMapForCopyBits (srcPort);
+        
+        for (i = 0; i < numRects; i++) {
+            
+            SetRect (&srcRect, rects[i].x, rects[i].y,
+                     rects[i].x + rects[i].w,
+                     rects[i].y + rects[i].h);
+            
+            SetRect (&dstRect,
+                     rects[i].x + offset.h, 
+                     rects[i].y + offset.v,
+                     rects[i].x + rects[i].w + offset.h,
+                     rects[i].y + rects[i].h + offset.v);
+                        
+            CopyBits (srcBits, dstBits,
+                      &srcRect, &dstRect, srcCopy, NULL);
+                        
+        }
+        
+        SetPort (savePort);
+    }
     else {
+    
+        /* Use QDFlushPortBuffer() to flush content to display */
         int i;
         RgnHandle dirty = NewRgn ();
         RgnHandle temp  = NewRgn ();
@@ -582,7 +973,7 @@
         }
         
         /* Flush the dirty region */
-        QDFlushPortBuffer ( [ windowView qdPort ], dirty );
+        QDFlushPortBuffer ( [ window_view qdPort ], dirty );
         DisposeRgn (dirty);
         DisposeRgn (temp);
     }
@@ -597,6 +988,7 @@
 static int  QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color) {
 
     CGSDisplayHWFill (display_id, rect->x, rect->y, rect->w, rect->h, color);
+
     return 0;
 }
 
@@ -605,7 +997,8 @@
     return 1;
 }
 
-static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface) { 
+static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface) {
+
 }
 
 static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface) {
@@ -621,24 +1014,43 @@
 static int QZ_SetGamma (_THIS, float red, float green, float blue) {
 
     const CGGammaValue min = 0.0, max = 1.0;
+
+    if (red == 0.0)
+        red = FLT_MAX;
+    else
+        red = 1.0 / red;
+
+    if (green == 0.0)
+        green = FLT_MAX;
+    else
+        green = 1.0 / green;
+
+    if (blue == 0.0)
+        blue = FLT_MAX;
+    else
+        blue  = 1.0 / blue;
     
-    if ( CGDisplayNoErr != CGSetDisplayTransferByFormula 
-        (display_id, min, max, red, min, max, green, min, max, blue) )
+    if ( CGDisplayNoErr == CGSetDisplayTransferByFormula 
+        (display_id, min, max, red, min, max, green, min, max, blue) ) {
+            
+        return 0;
+    }
+    else {
+    
         return -1;
-    
-    return 0;
+    }
 }
 
 static int QZ_GetGamma (_THIS, float *red, float *green, float *blue) {
 
     CGGammaValue dummy;
-    if ( CGDisplayNoErr != CGGetDisplayTransferByFormula
+    if ( CGDisplayNoErr == CGGetDisplayTransferByFormula
         (display_id, &dummy, &dummy, red, 
 	 &dummy, &dummy, green, &dummy, &dummy, blue) )
         
+        return 0;
+    else
         return -1;
-    
-    return 0;
 }
 
 static int QZ_SetGammaRamp (_THIS, Uint16 *ramp) {
@@ -660,11 +1072,11 @@
    for (i=512; i < 768; i++)
      blueTable[i % 256] = ramp[i] / 65535.0;
      
-    if ( CGDisplayNoErr != CGSetDisplayTransferByTable 
+    if ( CGDisplayNoErr == CGSetDisplayTransferByTable 
             (display_id, tableSize, redTable, greenTable, blueTable) )        
+        return 0;
+    else
         return -1;
-    
-    return 0;
 }
 
 static int QZ_GetGammaRamp (_THIS, Uint16 *ramp) {
@@ -695,7 +1107,8 @@
     return 0;    
 }
 
-/* OpenGL helper functions */
+/* OpenGL helper functions (used internally) */
+
 static int QZ_SetupOpenGL (_THIS, int bpp, Uint32 flags) {
 
     NSOpenGLPixelFormatAttribute attr[32];
@@ -761,6 +1174,7 @@
     [ gl_context release ];
 }
 
+
 /* SDL OpenGL functions */
 
 static int    QZ_GL_LoadLibrary    (_THIS, const char *location) {
@@ -805,6 +1219,7 @@
     
     CGLGetParameter (ctx, param, (long*)value);
 */
+
     *value = -1;
     return -1;
 }
@@ -817,3 +1232,5 @@
 static void   QZ_GL_SwapBuffers    (_THIS) {
     [ gl_context flushBuffer ];
 }
+
+