Extensive ScrollDC investigation (resubmit)

Aric Stewart aric at codeweavers.com
Tue May 1 07:50:54 CDT 2007


Reworking some parts of ScrollDC to properly mask and scroll the right 
areas of
the DC as well as generate the correct update regions.
Extensive testing used to verify the behavior using winetest_interactive
---
  dlls/user32/tests/win.c   |  318 
+++++++++++++++++++++++++++++++++++++++++++++
  dlls/winex11.drv/scroll.c |   64 +++++++++-
  2 files changed, 379 insertions(+), 3 deletions(-)
-------------- next part --------------
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c
index 632fcc1..1ad4857 100644
--- a/dlls/user32/tests/win.c
+++ b/dlls/user32/tests/win.c
@@ -42,6 +42,12 @@
 #define LONG_PTR INT_PTR
 #define ULONG_PTR UINT_PTR
 
+/* The following macro causes the morescroll tests to pause after each drawing
+ * This allow the tester to verify the drawing of the functions by eye.
+ * if you wish to run interactive tests without the pause. change this line
+ */
+#define DRAW_CHECK if (winetest_interactive > 0) {char c; scanf("%c",&c);}
+
 void dump_region(HRGN hrgn);
 
 static HWND (WINAPI *pGetAncestor)(HWND,UINT);
@@ -2922,6 +2928,317 @@ static void test_window_styles(void)
     check_window_style(0, WS_EX_APPWINDOW, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_APPWINDOW|WS_EX_WINDOWEDGE);
 }
 
