[PATCH 2/7] win32u: Move NtUserScrollWindowEx implementation from user32.

Jacek Caban wine at gitlab.winehq.org
Wed Jun 8 19:14:31 CDT 2022


From: Jacek Caban <jacek at codeweavers.com>

Signed-off-by: Jacek Caban <jacek at codeweavers.com>
---
 dlls/user32/edit.c           |   4 +-
 dlls/user32/listbox.c        |   8 +-
 dlls/user32/mdi.c            |   8 +-
 dlls/user32/painting.c       | 240 +----------------------------------
 dlls/user32/user32.spec      |   2 +-
 dlls/win32u/dce.c            | 204 +++++++++++++++++++++++++++++
 dlls/win32u/gdiobj.c         |   1 +
 dlls/win32u/win32u.spec      |   2 +-
 dlls/win32u/win32u_private.h |   4 +
 dlls/win32u/window.c         |   2 +-
 dlls/win32u/wrappers.c       |   9 ++
 include/ntuser.h             |   6 +
 12 files changed, 239 insertions(+), 251 deletions(-)

diff --git a/dlls/user32/edit.c b/dlls/user32/edit.c
index b7eaf2b9ddf..70f105b9a0a 100644
--- a/dlls/user32/edit.c
+++ b/dlls/user32/edit.c
@@ -1708,8 +1708,8 @@ static BOOL EDIT_EM_LineScroll_internal(EDITSTATE *es, INT dx, INT dy)
 
 		GetClientRect(es->hwndSelf, &rc1);
 		IntersectRect(&rc, &rc1, &es->format_rect);
-		ScrollWindowEx(es->hwndSelf, -dx, dy,
-				NULL, &rc, NULL, NULL, SW_INVALIDATE);
+                NtUserScrollWindowEx(es->hwndSelf, -dx, dy,
+                                     NULL, &rc, NULL, NULL, SW_INVALIDATE);
 		/* force scroll info update */
 		EDIT_UpdateScrollInfo(es);
 	}
diff --git a/dlls/user32/listbox.c b/dlls/user32/listbox.c
index 0eed2fdfe65..b8f127c0963 100644
--- a/dlls/user32/listbox.c
+++ b/dlls/user32/listbox.c
@@ -439,8 +439,8 @@ static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll )
         else
             dy = (descr->top_item - index) * descr->item_height;
 
-        ScrollWindowEx( descr->self, dx, dy, NULL, NULL, 0, NULL,
-                        SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
+        NtUserScrollWindowEx( descr->self, dx, dy, NULL, NULL, 0, NULL,
+                              SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
     }
     else
         InvalidateRect( descr->self, NULL, TRUE );
@@ -1340,8 +1340,8 @@ static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
         /* Invalidate the focused item so it will be repainted correctly */
         if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
             InvalidateRect( descr->self, &rect, TRUE );
-        ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
-                          SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
+        NtUserScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
+                              SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
     }
     else
         InvalidateRect( descr->self, NULL, TRUE );
