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

Jinoh Kang wine at gitlab.winehq.org
Sun Jun 12 12:44:17 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 | 288 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 288 insertions(+)

diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c
index 2705914d5e5..6574318845e 100644
--- a/dlls/user32/tests/msg.c
+++ b/dlls/user32/tests/msg.c
@@ -9035,6 +9035,288 @@ static void test_paint_messages(void)
     DeleteObject( hrgn2 );
 }
 
+static LRESULT WINAPI vis_child_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    HDC hdc, hdcsrc;
+    PAINTSTRUCT ps;
+    RECT rc;
+
+    switch (message)
+    {
+    case WM_ERASEBKGND:
+        return 0;
+    case WM_PAINT:
+        hdc = BeginPaint( hwnd, &ps );
+        hdcsrc = (HDC)GetWindowLongPtrA( hwnd, GWLP_USERDATA );
+        GetClientRect( hwnd, &rc );
+        BitBlt( hdc, 0, 0, rc.right, rc.bottom, hdcsrc, 0, 0, SRCCOPY );
+        EndPaint( hwnd, &ps );
+        return 0;
+    }
+
+    return DefWindowProcW(hwnd, message, wParam, lParam);
+}
+
+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 = 60000;
+    BOOL wait = TRUE, toggle = TRUE;
+
+    GetClientRect( hwnd, &rect );
+
+    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 );
+
+    hdc = GetDC( hwnd );
+    hbitmap = CreateCompatibleBitmap( hdc, rect.right, rect.bottom );
+    hdctmp = CreateCompatibleDC( hdc );
+    SelectObject( hdctmp, hbitmap );
+
+    FillRgn( hdctmp, hrgn_expect, b_expectonly );
+    FillRgn( hdctmp, hrgn_actual, b_actualonly );
+    FillRgn( hdctmp, hrgn_intersect, b_intersect );
+
+    DeleteObject( hrgn_intersect );
+    DeleteObject( b_intersect );
+    DeleteObject( b_actualonly );
+    DeleteObject( b_expectonly );
+
+    hchild = CreateWindowExA( 0, "SimpleWindowClass", "Test child", WS_CHILD,
+                              0, 0, rect.right, rect.bottom, hwnd, 0, 0, NULL );
+    SetWindowLongPtrA( hchild, GWLP_WNDPROC, (LONG_PTR)vis_child_wnd_proc );
+    SetWindowLongPtrA( hchild, GWLP_USERDATA, (LONG_PTR)hdctmp );
+
+    hshow = hchild;
+    hhide = hother;
+
+    start_time = GetTickCount();
+    while ((elapsed = GetTickCount() - start_time) < timeout)
+    {
+        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 (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( hdctmp );
+    DeleteObject( hbitmap );
+}
+
+struct exposure_test {
+    int ex_style;
+    int style;
+    int region_op;
+    BOOL todo;
+};
+
+#define subtest_swp_paint_regions(w,p,c,t) subtest_swp_paint_regions_(__LINE__,w,p,c,t)
+
+static void subtest_swp_paint_regions_( int line, int wrap_toplevel, LPCSTR parent_class, LPCSTR child_class, const struct exposure_test *exposure_tests )
+{
+    const struct exposure_test *extest;
+    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;
+
+    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;
+    }
+
+    hparent = CreateWindowExA( 0, parent_class, "Test parent", base_style,
+                               80, 80, 200, 200, htoplevel, 0, 0, NULL );
+    ok( hparent != 0, "Failed to create parent window (%s): %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, "Failed to create child window (%s): %lu\n",
+        debugstr_a( child_class ), GetLastError() );
+
+    GetClientRect( hparent, &rect_cli );
+    hrgn_clip = CreateRectRgnIndirect( &rect_cli );
+
+    for (extest = exposure_tests; extest->region_op; extest++)
+    {
+        int delta;
+
+        winetest_push_context( "%d: SetWindowPos redraw #%Id (ex_style = %#x, style = %#x, region_op = %d)",
+                               line, extest - exposure_tests, extest->ex_style, extest->style, extest->region_op );
+
+        SetWindowLongA( hparent, GWL_EXSTYLE, extest->ex_style );
+        SetWindowLongA( hparent, GWL_STYLE, base_style | extest->style );
+
+        for (delta = -20; delta <= 20; delta += 20)
+        {
+            RECT rect_new = rect_old;
+            int update_region_type;
+            int rgn_equal;
+
+            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, extest->region_op );
+                CombineRgn( hrgn_expect, hrgn_expect, hrgn_clip, RGN_AND );
+            }
+
+            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 );
+
+            SetRectRgn( hrgn_actual, 0, 0, 0, 0 );
+            update_region_type = GetUpdateRgn( hparent, hrgn_actual, FALSE );
+            ok( update_region_type != ERROR, "GetUpdateRgn failed\n" );
+
+            rgn_equal = EqualRgn( hrgn_expect, hrgn_actual );
+            todo_wine_if( extest->todo && !EqualRect( &rect_old, &rect_new ) )
+            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)
+{
+    static const struct exposure_test nocomposited[] = {
+        {                0, WS_CLIPCHILDREN, RGN_DIFF, FALSE },
+        {                0,               0, RGN_DIFF, TRUE  },
+        { WS_EX_COMPOSITED, WS_CLIPCHILDREN, RGN_DIFF, FALSE },
+        { WS_EX_COMPOSITED,               0, RGN_DIFF, TRUE  },
+        { 0 }
+    };
+    static const struct exposure_test composited[] = {
+        {                0, WS_CLIPCHILDREN, RGN_DIFF, FALSE },
+        {                0,               0, RGN_DIFF, TRUE  },
+        { WS_EX_COMPOSITED, WS_CLIPCHILDREN, RGN_OR  , TRUE  },
+        { WS_EX_COMPOSITED,               0, RGN_OR  , TRUE  },
+        { 0 }
+    };
+    subtest_swp_paint_regions( 1, "SimpleWindowClass", "SimpleWindowClass", composited );
+    subtest_swp_paint_regions( 1, "SimpleWindowClass", "SimpleWindowClassWithParentDC", composited );
+    subtest_swp_paint_regions( 1, "SimpleWindowClassWithParentDC", "SimpleWindowClass", nocomposited );
+    subtest_swp_paint_regions( 1, "SimpleWindowClassWithParentDC", "SimpleWindowClassWithParentDC", nocomposited );
+    subtest_swp_paint_regions( 0, "SimpleWindowClass", "SimpleWindowClass", composited );
+    subtest_swp_paint_regions( 0, "SimpleWindowClass", "SimpleWindowClassWithParentDC", composited );
+    subtest_swp_paint_regions( 0, "SimpleWindowClassWithParentDC", "SimpleWindowClass", composited );
+    subtest_swp_paint_regions( 0, "SimpleWindowClassWithParentDC", "SimpleWindowClassWithParentDC", composited );
+}
+
 struct wnd_event
 {
     HWND hwnd;
@@ -10387,6 +10669,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 +19157,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