winemac: Set windows to transparent until they have content to draw, to reduce flicker.

Ken Thomases ken at codeweavers.com
Fri Sep 2 03:08:01 CDT 2016


When a window is shown, it may not have drawn its content into the backing
surface, yet.  Cocoa will draw the window, starting with its standard light
gray background and then the content view.  However, the content view won't
have anything to draw, yet, though, so the window background is not drawn over.

A short while later, usually, the app will paint its content into the window
backing surface and Cocoa will be told to redraw the window.  This works, but
the user can often see the flash of the window background color first.  This
is especially visible for windows with dark content.

Part of the fix is to set the window background to transparent until the
content view has actually drawn once since the window was shown.

That's not sufficient on its own, though.  We had disabled Cocoa's automatic
display mechanism for windows and put display on a display-link timer.  This
meant that the window was not actually cleared to its transparent color.  When
the window was shown, the Window Server displayed a white backing buffer.  It
is the app process which should fill that backing buffer with clear color but,
because we had disabled auto-display, that wasn't getting done at the same
time the window was displayed.  It was happening some time after.  Again, the
result was a visible flicker of white.

So, we now temporarily re-enable auto-display just before showing a window.

Signed-off-by: Ken Thomases <ken at codeweavers.com>
---
 dlls/winemac.drv/cocoa_window.h |  1 +
 dlls/winemac.drv/cocoa_window.m | 31 ++++++++++++++++++++++++++++---
 2 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h
index d52a9d2..f037b08 100644
--- a/dlls/winemac.drv/cocoa_window.h
+++ b/dlls/winemac.drv/cocoa_window.h
@@ -34,6 +34,7 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
     BOOL fullscreen;
     BOOL pendingMinimize;
     BOOL savedVisibleState;
+    BOOL drawnSinceShown;
     WineWindow* latentParentWindow;
     NSMutableArray* latentChildWindows;
 
diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m
index c6e0709..8e8ac9f 100644
--- a/dlls/winemac.drv/cocoa_window.m
+++ b/dlls/winemac.drv/cocoa_window.m
@@ -317,6 +317,7 @@ @interface WineWindow ()
 @property (readwrite, nonatomic) BOOL disabled;
 @property (readwrite, nonatomic) BOOL noActivate;
 @property (readwrite, nonatomic) BOOL floating;
+ at property (readwrite, nonatomic) BOOL drawnSinceShown;
 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
 @property (retain, nonatomic) NSWindow* latentParentWindow;
 
@@ -346,6 +347,8 @@ - (void) updateForGLSubviews;
     - (BOOL) becameEligibleParentOrChild;
     - (void) becameIneligibleChild;
 
+    - (void) windowDidDrawContent;
+
 @end
 
 
@@ -383,7 +386,7 @@ - (void) drawRect:(NSRect)rect
         if ([window contentView] != self)
             return;
 
-        if (window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
+        if (window.drawnSinceShown && window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
         {
             [[NSColor clearColor] setFill];
             NSRectFill(rect);
@@ -441,6 +444,8 @@ - (void) drawRect:(NSRect)rect
                         CGImageRelease(image);
                     }
                 }
+
+                [window windowDidDrawContent];
             }
 
             pthread_mutex_unlock(window.surface_mutex);
@@ -449,7 +454,7 @@ - (void) drawRect:(NSRect)rect
         // If the window may be transparent, then we have to invalidate the
         // shadow every time we draw.  Also, if this is the first time we've
         // drawn since changing from transparent to opaque.
-        if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
+        if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
         {
             window.shapeChangedSinceLastDraw = FALSE;
             [window invalidateShadow];
@@ -794,6 +799,7 @@ @implementation WineWindow
     static WineWindow* causing_becomeKeyWindow;
 
     @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
+    @synthesize drawnSinceShown;
     @synthesize surface, surface_mutex;
     @synthesize shape, shapeData, shapeChangedSinceLastDraw;
     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
@@ -831,7 +837,8 @@ + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)w
         [window setAcceptsMouseMovedEvents:YES];
         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
         [window setDelegate:window];
-        [window setAutodisplay:NO];
+        [window setBackgroundColor:[NSColor clearColor]];
+        [window setOpaque:NO];
         window.hwnd = hwnd;
         window.queue = queue;
         window->savedContentMinSize = NSZeroSize;
@@ -1191,6 +1198,8 @@ - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
         {
             if ([self level] > [child level])
                 [child setLevel:[self level]];
+            if (![child isVisible])
+                [child setAutodisplay:YES];
             [self addChildWindow:child ordered:NSWindowAbove];
             [child checkWineDisplayLink];
             [latentChildWindows removeObjectIdenticalTo:child];
@@ -1455,6 +1464,7 @@ - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)a
                     // Then the levels get fixed by -adjustWindowLevels.
                     if ([self level] != [other level])
                         [self setLevel:[other level]];
+                    [self setAutodisplay:YES];
                     [self orderWindow:orderingMode relativeTo:[other windowNumber]];
                     [self checkWineDisplayLink];
 
@@ -1474,6 +1484,7 @@ - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)a
                 next = [controller frontWineWindow];
                 if (next && [self level] < [next level])
                     [self setLevel:[next level]];
+                [self setAutodisplay:YES];
                 [self orderFront:nil];
                 [self checkWineDisplayLink];
                 needAdjustWindowLevels = TRUE;
@@ -1530,6 +1541,9 @@ - (void) doOrderOut
         else
             [self orderOut:nil];
         [self checkWineDisplayLink];
+        [self setBackgroundColor:[NSColor clearColor]];
+        [self setOpaque:NO];
+        drawnSinceShown = NO;
         savedVisibleState = FALSE;
         if (wasVisible && wasOnActiveSpace && fullscreen)
             [controller updateFullscreenWindows];
@@ -2266,6 +2280,17 @@ - (void) displayIfNeeded
         [self setAutodisplay:NO];
     }
 
+    - (void) windowDidDrawContent
+    {
+        if (!drawnSinceShown)
+        {
+            drawnSinceShown = YES;
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [self checkTransparency];
+            });
+        }
+    }
+
     - (NSArray*) childWineWindows
     {
         NSArray* childWindows = self.childWindows;
-- 
2.8.2




More information about the wine-patches mailing list