[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