Extensive ScrollDC investigation

Aric Stewart aric at codeweavers.com
Sun Apr 29 16:53:14 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.
---
  dlls/user32/tests/win.c   |  316 
+++++++++++++++++++++++++++++++++++++++++++++
  dlls/winex11.drv/scroll.c |   64 +++++++++-
  2 files changed, 377 insertions(+), 3 deletions(-)
-------------- next part --------------
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c
index 632fcc1..e60e37a 100644
--- a/dlls/user32/tests/win.c
+++ b/dlls/user32/tests/win.c
@@ -42,6 +42,8 @@
 #define LONG_PTR INT_PTR
 #define ULONG_PTR UINT_PTR
 
+#define DRAW_CHECK if (winetest_debug > 0) {char c; scanf("%c",&c);}
+
 void dump_region(HRGN hrgn);
 
 static HWND (WINAPI *pGetAncestor)(HWND,UINT);
@@ -2922,6 +2924,319 @@ 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_debug > 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_debug > 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_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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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_debug > 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;
@@ -4370,6 +4685,7 @@ START_TEST(win)
     test_scrollvalidate( hwndMain);
     test_scrolldc( hwndMain);
     test_scroll();
+    test_morescrolldc(hwndMain);
     test_IsWindowUnicode();
     test_vis_rgn(hwndMain);
 
diff --git a/dlls/winex11.drv/scroll.c b/dlls/winex11.drv/scroll.c
index 70b2f40..cdde3f3 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