diff --git a/dlls/user32/mdi.c b/dlls/user32/mdi.c
index e5a52012413..26772aed763 100644
--- a/dlls/user32/mdi.c
+++ b/dlls/user32/mdi.c
@@ -1822,11 +1822,11 @@ void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam,
     SetScrollPos(hWnd, (uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ , newPos, TRUE);
 
     if( uMsg == WM_VSCROLL )
-	ScrollWindowEx(hWnd ,0 ,curPos - newPos, NULL, NULL, 0, NULL,
-			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
+	NtUserScrollWindowEx( hWnd ,0 ,curPos - newPos, NULL, NULL, 0, NULL,
+                              SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
     else
-	ScrollWindowEx(hWnd ,curPos - newPos, 0, NULL, NULL, 0, NULL,
-			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
+	NtUserScrollWindowEx( hWnd ,curPos - newPos, 0, NULL, NULL, 0, NULL,
+                              SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
 done:
     SetThreadDpiAwarenessContext( context );
 }
diff --git a/dlls/user32/painting.c b/dlls/user32/painting.c
index e7be362773a..419c952d998 100644
--- a/dlls/user32/painting.c
+++ b/dlls/user32/painting.c
@@ -19,72 +19,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#include <assert.h>
-#include <stdarg.h>
-#include <string.h>
-
-#include "ntstatus.h"
-#define WIN32_NO_STATUS
 #include "user_private.h"
-#include "win.h"
-#include "controls.h"
-#include "wine/server.h"
-#include "wine/list.h"
-#include "wine/debug.h"
-
-WINE_DEFAULT_DEBUG_CHANNEL(win);
-
-
-/*************************************************************************
- *             fix_caret
- *
- * Helper for ScrollWindowEx:
- * If the return value is 0, no special caret handling is necessary.
- * Otherwise the return value is the handle of the window that owns the
- * caret. Its caret needs to be hidden during the scroll operation and
- * moved to new_caret_pos if move_caret is TRUE.
- */
-static HWND fix_caret(HWND hWnd, const RECT *scroll_rect, INT dx, INT dy,
-                     UINT flags, LPBOOL move_caret, LPPOINT new_caret_pos)
-{
-    GUITHREADINFO info;
-    RECT rect, mapped_rcCaret;
-
-    info.cbSize = sizeof(info);
-    if (!NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info )) return 0;
-    if (!info.hwndCaret) return 0;
-    
-    mapped_rcCaret = info.rcCaret;
-    if (info.hwndCaret == hWnd)
-    {
-        /* The caret needs to be moved along with scrolling even if it's
-         * outside the visible area. Otherwise, when the caret is scrolled
-         * out from the view, the position won't get updated anymore and
-         * the caret will never scroll back again. */
-        *move_caret = TRUE;
-        new_caret_pos->x = info.rcCaret.left + dx;
-        new_caret_pos->y = info.rcCaret.top + dy;
-    }
-    else
-    {
-        *move_caret = FALSE;
-        if (!(flags & SW_SCROLLCHILDREN) || !IsChild(hWnd, info.hwndCaret))
-            return 0;
-        MapWindowPoints(info.hwndCaret, hWnd, (LPPOINT)&mapped_rcCaret, 2);
-    }
-
-    /* If the caret is not in the src/dest rects, all is fine done. */
-    if (!IntersectRect(&rect, scroll_rect, &mapped_rcCaret))
-    {
-        rect = *scroll_rect;
-        OffsetRect(&rect, dx, dy);
-        if (!IntersectRect(&rect, &rect, &mapped_rcCaret))
-            return 0;
-    }
-
-    /* Indicate that the caret needs to be updated during the scrolling. */
-    return info.hwndCaret;
-}
 
 
 /***********************************************************************
@@ -197,177 +132,6 @@ BOOL WINAPI ValidateRect( HWND hwnd, const RECT *rect )
 }
 
 
-static INT scroll_window( HWND hwnd, INT dx, INT dy, const RECT *rect, const RECT *clipRect,
-                          HRGN hrgnUpdate, LPRECT rcUpdate, UINT flags, BOOL is_ex )
-{
-    INT   retVal = NULLREGION;
-    BOOL  bOwnRgn = TRUE;
-    BOOL  bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE));
-    int rdw_flags;
-    HRGN  hrgnTemp;
-    HRGN  hrgnWinupd = 0;
-    HDC   hDC;
-    RECT  rc, cliprc;
-    HWND hwndCaret = NULL;
-    BOOL moveCaret = FALSE;
-    POINT newCaretPos;
-
-    TRACE( "%p, %d,%d hrgnUpdate=%p rcUpdate = %p %s %04x\n",
-           hwnd, dx, dy, hrgnUpdate, rcUpdate, wine_dbgstr_rect(rect), flags );
-    TRACE( "clipRect = %s\n", wine_dbgstr_rect(clipRect));
-    if( flags & ~( SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE))
-        FIXME("some flags (%04x) are unhandled\n", flags);
-
-    rdw_flags = (flags & SW_ERASE) && (flags & SW_INVALIDATE) ?
-                                RDW_INVALIDATE | RDW_ERASE  : RDW_INVALIDATE ;
-
-    if (!WIN_IsWindowDrawable( hwnd, TRUE )) return ERROR;
-    hwnd = WIN_GetFullHandle( hwnd );
-
-    GetClientRect(hwnd, &rc);
-
-    if (clipRect) IntersectRect(&cliprc,&rc,clipRect);
-    else cliprc = rc;
-
-    if (rect) IntersectRect(&rc, &rc, rect);
-
-    if( hrgnUpdate ) bOwnRgn = FALSE;
-    else if( bUpdate ) hrgnUpdate = CreateRectRgn( 0, 0, 0, 0 );
-
-    newCaretPos.x = newCaretPos.y = 0;
-
-    if( !IsRectEmpty(&cliprc) && (dx || dy)) {
-        DWORD dcxflags = 0;
-        DWORD style = GetWindowLongW( hwnd, GWL_STYLE );
-
-        hwndCaret = fix_caret(hwnd, &rc, dx, dy, flags, &moveCaret, &newCaretPos);
-        if (hwndCaret)
-            NtUserHideCaret( hwndCaret );
-
-        if (is_ex) dcxflags |= DCX_CACHE;
-        if( style & WS_CLIPSIBLINGS) dcxflags |= DCX_CLIPSIBLINGS;
-        if( GetClassLongW( hwnd, GCL_STYLE ) & CS_PARENTDC)
-            dcxflags |= DCX_PARENTCLIP;
-        if( !(flags & SW_SCROLLCHILDREN) && (style & WS_CLIPCHILDREN))
-            dcxflags |= DCX_CLIPCHILDREN;
-        hDC = NtUserGetDCEx( hwnd, 0, dcxflags);
-        if (hDC)
-        {
-            NtUserScrollDC( hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate );
-
-            NtUserReleaseDC( hwnd, hDC );
-
-            if (!bUpdate)
-                NtUserRedrawWindow( hwnd, NULL, hrgnUpdate, rdw_flags);
-        }
-
-        /* If the windows has an update region, this must be
-         * scrolled as well. Keep a copy in hrgnWinupd
-         * to be added to hrngUpdate at the end. */
-        hrgnTemp = CreateRectRgn( 0, 0, 0, 0 );
-        retVal = NtUserGetUpdateRgn( hwnd, hrgnTemp, FALSE );
-        if (retVal != NULLREGION)
-        {
-            HRGN hrgnClip = CreateRectRgnIndirect(&cliprc);
-            if( !bOwnRgn) {
-                hrgnWinupd = CreateRectRgn( 0, 0, 0, 0);
-                CombineRgn( hrgnWinupd, hrgnTemp, 0, RGN_COPY);
-            }
-            OffsetRgn( hrgnTemp, dx, dy );
-            CombineRgn( hrgnTemp, hrgnTemp, hrgnClip, RGN_AND );
-            if( !bOwnRgn)
-                CombineRgn( hrgnWinupd, hrgnWinupd, hrgnTemp, RGN_OR );
-            NtUserRedrawWindow( hwnd, NULL, hrgnTemp, rdw_flags);
-
-           /* Catch the case where the scrolling amount exceeds the size of the
-            * original window. This generated a second update area that is the
-            * location where the original scrolled content would end up.
-            * This second region is not returned by the ScrollDC and sets
-            * ScrollWindowEx apart from just a ScrollDC.
-            *
-            * This has been verified with testing on windows.
-            */
-            if (abs(dx) > abs(rc.right - rc.left) ||
-                abs(dy) > abs(rc.bottom - rc.top))
-            {
-                SetRectRgn( hrgnTemp, rc.left + dx, rc.top + dy, rc.right+dx, rc.bottom + dy);
-                CombineRgn( hrgnTemp, hrgnTemp, hrgnClip, RGN_AND );
-                CombineRgn( hrgnUpdate, hrgnUpdate, hrgnTemp, RGN_OR );
-
-                if (rcUpdate)
-                {
-                    RECT rcTemp;
-                    GetRgnBox( hrgnTemp, &rcTemp );
-                    UnionRect( rcUpdate, rcUpdate, &rcTemp );
-                }
-
-                if( !bOwnRgn)
-                    CombineRgn( hrgnWinupd, hrgnWinupd, hrgnTemp, RGN_OR );
-            }
-            DeleteObject( hrgnClip );
-        }
-        DeleteObject( hrgnTemp );
-    } else {
-        /* nothing was scrolled */
-        if( !bOwnRgn)
-            SetRectRgn( hrgnUpdate, 0, 0, 0, 0 );
-        SetRectEmpty( rcUpdate);
-    }
-
-    if( flags & SW_SCROLLCHILDREN )
-    {
-        HWND *list = WIN_ListChildren( hwnd );
-        if (list)
-        {
-            int i;
-            RECT r, dummy;
-            for (i = 0; list[i]; i++)
-            {
-                WIN_GetRectangles( list[i], COORDS_PARENT, &r, NULL );
-                if (!rect || IntersectRect(&dummy, &r, rect))
-                    NtUserSetWindowPos( list[i], 0, r.left + dx, r.top  + dy, 0, 0,
-                                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
-                                        SWP_NOREDRAW | SWP_DEFERERASE );
-            }
-            HeapFree( GetProcessHeap(), 0, list );
-        }
-    }
-
-    if( flags & (SW_INVALIDATE | SW_ERASE) )
-        NtUserRedrawWindow( hwnd, NULL, hrgnUpdate, rdw_flags |
-                            ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0 ) );
-
-    if( hrgnWinupd) {
-        CombineRgn( hrgnUpdate, hrgnUpdate, hrgnWinupd, RGN_OR);
-        DeleteObject( hrgnWinupd);
-    }
-
-    if( moveCaret )
-        SetCaretPos( newCaretPos.x, newCaretPos.y );
-    if( hwndCaret )
-        NtUserShowCaret( hwndCaret );
-
-    if( bOwnRgn && hrgnUpdate ) DeleteObject( hrgnUpdate );
-
-    return retVal;
-}
-
-
-/*************************************************************************
- *		ScrollWindowEx (USER32.@)
- *
- * Note: contrary to what the doc says, pixels that are scrolled from the
- *      outside of clipRect to the inside are NOT painted.
- *
- */
-INT WINAPI ScrollWindowEx( HWND hwnd, INT dx, INT dy,
-                           const RECT *rect, const RECT *clipRect,
-                           HRGN hrgnUpdate, LPRECT rcUpdate,
-                           UINT flags )
-{
-    return scroll_window( hwnd, dx, dy, rect, clipRect, hrgnUpdate, rcUpdate, flags, TRUE );
-}
-
 /*************************************************************************
  *		ScrollWindow (USER32.@)
  *
@@ -375,8 +139,8 @@ INT WINAPI ScrollWindowEx( HWND hwnd, INT dx, INT dy,
 BOOL WINAPI ScrollWindow( HWND hwnd, INT dx, INT dy,
                           const RECT *rect, const RECT *clipRect )
 {
-    return scroll_window( hwnd, dx, dy, rect, clipRect, 0, NULL,
-                          SW_INVALIDATE | SW_ERASE | (rect ? 0 : SW_SCROLLCHILDREN), FALSE ) != ERROR;
+    UINT flags = SW_INVALIDATE | SW_ERASE | (rect ? 0 : SW_SCROLLCHILDREN) | SW_NODCCACHE;
+    return NtUserScrollWindowEx( hwnd, dx, dy, rect, clipRect, 0, NULL, flags );
 }
 
 /************************************************************************
diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec
index ee533305a6e..8a7387040dc 100644
--- a/dlls/user32/user32.spec
+++ b/dlls/user32/user32.spec
@@ -630,7 +630,7 @@
 @ stdcall ScrollChildren(long long long long)
 @ stdcall ScrollDC(long long long ptr ptr long ptr) NtUserScrollDC
 @ stdcall ScrollWindow(long long long ptr ptr)
-@ stdcall ScrollWindowEx(long long long ptr ptr long ptr long)
+@ stdcall ScrollWindowEx(long long long ptr ptr long ptr long) NtUserScrollWindowEx
 @ stdcall SendDlgItemMessageA(long long long long long)
 @ stdcall SendDlgItemMessageW(long long long long long)
 @ stdcall SendIMEMessageExA(long long)
diff --git a/dlls/win32u/dce.c b/dlls/win32u/dce.c
index acbb213d7b7..872112e4a24 100644
--- a/dlls/win32u/dce.c
+++ b/dlls/win32u/dce.c
@@ -1591,3 +1591,207 @@ BOOL WINAPI NtUserLockWindowUpdate( HWND hwnd )
     }
     return !InterlockedCompareExchangePointer( (void **)&locked_hwnd, hwnd, 0 );
 }
+
+/*************************************************************************
+ *             fix_caret
+ *
+ * Helper for NtUserScrollWindowEx:
+ * If the return value is 0, no special caret handling is necessary.
+ * Otherwise the return value is the handle of the window that owns the
+ * caret. Its caret needs to be hidden during the scroll operation and
+ * moved to new_caret_pos if move_caret is TRUE.
+ */
+static HWND fix_caret( HWND hwnd, const RECT *scroll_rect, INT dx, INT dy,
+                       UINT flags, BOOL *move_caret, POINT *new_caret_pos )
+{
+    RECT rect, mapped_caret;
+    GUITHREADINFO info;
+
+    info.cbSize = sizeof(info);
+    if (!NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info )) return 0;
+    if (!info.hwndCaret) return 0;
+
+    mapped_caret = info.rcCaret;
+    if (info.hwndCaret == hwnd)
+    {
+        /* The caret needs to be moved along with scrolling even if it's
+         * outside the visible area. Otherwise, when the caret is scrolled
+         * out from the view, the position won't get updated anymore and
+         * the caret will never scroll back again. */
+        *move_caret = TRUE;
+        new_caret_pos->x = info.rcCaret.left + dx;
+        new_caret_pos->y = info.rcCaret.top + dy;
+    }
+    else
+    {
+        *move_caret = FALSE;
+        if (!(flags & SW_SCROLLCHILDREN) || !is_child( hwnd, info.hwndCaret ))
+            return 0;
+        map_window_points( info.hwndCaret, hwnd, (POINT *)&mapped_caret, 2, get_thread_dpi() );
+    }
+
+    /* If the caret is not in the src/dest rects, all is fine done. */
+    if (!intersect_rect( &rect, scroll_rect, &mapped_caret ))
+    {
+        rect = *scroll_rect;
+        OffsetRect( &rect, dx, dy );
+        if (!intersect_rect( &rect, &rect, &mapped_caret ))
+            return 0;
+    }
+
+    /* Indicate that the caret needs to be updated during the scrolling. */
+    return info.hwndCaret;
+}
+
+/*************************************************************************
+ *           NtUserScrollWindowEx (win32u.@)
+ *
+ * Note: contrary to what the doc says, pixels that are scrolled from the
+ *      outside of clipRect to the inside are NOT painted.
+ */
+INT WINAPI NtUserScrollWindowEx( HWND hwnd, INT dx, INT dy, const RECT *rect,
+                                 const RECT *clip_rect, HRGN update_rgn,
+                                 RECT *update_rect, UINT flags )
+{
+    BOOL update = update_rect || update_rgn || flags & (SW_INVALIDATE | SW_ERASE);
+    BOOL own_rgn = TRUE, move_caret = FALSE;
+    HRGN temp_rgn, winupd_rgn = 0;
+    INT retval = NULLREGION;
+    HWND caret_hwnd = NULL;
+    POINT new_caret_pos;
+    RECT rc, cliprc;
+    int rdw_flags;
+    HDC hdc;
+
+    TRACE( "%p, %d,%d update_rgn=%p update_rect = %p %s %04x\n",
+           hwnd, dx, dy, update_rgn, update_rect, wine_dbgstr_rect(rect), flags );
+    TRACE( "clip_rect = %s\n", wine_dbgstr_rect(clip_rect) );
+    if (flags & ~(SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE))
+        FIXME( "some flags (%04x) are unhandled\n", flags );
+
+    rdw_flags = (flags & SW_ERASE) && (flags & SW_INVALIDATE) ?
+        RDW_INVALIDATE | RDW_ERASE  : RDW_INVALIDATE;
+
+    if (!is_window_drawable( hwnd, TRUE )) return ERROR;
+    hwnd = get_full_window_handle( hwnd );
+
+    get_client_rect( hwnd, &rc );
+    if (clip_rect) intersect_rect( &cliprc, &rc, clip_rect );
+    else cliprc = rc;
+
+    if (rect) intersect_rect( &rc, &rc, rect );
+    if (update_rgn) own_rgn = FALSE;
+    else if (update) update_rgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
+
+    new_caret_pos.x = new_caret_pos.y = 0;
+
+    if (!IsRectEmpty( &cliprc ) && (dx || dy))
+    {
+        DWORD style = get_window_long( hwnd, GWL_STYLE );
+        DWORD dcxflags = 0;
+
+        caret_hwnd = fix_caret( hwnd, &rc, dx, dy, flags, &move_caret, &new_caret_pos );
+        if (caret_hwnd) NtUserHideCaret( caret_hwnd );
+
+        if (!(flags & SW_NODCCACHE)) dcxflags |= DCX_CACHE;
+        if (style & WS_CLIPSIBLINGS) dcxflags |= DCX_CLIPSIBLINGS;
+        if (get_class_long( hwnd, GCL_STYLE, FALSE ) & CS_PARENTDC) dcxflags |= DCX_PARENTCLIP;
+        if (!(flags & SW_SCROLLCHILDREN) && (style & WS_CLIPCHILDREN))
+            dcxflags |= DCX_CLIPCHILDREN;
+        hdc = NtUserGetDCEx( hwnd, 0, dcxflags);
+        if (hdc)
+        {
+            NtUserScrollDC( hdc, dx, dy, &rc, &cliprc, update_rgn, update_rect );
+            NtUserReleaseDC( hwnd, hdc );
+            if (!update) NtUserRedrawWindow( hwnd, NULL, update_rgn, rdw_flags );
+        }
+
+        /* If the windows has an update region, this must be scrolled as well.
+         * Keep a copy in winupd_rgn to be added to hrngUpdate at the end. */
+        temp_rgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
+        retval = NtUserGetUpdateRgn( hwnd, temp_rgn, FALSE );
+        if (retval != NULLREGION)
+        {
+            HRGN clip_rgn = NtGdiCreateRectRgn( cliprc.left, cliprc.top,
+                                                cliprc.right, cliprc.bottom );
+            if (!own_rgn)
+            {
+                winupd_rgn = NtGdiCreateRectRgn( 0, 0, 0, 0);
+                NtGdiCombineRgn( winupd_rgn, temp_rgn, 0, RGN_COPY);
+            }
+            NtGdiOffsetRgn( temp_rgn, dx, dy );
+            NtGdiCombineRgn( temp_rgn, temp_rgn, clip_rgn, RGN_AND );
+            if (!own_rgn) NtGdiCombineRgn( winupd_rgn, winupd_rgn, temp_rgn, RGN_OR );
+            NtUserRedrawWindow( hwnd, NULL, temp_rgn, rdw_flags );
+
+           /*
+            * Catch the case where the scrolling amount exceeds the size of the
+            * original window. This generated a second update area that is the
+            * location where the original scrolled content would end up.
+            * This second region is not returned by the ScrollDC and sets
+            * ScrollWindowEx apart from just a ScrollDC.
+            *
+            * This has been verified with testing on windows.
+            */
+            if (abs( dx ) > abs( rc.right - rc.left ) || abs( dy ) > abs( rc.bottom - rc.top ))
+            {
+                NtGdiSetRectRgn( temp_rgn, rc.left + dx, rc.top + dy, rc.right+dx, rc.bottom + dy );
+                NtGdiCombineRgn( temp_rgn, temp_rgn, clip_rgn, RGN_AND );
+                NtGdiCombineRgn( update_rgn, update_rgn, temp_rgn, RGN_OR );
+
+                if (update_rect)
+                {
+                    RECT temp_rect;
+                    NtGdiGetRgnBox( temp_rgn, &temp_rect );
+                    union_rect( update_rect, update_rect, &temp_rect );
+                }
+
+                if (!own_rgn) NtGdiCombineRgn( winupd_rgn, winupd_rgn, temp_rgn, RGN_OR );
+            }
+            NtGdiDeleteObjectApp( clip_rgn );
+        }
+        NtGdiDeleteObjectApp( temp_rgn );
+    }
+    else
+    {
+        /* nothing was scrolled */
+        if (!own_rgn) NtGdiSetRectRgn( update_rgn, 0, 0, 0, 0 );
+        SetRectEmpty( update_rect );
+    }
+
+    if (flags & SW_SCROLLCHILDREN)
+    {
+        HWND *list = list_window_children( 0, hwnd, NULL, 0 );
+        if (list)
+        {
+            RECT r, dummy;
+            int i;
+
+            for (i = 0; list[i]; i++)
+            {
+                get_window_rects( list[i], COORDS_PARENT, &r, NULL, get_thread_dpi() );
+                if (!rect || intersect_rect( &dummy, &r, rect ))
+                    NtUserSetWindowPos( list[i], 0, r.left + dx, r.top  + dy, 0, 0,
+                                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
+                                        SWP_NOREDRAW | SWP_DEFERERASE );
+            }
+            free( list );
+        }
+    }
+
+    if (flags & (SW_INVALIDATE | SW_ERASE))
+        NtUserRedrawWindow( hwnd, NULL, update_rgn, rdw_flags |
+                            ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0 ) );
+
+    if (winupd_rgn)
+    {
+        NtGdiCombineRgn( update_rgn, update_rgn, winupd_rgn, RGN_OR );
+        NtGdiDeleteObjectApp( winupd_rgn );
+    }
+
+    if (move_caret) set_caret_pos( new_caret_pos.x, new_caret_pos.y );
+    if (caret_hwnd) NtUserShowCaret( caret_hwnd );
+    if (own_rgn && update_rgn) NtGdiDeleteObjectApp( update_rgn );
+
+    return retval;
+}
diff --git a/dlls/win32u/gdiobj.c b/dlls/win32u/gdiobj.c
index c29e7773e6c..222b74b45d6 100644
--- a/dlls/win32u/gdiobj.c
+++ b/dlls/win32u/gdiobj.c
@@ -1200,6 +1200,7 @@ static struct unix_funcs unix_funcs =
     NtUserRegisterHotKey,
     NtUserReleaseDC,
     NtUserScrollDC,
