Skip to content

Commit f7b9c46

Browse files
committed
Windows: no white flash when a shaped window's region changes
SetWindowRgn with redraw erased the area a new region exposed, and a window whose content comes from a child swap chain (Flutter's view) showed it white until the child presented again: a white flash on every frame of an animated contour. Set the region without redraw and invalidate the children without an erase instead. A window with a region also has no DWM frame, so an activation change painted the classic caption over the content of a window with a hidden title bar. Pass lParam -1 to WM_NCACTIVATE there to skip that repaint.
1 parent 676f518 commit f7b9c46

1 file changed

Lines changed: 21 additions & 6 deletions

File tree

‎src/platform/windows/window_windows.cpp‎

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,15 @@ static std::optional<LRESULT> HandleHiddenTitleBarFrame(HWND hwnd, UINT message,
314314
const LRESULT top = TopResizeHit(hwnd, lp);
315315
return top != 0 ? top : hit;
316316
}
317+
if (message == WM_NCACTIVATE) {
318+
// A window with a region has no DWM frame, so an activation change repaints the
319+
// classic caption, over the content that took its place. lParam -1 keeps the
320+
// activation handling but skips that repaint.
321+
HRGN region = CreateRectRgn(0, 0, 0, 0);
322+
const bool shaped = region && GetWindowRgn(hwnd, region) != ERROR;
323+
if (region) DeleteObject(region);
324+
if (shaped) return DefSubclassProc(hwnd, message, wp, -1);
325+
}
317326
return std::nullopt;
318327
}
319328
#endif
@@ -346,7 +355,8 @@ static LRESULT CALLBACK WindowLifetimeProc(HWND hwnd, UINT message, WPARAM wp, L
346355
return result;
347356
}
348357
#ifndef NATIVEAPI_ENABLE_WINUI3
349-
if (message == WM_NCCALCSIZE || message == WM_NCHITTEST || message == WM_PARENTNOTIFY) {
358+
if (message == WM_NCCALCSIZE || message == WM_NCHITTEST || message == WM_PARENTNOTIFY ||
359+
message == WM_NCACTIVATE) {
350360
if (auto handled = HandleHiddenTitleBarFrame(hwnd, message, wp, lp)) return *handled;
351361
}
352362
#endif
@@ -1786,15 +1796,15 @@ bool Window::SetShape(std::shared_ptr<WindowShape> shape) {
17861796
HWND hwnd = pimpl_->hwnd_;
17871797
if (!IsWindow(hwnd)) return false;
17881798
if (!shape) {
1789-
if (!SetWindowRgn(hwnd, nullptr, TRUE)) return false;
1799+
if (!SetWindowRgn(hwnd, nullptr, FALSE)) return false;
17901800
if (HasShadow() && GetTitleBarStyle() == TitleBarStyle::Hidden) shape_shadow::Refresh(hwnd);
17911801
else shape_shadow::Clear(hwnd);
17921802
// A changed top-level region can invalidate child composition surfaces
17931803
// without requesting their paint (e.g. Flutter's view). Repaint the whole
17941804
// hierarchy so retained content is visible without another user interaction.
17951805
// Let WM_PAINT run normally: a synchronous paint here can precede the
1796-
// embedding framework's pending layout/frame update.
1797-
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ALLCHILDREN);
1806+
// embedding framework's pending layout/frame update. No erase: see below.
1807+
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_NOERASE | RDW_ALLCHILDREN);
17981808
return true;
17991809
}
18001810
if (shape->GetPointCount() < 3 || GetTitleBarStyle() != TitleBarStyle::Hidden) return false;
@@ -1810,7 +1820,12 @@ bool Window::SetShape(std::shared_ptr<WindowShape> shape) {
18101820
}
18111821
HRGN region = CreatePolygonRgn(points.data(), static_cast<int>(points.size()), ALTERNATE);
18121822
if (!region) return false;
1813-
if (!SetWindowRgn(hwnd, region, TRUE)) {
1823+
// No redraw from SetWindowRgn itself: it erases what the new region exposes, and a
1824+
// window whose content comes from a child swap chain (Flutter's view) shows that
1825+
// erased area white until the child presents again, which during an animated
1826+
// contour is a white flash on every frame the region grows. The children are
1827+
// invalidated below without an erase instead.
1828+
if (!SetWindowRgn(hwnd, region, FALSE)) {
18141829
DeleteObject(region);
18151830
return false;
18161831
}
@@ -1821,7 +1836,7 @@ bool Window::SetShape(std::shared_ptr<WindowShape> shape) {
18211836
if (copy && GetWindowRgn(hwnd, copy) != ERROR) shape_shadow::Update(hwnd, copy, scale);
18221837
if (copy) DeleteObject(copy);
18231838
}
1824-
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ALLCHILDREN);
1839+
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_NOERASE | RDW_ALLCHILDREN);
18251840
return true;
18261841
}
18271842

0 commit comments

Comments
 (0)