These patches fix painting in an application that draws outside of a CS_PARENTDC child client area in its WM_PAINT handler.
Comments and suggestions are welcome.
From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- server/window.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/server/window.c b/server/window.c index 242e93f303a..fc16acc1822 100644 --- a/server/window.c +++ b/server/window.c @@ -1530,7 +1530,17 @@ static void redraw_window( struct window *win, struct region *region, int frame, { if ((tmp = crop_region_to_win_rect( win, region, frame ))) { - if (!subtract_region( tmp, win->update_region, tmp )) + rectangle_t rect = win->window_rect; + + offset_rect( &rect, -rect.left, -rect.top ); + + /* region covers whole window: validate everything */ + if (rect_in_region( tmp, &rect )) + { + free_region( tmp ); + tmp = NULL; + } + else if (!subtract_region( tmp, win->update_region, tmp )) { free_region( tmp ); return;
From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- server/window.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/server/window.c b/server/window.c index fc16acc1822..cc48b8343ee 100644 --- a/server/window.c +++ b/server/window.c @@ -1313,7 +1313,19 @@ static struct region *crop_region_to_win_rect( struct window *win, struct region rectangle_t rect; struct region *tmp;
- if (!get_window_visible_rect( win, &rect, frame )) return NULL; + if (win->parent && is_window_using_parent_dc( win )) + { + int offset_x, offset_y; + + if (!get_window_visible_rect( win->parent, &rect, 0 )) return NULL; + + offset_x = rect.left + (frame ? win->window_rect.left : win->client_rect.left); + offset_y = rect.top + (frame ? win->window_rect.top : win->client_rect.top); + offset_rect( &rect, -offset_x, -offset_y ); + } + else + if (!get_window_visible_rect( win, &rect, frame )) return NULL; + if (!(tmp = create_empty_region())) return NULL; set_region_rect( tmp, &rect );
From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/win32u/dce.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/dlls/win32u/dce.c b/dlls/win32u/dce.c index 5df30550cae..b7aea7b8699 100644 --- a/dlls/win32u/dce.c +++ b/dlls/win32u/dce.c @@ -1237,6 +1237,8 @@ static BOOL send_erase( HWND hwnd, UINT flags, HRGN client_rgn, HDC hdc = 0; RECT dummy;
+ TRACE( "hwnd %p, flags %08x, client_rgn %p\n", hwnd, flags, client_rgn ); + if (!clip_rect) clip_rect = &dummy; if (hdc_ret || (flags & UPDATE_ERASE)) { @@ -1247,6 +1249,13 @@ static BOOL send_erase( HWND hwnd, UINT flags, HRGN client_rgn, { INT type = NtGdiGetAppClipBox( hdc, clip_rect );
+ if (get_class_long( hwnd, GCL_STYLE, FALSE ) & CS_PARENTDC) + { + RECT client_rect; + get_client_rect( hwnd, &client_rect ); + intersect_rect( clip_rect, clip_rect, &client_rect ); + } + if (flags & UPDATE_ERASE) { /* don't erase if the clip box is empty */
From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/user32/tests/dce.c | 8 ++++---- dlls/user32/tests/win.c | 24 ++++++++++++------------ dlls/win32u/dce.c | 35 ++++++++++++++++++----------------- 3 files changed, 34 insertions(+), 33 deletions(-)
diff --git a/dlls/user32/tests/dce.c b/dlls/user32/tests/dce.c index 18f31e3708c..64becb6084d 100644 --- a/dlls/user32/tests/dce.c +++ b/dlls/user32/tests/dce.c @@ -515,10 +515,10 @@ static void test_begin_paint(void) EndPaint( hwnd_parentdc, &ps ); GetClientRect( hwnd_parent, &parent_rect );
- todo_wine ok( rect.left == parent_rect.left, "rect.left = %ld, expected %ld\n", rect.left, parent_rect.left ); - todo_wine ok( rect.top == parent_rect.top, "rect.top = %ld, expected %ld\n", rect.top, parent_rect.top ); - todo_wine ok( rect.right == parent_rect.right, "rect.right = %ld, expected %ld\n", rect.right, parent_rect.right ); - todo_wine ok( rect.bottom == parent_rect.bottom, "rect.bottom = %ld, expected %ld\n", rect.bottom, parent_rect.bottom ); + ok( rect.left == parent_rect.left, "rect.left = %ld, expected %ld\n", rect.left, parent_rect.left ); + ok( rect.top == parent_rect.top, "rect.top = %ld, expected %ld\n", rect.top, parent_rect.top ); + ok( rect.right == parent_rect.right, "rect.right = %ld, expected %ld\n", rect.right, parent_rect.right ); + ok( rect.bottom == parent_rect.bottom, "rect.bottom = %ld, expected %ld\n", rect.bottom, parent_rect.bottom );
hdc = GetDC( hwnd_parent ); todo_wine ok( GetPixel( hdc, 60, 60 ) == cr, "error drawing outside of window client area\n" ); diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 775164e3e9f..b92c386251b 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -6274,49 +6274,49 @@ static void test_csparentdc(void) struct parentdc_test test_answer;
#define nothing_todo {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}} - const struct parentdc_test test1 = + const struct parentdc_test test1 = { {{0, 0, 150, 150}, {0, 0, 150, 150}, {0, 0, 150, 150}}, nothing_todo, - {{0, 0, 40, 40}, {-20, -20, 130, 130}, {0, 0, 40, 40}}, {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}}, - {{0, 0, 40, 40}, {-40, -40, 110, 110}, {0, 0, 40, 40}}, {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}}, + {{0, 0, 40, 40}, {-20, -20, 130, 130}, {0, 0, 40, 40}}, nothing_todo, + {{0, 0, 40, 40}, {-40, -40, 110, 110}, {0, 0, 40, 40}}, nothing_todo, };
- const struct parentdc_test test2 = + const struct parentdc_test test2 = { {{0, 0, 150, 150}, {0, 0, 50, 50}, {0, 0, 50, 50}}, nothing_todo, - {{0, 0, 40, 40}, {-20, -20, 30, 30}, {0, 0, 30, 30}}, {{0, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}}, - {{0, 0, 40, 40}, {-40, -40, 10, 10}, {0, 0, 10, 10}}, {{0, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}}, + {{0, 0, 40, 40}, {-20, -20, 30, 30}, {0, 0, 30, 30}}, nothing_todo, + {{0, 0, 40, 40}, {-40, -40, 10, 10}, {0, 0, 10, 10}}, nothing_todo, };
- const struct parentdc_test test3 = + const struct parentdc_test test3 = { {{0, 0, 150, 150}, {0, 0, 10, 10}, {0, 0, 10, 10}}, nothing_todo, {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo, {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo, };
- const struct parentdc_test test4 = + const struct parentdc_test test4 = { {{0, 0, 150, 150}, {40, 40, 50, 50}, {40, 40, 50, 50}}, nothing_todo, {{0, 0, 40, 40}, {20, 20, 30, 30}, {20, 20, 30, 30}}, nothing_todo, {{0, 0, 40, 40}, {0, 0, 10, 10}, {0, 0, 10, 10}}, nothing_todo, };
- const struct parentdc_test test5 = + const struct parentdc_test test5 = { {{0, 0, 150, 150}, {20, 20, 60, 60}, {20, 20, 60, 60}}, nothing_todo, {{0, 0, 40, 40}, {-20, -20, 130, 130}, {0, 0, 40, 40}}, {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}}, - {{0, 0, 40, 40}, {-20, -20, 20, 20}, {0, 0, 20, 20}}, {{0, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}}, + {{0, 0, 40, 40}, {-20, -20, 20, 20}, {0, 0, 20, 20}}, nothing_todo, };
- const struct parentdc_test test6 = + const struct parentdc_test test6 = { {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo, {{0, 0, 40, 40}, {0, 0, 10, 10}, {0, 0, 10, 10}}, nothing_todo, {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo, };
- const struct parentdc_test test7 = + const struct parentdc_test test7 = { {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo, {{0, 0, 40, 40}, {-20, -20, 130, 130}, {0, 0, 40, 40}}, {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}}, diff --git a/dlls/win32u/dce.c b/dlls/win32u/dce.c index b7aea7b8699..5fff59570f7 100644 --- a/dlls/win32u/dce.c +++ b/dlls/win32u/dce.c @@ -1163,10 +1163,13 @@ static BOOL get_update_flags( HWND hwnd, HWND *child, UINT *flags ) */ static HRGN send_ncpaint( HWND hwnd, HWND *child, UINT *flags ) { - HRGN whole_rgn = get_update_region( hwnd, flags, child ); - HRGN client_rgn = 0; + HRGN whole_rgn; DWORD style;
+ TRACE( "hwnd %p, flags %08x\n", hwnd, *flags ); + + whole_rgn = get_update_region( hwnd, flags, child ); + if (child) hwnd = *child;
if (hwnd == get_desktop_window()) return whole_rgn; @@ -1176,6 +1179,7 @@ static HRGN send_ncpaint( HWND hwnd, HWND *child, UINT *flags ) DPI_AWARENESS_CONTEXT context; RECT client, window, update; INT type; + HRGN nc_rgn = 0;
context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
@@ -1187,23 +1191,17 @@ static HRGN send_ncpaint( HWND hwnd, HWND *child, UINT *flags ) update.left < client.left || update.top < client.top || update.right > client.right || update.bottom > client.bottom) { - client_rgn = NtGdiCreateRectRgn( client.left, client.top, client.right, client.bottom ); - NtGdiCombineRgn( client_rgn, client_rgn, whole_rgn, RGN_AND ); - /* check if update rgn contains complete nonclient area */ - if (type == SIMPLEREGION && EqualRect( &window, &update )) + if (type == SIMPLEREGION && update.left <= window.left && update.top <= window.top && + update.right >= window.right && update.bottom >= window.bottom) { - NtGdiDeleteObjectApp( whole_rgn ); - whole_rgn = (HRGN)1; + nc_rgn = (HRGN)1; } - } - else - { - client_rgn = whole_rgn; - whole_rgn = 0; + else + nc_rgn = whole_rgn; }
- if (whole_rgn) /* NOTE: WM_NCPAINT allows wParam to be 1 */ + if (nc_rgn) /* NOTE: WM_NCPAINT allows wParam to be 1 */ { if (*flags & UPDATE_NONCLIENT) { @@ -1214,13 +1212,12 @@ static HRGN send_ncpaint( HWND hwnd, HWND *child, UINT *flags ) if (style & WS_VSCROLL) set_standard_scroll_painted( hwnd, SB_VERT, FALSE );
- send_message( hwnd, WM_NCPAINT, (WPARAM)whole_rgn, 0 ); + send_message( hwnd, WM_NCPAINT, (WPARAM)nc_rgn, 0 ); } - if (whole_rgn > (HRGN)1) NtGdiDeleteObjectApp( whole_rgn ); } SetThreadDpiAwarenessContext( context ); } - return client_rgn; + return whole_rgn; }
/*********************************************************************** @@ -1366,6 +1363,8 @@ HDC WINAPI NtUserBeginPaint( HWND hwnd, PAINTSTRUCT *ps ) RECT rect; UINT flags = UPDATE_NONCLIENT | UPDATE_ERASE | UPDATE_PAINT | UPDATE_INTERNALPAINT | UPDATE_NOCHILDREN;
+ TRACE( "hwnd %p\n", hwnd ); + NtUserHideCaret( hwnd );
if (!(hrgn = send_ncpaint( hwnd, NULL, &flags ))) return 0; @@ -1390,6 +1389,8 @@ HDC WINAPI NtUserBeginPaint( HWND hwnd, PAINTSTRUCT *ps ) */ BOOL WINAPI NtUserEndPaint( HWND hwnd, const PAINTSTRUCT *ps ) { + TRACE( "hwnd %p\n", hwnd ); + NtUserShowCaret( hwnd ); flush_window_surfaces( FALSE ); if (!ps) return FALSE;
From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/user32/tests/dce.c | 2 +- dlls/win32u/dce.c | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/dlls/user32/tests/dce.c b/dlls/user32/tests/dce.c index 64becb6084d..c7e498c60d0 100644 --- a/dlls/user32/tests/dce.c +++ b/dlls/user32/tests/dce.c @@ -521,7 +521,7 @@ static void test_begin_paint(void) ok( rect.bottom == parent_rect.bottom, "rect.bottom = %ld, expected %ld\n", rect.bottom, parent_rect.bottom );
hdc = GetDC( hwnd_parent ); - todo_wine ok( GetPixel( hdc, 60, 60 ) == cr, "error drawing outside of window client area\n" ); + ok( GetPixel( hdc, 60, 60 ) == cr, "error drawing outside of window client area\n" ); ReleaseDC( hwnd_parent, hdc ); }
diff --git a/dlls/win32u/dce.c b/dlls/win32u/dce.c index 5fff59570f7..78849906dc9 100644 --- a/dlls/win32u/dce.c +++ b/dlls/win32u/dce.c @@ -494,8 +494,16 @@ static void update_visible_region( struct dce *dce ) top_win = wine_server_ptr_handle( reply->top_win ); win_rect.left = reply->win_rect.left; win_rect.top = reply->win_rect.top; - win_rect.right = reply->win_rect.right; - win_rect.bottom = reply->win_rect.bottom; + if (flags & DCX_PARENTCLIP) + { + win_rect.right = reply->top_rect.right; + win_rect.bottom = reply->top_rect.bottom; + } + else + { + win_rect.right = reply->win_rect.right; + win_rect.bottom = reply->win_rect.bottom; + } top_rect.left = reply->top_rect.left; top_rect.top = reply->top_rect.top; top_rect.right = reply->top_rect.right;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=145610
Your paranoid android.
=== w1064v1507 (32 bit report) ===
user32: win.c:3877: Test failed: GetActiveWindow() = 00080034 win.c:3881: Test failed: GetFocus() = 00000000 win.c:3884: Test failed: GetFocus() = 00000000
=== w10pro64 (32 bit report) ===
user32: win.c:3818: Test failed: GetForegroundWindow returned 000202BE win.c:3749: Test failed: SetForegroundWindow failed, error 0 win.c:3752: Test failed: GetForegroundWindow returned 000202BE win.c:3789: Test failed: GetForegroundWindow returned 000202BE win.c:3877: Test failed: GetActiveWindow() = 000301D6 win.c:3881: Test failed: GetFocus() = 00000000 win.c:3884: Test failed: GetFocus() = 00000000
=== w10pro64_ja (64 bit report) ===
user32: win.c:13062: Test failed: window is minimized.
=== debian11b (64 bit WoW report) ===
comctl32: listview.c:2201: Test succeeded inside todo block: Expected 0, got 0 listview.c:2211: Test succeeded inside todo block: Expected 0, got 0 listview.c:2221: Test succeeded inside todo block: Expected 0, got 0 listview.c:2201: Test succeeded inside todo block: Expected 0, got 0 listview.c:2211: Test succeeded inside todo block: Expected 0, got 0 listview.c:2221: Test succeeded inside todo block: Expected 0, got 0 static.c:130: Test failed: expected 2, got 4 static.c:130: Test failed: expected 2, got 4
user32: msg.c:8624: Test failed: Regions are different msg.c:8658: Test failed: Update region shouldn't be empty msg.c:8668: Test failed: Erase: 0: the msg sequence is not complete: expected msg 0014 - actual msg 0000 msg.c:8670: Test failed: Update region shouldn't be empty msg.c:8677: Test failed: Paint: 0: the msg sequence is not complete: expected msg 000f - actual msg 0000 msg.c:8712: Test failed: Invalid GetUpdateRgn result 2 msg.c:8751: Test failed: Regions are different msg.c:8890: Test failed: WmChildPaintNc: 0: the msg sequence is not complete: expected msg 000f - actual msg 0000 msg.c:9395: Test failed: 9442: SetWindowPos redraw #3 (ex_style = 0x2000000, style = 0, shuffle_zorder = 0): delta = -20: Child update region shall match expected region msg.c:9395: Test failed: 9442: SetWindowPos redraw #4 (ex_style = 0x2000000, style = 0, shuffle_zorder = 1): delta = -20: Child update region shall match expected region msg.c:9395: Test failed: 9442: SetWindowPos redraw #4 (ex_style = 0x2000000, style = 0, shuffle_zorder = 1): delta = +0: Child update region shall match expected region msg.c:9395: Test failed: 9443: SetWindowPos redraw #3 (ex_style = 0x2000000, style = 0, shuffle_zorder = 0): delta = -20: Child update region shall match expected region msg.c:9395: Test failed: 9443: SetWindowPos redraw #4 (ex_style = 0x2000000, style = 0, shuffle_zorder = 1): delta = -20: Child update region shall match expected region msg.c:9395: Test failed: 9443: SetWindowPos redraw #4 (ex_style = 0x2000000, style = 0, shuffle_zorder = 1): delta = +0: Child update region shall match expected region msg.c:9395: Test failed: 9444: SetWindowPos redraw #3 (ex_style = 0x2000000, style = 0, shuffle_zorder = 0): delta = -20: Child update region shall match expected region msg.c:9395: Test failed: 9444: SetWindowPos redraw #4 (ex_style = 0x2000000, style = 0, shuffle_zorder = 1): delta = -20: Child update region shall match expected region msg.c:9395: Test failed: 9444: SetWindowPos redraw #4 (ex_style = 0x2000000, style = 0, shuffle_zorder = 1): delta = +0: Child update region shall match expected region msg.c:9395: Test failed: 9445: SetWindowPos redraw #3 (ex_style = 0x2000000, style = 0, shuffle_zorder = 0): delta = -20: Child update region shall match expected region msg.c:9395: Test failed: 9445: SetWindowPos redraw #4 (ex_style = 0x2000000, style = 0, shuffle_zorder = 1): delta = -20: Child update region shall match expected region msg.c:9395: Test failed: 9445: SetWindowPos redraw #4 (ex_style = 0x2000000, style = 0, shuffle_zorder = 1): delta = +0: Child update region shall match expected region msg.c:9497: Test failed: child update region: got (-1,-1)-(89,89), expected (0,0)-(88,88) msg.c:9534: Test failed: child update region: got (-1,-1)-(99,99), expected (0,0)-(98,98) static.c:124: Test failed: expected 2, got 4 static.c:124: Test failed: expected 2, got 4