Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com
-- v2: server: Correctly expose composited parent window on SetWindowPos(). user32/tests: Test for parent window exposure by SetWindowPos().
From: Jinoh Kang jinoh.kang.kr@gmail.com
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- dlls/user32/tests/msg.c | 278 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+)
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 2705914d5e5..67f58032cb3 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -9035,6 +9035,278 @@ static void test_paint_messages(void) DeleteObject( hrgn2 ); }
+static void visualize_region_differences( HWND hwnd, HWND hother, HRGN hrgn_expect, HRGN hrgn_actual ) +{ + HBRUSH b_expectonly, b_actualonly, b_intersect; + HRGN hrgn_intersect; + HWND hchild, hshow, hhide; + HDC hdc, hdctmp; + HBITMAP hbitmap; + MSG msg; + RECT rect; + DWORD start_time, elapsed, timeout = 60 * 1000; + BOOL wait = TRUE, toggle = TRUE; + + b_expectonly = CreateSolidBrush( RGB( 64, 64, 255 )); + b_actualonly = CreateSolidBrush( RGB( 255, 64, 64 )); + b_intersect = CreateSolidBrush( RGB( 159, 64, 159 )); + + hrgn_intersect = CreateRectRgn( 0, 0, 0, 0 ); + CombineRgn( hrgn_intersect, hrgn_expect, hrgn_actual, RGN_AND ); + + GetClientRect( hwnd, &rect ); + hdc = GetDC( hwnd ); + hbitmap = CreateCompatibleBitmap( hdc, rect.right, rect.bottom ); + hdctmp = CreateCompatibleDC( hdc ); + ReleaseDC( hwnd, hdc ); + + SelectObject( hdctmp, hbitmap ); + FillRgn( hdctmp, hrgn_expect, b_expectonly ); + FillRgn( hdctmp, hrgn_actual, b_actualonly ); + FillRgn( hdctmp, hrgn_intersect, b_intersect ); + + DeleteObject( hdctmp ); + DeleteObject( hrgn_intersect ); + DeleteObject( b_intersect ); + DeleteObject( b_actualonly ); + DeleteObject( b_expectonly ); + + hchild = CreateWindowExA( 0, WC_STATICA, "", WS_CHILD | SS_BITMAP, + 0, 0, rect.right, rect.bottom, hwnd, 0, 0, NULL ); + SendMessageA( hchild, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbitmap ); + + hshow = hchild; + hhide = hother; + + start_time = GetTickCount(); + for (;;) + { + if (toggle) + { + HWND htmp; + if (hhide) + { + ShowWindow( hhide, SW_HIDE ); + } + if (hshow) + { + SetWindowPos( hshow, HWND_TOP, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW ); + } + htmp = hshow; + hshow = hhide; + hhide = htmp; + toggle = FALSE; + } + if ((elapsed = GetTickCount() - start_time) >= timeout) + { + break; + } + if (wait) + { + MsgWaitForMultipleObjects( 0, NULL, FALSE, timeout - elapsed, QS_ALLINPUT ); + wait = FALSE; + continue; + } + if (!PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) + { + wait = TRUE; + continue; + } + TranslateMessage( &msg ); + DispatchMessageA( &msg ); + if (msg.message == WM_MOUSEMOVE) + { + start_time = GetTickCount(); + } + else if (msg.message == WM_LBUTTONUP) + { + toggle = TRUE; + } + else if (msg.message == WM_RBUTTONUP) + { + break; + } + } + + DestroyWindow( hchild ); + DeleteObject( hbitmap ); +} + +#define subtest_swp_paint_regions(w,p,c) subtest_swp_paint_regions_(__LINE__,w,p,c) + +static void subtest_swp_paint_regions_( int line, int wrap_toplevel, LPCSTR parent_class, LPCSTR child_class ) +{ + static const struct exposure_test { + int ex_style, style; + } exposure_tests[] = { + { 0, WS_CLIPCHILDREN }, + { 0, 0 }, + { WS_EX_COMPOSITED, WS_CLIPCHILDREN }, + { WS_EX_COMPOSITED, 0 }, + }; + size_t i; + HWND htoplevel = NULL, hparent, hchild; + RECT rect_old = { 10, 10, 100, 100 }, rect_cli; + HRGN hrgn_clip; + HRGN hrgn_old = CreateRectRgnIndirect( &rect_old ); + HRGN hrgn_new = CreateRectRgn( 0, 0, 0, 0 ); + HRGN hrgn_expect = CreateRectRgn( 0, 0, 0, 0 ); + HRGN hrgn_actual = CreateRectRgn( 0, 0, 0, 0 ); + int base_style; + BOOL is_composition_possible, has_parentdc_anomaly; + WNDCLASSA parent_wc; + + if (wrap_toplevel) + { + htoplevel = CreateWindowExA( 0, "SimpleWindowClass", "Test toplevel", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 400, 400, 0, 0, 0, NULL ); + ok( htoplevel != 0, "Failed to create top-level window: %lu\n", GetLastError() ); + base_style = WS_CHILD | WS_VISIBLE; + } + else + { + base_style = WS_OVERLAPPEDWINDOW | WS_VISIBLE; + } + + ok( GetClassInfoA( GetModuleHandleA( NULL ), parent_class, &parent_wc ), + "GetClassInfoA failed\n" ); + + is_composition_possible = (base_style & (WS_POPUP|WS_CHILD)) != WS_CHILD || + (parent_wc.style & CS_PARENTDC) == 0; + + has_parentdc_anomaly = (base_style & (WS_POPUP|WS_CHILD)) != WS_CHILD && + (parent_wc.style & CS_PARENTDC) != 0; + + hparent = CreateWindowExA( 0, parent_class, "Test parent", base_style, + 80, 80, 200, 200, htoplevel, 0, 0, NULL ); + ok( hparent != 0, "Creating parent window (%s) returned error %lu\n", + debugstr_a( parent_class ), GetLastError() ); + + hchild = CreateWindowExA( 0, child_class, "Test child", WS_CHILD | WS_VISIBLE | WS_BORDER, + rect_old.left, rect_old.top, + rect_old.right - rect_old.left, rect_old.bottom - rect_old.top, + hparent, 0, 0, NULL ); + ok( hchild != 0, "Creating child window (%s) returned error %lu\n", + debugstr_a( child_class ), GetLastError() ); + + GetClientRect( hparent, &rect_cli ); + hrgn_clip = CreateRectRgnIndirect( &rect_cli ); + + for (i = 0; i < ARRAY_SIZE(exposure_tests); i++) + { + const struct exposure_test *extest = &exposure_tests[i]; + BOOL is_composited = (extest->ex_style & WS_EX_COMPOSITED) != 0; + int delta; + + winetest_push_context( "%d: SetWindowPos redraw #%Id (ex_style = %#x, style = %#x)", + line, i, extest->ex_style, extest->style ); + + SetWindowLongA( hparent, GWL_EXSTYLE, extest->ex_style ); + SetWindowLongA( hparent, GWL_STYLE, base_style | extest->style ); + RedrawWindow( hparent, NULL, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_FRAME ); + + for (delta = -20; delta <= 20; delta += 20) + { + RECT rect_new = rect_old; + int rgn_equal; + int region_op = (is_composition_possible && is_composited) ? RGN_OR : RGN_DIFF; + + winetest_push_context( "delta = %+d", delta ); + + OffsetRect( &rect_new, delta, delta ); + SetRectRgn( hrgn_new, rect_new.left, rect_new.top, rect_new.right, rect_new.bottom ); + if (EqualRect( &rect_old, &rect_new )) + { + SetRectRgn( hrgn_expect, 0, 0, 0, 0 ); + } + else + { + CombineRgn( hrgn_expect, hrgn_old, hrgn_new, region_op ); + CombineRgn( hrgn_expect, hrgn_expect, hrgn_clip, RGN_AND ); + } + SetRectRgn( hrgn_actual, 0, 0, 0, 0 ); + + SetWindowPos( hchild, 0, + rect_old.left, + rect_old.top, + rect_old.right - rect_old.left, + rect_old.bottom - rect_old.top, + SWP_NOACTIVATE | SWP_NOZORDER ); + + UpdateWindow( hparent ); + flush_events(); + + SetWindowPos( hchild, 0, + rect_new.left, + rect_new.top, + rect_new.right - rect_new.left, + rect_new.bottom - rect_new.top, + SWP_NOACTIVATE | SWP_NOZORDER ); + + ok( GetUpdateRgn( hparent, hrgn_actual, FALSE ) != ERROR, + "GetUpdateRgn shall succeed\n" ); + + rgn_equal = EqualRgn( hrgn_expect, hrgn_actual ); + if (!rgn_equal && broken( has_parentdc_anomaly && is_composited && + LOBYTE(LOWORD(GetVersion())) < 8 ) /* Win7 */) + { + trace( "Forcing composited update region (broken)\n" ); + CombineRgn( hrgn_expect, hrgn_old, hrgn_new, RGN_DIFF ); + CombineRgn( hrgn_expect, hrgn_expect, hrgn_clip, RGN_AND ); + rgn_equal = EqualRgn( hrgn_expect, hrgn_actual ); + } + todo_wine_if( !EqualRect( &rect_old, &rect_new ) && + ((extest->style & WS_CLIPCHILDREN) == 0 || region_op == RGN_OR) ) + ok( !!rgn_equal, "Update region shall match expected region\n" ); + + flush_events(); + + if (!rgn_equal && winetest_debug > 0) + { + printf( "Expected update region: " ); + dump_region( hrgn_expect ); + printf( "Actual update region: " ); + dump_region( hrgn_actual ); + printf( "Old window position: " ); + dump_region( hrgn_old ); + printf( "New window position: " ); + dump_region( hrgn_new ); + + if (winetest_interactive) + { + visualize_region_differences( hparent, hchild, hrgn_expect, hrgn_actual ); + } + } + + winetest_pop_context(); + } + + winetest_pop_context(); + } + + DestroyWindow( hchild ); + DestroyWindow( hparent ); + if (htoplevel) DestroyWindow( htoplevel ); + + DeleteObject( hrgn_actual ); + DeleteObject( hrgn_expect ); + DeleteObject( hrgn_new ); + DeleteObject( hrgn_old ); +} + +static void test_swp_paint_regions(void) +{ + subtest_swp_paint_regions( 1, "SimpleWindowClass", "SimpleWindowClass" ); + subtest_swp_paint_regions( 1, "SimpleWindowClass", "SimpleWindowClassWithParentDC" ); + subtest_swp_paint_regions( 1, "SimpleWindowClassWithParentDC", "SimpleWindowClass" ); + subtest_swp_paint_regions( 1, "SimpleWindowClassWithParentDC", "SimpleWindowClassWithParentDC" ); + subtest_swp_paint_regions( 0, "SimpleWindowClass", "SimpleWindowClass" ); + subtest_swp_paint_regions( 0, "SimpleWindowClass", "SimpleWindowClassWithParentDC" ); + subtest_swp_paint_regions( 0, "SimpleWindowClassWithParentDC", "SimpleWindowClass" ); + subtest_swp_paint_regions( 0, "SimpleWindowClassWithParentDC", "SimpleWindowClassWithParentDC" ); +} + struct wnd_event { HWND hwnd; @@ -10387,6 +10659,11 @@ static BOOL RegisterWindowClasses(void) cls.lpszClassName = "TestDialogClass"; if(!RegisterClassA(&cls)) return FALSE;
+ cls.lpfnWndProc = DefWindowProcA; + cls.style = CS_PARENTDC; + cls.lpszClassName = "SimpleWindowClassWithParentDC"; + if(!RegisterClassA(&cls)) return FALSE; + clsW.style = 0; clsW.lpfnWndProc = MsgCheckProcW; clsW.cbClsExtra = 0; @@ -18870,6 +19147,7 @@ START_TEST(msg) test_combobox_messages(); test_wmime_keydown_message(); test_paint_messages(); + test_swp_paint_regions(); test_interthread_messages(); test_message_conversion(); test_accelerators();
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=116868
Your paranoid android.
=== w10pro64_en_AE_u8 (64 bit report) ===
user32: msg.c:13138: Test failed: message time not advanced: 15399 15399 msg.c:13139: Test failed: coords not changed: (101 101) (101 101) msg.c:13156: Test failed: message time not advanced: 15399 15399 msg.c:13157: Test failed: coords not changed: (101 101) (101 101)
=== w10pro64_ar (64 bit report) ===
user32: msg.c:13138: Test failed: message time not advanced: 1533b 1533b msg.c:13139: Test failed: coords not changed: (101 101) (101 101) msg.c:13156: Test failed: message time not advanced: 1533b 1533b msg.c:13157: Test failed: coords not changed: (101 101) (101 101)
=== w10pro64_ja (64 bit report) ===
user32: msg.c:13138: Test failed: message time not advanced: 15791 15791 msg.c:13139: Test failed: coords not changed: (101 101) (101 101) msg.c:13156: Test failed: message time not advanced: 15791 15791 msg.c:13157: Test failed: coords not changed: (101 101) (101 101)
From: Jinoh Kang jinoh.kang.kr@gmail.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53153 Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- dlls/user32/tests/msg.c | 2 +- server/class.c | 5 +++++ server/user.h | 1 + server/window.c | 37 +++++++++++++++++++++++++++++-------- 4 files changed, 36 insertions(+), 9 deletions(-)
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 67f58032cb3..a431e3b04d6 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -9257,7 +9257,7 @@ static void subtest_swp_paint_regions_( int line, int wrap_toplevel, LPCSTR pare rgn_equal = EqualRgn( hrgn_expect, hrgn_actual ); } todo_wine_if( !EqualRect( &rect_old, &rect_new ) && - ((extest->style & WS_CLIPCHILDREN) == 0 || region_op == RGN_OR) ) + (extest->style & WS_CLIPCHILDREN) == 0 && region_op != RGN_OR ) ok( !!rgn_equal, "Update region shall match expected region\n" );
flush_events(); diff --git a/server/class.c b/server/class.c index e1e180bd97c..3231f366b26 100644 --- a/server/class.c +++ b/server/class.c @@ -141,6 +141,11 @@ int is_hwnd_message_class( struct window_class *class ) return (!class->local && class->atom == find_global_atom( NULL, &name )); }
+int get_class_style( struct window_class *class ) +{ + return class->style; +} + atom_t get_class_atom( struct window_class *class ) { return class->base_atom; diff --git a/server/user.h b/server/user.h index 55a0d35feff..0356fe0c5cd 100644 --- a/server/user.h +++ b/server/user.h @@ -175,6 +175,7 @@ extern struct window_class *grab_class( struct process *process, atom_t atom, extern void release_class( struct window_class *class ); extern int is_desktop_class( struct window_class *class ); extern int is_hwnd_message_class( struct window_class *class ); +extern int get_class_style( struct window_class *class ); extern atom_t get_class_atom( struct window_class *class ); extern client_ptr_t get_class_client_ptr( struct window_class *class );
diff --git a/server/window.c b/server/window.c index 7675cd1103d..d007e38f49c 100644 --- a/server/window.c +++ b/server/window.c @@ -789,6 +789,16 @@ int is_window_transparent( user_handle_t window ) return (win->ex_style & (WS_EX_LAYERED|WS_EX_TRANSPARENT)) == (WS_EX_LAYERED|WS_EX_TRANSPARENT); }
+static int is_window_using_parent_dc( struct window *win ) +{ + return (win->style & (WS_POPUP|WS_CHILD)) == WS_CHILD && (get_class_style( win->class ) & CS_PARENTDC) != 0; +} + +static int is_window_composited( struct window *win ) +{ + return (win->ex_style & WS_EX_COMPOSITED) != 0 && !is_window_using_parent_dc(win); +} + /* check if point is inside the window, and map to window dpi */ static int is_point_in_window( struct window *win, int *x, int *y, unsigned int dpi ) { @@ -1705,7 +1715,6 @@ static unsigned int get_window_update_flags( struct window *win, struct window * return 0; }
- /* expose the areas revealed by a vis region change on the window parent */ /* returns the region exposed on the window itself (in client coordinates) */ static struct region *expose_window( struct window *win, const rectangle_t *old_window_rect, @@ -1732,21 +1741,33 @@ static struct region *expose_window( struct window *win, const rectangle_t *old_
if (win->parent && !is_desktop_window( win->parent )) { + struct region *parent_expose_rgn; + /* make it relative to the old window pos for subtracting */ offset_region( new_vis_rgn, win->window_rect.left - old_window_rect->left, win->window_rect.top - old_window_rect->top );
- if ((win->parent->style & WS_CLIPCHILDREN) ? - subtract_region( new_vis_rgn, old_vis_rgn, new_vis_rgn ) : - xor_region( new_vis_rgn, old_vis_rgn, new_vis_rgn )) + if (is_window_composited( win->parent )) { - if (!is_region_empty( new_vis_rgn )) + parent_expose_rgn = xor_region( new_vis_rgn, old_vis_rgn, new_vis_rgn ); + if (parent_expose_rgn && !is_region_empty( parent_expose_rgn )) { - /* make it relative to parent */ - offset_region( new_vis_rgn, old_window_rect->left, old_window_rect->top ); - redraw_window( win->parent, new_vis_rgn, 0, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN ); + parent_expose_rgn = union_region( parent_expose_rgn, old_vis_rgn, parent_expose_rgn ); } } + else + { + parent_expose_rgn = (win->parent->style & WS_CLIPCHILDREN) ? + subtract_region( new_vis_rgn, old_vis_rgn, new_vis_rgn ) : + xor_region( new_vis_rgn, old_vis_rgn, new_vis_rgn ); + } + + if (parent_expose_rgn && !is_region_empty( parent_expose_rgn )) + { + /* make it relative to parent */ + offset_region( parent_expose_rgn, old_window_rect->left, old_window_rect->top ); + redraw_window( win->parent, parent_expose_rgn, 0, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN ); + } } free_region( new_vis_rgn ); return exposed_rgn;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=116869
Your paranoid android.
=== debian11 (64 bit WoW report) ===
user32: msg.c:18754: Test failed: SendMessage from other thread 1: 1: the msg 0x0400 was expected, but got msg 0x0403 instead msg.c:18754: Test failed: SendMessage from other thread 1: 2: the msg sequence is not complete: expected 0000 - actual 0400 msg.c:18768: Test failed: wrong status 00080000 msg.c:18774: Test failed: SendMessage from other thread 3: 0: the msg 0x0403 was expected, but got msg 0x0401 instead msg.c:18774: Test failed: SendMessage from other thread 3: 1: the msg sequence is not complete: expected 0401 - actual 0000