+    NtUserScrollWindowEx,
     NtUserSelectPalette,
     NtUserSendInput,
     NtUserSetActiveWindow,
diff --git a/dlls/win32u/win32u.spec b/dlls/win32u/win32u.spec
index 8584fc4e5c3..86d359b8386 100644
--- a/dlls/win32u/win32u.spec
+++ b/dlls/win32u/win32u.spec
@@ -1156,7 +1156,7 @@
 @ stub NtUserRestoreWindowDpiChanges
 @ stub NtUserSBGetParms
 @ stdcall NtUserScrollDC(long long long ptr ptr long ptr)
-@ stub NtUserScrollWindowEx
+@ stdcall NtUserScrollWindowEx(long long long ptr ptr long ptr long)
 @ stdcall NtUserSelectPalette(long long long)
 @ stub NtUserSendEventMessage
 @ stdcall NtUserSendInput(long ptr long)
diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h
index c5303010ef3..2394a8850a5 100644
--- a/dlls/win32u/win32u_private.h
+++ b/dlls/win32u/win32u_private.h
@@ -268,6 +268,9 @@ struct unix_funcs
     INT      (WINAPI *pNtUserReleaseDC)( HWND hwnd, HDC hdc );
     BOOL     (WINAPI *pNtUserScrollDC)( HDC hdc, INT dx, INT dy, const RECT *scroll, const RECT *clip,
                                         HRGN ret_update_rgn, RECT *update_rect );
