From: Jinoh Kang jinoh.kang.kr@gmail.com
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- dlls/user32/tests/msg.c | 378 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 378 insertions(+)
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 2705914d5e5..c62b25446a2 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -9035,6 +9035,378 @@ 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 hstatic, hshow, hhide; + HDC hdc, hdctmp; + HBITMAP hbitmap; + MSG msg; + RECT rect; + DWORD start_time, elapsed, timeout = 60 * 1000; + BOOL toggle = TRUE, stop = FALSE; + + start_time = GetTickCount(); + + 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 ); + + hstatic = CreateWindowExA( 0, WC_STATICA, "", WS_CHILD | SS_BITMAP, + 0, 0, rect.right, rect.bottom, hwnd, 0, 0, NULL ); + SendMessageA( hstatic, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbitmap ); + + hshow = hstatic; + hhide = hother; + + for (;;) + { + if (stop) toggle = hshow == hother; + if (toggle) + { + HWND htmp; + HDWP hdwp; + + hdwp = BeginDeferWindowPos( !!hhide + !!hshow ); + if (hhide) + { + DeferWindowPos( hdwp, hhide, NULL, 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER ); + } + if (hshow) + { + DeferWindowPos( hdwp, hshow, HWND_TOP, 0, 0, 0, 0, + SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE ); + } + EndDeferWindowPos( hdwp ); + + htmp = hshow; + hshow = hhide; + hhide = htmp; + toggle = FALSE; + } + if (stop) break; + if ((elapsed = GetTickCount() - start_time) >= timeout) + { + stop = TRUE; + continue; + } + MsgWaitForMultipleObjects( 0, NULL, FALSE, timeout - elapsed, QS_ALLINPUT ); + while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + DispatchMessageA( &msg ); + if (msg.message == WM_MOUSEMOVE) + { + start_time = GetTickCount(); + } + else if (msg.message == WM_LBUTTONUP || (msg.message == WM_CHAR && msg.wParam == VK_SPACE)) + { + toggle = !toggle; + } + else if (msg.message == WM_RBUTTONUP || (msg.message == WM_CHAR && msg.wParam == VK_RETURN)) + { + stop = TRUE; + } + } + } + + DestroyWindow( hstatic ); + 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; + BOOL shuffle_zorder; + HWND insert_after; + } exposure_tests[] = { + { 0, WS_CLIPCHILDREN, FALSE, NULL }, + { 0, 0, FALSE, NULL }, + { WS_EX_COMPOSITED, WS_CLIPCHILDREN, FALSE, NULL }, + { WS_EX_COMPOSITED, WS_CLIPCHILDREN, TRUE , HWND_TOP }, + { WS_EX_COMPOSITED, WS_CLIPCHILDREN, TRUE , HWND_BOTTOM }, + { WS_EX_COMPOSITED, 0, FALSE, NULL }, + { WS_EX_COMPOSITED, 0, TRUE , HWND_TOP }, + { WS_EX_COMPOSITED, 0, TRUE , HWND_BOTTOM }, + }; + size_t i; + HWND htoplevel = NULL, hparent, hchild, hauxchild; + const RECT rect_old = { 10, 10, 100, 100 }; + HRGN hrgn_old_vis = CreateRectRgn( 0, 0, 0, 0 ); + HRGN hrgn_new_vis = CreateRectRgn( 0, 0, 0, 0 ); + HRGN hrgn_expect = CreateRectRgn( 0, 0, 0, 0 ); + HRGN hrgn_actual = CreateRectRgn( 0, 0, 0, 0 ); + HRGN hrgn_old_vis_child = CreateRectRgn( 0, 0, 0, 0 ); + HRGN hrgn_new_vis_child = CreateRectRgn( 0, 0, 0, 0 ); + HRGN hrgn_expect_child = CreateRectRgn( 0, 0, 0, 0 ); + HRGN hrgn_actual_child = 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() ); + + hauxchild = CreateWindowExA( 0, child_class, "Auxiliary child for z order test", WS_CHILD | WS_VISIBLE, + 110, 0, 0, 0, hparent, 0, 0, NULL ); + ok( hauxchild != 0, "Creating child window (%s) returned error %lu\n", + debugstr_a( child_class ), GetLastError() ); + + for (i = 0; i < ARRAY_SIZE(exposure_tests); i++) + { + const struct exposure_test *extest = &exposure_tests[i]; + BOOL has_ws_ex_composited = (extest->ex_style & WS_EX_COMPOSITED) != 0; + BOOL is_composited = is_composition_possible && has_ws_ex_composited; + BOOL is_zorder_redraw = is_composited && extest->shuffle_zorder; + int delta; + + winetest_push_context( "%d: SetWindowPos redraw #%Id (ex_style = %#x, style = %#x, shuffle_zorder = %d, insert_after = %Id)", + line, i, extest->ex_style, extest->style, extest->shuffle_zorder, (LONG_PTR)extest->insert_after ); + + 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_old_vis, rect_new, rect_new_vis; + RECT rect_parent_clip, rect_child_clip; + RECT rect_old_vis_child, rect_new_vis_child; + BOOL rgn_equal; + + winetest_push_context( "delta = %+d", delta ); + + SetWindowPos( hchild, HWND_TOP, + rect_old.left, + rect_old.top, + rect_old.right - rect_old.left, + rect_old.bottom - rect_old.top, + SWP_NOACTIVATE ); + + rect_new = rect_old; + OffsetRect( &rect_new, delta, delta ); + + rect_old_vis_child = rect_old; + MapWindowPoints( hparent, hchild, (POINT *)&rect_old_vis_child, 2 ); + + SetRectRgn( hrgn_actual, 0, 0, 0, 0 ); + SetRectRgn( hrgn_actual_child, 0, 0, 0, 0 ); + + UpdateWindow( hparent ); + flush_events(); + + if (extest->shuffle_zorder) + { + /* bring sibling to top/bottom first so we can trigger z-order change */ + SetWindowPos( hauxchild, extest->insert_after, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE ); + } + + SetWindowPos( hchild, extest->insert_after, + rect_new.left, + rect_new.top, + rect_new.right - rect_new.left, + rect_new.bottom - rect_new.top, + SWP_NOACTIVATE | + (extest->shuffle_zorder || extest->insert_after ? 0 : SWP_NOZORDER) ); + + ok( GetUpdateRgn( hparent, hrgn_actual, FALSE ) != ERROR, + "GetUpdateRgn on parentshall succeed\n" ); + ok( GetUpdateRgn( hchild, hrgn_actual_child, FALSE ) != ERROR, + "GetUpdateRgn on child shall succeed\n" ); + + /* Compute parent window expose region */ + GetClientRect( hparent, &rect_parent_clip ); + IntersectRect( &rect_old_vis, &rect_old, &rect_parent_clip ); + SetRectRgn( hrgn_old_vis, rect_old_vis.left, rect_old_vis.top, rect_old_vis.right, rect_old_vis.bottom ); + IntersectRect( &rect_new_vis, &rect_new, &rect_parent_clip ); + SetRectRgn( hrgn_new_vis, rect_new_vis.left, rect_new_vis.top, rect_new_vis.right, rect_new_vis.bottom ); + + if (!EqualRect( &rect_old, &rect_new ) || is_zorder_redraw) + { + CombineRgn( hrgn_expect, hrgn_old_vis, hrgn_new_vis, is_composited ? RGN_OR : RGN_DIFF ); + } + else + { + SetRectRgn( hrgn_expect, 0, 0, 0, 0 ); + } + + rgn_equal = EqualRgn( hrgn_expect, hrgn_actual ); + if (!rgn_equal && broken( has_parentdc_anomaly && is_composited && + LOBYTE(LOWORD(GetVersion())) < 8 ) /* Win7 */) + { + trace( "Forcing non-composited update region (broken)\n" ); + CombineRgn( hrgn_expect, hrgn_old_vis, hrgn_new_vis, RGN_DIFF ); + rgn_equal = EqualRgn( hrgn_expect, hrgn_actual ); + } + todo_wine_if( EqualRect( &rect_old, &rect_new ) ? is_zorder_redraw : + ((extest->style & WS_CLIPCHILDREN) == 0 || is_composited) ) + ok( !!rgn_equal, "Parent update region shall match expected region\n" ); + + if (!rgn_equal && winetest_debug > 0) + { + printf( "Expected parent update region: " ); + dump_region( hrgn_expect ); + printf( "Actual parent update region: " ); + dump_region( hrgn_actual ); + printf( "Old child window visible area: %s\n", wine_dbgstr_rect( &rect_old_vis ) ); + printf( "New child window visible area: %s\n", wine_dbgstr_rect( &rect_new_vis ) ); + } + + if (winetest_interactive) + { + if (!rgn_equal) + { + visualize_region_differences( hparent, hchild, hrgn_expect, hrgn_actual ); + } + + /* Let the position change be visible to the user */ + flush_events(); + } + + rect_new_vis_child = rect_new; + MapWindowPoints( hparent, hchild, (POINT *)&rect_new_vis_child, 2 ); + + /* Compute child window expose region */ + GetClientRect( hchild, &rect_child_clip ); + if (is_composited) + { + RECT rect_outer_clip; + GetClientRect( hparent, &rect_outer_clip ); + MapWindowPoints( hparent, hchild, (POINT *)&rect_outer_clip, 2 ); + IntersectRect( &rect_child_clip, &rect_child_clip, &rect_outer_clip ); + } + IntersectRect( &rect_old_vis_child, &rect_old_vis_child, &rect_child_clip ); + SetRectRgn( hrgn_old_vis_child, rect_old_vis_child.left, rect_old_vis_child.top, rect_old_vis_child.right, rect_old_vis_child.bottom ); + IntersectRect( &rect_new_vis_child, &rect_new_vis_child, &rect_child_clip ); + SetRectRgn( hrgn_new_vis_child, rect_new_vis_child.left, rect_new_vis_child.top, rect_new_vis_child.right, rect_new_vis_child.bottom ); + + if (!EqualRect( &rect_old, &rect_new ) || is_zorder_redraw) + { + CombineRgn( hrgn_expect_child, hrgn_new_vis_child, hrgn_old_vis_child, is_composited ? RGN_OR : RGN_DIFF ); + } + else + { + SetRectRgn( hrgn_expect_child, 0, 0, 0, 0 ); + } + + rgn_equal = EqualRgn( hrgn_expect_child, hrgn_actual_child ); + if (!rgn_equal && broken( has_parentdc_anomaly && is_composited && + LOBYTE(LOWORD(GetVersion())) < 8 ) /* Win7 */) + { + trace( "Forcing non-composited update region (broken)\n" ); + CombineRgn( hrgn_expect_child, hrgn_new_vis_child, hrgn_old_vis_child, RGN_DIFF ); + rgn_equal = EqualRgn( hrgn_expect, hrgn_actual ); + } + todo_wine_if( EqualRect( &rect_old, &rect_new ) ? is_zorder_redraw : + ((extest->style & WS_CLIPCHILDREN) == 0 || is_composited) ) + ok( !!rgn_equal, "Child update region shall match expected region\n" ); + + if (!rgn_equal && winetest_debug > 0) + { + printf( "Expected child update region: " ); + dump_region( hrgn_expect_child ); + printf( "Actual child update region: " ); + dump_region( hrgn_actual_child ); + printf( "Old child window client visible area: %s\n", wine_dbgstr_rect( &rect_old_vis_child ) ); + printf( "New child window client visible area: %s\n", wine_dbgstr_rect( &rect_new_vis_child ) ); + } + + if (winetest_interactive) + { + if (!rgn_equal) + { + visualize_region_differences( hchild, NULL, hrgn_expect_child, hrgn_actual_child ); + } + + /* Let the position change be visible to the user */ + flush_events(); + } + + winetest_pop_context(); + } + + winetest_pop_context(); + } + + DestroyWindow( hauxchild ); + DestroyWindow( hchild ); + DestroyWindow( hparent ); + if (htoplevel) DestroyWindow( htoplevel ); + + DeleteObject( hrgn_actual ); + DeleteObject( hrgn_expect ); + DeleteObject( hrgn_new_vis ); + DeleteObject( hrgn_old_vis ); +} + +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 +10759,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 +19247,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();