Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58210 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=38975
Supersedes https://gitlab.winehq.org/wine/wine/-/merge_requests/8013
-- v7: server: Update fill window invalidate rect with window resize. server: Pass NULL as child region if region is NULL. user32/tests: Add tests for NULL invalidated rect with window resize.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/user32/tests/win.c | 137 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 7d84bef71d7..8ccf2f8cdf3 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -127,6 +127,17 @@ static void flush_events( BOOL remove_messages ) } }
+static void pump_messages(void) +{ + MSG msg; + + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageA(&msg); + } +} + /* check the values returned by the various parent/owner functions on a given window */ static void check_parents( HWND hwnd, HWND ga_parent, HWND gwl_parent, HWND get_parent, HWND gw_owner, HWND ga_root, HWND ga_root_owner ) @@ -10882,6 +10893,8 @@ static void test_update_region(void) const RECT rc = {15, 15, 40, 40}; const POINT wnd_orig = {30, 20}; const POINT child_orig = {10, 5}; + RECT r, expect_rect; + BOOL bret;
parent = CreateWindowExA(0, "MainWindowClass", NULL, WS_VISIBLE | WS_CLIPCHILDREN, @@ -10939,7 +10952,131 @@ static void test_update_region(void)
DeleteObject(rgn1); DeleteObject(rgn2); + + pump_messages(); + /* Test that NULL invalidated region means current full client rect and not the one at the moment of + * invalidation. */ + ValidateRect(parent, NULL); + GetUpdateRect(parent, &r, FALSE); + SetRect(&expect_rect, 0, 0, 0, 0); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + InvalidateRect(parent, NULL, FALSE); + SetRect(&r, 0, 0, 10, 10); + /* Adding a rectangle to NULL one still keeps that as full window. */ + InvalidateRect(parent, &r, FALSE); + GetUpdateRect(parent, &r, FALSE); + GetClientRect(parent, &expect_rect); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + SetWindowPos(parent, NULL, 0, 0, 350, 200, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + GetUpdateRect(parent, &r, FALSE); + GetClientRect(parent, &expect_rect); + todo_wine ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + ValidateRect(parent, NULL); + + SetWindowPos(parent, NULL, 0, 0, 300, 150, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + GetUpdateRect(parent, &r, FALSE); + SetRect(&expect_rect, 0, 0, 0, 0); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + RedrawWindow(parent, NULL, 0, RDW_INVALIDATE | RDW_FRAME); + RedrawWindow(parent, NULL, 0, RDW_INVALIDATE); + GetUpdateRect(parent, &r, FALSE); + GetClientRect(parent, &expect_rect); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + SetWindowPos(parent, NULL, 0, 0, 350, 200, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + GetUpdateRect(parent, &r, FALSE); + GetClientRect(parent, &expect_rect); + todo_wine ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + pump_messages(); + RedrawWindow(parent, NULL, 0, RDW_VALIDATE | RDW_FRAME | RDW_ALLCHILDREN); + + ValidateRect(hwnd, NULL); + SetRect(&expect_rect, 0, 0, 0, 0); + GetUpdateRect(hwnd, &r, FALSE); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + GetClientRect(hwnd, &expect_rect); + RedrawWindow(parent, NULL, 0, RDW_INVALIDATE | RDW_ALLCHILDREN); + GetUpdateRect(hwnd, &r, FALSE); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + SetWindowPos(hwnd, NULL, 0, 0, 205, 105, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + SetWindowPos(hwnd, NULL, 0, 0, 210, 110, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + GetUpdateRect(hwnd, &r, FALSE); + GetClientRect(hwnd, &expect_rect); + todo_wine ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + ValidateRect(hwnd, NULL); + ValidateRect(parent, NULL); + SetWindowPos(hwnd, NULL, 0, 0, 200, 100, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + + SetRect(&expect_rect, 0, 0, 0, 0); + GetUpdateRect(hwnd, &r, FALSE); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + GetClientRect(hwnd, &expect_rect); + InvalidateRect(hwnd, NULL, FALSE); + GetUpdateRect(hwnd, &r, FALSE); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + SetWindowPos(hwnd, NULL, 0, 0, 210, 110, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + GetUpdateRect(hwnd, &r, FALSE); + GetClientRect(hwnd, &expect_rect); + todo_wine ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + ValidateRect(hwnd, NULL); + ValidateRect(parent, NULL); + SetWindowPos(hwnd, NULL, 0, 0, 200, 100, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + + SetRect(&expect_rect, 0, 0, 0, 0); + GetUpdateRect(hwnd, &r, FALSE); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + GetClientRect(hwnd, &expect_rect); + InvalidateRect(hwnd, NULL, FALSE); + GetUpdateRect(hwnd, &r, FALSE); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + /* Child window bottom is outside parent window. Invalidated area is still new child window extents + * coordinates cropped to visible part. */ + SetWindowPos(hwnd, NULL, 0, 150, 210, 100, SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + GetClientRect(hwnd, &expect_rect); + GetClientRect(parent, &r); + expect_rect.bottom = r.bottom - 150; + GetUpdateRect(hwnd, &r, FALSE); + todo_wine ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + ValidateRect(hwnd, NULL); + ValidateRect(parent, NULL); + SetWindowPos(hwnd, NULL, 0, 0, 200, 100, SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + + SetWindowPos(parent, NULL, 0, 0, 300, 150, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + GetUpdateRect(parent, &r, FALSE); + SetRect(&expect_rect, 0, 0, 0, 0); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + + GetClientRect(parent, &r); + InvalidateRect(parent, &r, FALSE); + expect_rect = r; + GetUpdateRect(parent, &r, FALSE); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + SetWindowPos(parent, NULL, 0, 0, 350, 200, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + GetUpdateRect(parent, &r, FALSE); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + GetClientRect(parent, &r); + ok(!EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + + ValidateRect(parent, NULL); + SetWindowPos(parent, NULL, 0, 0, 300, 150, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + GetUpdateRect(parent, &r, FALSE); + SetRect(&expect_rect, 0, 0, 0, 0); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + InvalidateRect(parent, NULL, FALSE); + SetRect(&r, 0, 0, 0, 0); + /* Subtracting empty rectangle from update region turns 'full client rect' into the specific coordinates + * region (unlike adding rectangle). */ + bret = ValidateRect(parent, &r); + ok(bret, "got error %lu.\n", GetLastError()); + GetClientRect(parent, &expect_rect); + GetUpdateRect(parent, &r, FALSE); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + SetWindowPos(parent, NULL, 0, 0, 350, 200, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); + GetUpdateRect(parent, &r, FALSE); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + + ValidateRect(parent, NULL); DestroyWindow(parent); + pump_messages(); }
static void test_window_without_child_style(void)
From: Rémi Bernon rbernon@codeweavers.com
--- server/window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/window.c b/server/window.c index c59b8646118..9d59e0837bc 100644 --- a/server/window.c +++ b/server/window.c @@ -1690,7 +1690,7 @@ static void redraw_window( struct window *win, struct region *region, unsigned i if (rect_in_region( child_rgn, &child->window_rect )) { offset_region( child_rgn, -child->client_rect.left, -child->client_rect.top ); - redraw_window( child, child_rgn, flags, 1 ); + redraw_window( child, region ? child_rgn : NULL, flags, 1 ); } } free_region( child_rgn );
From: Paul Gofman pgofman@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58210 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=38975 --- dlls/user32/tests/win.c | 10 +++++----- server/window.c | 9 ++++++++- 2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 8ccf2f8cdf3..6f5733a6a66 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -10970,7 +10970,7 @@ static void test_update_region(void) SetWindowPos(parent, NULL, 0, 0, 350, 200, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); GetUpdateRect(parent, &r, FALSE); GetClientRect(parent, &expect_rect); - todo_wine ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); ValidateRect(parent, NULL);
SetWindowPos(parent, NULL, 0, 0, 300, 150, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); @@ -10985,7 +10985,7 @@ static void test_update_region(void) SetWindowPos(parent, NULL, 0, 0, 350, 200, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); GetUpdateRect(parent, &r, FALSE); GetClientRect(parent, &expect_rect); - todo_wine ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); pump_messages(); RedrawWindow(parent, NULL, 0, RDW_VALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
@@ -11001,7 +11001,7 @@ static void test_update_region(void) SetWindowPos(hwnd, NULL, 0, 0, 210, 110, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); GetUpdateRect(hwnd, &r, FALSE); GetClientRect(hwnd, &expect_rect); - todo_wine ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); ValidateRect(hwnd, NULL); ValidateRect(parent, NULL); SetWindowPos(hwnd, NULL, 0, 0, 200, 100, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); @@ -11016,7 +11016,7 @@ static void test_update_region(void) SetWindowPos(hwnd, NULL, 0, 0, 210, 110, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); GetUpdateRect(hwnd, &r, FALSE); GetClientRect(hwnd, &expect_rect); - todo_wine ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); ValidateRect(hwnd, NULL); ValidateRect(parent, NULL); SetWindowPos(hwnd, NULL, 0, 0, 200, 100, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); @@ -11035,7 +11035,7 @@ static void test_update_region(void) GetClientRect(parent, &r); expect_rect.bottom = r.bottom - 150; GetUpdateRect(hwnd, &r, FALSE); - todo_wine ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); + ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect)); ValidateRect(hwnd, NULL); ValidateRect(parent, NULL); SetWindowPos(hwnd, NULL, 0, 0, 200, 100, SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); diff --git a/server/window.c b/server/window.c index 9d59e0837bc..4bdf78bfa1f 100644 --- a/server/window.c +++ b/server/window.c @@ -134,6 +134,7 @@ static const struct object_ops window_ops = #define PAINT_NONCLIENT 0x0040 /* needs WM_NCPAINT */ #define PAINT_DELAYED_ERASE 0x0080 /* still needs erase after WM_ERASEBKGND */ #define PAINT_PIXEL_FORMAT_CHILD 0x0100 /* at least one child has a custom pixel format */ +#define PAINT_INVALIDATED 0x0200 /* window has been fully invalidated */
/* growable array of user handles */ struct user_handle_array @@ -1466,7 +1467,7 @@ static void set_update_region( struct window *win, struct region *region ) inc_window_paint_count( win, -1 ); free_region( win->update_region ); } - win->paint_flags &= ~(PAINT_ERASE | PAINT_DELAYED_ERASE | PAINT_NONCLIENT); + win->paint_flags &= ~(PAINT_ERASE | PAINT_DELAYED_ERASE | PAINT_NONCLIENT | PAINT_INVALIDATED); win->update_region = NULL; if (region) free_region( region ); } @@ -1629,6 +1630,7 @@ static void redraw_window( struct window *win, struct region *region, unsigned i
if (!add_update_region( win, tmp )) return;
+ if (!region) win->paint_flags |= PAINT_INVALIDATED; if (flags & RDW_FRAME) win->paint_flags |= PAINT_NONCLIENT; if (flags & RDW_ERASE) win->paint_flags |= PAINT_ERASE; } @@ -1653,6 +1655,7 @@ static void redraw_window( struct window *win, struct region *region, unsigned i if (flags & RDW_NOFRAME) validate_non_client( win ); if (flags & RDW_NOERASE) win->paint_flags &= ~(PAINT_ERASE | PAINT_DELAYED_ERASE); } + win->paint_flags &= ~PAINT_INVALIDATED; }
if ((flags & RDW_INTERNALPAINT) && !(win->paint_flags & PAINT_INTERNAL)) @@ -1964,6 +1967,10 @@ static void set_window_pos( struct window *win, struct window *previous,
if (win->update_region) { + int frame = win->paint_flags & PAINT_NONCLIENT; + if ((win->paint_flags & PAINT_INVALIDATED) && get_window_visible_rect( win, &rect, frame )) + set_region_rect( win->update_region, &rect ); + if (get_window_visible_rect( win, &rect, 1 )) { struct region *tmp = create_empty_region();
v4: Rebase to fix conflicts.
Jinoh Kang (@iamahuman) commented about dlls/user32/tests/win.c:
- SetWindowPos(parent, NULL, 0, 0, 350, 200, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
- GetUpdateRect(parent, &r, FALSE);
- GetClientRect(parent, &expect_rect);
- ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect));
- pump_messages();
- RedrawWindow(parent, NULL, 0, RDW_VALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
- ValidateRect(hwnd, NULL);
- SetRect(&expect_rect, 0, 0, 0, 0);
- GetUpdateRect(hwnd, &r, FALSE);
- ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect));
- GetClientRect(hwnd, &expect_rect);
- RedrawWindow(parent, NULL, 0, RDW_INVALIDATE | RDW_ALLCHILDREN);
- GetUpdateRect(hwnd, &r, FALSE);
- ok(EqualRect(&r, &expect_rect), "got %s, expected %s.\n", wine_dbgstr_rect(&r), wine_dbgstr_rect(&expect_rect));
- SetWindowPos(hwnd, NULL, 0, 0, 205, 105, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
Sorry, what's the purpose of this extra SWP?
On Tue Jun 3 10:34:47 2025 +0000, Jinoh Kang wrote:
Sorry, what's the purpose of this extra SWP?
To check that the "whole window invalidated" state is kept across SetWindowPos calls, until the window is actually validated at which point only the window rect should be latched into the update region.