[PATCH v2 1/2] user32/tests: Test for parent window exposure by SetWindowPos().

Jinoh Kang wine at gitlab.winehq.org
Mon Jun 13 11:46:32 CDT 2022


From: Jinoh Kang <jinoh.kang.kr at gmail.com>

Signed-off-by: Jinoh Kang <jinoh.kang.kr at 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();
-- 
GitLab


https://gitlab.winehq.org/wine/wine/-/merge_requests/231



More information about the wine-devel mailing list