+    INT      (WINAPI *pNtUserScrollWindowEx)( HWND hwnd, INT dx, INT dy, const RECT *rect,
+                                              const RECT *clip_rect, HRGN update_rgn,
+                                              RECT *update_rect, UINT flags );
     HPALETTE (WINAPI *pNtUserSelectPalette)( HDC hdc, HPALETTE hpal, WORD bkg );
     UINT     (WINAPI *pNtUserSendInput)( UINT count, INPUT *inputs, int size );
     HWND     (WINAPI *pNtUserSetActiveWindow)( HWND hwnd );
@@ -449,6 +452,7 @@ extern HWND is_current_process_window( HWND hwnd ) DECLSPEC_HIDDEN;
 extern HWND is_current_thread_window( HWND hwnd ) DECLSPEC_HIDDEN;
 extern BOOL is_desktop_window( HWND hwnd ) DECLSPEC_HIDDEN;
 extern BOOL is_iconic( HWND hwnd ) DECLSPEC_HIDDEN;
+extern BOOL is_window_drawable( HWND hwnd, BOOL icon ) DECLSPEC_HIDDEN;
 extern BOOL is_window_enabled( HWND hwnd ) DECLSPEC_HIDDEN;
 extern BOOL is_window_unicode( HWND hwnd ) DECLSPEC_HIDDEN;
 extern DWORD get_window_long( HWND hwnd, INT offset ) DECLSPEC_HIDDEN;
diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c
index 319f47712e6..34bf87bc9ba 100644
--- a/dlls/win32u/window.c
+++ b/dlls/win32u/window.c
@@ -762,7 +762,7 @@ static BOOL is_window_visible( HWND hwnd )
  * minimized, and it is itself not minimized unless we are
  * trying to draw its default class icon.
  */
-static BOOL is_window_drawable( HWND hwnd, BOOL icon )
+BOOL is_window_drawable( HWND hwnd, BOOL icon )
 {
     HWND *list;
     BOOL retval = TRUE;
diff --git a/dlls/win32u/wrappers.c b/dlls/win32u/wrappers.c
index b336059fe96..679cce95b9a 100644
--- a/dlls/win32u/wrappers.c
+++ b/dlls/win32u/wrappers.c
@@ -1112,6 +1112,15 @@ BOOL WINAPI NtUserScrollDC( HDC hdc, INT dx, INT dy, const RECT *scroll, const R
     return unix_funcs->pNtUserScrollDC( hdc, dx, dy, scroll, clip, ret_update_rgn, update_rect );
 }
 
+INT WINAPI NtUserScrollWindowEx( HWND hwnd, INT dx, INT dy, const RECT *rect,
+                                 const RECT *clip_rect, HRGN update_rgn,
+                                 RECT *update_rect, UINT flags )
+{
+    if (!unix_funcs) return 0;
+    return unix_funcs->pNtUserScrollWindowEx( hwnd, dx, dy, rect, clip_rect,
+                                              update_rgn, update_rect, flags );
+}
+
 HPALETTE WINAPI NtUserSelectPalette( HDC hdc, HPALETTE hpal, WORD bkg )
 {
     if (!unix_funcs) return 0;
diff --git a/include/ntuser.h b/include/ntuser.h
index e0c65a9f532..b6b69879dd7 100644
--- a/include/ntuser.h
+++ b/include/ntuser.h
@@ -227,6 +227,9 @@ struct send_message_callback_params
 #define NTUSER_OBJ_ACCEL    0x08
 #define NTUSER_OBJ_HOOK     0x0f
 
+/* NtUserScrollWindowEx flag */
+#define SW_NODCCACHE  0x8000
+
 /* NtUserInitializeClientPfnArrays parameter, not compatible with Windows */
 struct user_client_procs
 {
@@ -615,6 +618,9 @@ BOOL    WINAPI NtUserRemoveMenu( HMENU menu, UINT id, UINT flags );
 HANDLE  WINAPI NtUserRemoveProp( HWND hwnd, const WCHAR *str );
 BOOL    WINAPI NtUserScrollDC( HDC hdc, INT dx, INT dy, const RECT *scroll, const RECT *clip,
                                HRGN ret_update_rgn, RECT *update_rect );
+INT     WINAPI NtUserScrollWindowEx( HWND hwnd, INT dx, INT dy, const RECT *rect,
+                                     const RECT *clip_rect, HRGN update_rgn,
+                                     RECT *update_rect, UINT flags ) DECLSPEC_HIDDEN;
 HPALETTE WINAPI NtUserSelectPalette( HDC hdc, HPALETTE palette, WORD force_background );
 UINT     WINAPI NtUserSendInput( UINT count, INPUT *inputs, int size );
 HWND     WINAPI NtUserSetActiveWindow( HWND hwnd );
-- 
GitLab


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



More information about the wine-devel mailing list