+static HWND create_listbox(HWND parent)
+{
+    HWND hwnd1;
+
+    hwnd1  = CreateWindowExA(0,"listbox",NULL,WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_BORDER, 25,10,100,150,parent,0,0,NULL);
+
+    SendMessage(hwnd1, LB_ADDSTRING,0,(LPARAM)"ENTRY 1");
+    SendMessage(hwnd1, LB_ADDSTRING,0,(LPARAM)"ENTRY 2");
+    SendMessage(hwnd1, LB_ADDSTRING,0,(LPARAM)"ENTRY 3");
+    SendMessage(hwnd1, LB_ADDSTRING,0,(LPARAM)"ENTRY 4");
+    SendMessage(hwnd1, LB_ADDSTRING,0,(LPARAM)"ENTRY 5");
+    SendMessage(hwnd1, LB_ADDSTRING,0,(LPARAM)"ENTRY 6");
+    SendMessage(hwnd1, LB_ADDSTRING,0,(LPARAM)"ENTRY 7");
+
+    return hwnd1;
+}
+
+static void test_morescrolldc(HWND parent)
+{
+    RECT rc,cliprc,rcu,rc2,exprc;
+    HRGN hrgn,exprgn,tmprgn;
+    HWND hwnd1;
+    HDC hdc;
+
+    hwnd1 = create_listbox(parent);
+    ShowWindow( parent, SW_SHOW);
+
+    hdc = GetDCEx(hwnd1, 0, DCX_CACHE);
+
+    hrgn = CreateRectRgn(0,0,0,0);
+    exprgn = CreateRectRgn(0,0,0,0);
+    tmprgn = CreateRectRgn(0,0,0,0);
+
+    trace("valid/invalid scrollDC tests\n");
+
+    SetRect(&rc,0,45,100,109);
+    SetRect(&cliprc,0,0,100,150);
+    SetRect(&rcu,0,0,0,0);
+
+    if (winetest_interactive > 0)
+    {
+        RedrawWindow(parent, NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+        flush_events();
+    }
+    DRAW_CHECK;
+
+    trace("no invalid area (1456767)\n");
+    UpdateWindow( hwnd1 );
+    if (winetest_interactive > 0) flush_events();
+    SetRect(&rcu,0,0,0,0);
+    ScrollDC(hdc, 0, -32, &rc, &cliprc, hrgn, &rcu);
+    DRAW_CHECK;
+
+    SetRectRgn( exprgn, 0,77,98,109);
+    ok( EqualRgn( exprgn, hrgn), "wrong update region after scroll\n");
+    if (winetest_interactive > 0) dump_region(hrgn);
+    SetRect(&exprc,0,77,98,109);
+    ok( EqualRect( &exprc, &rcu ), "wrong update rect after scroll\n");
+    if (winetest_debug > 0) trace("update rect is %d,%d - %d,%d\n",
+            rcu.left,rcu.top,rcu.right,rcu.bottom);
+
+    if (winetest_interactive > 0)
+    {
+        RedrawWindow(parent, NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+        flush_events();
+    }
+    DRAW_CHECK;
+
+    trace("fully invalid area (1456767)\n");
+    UpdateWindow( hwnd1 );
+    if (winetest_interactive > 0) flush_events();
+    InvalidateRect( hwnd1, NULL, FALSE );
+    GetUpdateRect( hwnd1, &rcu, 0);
+    trace("invalid rect is %d,%d - %d,%d\n",
+            rcu.left,rcu.top,rcu.right,rcu.bottom);
+    SetRect(&rcu,0,0,0,0);
+    ScrollDC(hdc, 0, -32, &rc, &cliprc, hrgn, &rcu);
+    DRAW_CHECK;
+
+    SetRectRgn( exprgn, 0,77,98,109);
+    ok( EqualRgn( exprgn, hrgn), "wrong update region after scroll\n");
+    if (winetest_debug > 0) dump_region(hrgn);
+    SetRect(&exprc,0,77,98,109);
+    ok( EqualRect( &exprc, &rcu ), "wrong update rect after scroll\n");
+    if (winetest_debug > 0) trace("update rect is %d,%d - %d,%d\n",
+            rcu.left,rcu.top,rcu.right,rcu.bottom);
+
+    if (winetest_interactive > 0)
+    {
+        RedrawWindow(parent, NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+        flush_events();
+    }
+    DRAW_CHECK;
+
+    trace("fully contained invalid area in target (1534567)\n");
+    UpdateWindow( hwnd1 );
+    if (winetest_interactive > 0) flush_events();
+    SetRect(&rc,0,61,100,93);
+    SetRect(&rc2,0,32,100,48);
+    InvalidateRect( hwnd1, &rc2, FALSE);
+    SetRect(&rcu,0,0,0,0);
+    ScrollDC(hdc, 0, -48, &rc, &cliprc, hrgn, &rcu);
+    DRAW_CHECK;
+
+    SetRectRgn( exprgn, 0,32,98,45);
+    SetRectRgn( tmprgn, 0,61,98,93);
+    CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+    ok( EqualRgn( exprgn, hrgn), "wrong update region after scroll\n");
+    if (winetest_debug > 0) dump_region(hrgn);
+    SetRect(&exprc,0,32,98,93);
+    ok( EqualRect( &exprc, &rcu ), "wrong update rect after scroll\n");
+    if (winetest_debug > 0) trace("update rect is %d,%d - %d,%d\n",
+            rcu.left,rcu.top,rcu.right,rcu.bottom);
+
+    if (winetest_interactive > 0)
+    {
+        RedrawWindow(parent, NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+        flush_events();
+    }
+    DRAW_CHECK;
+
+    trace("fully contained invalid area in overlap (1436567)\n");
+    UpdateWindow( hwnd1 );
+    if (winetest_interactive > 0) flush_events();
+    SetRect(&rc,0,45,100,109);
+    SetRect(&rc2,0,61,100,77);
+    InvalidateRect( hwnd1, &rc2, FALSE);
+    SetRect(&rcu,0,0,0,0);
+    ScrollDC(hdc, 0, -32, &rc, &cliprc, hrgn, &rcu);
+    DRAW_CHECK;
+
+    SetRectRgn( exprgn, 0,29,98,45);
+    SetRectRgn( tmprgn, 0,61,98,109);
+    CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+    ok( EqualRgn( exprgn, hrgn), "wrong update region after scroll\n");
+    if (winetest_debug > 0) dump_region(hrgn);
+    SetRect(&exprc,0,29,98,109);
+    ok( EqualRect( &exprc, &rcu ), "wrong update rect after scroll\n");
+    if (winetest_debug > 0) trace("update rect is %d,%d - %d,%d\n",
+            rcu.left,rcu.top,rcu.right,rcu.bottom);
+
+    if (winetest_interactive > 0)
+    {
+        RedrawWindow(parent, NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+        flush_events();
+    }
+    DRAW_CHECK;
+
+    trace("fully contained invalid area in source (1454767)\n");
+    UpdateWindow( hwnd1 );
+    if (winetest_interactive > 0) flush_events();
+    SetRect(&rc,0,45,100,109);
+    SetRect(&rc2,0,77,100,93);
+    InvalidateRect( hwnd1, &rc2, FALSE);
+    SetRect(&rcu,0,0,0,0);
+    ScrollDC(hdc, 0, -32, &rc, &cliprc, hrgn, &rcu);
+    DRAW_CHECK;
+
+    SetRectRgn( exprgn, 0,45,98,61);
+    SetRectRgn( tmprgn, 0,77,98,109);
+    CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+    ok( EqualRgn( exprgn, hrgn), "wrong update region after scroll\n");
+    if (winetest_debug > 0) dump_region(hrgn);
+    SetRect(&exprc,0,45,98,109);
+    ok( EqualRect( &exprc, &rcu ), "wrong update rect after scroll\n");
+    if (winetest_debug > 0) trace("update rect is %d,%d - %d,%d\n",
+            rcu.left,rcu.top,rcu.right,rcu.bottom);
+
+    if (winetest_interactive > 0)
+    {
+        RedrawWindow(parent, NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+        flush_events();
+    }
+    DRAW_CHECK;
+
+    trace("invalid rect outside of the scroll area (1456767)\n");
+    UpdateWindow( hwnd1 );
+    if (winetest_interactive > 0) flush_events();
+    SetRect(&rc2,0,6,100,10);
+    InvalidateRect( hwnd1, &rc2, FALSE);
+    SetRect(&rcu,0,0,0,0);
+    ScrollDC(hdc, 0, -32, &rc, &cliprc, hrgn, &rcu);
+    DRAW_CHECK;
+
+    SetRectRgn( exprgn, 0,77,98,109);
+    ok( EqualRgn( exprgn, hrgn), "wrong update region after scroll\n");
+    if (winetest_debug > 0) dump_region(hrgn);
+    SetRect(&exprc,0,77,98,109);
+    ok( EqualRect( &exprc, &rcu ), "wrong update rect after scroll\n");
+    if (winetest_debug > 0) trace("update rect is %d,%d - %d,%d\n",
+            rcu.left,rcu.top,rcu.right,rcu.bottom);
+
+    if (winetest_interactive > 0)
+    {
+        RedrawWindow(parent, NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+        flush_events();
+    }
+    DRAW_CHECK;
+
+    trace("invalid rect overlapping the scroll target area (1245677)\n");
+    UpdateWindow( hwnd1 );
+    if (winetest_interactive > 0) flush_events();
+    SetRect(&rc2,0,6,100,35);
+    SetRect(&rc,0,45,100,109);
+    InvalidateRect( hwnd1, &rc2, FALSE);
+    SetRect(&rcu,0,0,0,0);
+    ScrollDC(hdc, 0, -16, &rc, &cliprc, hrgn, &rcu);
+    DRAW_CHECK;
+
+    SetRectRgn( exprgn, 0,29,98,35);
+    SetRectRgn( tmprgn, 0,93,98,109);
+    CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+    ok( EqualRgn( exprgn, hrgn), "wrong update region after scroll\n");
+    if (winetest_debug > 0) dump_region(hrgn);
+    SetRect(&exprc,0,29,98,109);
+    ok( EqualRect( &exprc, &rcu ), "wrong update rect after scroll\n");
+    if (winetest_debug > 0) trace("update rect is %d,%d - %d,%d\n",
+            rcu.left,rcu.top,rcu.right,rcu.bottom);
+
+    if (winetest_interactive > 0)
+    {
+        RedrawWindow(parent, NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+        flush_events();
+    }
+    DRAW_CHECK;
+
+    trace("invalid rect overlapping the scroll source area (1456567)\n");
+    UpdateWindow( hwnd1 );
+    if (winetest_interactive > 0) flush_events();
+    SetRect(&rc,0,45,100,109);
+    SetRect(&rc2,0,93,100,125);
+    InvalidateRect( hwnd1, &rc2, FALSE);
+    SetRect(&rcu,0,0,0,0);
+    ScrollDC(hdc, 0, -32, &rc, &cliprc, hrgn, &rcu);
+    DRAW_CHECK;
+
+    SetRectRgn( exprgn, 0,61,98,109);
+    ok( EqualRgn( exprgn, hrgn), "wrong update region after scroll\n");
+    if (winetest_debug > 0) dump_region(hrgn);
+    SetRect(&exprc,0,61,98,109);
+    ok( EqualRect( &exprc, &rcu ), "wrong update rect after scroll\n");
+    if (winetest_debug > 0) trace("update rect is %d,%d - %d,%d\n",
+            rcu.left,rcu.top,rcu.right,rcu.bottom);
+
+    if (winetest_interactive > 0)
+    {
+        RedrawWindow(parent, NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+        flush_events();
+    }
+    DRAW_CHECK;
+
+    trace("invalid area in target overlaping source (1234767)\n");
+    UpdateWindow( hwnd1 );
+    if (winetest_interactive > 0) flush_events();
+    SetRect(&rc,0,45,100,109);
+    SetRect(&rc2,0,29,100,61);
+    InvalidateRect( hwnd1, &rc2, FALSE);
+    SetRect(&rcu,0,0,0,0);
+    ScrollDC(hdc, 0, -32, &rc, &cliprc, hrgn, &rcu);
+    DRAW_CHECK;
+
+    SetRectRgn( exprgn, 0,13,98,61);
+    SetRectRgn( tmprgn, 0,77,98,109);
+    CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+    ok( EqualRgn( exprgn, hrgn), "wrong update region after scroll\n");
+    if (winetest_debug > 0) dump_region(hrgn);
+    SetRect(&exprc,0,13,98,109);
+    ok( EqualRect( &exprc, &rcu ), "wrong update rect after scroll\n");
+    if (winetest_debug > 0) trace("update rect is %d,%d - %d,%d\n",
+            rcu.left,rcu.top,rcu.right,rcu.bottom);
+
+    if (winetest_interactive > 0)
+    {
+        RedrawWindow(parent, NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+        flush_events();
+    }
+    DRAW_CHECK;
+
+    trace("small invalid area in source (mangled...about 125*767)\n");
+    UpdateWindow( hwnd1 );
+    if (winetest_interactive > 0) flush_events();
+    SetRect(&rc,0,45,100,109);
+    SetRect(&rc2,0,53,100,61);
+    InvalidateRect( hwnd1, &rc2, FALSE);
+    SetRect(&rcu,0,0,0,0);
+    ScrollDC(hdc, 0, -32, &rc, &cliprc, hrgn, &rcu);
+    DRAW_CHECK;
+
+    SetRectRgn( exprgn, 0,21,98,29);
+    SetRectRgn( tmprgn, 0,53,98,61);
+    CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+    SetRectRgn( tmprgn, 0,77,98,109);
+    CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+    ok( EqualRgn( exprgn, hrgn), "wrong update region after scroll\n");
+    if (winetest_debug > 0) dump_region(hrgn);
+    SetRect(&exprc,0,21,98,109);
+    ok( EqualRect( &exprc, &rcu ), "wrong update rect after scroll\n");
+    if (winetest_debug > 0) trace("update rect is %d,%d - %d,%d\n",
+            rcu.left,rcu.top,rcu.right,rcu.bottom);
+
+    if (winetest_interactive > 0)
+    {
+        RedrawWindow(parent, NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+        flush_events();
+    }
+    DRAW_CHECK;
+
+    DestroyWindow(hwnd1);
+    ShowWindow( parent, SW_HIDE);
+}
+
 static void test_scrollvalidate( HWND parent)
 {
     HDC hdc;
@@ -4369,6 +4686,7 @@ START_TEST(win)
     test_nccalcscroll( hwndMain);
     test_scrollvalidate( hwndMain);
     test_scrolldc( hwndMain);
+    test_morescrolldc(hwndMain);
     test_scroll();
     test_IsWindowUnicode();
     test_vis_rgn(hwndMain);
diff --git a/dlls/winex11.drv/scroll.c b/dlls/winex11.drv/scroll.c
index 70b2f40..c8f5b18 100644
--- a/dlls/winex11.drv/scroll.c
+++ b/dlls/winex11.drv/scroll.c
@@ -66,6 +66,8 @@ BOOL X11DRV_ScrollDC( HDC hdc, INT dx, I
     RECT rcSrc, rcClip, offset;
     INT dxdev, dydev, res;
     HRGN DstRgn, clipRgn, visrgn;
+    HRGN UpdateRgn = CreateRectRgn(0,0,0,0);
+    HRGN OffsetUpdate = 0;
     INT code = X11DRV_START_EXPOSURES;
 
     TRACE("dx,dy %d,%d rcScroll %s rcClip %s hrgnUpdate %p lprcUpdate %p\n",
@@ -120,13 +122,58 @@ BOOL X11DRV_ScrollDC( HDC hdc, INT dx, I
     if( TRACE_ON( scroll)) dump_region( "Destination scroll region: ", DstRgn);
     /* if there are any, do it */
     if( res > NULLREGION) {
-        RECT rect ;
+        RECT rect;
+        HRGN UpdateClip = CreateRectRgn(0,0,0,0);
+        /* Scroll Window does not scroll any already invalid areas */
+        if (GetUpdateRgn(WindowFromDC(hdc),UpdateRgn,FALSE)!=NULLREGION)
+        {
+            HRGN SrcMask;
+            /* Apparently special case.  If the update Rgn fills the entire
+               DstRgn windows appears to ignore it */
+
+            OffsetUpdate = CreateRectRgn(0,0,0,0);
+            CombineRgn(OffsetUpdate,UpdateRgn,OffsetUpdate,RGN_OR);
+            OffsetRgn(OffsetUpdate,dxdev,dydev);
+
+            SrcMask = CreateRectRgn(0,0,0,0);
+            CombineRgn(SrcMask,OffsetUpdate,DstRgn,RGN_AND);
+            if (!EqualRgn(DstRgn,SrcMask))
+            {
+                DeleteObject(SrcMask);
+
+                /* Mask out invalid areas in our Source region */
+                SrcMask = CreateRectRgnIndirect( &rcSrc);
+                OffsetRgn(SrcMask,dxdev,dydev);
+                CombineRgn(SrcMask,OffsetUpdate,SrcMask,RGN_AND);
+                CombineRgn(SrcMask,UpdateRgn,SrcMask,RGN_OR);
+                CombineRgn(UpdateClip,DstRgn,SrcMask, RGN_DIFF);
+
+                /* later we are only interested in scrolled update region that
+                   fall in our source area */
+                CombineRgn(OffsetUpdate,OffsetUpdate,DstRgn,RGN_AND);
+            }
+            else
+            {
+                /* Same as no update region */
+                DeleteObject(OffsetUpdate);
+                OffsetUpdate = 0;
+                DeleteObject(UpdateRgn);
+                UpdateRgn = CreateRectRgn(0,0,0,0);
+                CombineRgn(UpdateClip,DstRgn,UpdateClip,RGN_OR);
+            }
+
+            DeleteObject(SrcMask);
+        }
+        else
+            CombineRgn(UpdateClip,DstRgn,UpdateClip,RGN_OR);
+
         /* clip to the destination region, so we can BitBlt with a simple
          * bounding rectangle */
         if( clipRgn)
-            ExtSelectClipRgn( hdc, DstRgn, RGN_AND);
+            ExtSelectClipRgn( hdc, UpdateClip, RGN_AND);
         else
-            SelectClipRgn( hdc, DstRgn);
+            SelectClipRgn( hdc, UpdateClip);
+
         GetRgnBox( DstRgn, &rect);
         DPtoLP(hdc, (LPPOINT)&rect, 2);
         TRACE("destination rect: %s\n", wine_dbgstr_rect(&rect));
@@ -134,6 +181,8 @@ BOOL X11DRV_ScrollDC( HDC hdc, INT dx, I
         BitBlt( hdc, rect.left, rect.top,
                     rect.right - rect.left, rect.bottom - rect.top,
                     hdc, rect.left - dx, rect.top - dy, SRCCOPY);
+
+        DeleteObject(UpdateClip);
     }
     /* compute the update areas.  This is the combined clip rectangle
      * minus the scrolled region, and intersected with the visible
@@ -167,6 +216,12 @@ BOOL X11DRV_ScrollDC( HDC hdc, INT dx, I
             hrgn = CreateRectRgnIndirect( &rcClip);
         CombineRgn( hrgn, hrgn, visrgn, RGN_AND);
         CombineRgn( hrgn, hrgn, DstRgn, RGN_DIFF);
+        /*add any invalid rects in the DstRgn */
+        CombineRgn( DstRgn, DstRgn, UpdateRgn, RGN_AND);
+        CombineRgn( hrgn, hrgn, DstRgn, RGN_OR);
+        /* add the offset invalid region also */
+        if (OffsetUpdate)
+            CombineRgn( hrgn, hrgn, OffsetUpdate, RGN_OR);
         /* add the exposures to this */
         if( ExpRgn) {
             if( TRACE_ON( scroll)) dump_region( "Expose region: ", ExpRgn);
@@ -187,6 +242,9 @@ BOOL X11DRV_ScrollDC( HDC hdc, INT dx, I
     SelectClipRgn( hdc, clipRgn);
     DeleteObject( visrgn);
     DeleteObject( DstRgn);
+    DeleteObject(UpdateRgn);
+    if (OffsetUpdate)
+        DeleteObject(OffsetUpdate);
     if( clipRgn) DeleteObject( clipRgn);
     return TRUE;
 }


More information about the wine-patches mailing list