[PATCH 6/7] win32u: Move scroll tracking implementation from user32.

Jacek Caban wine at gitlab.winehq.org
Mon Jul 4 07:05:57 CDT 2022


From: Jacek Caban <jacek at codeweavers.com>

---
 dlls/user32/controls.h       |    3 -
 dlls/user32/defwnd.c         |   24 +-
 dlls/user32/edit.c           |    4 +-
 dlls/user32/listbox.c        |   14 +-
 dlls/user32/mdi.c            |    4 +-
 dlls/user32/nonclient.c      |   69 --
 dlls/user32/scroll.c         | 1304 +---------------------------------
 dlls/user32/user32.spec      |    6 +-
 dlls/user32/user_main.c      |   12 +-
 dlls/user32/win.c            |   14 -
 dlls/user32/win.h            |    1 -
 dlls/win32u/defwnd.c         |   77 +-
 dlls/win32u/gdiobj.c         |    3 +
 dlls/win32u/ntuser_private.h |    1 -
 dlls/win32u/scroll.c         | 1032 ++++++++++++++++++++++++++-
 dlls/win32u/win32u.spec      |    6 +-
 dlls/win32u/win32u_private.h |    8 +
 dlls/win32u/window.c         |   11 +-
 dlls/win32u/wrappers.c       |   18 +
 include/ntuser.h             |   36 +-
 20 files changed, 1219 insertions(+), 1428 deletions(-)

diff --git a/dlls/user32/controls.h b/dlls/user32/controls.h
index 48127ca6bb2..955c9092a54 100644
--- a/dlls/user32/controls.h
+++ b/dlls/user32/controls.h
@@ -113,13 +113,10 @@ extern HBRUSH DEFWND_ControlColor( HDC hDC, UINT ctlType ) DECLSPEC_HIDDEN;
 extern BOOL update_wallpaper( const WCHAR *wallpaper, const WCHAR *pattern ) DECLSPEC_HIDDEN;
 
 /* nonclient area */
-extern LRESULT NC_HandleNCMouseMove( HWND hwnd, WPARAM wParam, LPARAM lParam ) DECLSPEC_HIDDEN;
-extern LRESULT NC_HandleNCMouseLeave( HWND hwnd ) DECLSPEC_HIDDEN;
 extern LRESULT NC_HandleSysCommand( HWND hwnd, WPARAM wParam, LPARAM lParam ) DECLSPEC_HIDDEN;
 
 /* scrollbar */
 
-extern void SCROLL_DrawNCScrollBar( HWND hwnd, HDC hdc, BOOL draw_horizontal, BOOL draw_vertical ) DECLSPEC_HIDDEN;
 extern void SCROLL_DrawScrollBar( HWND hwnd, HDC hdc, INT nBar, enum SCROLL_HITTEST hit_test,
                                   const struct SCROLL_TRACKING_INFO *tracking_info, BOOL arrows,
                                   BOOL interior ) DECLSPEC_HIDDEN;
diff --git a/dlls/user32/defwnd.c b/dlls/user32/defwnd.c
index d29846ad623..8373aa8e797 100644
--- a/dlls/user32/defwnd.c
+++ b/dlls/user32/defwnd.c
@@ -103,20 +103,12 @@ LRESULT WINAPI DefWindowProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam
             if(cs->style & (WS_HSCROLL | WS_VSCROLL))
             {
                 SCROLLINFO si = {sizeof si, SIF_ALL, 0, 100, 0, 0, 0};
-                SetScrollInfo( hwnd, SB_HORZ, &si, FALSE );
-                SetScrollInfo( hwnd, SB_VERT, &si, FALSE );
+                NtUserSetScrollInfo( hwnd, SB_HORZ, &si, FALSE );
+                NtUserSetScrollInfo( hwnd, SB_VERT, &si, FALSE );
             }
         }
         break;
 
-    case WM_NCMOUSEMOVE:
-        result = NC_HandleNCMouseMove( hwnd, wParam, lParam );
-        break;
-
-    case WM_NCMOUSELEAVE:
-        result = NC_HandleNCMouseLeave( hwnd );
-        break;
-
     case WM_SYSCOMMAND:
         result = NC_HandleSysCommand( hwnd, wParam, lParam );
         break;
@@ -238,20 +230,12 @@ LRESULT WINAPI DefWindowProcW(
             if(cs->style & (WS_HSCROLL | WS_VSCROLL))
             {
                 SCROLLINFO si = {sizeof si, SIF_ALL, 0, 100, 0, 0, 0};
-                SetScrollInfo( hwnd, SB_HORZ, &si, FALSE );
-                SetScrollInfo( hwnd, SB_VERT, &si, FALSE );
+                NtUserSetScrollInfo( hwnd, SB_HORZ, &si, FALSE );
+                NtUserSetScrollInfo( hwnd, SB_VERT, &si, FALSE );
             }
         }
         break;
 
-    case WM_NCMOUSEMOVE:
-        result = NC_HandleNCMouseMove( hwnd, wParam, lParam );
-        break;
-
-    case WM_NCMOUSELEAVE:
-        result = NC_HandleNCMouseLeave( hwnd );
-        break;
-
     case WM_SYSCOMMAND:
         result = NC_HandleSysCommand( hwnd, wParam, lParam );
         break;
diff --git a/dlls/user32/edit.c b/dlls/user32/edit.c
index 439e33c0b14..80758dd60ef 100644
--- a/dlls/user32/edit.c
+++ b/dlls/user32/edit.c
@@ -1639,7 +1639,7 @@ static void EDIT_UpdateScrollInfo(EDITSTATE *es)
 	si.nPos		= es->y_offset;
 	TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
 		si.nMin, si.nMax, si.nPage, si.nPos);
-	SetScrollInfo(es->hwndSelf, SB_VERT, &si, TRUE);
+	NtUserSetScrollInfo(es->hwndSelf, SB_VERT, &si, TRUE);
     }
 
     if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
@@ -1653,7 +1653,7 @@ static void EDIT_UpdateScrollInfo(EDITSTATE *es)
 	si.nPos		= es->x_offset;
 	TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
 		si.nMin, si.nMax, si.nPage, si.nPos);
-	SetScrollInfo(es->hwndSelf, SB_HORZ, &si, TRUE);
+	NtUserSetScrollInfo(es->hwndSelf, SB_HORZ, &si, TRUE);
     }
 }
 
diff --git a/dlls/user32/listbox.c b/dlls/user32/listbox.c
index b8b683e6e3c..d96fc8c219d 100644
--- a/dlls/user32/listbox.c
+++ b/dlls/user32/listbox.c
@@ -329,7 +329,7 @@ static void LISTBOX_UpdateScroll( LB_DESCR *descr )
     SCROLLINFO info;
 
     /* Check the listbox scroll bar flags individually before we call
-       SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
+       NtUserSetScrollInfo otherwise when the listbox style is WS_HSCROLL and
        no WS_VSCROLL, we end up with an uninitialized, visible horizontal
        scroll bar when we do not need one.
     if (!(descr->style & WS_VSCROLL)) return;
@@ -357,11 +357,11 @@ static void LISTBOX_UpdateScroll( LB_DESCR *descr )
         if (descr->style & LBS_DISABLENOSCROLL)
             info.fMask |= SIF_DISABLENOSCROLL;
         if (descr->style & WS_HSCROLL)
-            SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
+            NtUserSetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
         info.nMax = 0;
         info.fMask = SIF_RANGE;
         if (descr->style & WS_VSCROLL)
-            SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
+            NtUserSetScrollInfo( descr->self, SB_VERT, &info, TRUE );
     }
     else
     {
@@ -373,7 +373,7 @@ static void LISTBOX_UpdateScroll( LB_DESCR *descr )
         if (descr->style & LBS_DISABLENOSCROLL)
             info.fMask |= SIF_DISABLENOSCROLL;
         if (descr->style & WS_VSCROLL)
-            SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
+            NtUserSetScrollInfo( descr->self, SB_VERT, &info, TRUE );
 
         if ((descr->style & WS_HSCROLL) && descr->horz_extent)
         {
@@ -382,7 +382,7 @@ static void LISTBOX_UpdateScroll( LB_DESCR *descr )
             info.fMask = SIF_POS | SIF_PAGE;
             if (descr->style & LBS_DISABLENOSCROLL)
                 info.fMask |= SIF_DISABLENOSCROLL;
-            SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
+            NtUserSetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
         }
         else
         {
@@ -391,7 +391,7 @@ static void LISTBOX_UpdateScroll( LB_DESCR *descr )
                 info.nMin  = 0;
                 info.nMax  = 0;
                 info.fMask = SIF_RANGE | SIF_DISABLENOSCROLL;
-                SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
+                NtUserSetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
             }
             else
             {
@@ -1366,7 +1366,7 @@ static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
         info.fMask = SIF_RANGE;
         if (descr->style & LBS_DISABLENOSCROLL)
             info.fMask |= SIF_DISABLENOSCROLL;
-        SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
+        NtUserSetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
     }
     if (descr->horz_pos > extent - descr->width)
         LISTBOX_SetHorizontalPos( descr, extent - descr->width );
diff --git a/dlls/user32/mdi.c b/dlls/user32/mdi.c
index fbac63dd881..11ba1382804 100644
--- a/dlls/user32/mdi.c
+++ b/dlls/user32/mdi.c
@@ -1730,7 +1730,7 @@ void WINAPI CalcChildScroll( HWND hwnd, INT scroll )
                             info.nMin = childRect.left;
                             info.nMax = childRect.right - clientRect.right;
                             info.nPos = clientRect.left - childRect.left;
-                            SetScrollInfo(hwnd, SB_HORZ, &info, TRUE);
+                            NtUserSetScrollInfo(hwnd, SB_HORZ, &info, TRUE);
                         }
 			if (scroll == SB_HORZ) break;
 			/* fall through */
@@ -1740,7 +1740,7 @@ void WINAPI CalcChildScroll( HWND hwnd, INT scroll )
                             info.nMin = childRect.top;
                             info.nMax = childRect.bottom - clientRect.bottom;
                             info.nPos = clientRect.top - childRect.top;
-                            SetScrollInfo(hwnd, SB_VERT, &info, TRUE);
+                            NtUserSetScrollInfo(hwnd, SB_VERT, &info, TRUE);
                         }
 			break;
     }
diff --git a/dlls/user32/nonclient.c b/dlls/user32/nonclient.c
index 3eb7ce7883b..095bcb3e24d 100644
--- a/dlls/user32/nonclient.c
+++ b/dlls/user32/nonclient.c
@@ -142,65 +142,6 @@ BOOL WINAPI DECLSPEC_HOTPATCH AdjustWindowRectExForDpi( LPRECT rect, DWORD style
 }
 
 
-LRESULT NC_HandleNCMouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-    RECT rect;
-    POINT pt;
-
-    TRACE("hwnd=%p wparam=%#Ix lparam=%#Ix\n", hwnd, wParam, lParam);
-
-    if (wParam != HTHSCROLL && wParam != HTVSCROLL)
-        return 0;
-
-    WIN_GetRectangles(hwnd, COORDS_CLIENT, &rect, NULL);
-
-    pt.x = (short)LOWORD(lParam);
-    pt.y = (short)HIWORD(lParam);
-    ScreenToClient(hwnd, &pt);
-    pt.x -= rect.left;
-    pt.y -= rect.top;
-    SCROLL_HandleScrollEvent(hwnd, wParam == HTHSCROLL ? SB_HORZ : SB_VERT, WM_NCMOUSEMOVE, pt);
-    return 0;
-}
-
-LRESULT NC_HandleNCMouseLeave(HWND hwnd)
-{
-    LONG style = GetWindowLongW(hwnd, GWL_STYLE);
-    POINT pt = {0, 0};
-
-    TRACE("hwnd=%p\n", hwnd);
-
-    if (style & WS_HSCROLL)
-        SCROLL_HandleScrollEvent(hwnd, SB_HORZ, WM_NCMOUSELEAVE, pt);
-    if (style & WS_VSCROLL)
-        SCROLL_HandleScrollEvent(hwnd, SB_VERT, WM_NCMOUSELEAVE, pt);
-
-    return 0;
-}
-
-
-/***********************************************************************
- *           NC_TrackScrollBar
- *
- * Track a mouse button press on the horizontal or vertical scroll-bar.
- */
-static void NC_TrackScrollBar( HWND hwnd, WPARAM wParam, POINT pt )
-{
-    INT scrollbar;
-
-    if ((wParam & 0xfff0) == SC_HSCROLL)
-    {
-        if ((wParam & 0x0f) != HTHSCROLL) return;
-	scrollbar = SB_HORZ;
-    }
-    else  /* SC_VSCROLL */
-    {
-        if ((wParam & 0x0f) != HTVSCROLL) return;
-	scrollbar = SB_VERT;
-    }
-    SCROLL_TrackScrollBar( hwnd, scrollbar, pt );
-}
-
 
 /***********************************************************************
  *           NC_HandleSysCommand
@@ -214,16 +155,6 @@ LRESULT NC_HandleSysCommand( HWND hwnd, WPARAM wParam, LPARAM lParam )
 
     switch (wParam & 0xfff0)
     {
-    case SC_VSCROLL:
-    case SC_HSCROLL:
-        {
-            POINT pt;
-            pt.x = (short)LOWORD(lParam);
-            pt.y = (short)HIWORD(lParam);
-            NC_TrackScrollBar( hwnd, wParam, pt );
-        }
-        break;
-
     case SC_TASKLIST:
         WinExec( "taskman.exe", SW_SHOWNORMAL );
         break;
diff --git a/dlls/user32/scroll.c b/dlls/user32/scroll.c
index 01f7f3b88ae..bf0a7d3350a 100644
--- a/dlls/user32/scroll.c
+++ b/dlls/user32/scroll.c
@@ -43,42 +43,9 @@ typedef struct
 
 #define SCROLLBAR_MAGIC 0x5c6011ba
 
-  /* Minimum size of the rectangle between the arrows */
-#define SCROLL_MIN_RECT  4
-
-  /* Minimum size of the thumb in pixels */
-#define SCROLL_MIN_THUMB 8
-
   /* Overlap between arrows and thumb */
 #define SCROLL_ARROW_THUMB_OVERLAP 0
 
-  /* Delay (in ms) before first repetition when holding the button down */
-#define SCROLL_FIRST_DELAY   200
-
-  /* Delay (in ms) between scroll repetitions */
-#define SCROLL_REPEAT_DELAY  50
-
-  /* Scroll timer id */
-#define SCROLL_TIMER   0
-
- /* What to do after SCROLL_SetScrollInfo() */
-#define SA_SSI_HIDE		0x0001
-#define SA_SSI_SHOW		0x0002
-#define SA_SSI_REFRESH		0x0004
-#define SA_SSI_REPAINT_ARROWS	0x0008
-
-/* Scroll Bar tracking information */
-static struct SCROLL_TRACKING_INFO g_tracking_info;
-
- /* Is the moving thumb being displayed? */
-static BOOL SCROLL_MovingThumb = FALSE;
-
- /* Local functions */
-static BOOL SCROLL_ShowScrollBar( HWND hwnd, INT nBar,
-				    BOOL fShowH, BOOL fShowV );
-static INT SCROLL_SetScrollInfo( HWND hwnd, INT nBar,
-                                 const SCROLLINFO *info, BOOL bRedraw );
-
 /*********************************************************************
  * scrollbar class descriptor
  */
@@ -92,20 +59,6 @@ const struct builtin_class_descr SCROLL_builtin_class =
     0                       /* brush */
 };
 
-/***********************************************************************
- *           SCROLL_ScrollInfoValid
- *
- *  Determine if the supplied SCROLLINFO struct is valid.
- *  info     [in] The SCROLLINFO struct to be tested
- */
-static inline BOOL SCROLL_ScrollInfoValid( LPCSCROLLINFO info )
-{
-    return !(info->fMask & ~(SIF_ALL | SIF_DISABLENOSCROLL)
-        || (info->cbSize != sizeof(*info)
-            && info->cbSize != sizeof(*info) - sizeof(info->nTrackPos)));
-}
-
-
 /***********************************************************************
  *           SCROLL_GetInternalInfo
 
@@ -167,270 +120,6 @@ SCROLLBAR_INFO *SCROLL_GetInternalInfo( HWND hwnd, INT nBar, BOOL alloc )
 }
 
 
-/***********************************************************************
- *           SCROLL_GetScrollBarRect
- *
- * Compute the scroll bar rectangle, in drawing coordinates (i.e. client
- * coords for SB_CTL, window coords for SB_VERT and SB_HORZ).
- * 'arrowSize' returns the width or height of an arrow (depending on
- * the orientation of the scrollbar), 'thumbSize' returns the size of
- * the thumb, and 'thumbPos' returns the position of the thumb
- * relative to the left or to the top.
- * Return TRUE if the scrollbar is vertical, FALSE if horizontal.
- */
-static BOOL SCROLL_GetScrollBarRect( HWND hwnd, INT nBar, RECT *lprect,
-                                     INT *arrowSize, INT *thumbSize,
-                                     INT *thumbPos )
-{
-    INT pixels, min_thumb_size;
-    BOOL vertical;
-    WND *wndPtr = WIN_GetPtr( hwnd );
-
-    if (!wndPtr || wndPtr == WND_OTHER_PROCESS || wndPtr == WND_DESKTOP) return FALSE;
-
-    switch(nBar)
-    {
-      case SB_HORZ:
-        WIN_GetRectangles( hwnd, COORDS_WINDOW, NULL, lprect );
-        lprect->top = lprect->bottom;
-        lprect->bottom += GetSystemMetrics(SM_CYHSCROLL);
-	if(wndPtr->dwStyle & WS_VSCROLL)
-	  lprect->right++;
-        vertical = FALSE;
-	break;
-
-      case SB_VERT:
-        WIN_GetRectangles( hwnd, COORDS_WINDOW, NULL, lprect );
-        if((wndPtr->dwExStyle & WS_EX_LEFTSCROLLBAR) != 0)
-        {
-            lprect->right = lprect->left;
-            lprect->left -= GetSystemMetrics(SM_CXVSCROLL);
-        }
-        else
-        {
-            lprect->left = lprect->right;
-            lprect->right += GetSystemMetrics(SM_CXVSCROLL);
-        }
-	if(wndPtr->dwStyle & WS_HSCROLL)
-	  lprect->bottom++;
-        vertical = TRUE;
-	break;
-
-      case SB_CTL:
-	GetClientRect( hwnd, lprect );
-        vertical = ((wndPtr->dwStyle & SBS_VERT) != 0);
-	break;
-
-    default:
-        WIN_ReleasePtr( wndPtr );
-        return FALSE;
-    }
-
-    if (vertical) pixels = lprect->bottom - lprect->top;
-    else pixels = lprect->right - lprect->left;
-
-    if (pixels <= 2*GetSystemMetrics(SM_CXVSCROLL) + SCROLL_MIN_RECT)
-    {
-        if (pixels > SCROLL_MIN_RECT)
-            *arrowSize = (pixels - SCROLL_MIN_RECT) / 2;
-        else
-            *arrowSize = 0;
-        *thumbPos = *thumbSize = 0;
-    }
-    else
-    {
-        SCROLLBAR_INFO *info = SCROLL_GetInternalInfo( hwnd, nBar, TRUE );
-        if (!info)
-        {
-            WARN("called for missing scroll bar\n");
-            WIN_ReleasePtr( wndPtr );
-            return FALSE;
-        }
-        *arrowSize = GetSystemMetrics(SM_CXVSCROLL);
-        pixels -= (2 * (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP));
-
-        if (info->page)
-        {
-	    *thumbSize = MulDiv(pixels,info->page,(info->maxVal-info->minVal+1));
-            min_thumb_size = MulDiv(SCROLL_MIN_THUMB, GetDpiForWindow(hwnd), 96);
-            if (*thumbSize < min_thumb_size) *thumbSize = min_thumb_size;
-        }
-        else *thumbSize = GetSystemMetrics(SM_CXVSCROLL);
-
-        if (((pixels -= *thumbSize ) < 0) ||
-            ((info->flags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH))
-        {
-            /* Rectangle too small or scrollbar disabled -> no thumb */
-            *thumbPos = *thumbSize = 0;
-        }
-        else
-        {
-            INT max = info->maxVal - max( info->page-1, 0 );
-            if (info->minVal >= max)
-                *thumbPos = *arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
-            else
-                *thumbPos = *arrowSize - SCROLL_ARROW_THUMB_OVERLAP
-		  + MulDiv(pixels, (info->curVal-info->minVal),(max - info->minVal));
-        }
-    }
-    WIN_ReleasePtr( wndPtr );
-    return vertical;
-}
-
-static void SCROLL_GetScrollBarDrawInfo( HWND hwnd, INT bar,
-                                         const struct SCROLL_TRACKING_INFO *tracking_info,
-                                         RECT *rect, INT *arrow_size, INT *thumb_size,
-                                         INT *thumb_pos, BOOL *vertical )
-{
-    INT pos, max_size;
-
-    if (bar == SB_CTL && GetWindowLongW( hwnd, GWL_STYLE ) & (SBS_SIZEGRIP | SBS_SIZEBOX))
-    {
-        GetClientRect( hwnd, rect );
-        *arrow_size = 0;
-        *thumb_pos = 0;
-        *thumb_size = 0;
-        *vertical = FALSE;
-        return;
-    }
-
-    *vertical = SCROLL_GetScrollBarRect( hwnd, bar, rect, arrow_size, thumb_size, thumb_pos );
-
-    if (SCROLL_MovingThumb && tracking_info->win == hwnd && tracking_info->bar == bar)
-    {
-        max_size = *vertical ? rect->bottom - rect->top : rect->right - rect->left;
-        max_size -= *arrow_size - SCROLL_ARROW_THUMB_OVERLAP + *thumb_size;
-
-        pos = tracking_info->thumb_pos;
-        if (pos < *arrow_size - SCROLL_ARROW_THUMB_OVERLAP)
-            pos = *arrow_size - SCROLL_ARROW_THUMB_OVERLAP;
-        else if (pos > max_size)
-            pos = max_size;
-
-        *thumb_pos = pos;
-    }
-}
-
-/***********************************************************************
- *           SCROLL_GetThumbVal
- *
- * Compute the current scroll position based on the thumb position in pixels
- * from the top of the scroll-bar.
- */
-static UINT SCROLL_GetThumbVal( HWND hwnd, SCROLLBAR_INFO *infoPtr, RECT *rect, BOOL vertical,
-                                INT pos )
-{
-    INT thumbSize, minThumbSize;
-    INT pixels = vertical ? rect->bottom-rect->top : rect->right-rect->left;
-    INT range;
-
-    if ((pixels -= 2*(GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP)) <= 0)
-        return infoPtr->minVal;
-
-    if (infoPtr->page)
-    {
-        thumbSize = MulDiv(pixels,infoPtr->page,(infoPtr->maxVal-infoPtr->minVal+1));
-        minThumbSize = MulDiv(SCROLL_MIN_THUMB, GetDpiForWindow(hwnd), 96);
-        if (thumbSize < minThumbSize) thumbSize = minThumbSize;
-    }
-    else thumbSize = GetSystemMetrics(SM_CXVSCROLL);
-
-    if ((pixels -= thumbSize) <= 0) return infoPtr->minVal;
-
-    pos = max( 0, pos - (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP) );
-    if (pos > pixels) pos = pixels;
-
-    if (!infoPtr->page)
-        range = infoPtr->maxVal - infoPtr->minVal;
-    else
-        range = infoPtr->maxVal - infoPtr->minVal - infoPtr->page + 1;
-
-    return infoPtr->minVal + MulDiv(pos, range, pixels);
-}
-
-/***********************************************************************
- *           SCROLL_PtInRectEx
- */
-static BOOL SCROLL_PtInRectEx( LPRECT lpRect, POINT pt, BOOL vertical )
-{
-    RECT rect = *lpRect;
-    int scrollbarWidth;
-
-    /* Pad hit rect to allow mouse to be dragged outside of scrollbar and
-     * still be considered in the scrollbar. */
-    if (vertical)
-    {
-        scrollbarWidth = lpRect->right - lpRect->left;
-        InflateRect(&rect, scrollbarWidth * 8, scrollbarWidth * 2);
-    }
-    else
-    {
-        scrollbarWidth = lpRect->bottom - lpRect->top;
-        InflateRect(&rect, scrollbarWidth * 2, scrollbarWidth * 8);
-    }
-    return PtInRect( &rect, pt );
-}
-
-/***********************************************************************
- *           SCROLL_ClipPos
- */
-static POINT SCROLL_ClipPos( LPRECT lpRect, POINT pt )
-{
-    if( pt.x < lpRect->left )
-	pt.x = lpRect->left;
-    else
-    if( pt.x > lpRect->right )
-	pt.x = lpRect->right;
-
-    if( pt.y < lpRect->top )
-	pt.y = lpRect->top;
-    else
-    if( pt.y > lpRect->bottom )
-	pt.y = lpRect->bottom;
-
-    return pt;
-}
-
-
-/***********************************************************************
- *           SCROLL_HitTest
- *
- * Scroll-bar hit testing (don't confuse this with WM_NCHITTEST!).
- */
-static enum SCROLL_HITTEST SCROLL_HitTest( HWND hwnd, INT nBar,
-                                           POINT pt, BOOL bDragging )
-{
-    INT arrowSize, thumbSize, thumbPos;
-    RECT rect;
-
-    BOOL vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
-                                           &arrowSize, &thumbSize, &thumbPos );
-
-    if ( (bDragging && !SCROLL_PtInRectEx( &rect, pt, vertical )) ||
-	 (!PtInRect( &rect, pt )) ) return SCROLL_NOWHERE;
-
-    if (vertical)
-    {
-        if (pt.y < rect.top + arrowSize) return SCROLL_TOP_ARROW;
-        if (pt.y >= rect.bottom - arrowSize) return SCROLL_BOTTOM_ARROW;
-        if (!thumbPos) return SCROLL_TOP_RECT;
-        pt.y -= rect.top;
-        if (pt.y < thumbPos) return SCROLL_TOP_RECT;
-        if (pt.y >= thumbPos + thumbSize) return SCROLL_BOTTOM_RECT;
-    }
-    else  /* horizontal */
-    {
-        if (pt.x < rect.left + arrowSize) return SCROLL_TOP_ARROW;
-        if (pt.x >= rect.right - arrowSize) return SCROLL_BOTTOM_ARROW;
-        if (!thumbPos) return SCROLL_TOP_RECT;
-        pt.x -= rect.left;
-        if (pt.x < thumbPos) return SCROLL_TOP_RECT;
-        if (pt.x >= thumbPos + thumbSize) return SCROLL_BOTTOM_RECT;
-    }
-    return SCROLL_THUMB;
-}
-
-
 /***********************************************************************
  *           SCROLL_DrawArrows
  *
@@ -630,102 +319,6 @@ void WINAPI USER_ScrollBarDraw( HWND hwnd, HDC hdc, INT nBar, enum SCROLL_HITTES
     }
 }
 
-void WINAPI SCROLL_SetStandardScrollPainted( HWND hwnd, INT bar, BOOL painted )
-{
-    LPSCROLLBAR_INFO info;
-
-    if (bar != SB_HORZ && bar != SB_VERT)
-        return;
-
-    info = SCROLL_GetInternalInfo( hwnd, bar, FALSE );
-    if (info)
-        info->painted = painted;
-}
-
-static BOOL SCROLL_IsStandardScrollPainted( HWND hwnd, INT bar )
-{
-    LPSCROLLBAR_INFO info;
-
-    if (bar != SB_HORZ && bar != SB_VERT)
-        return FALSE;
-
-    info = SCROLL_GetInternalInfo( hwnd, bar, FALSE );
-    return info ? info->painted : FALSE;
-}
-
-/***********************************************************************
- *           SCROLL_DrawScrollBar
- *
- * Redraw the whole scrollbar.
- */
-void SCROLL_DrawScrollBar( HWND hwnd, HDC hdc, INT bar, enum SCROLL_HITTEST hit_test,
-                           const struct SCROLL_TRACKING_INFO *tracking_info, BOOL draw_arrows,
-                           BOOL draw_interior )
-{
-    INT arrow_size, thumb_size, thumb_pos;
-    RECT rect, clip_box, intersect;
-    BOOL vertical;
-    DWORD style;
-
-    if (!(hwnd = WIN_GetFullHandle( hwnd )))
-        return;
-
-    style = GetWindowLongW( hwnd, GWL_STYLE );
-    if ((bar == SB_VERT && !(style & WS_VSCROLL)) || (bar == SB_HORZ && !(style & WS_HSCROLL)))
-        return;
-
-    if (!WIN_IsWindowDrawable( hwnd, FALSE ))
-        return;
-
-    SCROLL_GetScrollBarDrawInfo( hwnd, bar, tracking_info, &rect, &arrow_size, &thumb_size,
-                                 &thumb_pos, &vertical );
-    /* do not draw if the scrollbar rectangle is empty */
-    if (IsRectEmpty( &rect ))
-        return;
-
-    TRACE("hwnd %p, hdc %p, bar %d, hit_test %d, tracking_info(win %p, bar %d, thumb_pos %d, "
-          "track_pos %d, vertical %d, hit_test %d), draw_arrows %d, draw_interior %d, rect %s, "
-          "arrow_size %d, thumb_pos %d, thumb_val %d, vertical %d, captured window %p\n", hwnd, hdc,
-          bar, hit_test, tracking_info->win, tracking_info->bar, tracking_info->thumb_pos,
-          tracking_info->thumb_val, tracking_info->vertical, tracking_info->hit_test, draw_arrows,
-          draw_interior, wine_dbgstr_rect(&rect), arrow_size, thumb_pos, thumb_size, vertical,
-          GetCapture());
-    user_api->pScrollBarDraw( hwnd, hdc, bar, hit_test, tracking_info, draw_arrows, draw_interior,
-                              &rect, arrow_size, thumb_pos, thumb_size, vertical );
-
-    if (bar == SB_HORZ || bar == SB_VERT)
-    {
-        GetClipBox( hdc, &clip_box );
-        if (IntersectRect(&intersect, &rect, &clip_box))
-            SCROLL_SetStandardScrollPainted( hwnd, bar, TRUE );
-    }
-}
-
-void SCROLL_DrawNCScrollBar( HWND hwnd, HDC hdc, BOOL draw_horizontal, BOOL draw_vertical )
-{
-    if (draw_horizontal)
-        SCROLL_DrawScrollBar( hwnd, hdc, SB_HORZ, g_tracking_info.hit_test, &g_tracking_info, TRUE, TRUE );
-    if (draw_vertical)
-        SCROLL_DrawScrollBar( hwnd, hdc, SB_VERT, g_tracking_info.hit_test, &g_tracking_info, TRUE, TRUE );
-}
-
-/***********************************************************************
- *           SCROLL_RefreshScrollBar
- *
- * Repaint the scroll bar interior after a SetScrollRange() or
- * SetScrollPos() call.
- */
-static void SCROLL_RefreshScrollBar( HWND hwnd, INT nBar,
-				     BOOL arrows, BOOL interior )
-{
-    HDC hdc = NtUserGetDCEx( hwnd, 0, DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW) );
-    if (!hdc) return;
-
-    SCROLL_DrawScrollBar( hwnd, hdc, nBar, g_tracking_info.hit_test, &g_tracking_info, arrows, interior );
-    NtUserReleaseDC( hwnd, hdc );
-}
-
-
 /***********************************************************************
  *           SCROLL_HandleKbdEvent
  *
@@ -762,507 +355,6 @@ static void SCROLL_HandleKbdEvent(HWND hwnd, WPARAM wParam, LPARAM lParam)
 }
 
 
-/***********************************************************************
- *           SCROLL_HandleScrollEvent
- *
- * Handle a mouse or timer event for the scrollbar.
- * 'pt' is the location of the mouse event in client (for SB_CTL) or
- * windows coordinates.
- */
-void SCROLL_HandleScrollEvent( HWND hwnd, INT nBar, UINT msg, POINT pt )
-{
-      /* Previous mouse position for timer events */
-    static POINT prevPt;
-      /* Thumb position when tracking started. */
-    static UINT trackThumbPos;
-      /* Position in the scroll-bar of the last button-down event. */
-    static INT lastClickPos;
-      /* Position in the scroll-bar of the last mouse event. */
-    static INT lastMousePos;
-
-    enum SCROLL_HITTEST hittest;
-    HWND hwndOwner, hwndCtl;
-    TRACKMOUSEEVENT tme;
-    BOOL vertical;
-    INT arrowSize, thumbSize, thumbPos;
-    RECT rect;
-    HDC hdc;
-
-    SCROLLBAR_INFO *infoPtr = SCROLL_GetInternalInfo( hwnd, nBar, FALSE );
-    if (!infoPtr) return;
-    if ((g_tracking_info.hit_test == SCROLL_NOWHERE)
-         && (msg != WM_LBUTTONDOWN && msg != WM_MOUSEMOVE && msg != WM_MOUSELEAVE
-         && msg != WM_NCMOUSEMOVE && msg != WM_NCMOUSELEAVE))
-		  return;
-
-    if (nBar == SB_CTL && (GetWindowLongW( hwnd, GWL_STYLE ) & (SBS_SIZEGRIP | SBS_SIZEBOX)))
-    {
-        switch(msg)
-        {
-            case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
-                NtUserHideCaret( hwnd ); /* hide caret while holding down LBUTTON */
-                NtUserSetCapture( hwnd );
-                prevPt = pt;
-                g_tracking_info.hit_test = hittest = SCROLL_THUMB;
-                break;
-            case WM_MOUSEMOVE:
-                GetClientRect(GetParent(GetParent(hwnd)),&rect);
-                prevPt = pt;
-                break;
-            case WM_LBUTTONUP:
-                ReleaseCapture();
-                g_tracking_info.hit_test = hittest = SCROLL_NOWHERE;
-                if (hwnd == GetFocus()) NtUserShowCaret( hwnd );
-                break;
-            case WM_SYSTIMER:
-                pt = prevPt;
-                break;
-        }
-        return;
-    }
-
-    hdc = NtUserGetDCEx( hwnd, 0, DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW));
-    vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
-                                        &arrowSize, &thumbSize, &thumbPos );
-    hwndOwner = (nBar == SB_CTL) ? GetParent(hwnd) : hwnd;
-    hwndCtl   = (nBar == SB_CTL) ? hwnd : 0;
-
-    switch(msg)
-    {
-      case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
-          NtUserHideCaret( hwnd ); /* hide caret while holding down LBUTTON */
-          g_tracking_info.vertical = vertical;
-          g_tracking_info.hit_test = hittest = SCROLL_HitTest( hwnd, nBar, pt, FALSE );
-          lastClickPos  = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
-          lastMousePos  = lastClickPos;
-          trackThumbPos = thumbPos;
-          prevPt = pt;
-          if (nBar == SB_CTL && (GetWindowLongW(hwnd, GWL_STYLE) & WS_TABSTOP)) NtUserSetFocus( hwnd );
-          NtUserSetCapture( hwnd );
-          break;
-
-      case WM_MOUSEMOVE:
-          hittest = SCROLL_HitTest( hwnd, nBar, pt, vertical == g_tracking_info.vertical && GetCapture() == hwnd );
-          prevPt = pt;
-
-          if (nBar != SB_CTL)
-              break;
-
-          tme.cbSize = sizeof(tme);
-          tme.dwFlags = TME_QUERY;
-          NtUserTrackMouseEvent( &tme );
-          if (!(tme.dwFlags & TME_LEAVE) || tme.hwndTrack != hwnd)
-          {
-              tme.dwFlags = TME_LEAVE;
-              tme.hwndTrack = hwnd;
-              NtUserTrackMouseEvent( &tme );
-          }
-
-          break;
-
-     case WM_NCMOUSEMOVE:
-          hittest = SCROLL_HitTest( hwnd, nBar, pt, vertical == g_tracking_info.vertical && GetCapture() == hwnd );
-          prevPt = pt;
-
-          if (nBar == SB_CTL)
-              break;
-
-          tme.cbSize = sizeof(tme);
-          tme.dwFlags = TME_QUERY;
-          NtUserTrackMouseEvent( &tme );
-          if (((tme.dwFlags & (TME_NONCLIENT | TME_LEAVE)) != (TME_NONCLIENT | TME_LEAVE)) || tme.hwndTrack != hwnd)
-          {
-              tme.dwFlags = TME_NONCLIENT | TME_LEAVE;
-              tme.hwndTrack = hwnd;
-              NtUserTrackMouseEvent( &tme );
-          }
-
-          break;
-
-      case WM_NCMOUSELEAVE:
-          if (nBar == SB_CTL)
-              return;
-
-          hittest = SCROLL_NOWHERE;
-          break;
-
-      case WM_MOUSELEAVE:
-          if (nBar != SB_CTL)
-              return;
-
-          hittest = SCROLL_NOWHERE;
-          break;
-
-      case WM_LBUTTONUP:
-          hittest = SCROLL_NOWHERE;
-          ReleaseCapture();
-          /* if scrollbar has focus, show back caret */
-          if (hwnd == GetFocus()) NtUserShowCaret( hwnd );
-          break;
-
-      case WM_SYSTIMER:
-          pt = prevPt;
-          hittest = SCROLL_HitTest( hwnd, nBar, pt, FALSE );
-          break;
-
-      default:
-          return;  /* Should never happen */
-    }
-
-    TRACE("Event: hwnd=%p bar=%d msg=%s pt=%ld,%ld hit=%d\n",
-          hwnd, nBar, SPY_GetMsgName(msg,hwnd), pt.x, pt.y, hittest );
-
-    switch (g_tracking_info.hit_test)
-    {
-    case SCROLL_NOWHERE:  /* No tracking in progress */
-        /* For standard scroll bars, hovered state gets painted only when the scroll bar was
-         * previously painted by DefWinProc(). If an application handles WM_NCPAINT by itself, then
-         * the scrollbar shouldn't be repainted here to avoid overwriting the application painted
-         * content */
-        if (msg == WM_MOUSEMOVE || msg == WM_MOUSELEAVE
-            || ((msg == WM_NCMOUSEMOVE || msg == WM_NCMOUSELEAVE)
-                && SCROLL_IsStandardScrollPainted( hwnd, nBar)))
-            SCROLL_DrawScrollBar( hwnd, hdc, nBar, hittest, &g_tracking_info, TRUE, TRUE );
-        break;
-
-    case SCROLL_TOP_ARROW:
-        SCROLL_DrawScrollBar( hwnd, hdc, nBar, hittest, &g_tracking_info, TRUE, FALSE );
-        if (hittest == g_tracking_info.hit_test)
-        {
-            if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
-            {
-                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
-                                SB_LINEUP, (LPARAM)hwndCtl );
-	    }
-
-	    NtUserSetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
-                                  SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY );
-        }
-        else KillSystemTimer( hwnd, SCROLL_TIMER );
-        break;
-
-    case SCROLL_TOP_RECT:
-        SCROLL_DrawScrollBar( hwnd, hdc, nBar, hittest, &g_tracking_info, FALSE, TRUE );
-        if (hittest == g_tracking_info.hit_test)
-        {
-            if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
-            {
-                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
-                                SB_PAGEUP, (LPARAM)hwndCtl );
-            }
-            NtUserSetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
-                                  SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY );
-        }
-        else KillSystemTimer( hwnd, SCROLL_TIMER );
-        break;
-
-    case SCROLL_THUMB:
-        if (msg == WM_LBUTTONDOWN)
-        {
-            g_tracking_info.win = hwnd;
-            g_tracking_info.bar = nBar;
-            g_tracking_info.thumb_pos = trackThumbPos + lastMousePos - lastClickPos;
-            g_tracking_info.thumb_val = SCROLL_GetThumbVal( hwnd, infoPtr, &rect, vertical,
-                                                            g_tracking_info.thumb_pos );
-            if (!SCROLL_MovingThumb)
-            {
-                SCROLL_MovingThumb = TRUE;
-                SCROLL_DrawScrollBar( hwnd, hdc, nBar, hittest, &g_tracking_info, FALSE, TRUE );
-            }
-        }
-        else if (msg == WM_LBUTTONUP)
-        {
-            SCROLL_DrawScrollBar( hwnd, hdc, nBar, SCROLL_NOWHERE, &g_tracking_info, FALSE, TRUE );
-        }
-        else if (msg == WM_MOUSEMOVE)
-        {
-            INT pos;
-
-            if (!SCROLL_PtInRectEx( &rect, pt, vertical )) pos = lastClickPos;
-            else
-	    {
-		pt = SCROLL_ClipPos( &rect, pt );
-		pos = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
-	    }
-            if ( (pos != lastMousePos) || (!SCROLL_MovingThumb) )
-            {
-                lastMousePos = pos;
-                g_tracking_info.thumb_pos = trackThumbPos + pos - lastClickPos;
-                g_tracking_info.thumb_val = SCROLL_GetThumbVal( hwnd, infoPtr, &rect, vertical,
-                                                                g_tracking_info.thumb_pos );
-                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
-                              MAKEWPARAM( SB_THUMBTRACK, g_tracking_info.thumb_val ),
-                              (LPARAM)hwndCtl );
-                SCROLL_MovingThumb = TRUE;
-                SCROLL_DrawScrollBar( hwnd, hdc, nBar, hittest, &g_tracking_info, FALSE, TRUE );
-            }
-        }
-        break;
-
-    case SCROLL_BOTTOM_RECT:
-        SCROLL_DrawScrollBar( hwnd, hdc, nBar, hittest, &g_tracking_info, FALSE, TRUE );
-        if (hittest == g_tracking_info.hit_test)
-        {
-            if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
-            {
-                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
-                                SB_PAGEDOWN, (LPARAM)hwndCtl );
-            }
-            NtUserSetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
-                                  SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY );
-        }
-        else KillSystemTimer( hwnd, SCROLL_TIMER );
-        break;
-
-    case SCROLL_BOTTOM_ARROW:
-        SCROLL_DrawScrollBar( hwnd, hdc, nBar, hittest, &g_tracking_info, TRUE, FALSE );
-        if (hittest == g_tracking_info.hit_test)
-        {
-            if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
-            {
-                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
-                                SB_LINEDOWN, (LPARAM)hwndCtl );
-	    }
-
-	    NtUserSetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
-                                  SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY );
-        }
-        else KillSystemTimer( hwnd, SCROLL_TIMER );
-        break;
-    }
-
-    if (msg == WM_LBUTTONDOWN)
-    {
-
-        if (hittest == SCROLL_THUMB)
-        {
-            UINT val = SCROLL_GetThumbVal( hwnd, infoPtr, &rect, vertical,
-                                 trackThumbPos + lastMousePos - lastClickPos );
-            SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
-                            MAKEWPARAM( SB_THUMBTRACK, val ), (LPARAM)hwndCtl );
-        }
-    }
-
-    if (msg == WM_LBUTTONUP)
-    {
-        hittest = g_tracking_info.hit_test;
-        g_tracking_info.hit_test = SCROLL_NOWHERE; /* Terminate tracking */
-
-        if (hittest == SCROLL_THUMB)
-        {
-            UINT val = SCROLL_GetThumbVal( hwnd, infoPtr, &rect, vertical,
-                                 trackThumbPos + lastMousePos - lastClickPos );
-            SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
-                            MAKEWPARAM( SB_THUMBPOSITION, val ), (LPARAM)hwndCtl );
-        }
-        /* SB_ENDSCROLL doesn't report thumb position */
-        SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
-                          SB_ENDSCROLL, (LPARAM)hwndCtl );
-
-        /* Terminate tracking */
-        g_tracking_info.win = 0;
-        SCROLL_MovingThumb = FALSE;
-        hittest = SCROLL_NOWHERE;
-        SCROLL_DrawScrollBar( hwnd, hdc, nBar, hittest, &g_tracking_info, TRUE, TRUE );
-    }
-
-    NtUserReleaseDC( hwnd, hdc );
-}
-
-
-/***********************************************************************
- *           SCROLL_TrackScrollBar
- *
- * Track a mouse button press on a scroll-bar.
- * pt is in screen-coordinates for non-client scroll bars.
- */
-void SCROLL_TrackScrollBar( HWND hwnd, INT scrollbar, POINT pt )
-{
-    MSG msg;
-    RECT rect;
-
-    if (scrollbar != SB_CTL)
-    {
-        WIN_GetRectangles( hwnd, COORDS_CLIENT, &rect, NULL );
-        ScreenToClient( hwnd, &pt );
-        pt.x -= rect.left;
-        pt.y -= rect.top;
-    }
-    else
-        rect.left = rect.top = 0;
-
-    SCROLL_HandleScrollEvent( hwnd, scrollbar, WM_LBUTTONDOWN, pt );
-
-    do
-    {
-        if (!GetMessageW( &msg, 0, 0, 0 )) break;
-        if (NtUserCallMsgFilter( &msg, MSGF_SCROLLBAR )) continue;
-        if (msg.message == WM_LBUTTONUP ||
-            msg.message == WM_MOUSEMOVE ||
-            msg.message == WM_MOUSELEAVE ||
-            msg.message == WM_NCMOUSEMOVE ||
-            msg.message == WM_NCMOUSELEAVE ||
-            (msg.message == WM_SYSTIMER && msg.wParam == SCROLL_TIMER))
-        {
-            pt.x = (short)LOWORD(msg.lParam) - rect.left;
-            pt.y = (short)HIWORD(msg.lParam) - rect.top;
-            SCROLL_HandleScrollEvent( hwnd, scrollbar, msg.message, pt );
-        }
-        else
-        {
-            TranslateMessage( &msg );
-            DispatchMessageW( &msg );
-        }
-        if (!IsWindow( hwnd ))
-        {
-            ReleaseCapture();
-            break;
-        }
-    } while (msg.message != WM_LBUTTONUP && GetCapture() == hwnd);
-}
-
-
-/*************************************************************************
- *           SCROLL_GetScrollInfo
- *
- * Internal helper for the API function
- *
- * PARAMS
- *    hwnd [I]  Handle of window with scrollbar(s)
- *    nBar [I]  One of SB_HORZ, SB_VERT, or SB_CTL
- *    info [IO] fMask specifies which values to retrieve
- *
- * RETURNS
- *    FALSE if requested field not filled (f.i. scroll bar does not exist)
- */
-static BOOL SCROLL_GetScrollInfo(HWND hwnd, INT nBar, LPSCROLLINFO info)
-{
-    LPSCROLLBAR_INFO infoPtr;
-
-    /* handle invalid data structure */
-    if (!SCROLL_ScrollInfoValid(info)
-        || !(infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, FALSE)))
-            return FALSE;
-
-    /* fill in the desired scroll info structure */
-    if (info->fMask & SIF_PAGE) info->nPage = infoPtr->page;
-    if (info->fMask & SIF_POS) info->nPos = infoPtr->curVal;
-    if ((info->fMask & SIF_TRACKPOS) && (info->cbSize == sizeof(*info)))
-        info->nTrackPos = (g_tracking_info.win == WIN_GetFullHandle(hwnd)) ? g_tracking_info.thumb_val : infoPtr->curVal;
-    if (info->fMask & SIF_RANGE)
-    {
-        info->nMin = infoPtr->minVal;
-        info->nMax = infoPtr->maxVal;
-    }
-
-    TRACE("cbSize %02x fMask %04x nMin %d nMax %d nPage %u nPos %d nTrackPos %d\n",
-           info->cbSize, info->fMask, info->nMin, info->nMax, info->nPage,
-           info->nPos, info->nTrackPos);
-
-    return (info->fMask & SIF_ALL) != 0;
-}
-
-
-/*************************************************************************
- *           SCROLL_GetScrollBarInfo
- *
- * Internal helper for the API function
- *
- * PARAMS
- *    hwnd     [I]  Handle of window with scrollbar(s)
- *    idObject [I]  One of OBJID_CLIENT, OBJID_HSCROLL, or OBJID_VSCROLL
- *    info     [IO] cbSize specifies the size of the structure
- *
- * RETURNS
- *    FALSE if failed
- */
-static BOOL SCROLL_GetScrollBarInfo(HWND hwnd, LONG idObject, LPSCROLLBARINFO info)
-{
-    LPSCROLLBAR_INFO infoPtr;
-    INT nBar;
-    INT nDummy;
-    DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
-    BOOL pressed;
-    RECT rect;
-
-    switch (idObject)
-    {
-        case OBJID_CLIENT: nBar = SB_CTL; break;
-        case OBJID_HSCROLL: nBar = SB_HORZ; break;
-        case OBJID_VSCROLL: nBar = SB_VERT; break;
-        default: return FALSE;
-    }
-
-    /* handle invalid data structure */
-    if (info->cbSize != sizeof(*info))
-        return FALSE;
-
-    SCROLL_GetScrollBarRect(hwnd, nBar, &info->rcScrollBar, &nDummy,
-                            &info->dxyLineButton, &info->xyThumbTop);
-    /* rcScrollBar needs to be in screen coordinates */
-    GetWindowRect(hwnd, &rect);
-    OffsetRect(&info->rcScrollBar, rect.left, rect.top);
-
-    info->xyThumbBottom = info->xyThumbTop + info->dxyLineButton;
-
-    infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, TRUE);
-    if (!infoPtr)
-        return FALSE;
-
-    /* Scroll bar state */
-    info->rgstate[0] = 0;
-    if ((nBar == SB_HORZ && !(style & WS_HSCROLL))
-        || (nBar == SB_VERT && !(style & WS_VSCROLL)))
-        info->rgstate[0] |= STATE_SYSTEM_INVISIBLE;
-    if (infoPtr->minVal >= infoPtr->maxVal - max(infoPtr->page - 1, 0))
-    {
-        if (!(info->rgstate[0] & STATE_SYSTEM_INVISIBLE))
-            info->rgstate[0] |= STATE_SYSTEM_UNAVAILABLE;
-        else
-            info->rgstate[0] |= STATE_SYSTEM_OFFSCREEN;
-    }
-    if (nBar == SB_CTL && !IsWindowEnabled(hwnd))
-        info->rgstate[0] |= STATE_SYSTEM_UNAVAILABLE;
-
-    pressed = ((nBar == SB_VERT) == g_tracking_info.vertical && GetCapture() == hwnd);
-
-    /* Top/left arrow button state. MSDN says top/right, but I don't believe it */
-    info->rgstate[1] = 0;
-    if (pressed && g_tracking_info.hit_test == SCROLL_TOP_ARROW)
-        info->rgstate[1] |= STATE_SYSTEM_PRESSED;
-    if (infoPtr->flags & ESB_DISABLE_LTUP)
-        info->rgstate[1] |= STATE_SYSTEM_UNAVAILABLE;
-
-    /* Page up/left region state. MSDN says up/right, but I don't believe it */
-    info->rgstate[2] = 0;
-    if (infoPtr->curVal == infoPtr->minVal)
-        info->rgstate[2] |= STATE_SYSTEM_INVISIBLE;
-    if (pressed && g_tracking_info.hit_test == SCROLL_TOP_RECT)
-        info->rgstate[2] |= STATE_SYSTEM_PRESSED;
-
-    /* Thumb state */
-    info->rgstate[3] = 0;
-    if (pressed && g_tracking_info.hit_test == SCROLL_THUMB)
-        info->rgstate[3] |= STATE_SYSTEM_PRESSED;
-
-    /* Page down/right region state. MSDN says down/left, but I don't believe it */
-    info->rgstate[4] = 0;
-    if (infoPtr->curVal >= infoPtr->maxVal - 1)
-        info->rgstate[4] |= STATE_SYSTEM_INVISIBLE;
-    if (pressed && g_tracking_info.hit_test == SCROLL_BOTTOM_RECT)
-        info->rgstate[4] |= STATE_SYSTEM_PRESSED;
-    
-    /* Bottom/right arrow button state. MSDN says bottom/left, but I don't believe it */
-    info->rgstate[5] = 0;
-    if (pressed && g_tracking_info.hit_test == SCROLL_BOTTOM_ARROW)
-        info->rgstate[5] |= STATE_SYSTEM_PRESSED;
-    if (infoPtr->flags & ESB_DISABLE_RTDN)
-        info->rgstate[5] |= STATE_SYSTEM_UNAVAILABLE;
-        
-    return TRUE;
-}
-
-
 /*************************************************************************
  *           SCROLL_GetScrollPos
  *
@@ -1305,77 +397,12 @@ static BOOL SCROLL_GetScrollRange(HWND hwnd, INT nBar, LPINT lpMin, LPINT lpMax)
 }
 
 
-/*************************************************************************
- *           SCROLL_SetScrollRange
- *
- * PARAMS
- *    hwnd  [I]  Handle of window with scrollbar(s)
- *    nBar  [I]  One of SB_HORZ, SB_VERT, or SB_CTL
- *    lpMin [I]  Minimum value
- *    lpMax [I]  Maximum value
- *
- */
-static BOOL SCROLL_SetScrollRange(HWND hwnd, INT nBar, INT minVal, INT maxVal)
-{
-    LPSCROLLBAR_INFO infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, FALSE);
-
-    TRACE("hwnd=%p nBar=%d min=%d max=%d\n", hwnd, nBar, minVal, maxVal);
-
-    if (infoPtr)
-    {
-        infoPtr->minVal = minVal;
-        infoPtr->maxVal = maxVal;
-    }
-    return TRUE;
-}
-
 LRESULT WINAPI USER_ScrollBarProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
 {
     if (!IsWindow( hwnd )) return 0;
 
     switch(message)
     {
-    case WM_ENABLE:
-        {
-	    SCROLLBAR_INFO *infoPtr;
-	    if ((infoPtr = SCROLL_GetInternalInfo( hwnd, SB_CTL, FALSE )))
-	    {
-		infoPtr->flags = wParam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH;
-		SCROLL_RefreshScrollBar(hwnd, SB_CTL, TRUE, TRUE);
-	    }
-	}
-	return 0;
-
-    case WM_LBUTTONDBLCLK:
-    case WM_LBUTTONDOWN:
-        if (GetWindowLongW( hwnd, GWL_STYLE ) & SBS_SIZEGRIP)
-        {
-            SendMessageW( GetParent(hwnd), WM_SYSCOMMAND,
-                          SC_SIZE + ((GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) ?
-                                     WMSZ_BOTTOMLEFT : WMSZ_BOTTOMRIGHT), lParam );
-        }
-        else
-        {
-	    POINT pt;
-	    pt.x = (short)LOWORD(lParam);
-	    pt.y = (short)HIWORD(lParam);
-            SCROLL_TrackScrollBar( hwnd, SB_CTL, pt );
-	}
-        break;
-    case WM_LBUTTONUP:
-    case WM_NCMOUSEMOVE:
-    case WM_NCMOUSELEAVE:
-    case WM_MOUSEMOVE:
-    case WM_MOUSELEAVE:
-    case WM_SYSTIMER:
-        {
-            POINT pt;
-            pt.x = (short)LOWORD(lParam);
-            pt.y = (short)HIWORD(lParam);
-            SCROLL_HandleScrollEvent( hwnd, SB_CTL, message, pt );
-        }
-        break;
-
     case WM_KEYDOWN:
         SCROLL_HandleKbdEvent(hwnd, wParam, lParam);
         break;
@@ -1384,21 +411,19 @@ LRESULT WINAPI USER_ScrollBarProc( HWND hwnd, UINT message, WPARAM wParam, LPARA
         NtUserShowCaret( hwnd );
         break;
 
+    case WM_ENABLE:
     case WM_SETFOCUS:
     case WM_KILLFOCUS:
     case WM_CREATE:
     case WM_ERASEBKGND:
     case WM_GETDLGCODE:
-        return NtUserMessageCall( hwnd, message, wParam, lParam, 0, NtUserScrollBarWndProc, !unicode );
-
     case WM_PAINT:
-        {
-            PAINTSTRUCT ps;
-            HDC hdc = wParam ? (HDC)wParam : NtUserBeginPaint( hwnd, &ps );
-            SCROLL_DrawScrollBar( hwnd, hdc, SB_CTL, g_tracking_info.hit_test, &g_tracking_info, TRUE, TRUE );
-            if (!wParam) NtUserEndPaint( hwnd, &ps );
-        }
-        break;
+    case SBM_SETRANGEREDRAW:
+    case SBM_SETRANGE:
+    case SBM_GETSCROLLINFO:
+    case SBM_GETSCROLLBARINFO:
+    case SBM_SETSCROLLINFO:
+        return NtUserMessageCall( hwnd, message, wParam, lParam, 0, NtUserScrollBarWndProc, !unicode );
 
     case WM_SETCURSOR:
         if (GetWindowLongW( hwnd, GWL_STYLE ) & SBS_SIZEGRIP)
@@ -1414,31 +439,11 @@ LRESULT WINAPI USER_ScrollBarProc( HWND hwnd, UINT message, WPARAM wParam, LPARA
     case SBM_GETPOS:
        return SCROLL_GetScrollPos(hwnd, SB_CTL);
 
-    case SBM_SETRANGEREDRAW:
-    case SBM_SETRANGE:
-        {
-            INT oldPos = SCROLL_GetScrollPos( hwnd, SB_CTL );
-            SCROLL_SetScrollRange( hwnd, SB_CTL, wParam, lParam );
-            if (message == SBM_SETRANGEREDRAW)
-                SCROLL_RefreshScrollBar( hwnd, SB_CTL, TRUE, TRUE );
-            if (oldPos != SCROLL_GetScrollPos( hwnd, SB_CTL )) return oldPos;
-        }
-        return 0;
-
     case SBM_GETRANGE:
         return SCROLL_GetScrollRange(hwnd, SB_CTL, (LPINT)wParam, (LPINT)lParam);
 
     case SBM_ENABLE_ARROWS:
-        return EnableScrollBar( hwnd, SB_CTL, wParam );
-
-    case SBM_SETSCROLLINFO:
-        return SCROLL_SetScrollInfo( hwnd, SB_CTL, (SCROLLINFO *)lParam, wParam );
-
-    case SBM_GETSCROLLINFO:
-        return SCROLL_GetScrollInfo(hwnd, SB_CTL, (SCROLLINFO *)lParam);
-
-    case SBM_GETSCROLLBARINFO:
-        return SCROLL_GetScrollBarInfo(hwnd, OBJID_CLIENT, (SCROLLBARINFO *)lParam);
+        return NtUserEnableScrollBar( hwnd, SB_CTL, wParam );
 
     case 0x00e5:
     case 0x00e7:
@@ -1471,186 +476,6 @@ LRESULT ScrollBarWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPar
      return user_api->pScrollBarWndProc( hwnd, msg, wParam, lParam, unicode );
 }
 
-/*************************************************************************
- *           SetScrollInfo   (USER32.@)
- *
- * SetScrollInfo can be used to set the position, upper bound,
- * lower bound, and page size of a scrollbar control.
- *
- * PARAMS
- *    hwnd    [I]  Handle of window with scrollbar(s)
- *    nBar    [I]  One of SB_HORZ, SB_VERT, or SB_CTL
- *    info    [I]  Specifies what to change and new values
- *    bRedraw [I]  Should scrollbar be redrawn afterwards?
- *
- * RETURNS
- *    Scrollbar position
- *
- * NOTE
- *    For 100 lines of text to be displayed in a window of 25 lines,
- *  one would for instance use info->nMin=0, info->nMax=75
- *  (corresponding to the 76 different positions of the window on
- *  the text), and info->nPage=25.
- */
-INT WINAPI DECLSPEC_HOTPATCH SetScrollInfo(HWND hwnd, INT nBar, const SCROLLINFO *info, BOOL bRedraw)
-{
-    TRACE("hwnd=%p nBar=%d info=%p, bRedraw=%d\n", hwnd, nBar, info, bRedraw);
-
-    /* Refer SB_CTL requests to the window */
-    if (nBar == SB_CTL)
-        return SendMessageW(hwnd, SBM_SETSCROLLINFO, bRedraw, (LPARAM)info);
-    else
-        return SCROLL_SetScrollInfo( hwnd, nBar, info, bRedraw );
-}
-
-static INT SCROLL_SetScrollInfo( HWND hwnd, INT nBar, LPCSCROLLINFO info, BOOL bRedraw )
-{
-    /* Update the scrollbar state and set action flags according to
-     * what has to be done graphics wise. */
-
-    SCROLLBAR_INFO *infoPtr;
-    UINT new_flags;
-    INT action = 0;
-
-    /* handle invalid data structure */
-    if (!SCROLL_ScrollInfoValid(info)
-        || !(infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, TRUE)))
-            return 0;
-
-    if (TRACE_ON(scroll))
-    {
-        TRACE("hwnd=%p bar=%d", hwnd, nBar);
-        if (info->fMask & SIF_PAGE) TRACE( " page=%d", info->nPage );
-        if (info->fMask & SIF_POS) TRACE( " pos=%d", info->nPos );
-        if (info->fMask & SIF_RANGE) TRACE( " min=%d max=%d", info->nMin, info->nMax );
-        TRACE("\n");
-    }
-
-    /* Set the page size */
-
-    if (info->fMask & SIF_PAGE)
-    {
-	if( infoPtr->page != info->nPage )
-	{
-            infoPtr->page = info->nPage;
-            action |= SA_SSI_REFRESH;
-	}
-    }
-
-    /* Set the scroll pos */
-
-    if (info->fMask & SIF_POS)
-    {
-	if( infoPtr->curVal != info->nPos )
-	{
-	    infoPtr->curVal = info->nPos;
-            action |= SA_SSI_REFRESH;
-	}
-    }
-
-    /* Set the scroll range */
-
-    if (info->fMask & SIF_RANGE)
-    {
-        /* Invalid range -> range is set to (0,0) */
-        if ((info->nMin > info->nMax) ||
-            ((UINT)(info->nMax - info->nMin) >= 0x80000000))
-        {
-            action |= SA_SSI_REFRESH;
-            infoPtr->minVal = 0;
-            infoPtr->maxVal = 0;
-        }
-        else
-        {
-	    if( infoPtr->minVal != info->nMin ||
-		infoPtr->maxVal != info->nMax )
-	    {
-                action |= SA_SSI_REFRESH;
-                infoPtr->minVal = info->nMin;
-                infoPtr->maxVal = info->nMax;
-	    }
-        }
-    }
-
-    /* Make sure the page size is valid */
-    if (infoPtr->page < 0) infoPtr->page = 0;
-    else if (infoPtr->page > infoPtr->maxVal - infoPtr->minVal + 1 )
-        infoPtr->page = infoPtr->maxVal - infoPtr->minVal + 1;
-
-    /* Make sure the pos is inside the range */
-
-    if (infoPtr->curVal < infoPtr->minVal)
-        infoPtr->curVal = infoPtr->minVal;
-    else if (infoPtr->curVal > infoPtr->maxVal - max( infoPtr->page-1, 0 ))
-        infoPtr->curVal = infoPtr->maxVal - max( infoPtr->page-1, 0 );
-
-    TRACE("    new values: page=%d pos=%d min=%d max=%d\n",
-		 infoPtr->page, infoPtr->curVal,
-		 infoPtr->minVal, infoPtr->maxVal );
-
-    /* don't change the scrollbar state if SetScrollInfo
-     * is just called with SIF_DISABLENOSCROLL
-     */
-    if(!(info->fMask & SIF_ALL)) goto done;
-
-    /* Check if the scrollbar should be hidden or disabled */
-
-    if (info->fMask & (SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL))
-    {
-        new_flags = infoPtr->flags;
-        if (infoPtr->minVal >= infoPtr->maxVal - max( infoPtr->page-1, 0 ))
-        {
-            /* Hide or disable scroll-bar */
-            if (info->fMask & SIF_DISABLENOSCROLL)
-	    {
-                new_flags = ESB_DISABLE_BOTH;
-                action |= SA_SSI_REFRESH;
-	    }
-            else if ((nBar != SB_CTL) && (action & SA_SSI_REFRESH))
-	    {
-                action = SA_SSI_HIDE;
-            }
-        }
-        else  /* Show and enable scroll-bar only if no page only changed. */
-        if (info->fMask != SIF_PAGE)
-        {
-	    new_flags = ESB_ENABLE_BOTH;
-            if ((nBar != SB_CTL) && ( (action & SA_SSI_REFRESH) ))
-                action |= SA_SSI_SHOW;
-        }
-
-        if (nBar == SB_CTL && bRedraw && IsWindowVisible(hwnd) &&
-               (new_flags == ESB_ENABLE_BOTH || new_flags == ESB_DISABLE_BOTH))
-        {
-            EnableWindow(hwnd, new_flags == ESB_ENABLE_BOTH);
-        }
-
-        if (infoPtr->flags != new_flags) /* check arrow flags */
-        {
-            infoPtr->flags = new_flags;
-            action |= SA_SSI_REPAINT_ARROWS;
-        }
-    }
-
-done:
-    if( action & SA_SSI_HIDE )
-        SCROLL_ShowScrollBar( hwnd, nBar, FALSE, FALSE );
-    else
-    {
-        if( action & SA_SSI_SHOW )
-            if( SCROLL_ShowScrollBar( hwnd, nBar, TRUE, TRUE ) )
-                return infoPtr->curVal; /* SetWindowPos() already did the painting */
-
-        if( bRedraw )
-            SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, TRUE );
-        else if( action & SA_SSI_REPAINT_ARROWS )
-            SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, FALSE );
-    }
-
-    /* Return current position */
-    return infoPtr->curVal;
-}
-
 
 /*************************************************************************
  *           GetScrollInfo   (USER32.@)
@@ -1678,33 +503,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetScrollInfo(HWND hwnd, INT nBar, LPSCROLLINFO in
         SendMessageW(hwnd, SBM_GETSCROLLINFO, 0, (LPARAM)info);
         return TRUE;
     }
-    return SCROLL_GetScrollInfo(hwnd, nBar, info);
-}
-
-
-/*************************************************************************
- *           GetScrollBarInfo   (USER32.@)
- *
- * GetScrollBarInfo can be used to retrieve information about a scrollbar
- * control.
- *
- * PARAMS
- *  hwnd     [I]  Handle of window with scrollbar(s)
- *  idObject [I]  One of OBJID_CLIENT, OBJID_HSCROLL, or OBJID_VSCROLL
- *  info     [IO] cbSize specifies the size of SCROLLBARINFO
- *
- * RETURNS
- *  TRUE if success
- */
-BOOL WINAPI GetScrollBarInfo(HWND hwnd, LONG idObject, LPSCROLLBARINFO info)
-{
-    TRACE("hwnd=%p idObject=%ld info=%p\n", hwnd, idObject, info);
-
-    /* Refer OBJID_CLIENT requests to the window */
-    if (idObject == OBJID_CLIENT)
-        return SendMessageW(hwnd, SBM_GETSCROLLBARINFO, 0, (LPARAM)info);
-    else
-        return SCROLL_GetScrollBarInfo(hwnd, idObject, info);
+    return NtUserGetScrollInfo( hwnd, nBar, info );
 }
 
 
@@ -1737,7 +536,7 @@ INT WINAPI DECLSPEC_HOTPATCH SetScrollPos( HWND hwnd, INT nBar, INT nPos, BOOL b
     info.cbSize = sizeof(info);
     info.nPos   = nPos;
     info.fMask  = SIF_POS;
-    SetScrollInfo( hwnd, nBar, &info, bRedraw );
+    NtUserSetScrollInfo( hwnd, nBar, &info, bRedraw );
     return oldPos;
 }
 
@@ -1799,7 +598,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH SetScrollRange(HWND hwnd, INT nBar, INT minVal, IN
     info.fMask  = SIF_RANGE;
     info.nMin   = minVal;
     info.nMax   = maxVal;
-    SetScrollInfo( hwnd, nBar, &info, bRedraw );
+    NtUserSetScrollInfo( hwnd, nBar, &info, bRedraw );
     return TRUE;
 }
 
@@ -1830,84 +629,3 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetScrollRange(HWND hwnd, INT nBar, LPINT lpMin, L
 
     return TRUE;
 }
-
-
-/*************************************************************************
- *           SCROLL_ShowScrollBar()
- *
- * Back-end for ShowScrollBar(). Returns FALSE if no action was taken.
- */
-static BOOL SCROLL_ShowScrollBar( HWND hwnd, INT nBar, BOOL fShowH, BOOL fShowV )
-{
-    ULONG old_style, set_bits = 0, clear_bits = 0;
-
-    TRACE("hwnd=%p bar=%d horz=%d, vert=%d\n", hwnd, nBar, fShowH, fShowV );
-
-    switch(nBar)
-    {
-    case SB_CTL:
-        NtUserShowWindow( hwnd, fShowH ? SW_SHOW : SW_HIDE );
-        return TRUE;
-
-    case SB_BOTH:
-    case SB_HORZ:
-        if (fShowH) set_bits |= WS_HSCROLL;
-        else clear_bits |= WS_HSCROLL;
-        if( nBar == SB_HORZ ) break;
-        /* fall through */
-    case SB_VERT:
-        if (fShowV) set_bits |= WS_VSCROLL;
-        else clear_bits |= WS_VSCROLL;
-        break;
-
-    default:
-        return FALSE;  /* Nothing to do! */
-    }
-
-    old_style = WIN_SetStyle( hwnd, set_bits, clear_bits );
-    if ((old_style & clear_bits) != 0 || (old_style & set_bits) != set_bits)
-    {
-        /* frame has been changed, let the window redraw itself */
-        NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0,
-                            SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
-        return TRUE;
-    }
-    return FALSE; /* no frame changes */
-}
-
-
-/*************************************************************************
- *           EnableScrollBar   (USER32.@)
- *
- * Enables or disables the scroll bars.
- */
-BOOL WINAPI DECLSPEC_HOTPATCH EnableScrollBar( HWND hwnd, UINT nBar, UINT flags )
-{
-    BOOL bFineWithMe;
-    SCROLLBAR_INFO *infoPtr;
-
-    flags &= ESB_DISABLE_BOTH;
-
-    if (nBar == SB_BOTH)
-    {
-	if (!(infoPtr = SCROLL_GetInternalInfo( hwnd, SB_VERT, TRUE ))) return FALSE;
-	if (!(bFineWithMe = (infoPtr->flags == flags)) )
-	{
-	    infoPtr->flags = flags;
-	    SCROLL_RefreshScrollBar( hwnd, SB_VERT, TRUE, TRUE );
-	}
-	nBar = SB_HORZ;
-    }
-    else
-	bFineWithMe = nBar != SB_CTL;
-
-    if (!(infoPtr = SCROLL_GetInternalInfo( hwnd, nBar, TRUE ))) return FALSE;
-    if (bFineWithMe && infoPtr->flags == flags) return FALSE;
-    infoPtr->flags = flags;
-
-    if (nBar == SB_CTL && (flags == ESB_DISABLE_BOTH || flags == ESB_ENABLE_BOTH))
-        EnableWindow(hwnd, flags == ESB_ENABLE_BOTH);
-
-    SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, TRUE );
-    return TRUE;
-}
diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec
index ce8365b1336..800dc4a9240 100644
--- a/dlls/user32/user32.spec
+++ b/dlls/user32/user32.spec
@@ -206,7 +206,7 @@
 @ stdcall EnableMenuItem(long long long) NtUserEnableMenuItem
 @ stdcall EnableMouseInPointer(long)
 @ stdcall EnableNonClientDpiScaling(long)
-@ stdcall EnableScrollBar(long long long)
+@ stdcall -import EnableScrollBar(long long long) NtUserEnableScrollBar
 @ stdcall EnableWindow(long long)
 @ stdcall EndDeferWindowPos(long)
 @ stdcall EndDialog(long long)
@@ -373,7 +373,7 @@
 @ stdcall GetRawInputDeviceList(ptr ptr long) NtUserGetRawInputDeviceList
 # @ stub GetReasonTitleFromReasonCode
 @ stdcall GetRegisteredRawInputDevices(ptr ptr long) NtUserGetRegisteredRawInputDevices
-@ stdcall GetScrollBarInfo(long long ptr)
+@ stdcall GetScrollBarInfo(long long ptr) NtUserGetScrollBarInfo
 @ stdcall GetScrollInfo(long long ptr)
 @ stdcall GetScrollPos(long long)
 @ stdcall GetScrollRange(long long ptr ptr)
@@ -699,7 +699,7 @@
 @ stdcall SetPropW(long wstr long)
 @ stdcall SetRect(ptr long long long long)
 @ stdcall SetRectEmpty(ptr)
-@ stdcall SetScrollInfo(long long ptr long)
+@ stdcall -import SetScrollInfo(long long ptr long) NtUserSetScrollInfo
 @ stdcall SetScrollPos(long long long long)
 @ stdcall SetScrollRange(long long long long long)
 @ stdcall SetShellWindow(long)
diff --git a/dlls/user32/user_main.c b/dlls/user32/user_main.c
index 7a85a86d786..b6c8f26cec6 100644
--- a/dlls/user32/user_main.c
+++ b/dlls/user32/user_main.c
@@ -157,7 +157,6 @@ static const struct user_callbacks user_funcs =
     ImmProcessKey,
     ImmTranslateMessage,
     NtWaitForMultipleObjects,
-    SCROLL_DrawNCScrollBar,
     free_win_ptr,
     SCROLL_GetInternalInfo,
     notify_ime,
@@ -174,6 +173,16 @@ static NTSTATUS WINAPI User32CopyImage( const struct copy_image_params *params,
     return HandleToUlong( ret );
 }
 
+static NTSTATUS WINAPI User32DrawScrollBar( const struct draw_scroll_bar_params *params, ULONG size )
+{
+    RECT rect = params->rect;
+    user_api->pScrollBarDraw( params->hwnd, params->hdc, params->bar, params->hit_test,
+                              &params->tracking_info, params->arrows, params->interior,
+                              &rect, params->arrow_size, params->thumb_pos,
+                              params->thumb_size, params->vertical );
+    return 0;
+}
+
 static NTSTATUS WINAPI User32DrawText( const struct draw_text_params *params, ULONG size )
 {
     size -= FIELD_OFFSET( struct draw_text_params, str );
@@ -220,6 +229,7 @@ static const void *kernel_callback_table[NtUserCallCount] =
     User32CallWindowProc,
     User32CallWindowsHook,
     User32CopyImage,
+    User32DrawScrollBar,
     User32DrawText,
     User32FreeCachedClipboardData,
     User32LoadDriver,
diff --git a/dlls/user32/win.c b/dlls/user32/win.c
index d1b25da0e68..8b5033ad821 100644
--- a/dlls/user32/win.c
+++ b/dlls/user32/win.c
@@ -1182,20 +1182,6 @@ BOOL WINAPI IsWindowVisible( HWND hwnd )
 }
 
 
-/***********************************************************************
- *           WIN_IsWindowDrawable
- *
- * hwnd is drawable when it is visible, all parents are not
- * minimized, and it is itself not minimized unless we are
- * trying to draw its default class icon.
- */
-BOOL WIN_IsWindowDrawable( HWND hwnd, BOOL icon )
-{
-    /* FIXME: move callers to win32u */
-    return NtUserCallHwndParam( hwnd, icon, NtUserIsWindowDrawable );
-}
-
-
 /*******************************************************************
  *		GetTopWindow (USER32.@)
  */
diff --git a/dlls/user32/win.h b/dlls/user32/win.h
index 59a7e0e2e38..8ef090fe983 100644
--- a/dlls/user32/win.h
+++ b/dlls/user32/win.h
@@ -44,7 +44,6 @@ extern HWND WIN_IsCurrentThread( HWND hwnd ) DECLSPEC_HIDDEN;
 extern ULONG WIN_SetStyle( HWND hwnd, ULONG set_bits, ULONG clear_bits ) DECLSPEC_HIDDEN;
 extern BOOL WIN_GetRectangles( HWND hwnd, enum coords_relative relative, RECT *rectWindow, RECT *rectClient ) DECLSPEC_HIDDEN;
 extern HWND WIN_CreateWindowEx( CREATESTRUCTW *cs, LPCWSTR className, HINSTANCE module, BOOL unicode ) DECLSPEC_HIDDEN;
-extern BOOL WIN_IsWindowDrawable( HWND hwnd, BOOL ) DECLSPEC_HIDDEN;
 extern HWND *WIN_ListChildren( HWND hwnd ) DECLSPEC_HIDDEN;
 extern void MDI_CalcDefaultChildPos( HWND hwndClient, INT total, LPPOINT lpPos, INT delta, UINT *id ) DECLSPEC_HIDDEN;
 extern HDESK open_winstation_desktop( HWINSTA hwinsta, LPCWSTR name, DWORD flags, BOOL inherit, ACCESS_MASK access ) DECLSPEC_HIDDEN;
diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c
index af51b933d49..702673e0cdd 100644
--- a/dlls/win32u/defwnd.c
+++ b/dlls/win32u/defwnd.c
@@ -922,6 +922,28 @@ static void sys_command_size_move( HWND hwnd, WPARAM wparam )
     }
 }
 
+/***********************************************************************
+ *           track_nc_scroll_bar
+ *
+ * Track a mouse button press on the horizontal or vertical scroll-bar.
+ */
+static void track_nc_scroll_bar( HWND hwnd, WPARAM wparam, POINT pt )
+{
+    int scrollbar;
+
+    if ((wparam & 0xfff0) == SC_HSCROLL)
+    {
+        if ((wparam & 0x0f) != HTHSCROLL) return;
+        scrollbar = SB_HORZ;
+    }
+    else  /* SC_VSCROLL */
+    {
+        if ((wparam & 0x0f) != HTVSCROLL) return;
+        scrollbar = SB_VERT;
+    }
+    track_scroll_bar( hwnd, scrollbar, pt );
+}
+
 static LRESULT handle_sys_command( HWND hwnd, WPARAM wparam, LPARAM lparam )
 {
     TRACE( "hwnd %p WM_SYSCOMMAND %lx %lx\n", hwnd, wparam, lparam );
@@ -956,7 +978,13 @@ static LRESULT handle_sys_command( HWND hwnd, WPARAM wparam, LPARAM lparam )
 
     case SC_VSCROLL:
     case SC_HSCROLL:
-        return 1; /* FIXME: handle on client side */
+        {
+            POINT pt;
+            pt.x = (short)LOWORD( lparam );
+            pt.y = (short)HIWORD( lparam );
+            track_nc_scroll_bar( hwnd, wparam, pt );
+        }
+        break;
 
     case SC_MOUSEMENU:
         track_mouse_menu_bar( hwnd, wparam & 0x000F, (short)LOWORD(lparam), (short)HIWORD(lparam) );
@@ -1736,8 +1764,7 @@ static void nc_paint( HWND hwnd, HRGN clip )
         draw_rect_edge( hdc, &rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST, 1 );
 
     /* Draw the scroll-bars */
-    if (user_callbacks)
-        user_callbacks->draw_nc_scrollbar( hwnd, hdc, style & WS_HSCROLL, style & WS_VSCROLL );
+    draw_nc_scrollbar( hwnd, hdc, style & WS_HSCROLL, style & WS_VSCROLL );
 
     /* Draw the "size-box" */
     if ((style & WS_VSCROLL) && (style & WS_HSCROLL))
@@ -2295,6 +2322,42 @@ static HBRUSH handle_control_color( HDC hdc, UINT type )
     return get_sys_color_brush( COLOR_WINDOW );
 }
 
+static LRESULT handle_nc_mouse_move( HWND hwnd, WPARAM wparam, LPARAM lparam )
+{
+    RECT rect;
+    POINT pt;
+
+    TRACE( "hwnd=%p wparam=%#lx lparam=%#lx\n", hwnd, wparam, lparam );
+
+    if (wparam != HTHSCROLL && wparam != HTVSCROLL)
+        return 0;
+
+    get_window_rects( hwnd, COORDS_CLIENT, &rect, NULL, get_thread_dpi() );
+
+    pt.x = (short)LOWORD( lparam );
+    pt.y = (short)HIWORD( lparam );
+    screen_to_client( hwnd, &pt );
+    pt.x -= rect.left;
+    pt.y -= rect.top;
+    handle_scroll_event( hwnd, wparam == HTHSCROLL ? SB_HORZ : SB_VERT, WM_NCMOUSEMOVE, pt );
+    return 0;
+}
+
+static LRESULT handle_nc_mouse_leave( HWND hwnd )
+{
+    LONG style = get_window_long( hwnd, GWL_STYLE );
+    POINT pt = {0, 0};
+
+    TRACE( "hwnd=%p\n", hwnd );
+
+    if (style & WS_HSCROLL)
+        handle_scroll_event( hwnd, SB_HORZ, WM_NCMOUSELEAVE, pt );
+    if (style & WS_VSCROLL)
+        handle_scroll_event( hwnd, SB_VERT, WM_NCMOUSELEAVE, pt );
+    return 0;
+}
+
+
 LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, BOOL ansi )
 {
     LRESULT result = 0;
@@ -2352,6 +2415,14 @@ LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
     case WM_NCLBUTTONDBLCLK:
         return handle_nc_button_dbl_click( hwnd, wparam, lparam );
 
+    case WM_NCMOUSEMOVE:
+        result = handle_nc_mouse_move( hwnd, wparam, lparam );
+        break;
+
+    case WM_NCMOUSELEAVE:
+        result = handle_nc_mouse_leave( hwnd );
+        break;
+
     case WM_RBUTTONUP:
         {
             POINT pt;
diff --git a/dlls/win32u/gdiobj.c b/dlls/win32u/gdiobj.c
index d843716e4c0..9aab651e551 100644
--- a/dlls/win32u/gdiobj.c
+++ b/dlls/win32u/gdiobj.c
@@ -1164,6 +1164,7 @@ static struct unix_funcs unix_funcs =
     NtUserDrawMenuBarTemp,
     NtUserEmptyClipboard,
     NtUserEnableMenuItem,
+    NtUserEnableScrollBar,
     NtUserEndDeferWindowPosEx,
     NtUserEndPaint,
     NtUserEnumDisplayDevices,
@@ -1184,6 +1185,7 @@ static struct unix_funcs unix_funcs =
     NtUserGetMessage,
     NtUserGetPriorityClipboardFormat,
     NtUserGetQueueStatus,
+    NtUserGetScrollBarInfo,
     NtUserGetSystemMenu,
     NtUserGetUpdateRect,
     NtUserGetUpdateRgn,
@@ -1226,6 +1228,7 @@ static struct unix_funcs unix_funcs =
     NtUserSetLayeredWindowAttributes,
     NtUserSetMenu,
     NtUserSetParent,
+    NtUserSetScrollInfo,
     NtUserSetSysColors,
     NtUserSetSystemMenu,
     NtUserSetWindowLong,
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h
index 36c11a49c4e..b2765a70a3d 100644
--- a/dlls/win32u/ntuser_private.h
+++ b/dlls/win32u/ntuser_private.h
@@ -35,7 +35,6 @@ struct user_callbacks
     BOOL (WINAPI *pImmProcessKey)(HWND, HKL, UINT, LPARAM, DWORD);
     BOOL (WINAPI *pImmTranslateMessage)(HWND, UINT, WPARAM, LPARAM);
     NTSTATUS (WINAPI *pNtWaitForMultipleObjects)(ULONG,const HANDLE*,BOOLEAN,BOOLEAN,const LARGE_INTEGER*);
-    void (CDECL *draw_nc_scrollbar)( HWND hwnd, HDC hdc, BOOL draw_horizontal, BOOL draw_vertical );
     void (CDECL *free_win_ptr)( struct tagWND *win );
     struct scroll_info *(CDECL *get_scroll_info)( HWND hwnd, INT nBar, BOOL alloc );
     void (CDECL *notify_ime)( HWND hwnd, UINT param );
diff --git a/dlls/win32u/scroll.c b/dlls/win32u/scroll.c
index 14bec1e3b83..a51c7d3cf93 100644
--- a/dlls/win32u/scroll.c
+++ b/dlls/win32u/scroll.c
@@ -23,7 +23,7 @@
 #pragma makedep unix
 #endif
 
-#include "win32u_private.h"
+#include "ntgdi_private.h"
 #include "ntuser_private.h"
 #include "wine/debug.h"
 
@@ -41,6 +41,27 @@ WINE_DEFAULT_DEBUG_CHANNEL(scroll);
 /* Overlap between arrows and thumb */
 #define SCROLL_ARROW_THUMB_OVERLAP 0
 
+/* Delay (in ms) before first repetition when holding the button down */
+#define SCROLL_FIRST_DELAY   200
+
+/* Delay (in ms) between scroll repetitions */
+#define SCROLL_REPEAT_DELAY  50
+
+/* Scroll timer id */
+#define SCROLL_TIMER   0
+
+/* What to do after set_scroll_info() */
+#define SA_SSI_HIDE            0x0001
+#define SA_SSI_SHOW            0x0002
+#define SA_SSI_REFRESH         0x0004
+#define SA_SSI_REPAINT_ARROWS  0x0008
+
+/* Scroll Bar tracking information */
+static struct SCROLL_TRACKING_INFO g_tracking_info;
+
+/* Is the moving thumb being displayed? */
+static BOOL scroll_moving_thumb = FALSE;
+
 static struct scroll_info *get_scroll_info_ptr( HWND hwnd, int bar, BOOL alloc )
 {
     struct scroll_info *ret = NULL;
@@ -200,6 +221,856 @@ static BOOL get_scroll_bar_rect( HWND hwnd, int bar, RECT *rect, int *arrow_size
     return vertical;
 }
 
+/***********************************************************************
+ *           draw_scroll_bar
+ *
+ * Redraw the whole scrollbar.
+ */
+void draw_scroll_bar( HWND hwnd, HDC hdc, int bar, enum SCROLL_HITTEST hit_test,
+                      const struct SCROLL_TRACKING_INFO *tracking_info, BOOL draw_arrows,
+                      BOOL draw_interior )
+{
+    struct draw_scroll_bar_params params;
+    struct scroll_info *info;
+    RECT clip_box, intersect;
+    DWORD style;
+    void *ret_ptr;
+    ULONG ret_len;
+
+    if (!(hwnd = get_full_window_handle( hwnd )))
+        return;
+
+    style = get_window_long( hwnd, GWL_STYLE );
+    if ((bar == SB_VERT && !(style & WS_VSCROLL)) || (bar == SB_HORZ && !(style & WS_HSCROLL)))
+        return;
+
+    if (!is_window_drawable( hwnd, FALSE ))
+        return;
+
+    if (!(info = get_scroll_info_ptr( hwnd, bar, TRUE ))) return;
+    params.enable_flags = info->flags;
+    release_scroll_info_ptr( info );
+
+    if (bar == SB_CTL && get_window_long( hwnd, GWL_STYLE ) & (SBS_SIZEGRIP | SBS_SIZEBOX))
+    {
+        get_client_rect( hwnd, &params.rect );
+        params.arrow_size = 0;
+        params.thumb_pos = 0;
+        params.thumb_size = 0;
+        params.vertical = FALSE;
+    }
+    else
+    {
+        int pos, max_size;
+
+        params.vertical = get_scroll_bar_rect( hwnd, bar, &params.rect, &params.arrow_size,
+                                               &params.thumb_size, &params.thumb_pos );
+
+        if (scroll_moving_thumb && tracking_info->win == hwnd && tracking_info->bar == bar)
+        {
+            max_size = params.vertical ?
+                params.rect.bottom - params.rect.top : params.rect.right - params.rect.left;
+            max_size -= params.arrow_size - SCROLL_ARROW_THUMB_OVERLAP + params.thumb_size;
+
+            pos = tracking_info->thumb_pos;
+            if (pos < params.arrow_size - SCROLL_ARROW_THUMB_OVERLAP)
+                pos = params.arrow_size - SCROLL_ARROW_THUMB_OVERLAP;
+            else if (pos > max_size)
+                pos = max_size;
+
+            params.thumb_pos = pos;
+        }
+    }
+
+    /* do not draw if the scrollbar rectangle is empty */
+    if (IsRectEmpty( &params.rect ))
+        return;
+
+    TRACE( "hwnd %p, hdc %p, bar %d, hit_test %d, tracking_info(win %p, bar %d, thumb_pos %d, "
+           "track_pos %d, vertical %d, hit_test %d), draw_arrows %d, draw_interior %d, rect %s, "
+           "arrow_size %d, thumb_pos %d, thumb_val %d, vertical %d, captured window %p\n", hwnd, hdc,
+           bar, hit_test, tracking_info->win, tracking_info->bar, tracking_info->thumb_pos,
+           tracking_info->thumb_val, tracking_info->vertical, tracking_info->hit_test, draw_arrows,
+           draw_interior, wine_dbgstr_rect(&params.rect), params.arrow_size, params.thumb_pos,
+           params.thumb_size, params.vertical, get_capture() );
+
+    params.hwnd = hwnd;
+    params.hdc = hdc;
+    params.bar = bar;
+    params.hit_test = hit_test;
+    params.arrows = draw_arrows;
+    params.interior = draw_interior;
+    KeUserModeCallback( NtUserDrawScrollBar, &params, sizeof(params), &ret_ptr, &ret_len );
+
+    if (bar == SB_HORZ || bar == SB_VERT)
+    {
+        NtGdiGetAppClipBox( hdc, &clip_box );
+        if (intersect_rect( &intersect, &params.rect, &clip_box ))
+            set_standard_scroll_painted( hwnd, bar, TRUE );
+    }
+}
+
+void draw_nc_scrollbar( HWND hwnd, HDC hdc, BOOL draw_horizontal, BOOL draw_vertical )
+{
+    if (draw_horizontal)
+        draw_scroll_bar( hwnd, hdc, SB_HORZ, g_tracking_info.hit_test, &g_tracking_info, TRUE, TRUE );
+    if (draw_vertical)
+        draw_scroll_bar( hwnd, hdc, SB_VERT, g_tracking_info.hit_test, &g_tracking_info, TRUE, TRUE );
+}
+
+static void refresh_scroll_bar( HWND hwnd, int nBar, BOOL arrows, BOOL interior )
+{
+    HDC hdc = NtUserGetDCEx( hwnd, 0, DCX_CACHE | (nBar == SB_CTL ? 0 : DCX_WINDOW) );
+    if (!hdc) return;
+
+    draw_scroll_bar( hwnd, hdc, nBar, g_tracking_info.hit_test, &g_tracking_info, arrows, interior );
+    NtUserReleaseDC( hwnd, hdc );
+}
+
+static BOOL point_in_scroll_rect( RECT *r, POINT pt, BOOL vertical )
+{
+    RECT rect = *r;
+    int width;
+
+    /* Pad hit rect to allow mouse to be dragged outside of scrollbar and
+     * still be considered in the scrollbar. */
+    if (vertical)
+    {
+        width = r->right - r->left;
+        InflateRect( &rect, width * 8, width * 2 );
+    }
+    else
+    {
+        width = r->bottom - r->top;
+        InflateRect( &rect, width * 2, width * 8 );
+    }
+    return PtInRect( &rect, pt );
+}
+
+/***********************************************************************
+ *           scroll_hit_test
+ *
+ * Scroll-bar hit testing (don't confuse this with WM_NCHITTEST!).
+ */
+static enum SCROLL_HITTEST scroll_hit_test( HWND hwnd, int bar, POINT pt, BOOL dragging )
+{
+    int arrow_size, thumb_size, thumb_pos;
+    BOOL vertical;
+    RECT rect;
+
+    vertical = get_scroll_bar_rect( hwnd, bar, &rect, &arrow_size, &thumb_size, &thumb_pos );
+
+    if ((dragging && !point_in_scroll_rect( &rect, pt, vertical )) || !PtInRect( &rect, pt ))
+        return SCROLL_NOWHERE;
+
+    if (vertical)
+    {
+        if (pt.y < rect.top + arrow_size) return SCROLL_TOP_ARROW;
+        if (pt.y >= rect.bottom - arrow_size) return SCROLL_BOTTOM_ARROW;
+        if (!thumb_pos) return SCROLL_TOP_RECT;
+        pt.y -= rect.top;
+        if (pt.y < thumb_pos) return SCROLL_TOP_RECT;
+        if (pt.y >= thumb_pos + thumb_size) return SCROLL_BOTTOM_RECT;
+    }
+    else  /* horizontal */
+    {
+        if (pt.x < rect.left + arrow_size) return SCROLL_TOP_ARROW;
+        if (pt.x >= rect.right - arrow_size) return SCROLL_BOTTOM_ARROW;
+        if (!thumb_pos) return SCROLL_TOP_RECT;
+        pt.x -= rect.left;
+        if (pt.x < thumb_pos) return SCROLL_TOP_RECT;
+        if (pt.x >= thumb_pos + thumb_size) return SCROLL_BOTTOM_RECT;
+    }
+    return SCROLL_THUMB;
+}
+
+static BOOL is_standard_scroll_painted( HWND hwnd, int bar )
+{
+    struct scroll_info *info;
+    BOOL ret;
+
+    if (bar != SB_HORZ && bar != SB_VERT) return FALSE;
+    if (!(info = get_scroll_info_ptr( hwnd, bar, FALSE ))) return FALSE;
+    ret = info->painted;
+    release_scroll_info_ptr( info );
+    return ret;
+}
+
+/***********************************************************************
+ *           get_thumb_val
+ *
+ * Compute the current scroll position based on the thumb position in pixels
+ * from the top of the scroll-bar.
+ */
+static UINT get_thumb_val( HWND hwnd, int bar, RECT *rect, BOOL vertical, int pos )
+{
+    int thumb_size, min_thumb_size, pixels, range;
+    struct scroll_info *info;
+    UINT ret;
+
+    pixels = vertical ? rect->bottom-rect->top : rect->right-rect->left;
+    pixels -= 2 * (get_system_metrics( SM_CXVSCROLL ) - SCROLL_ARROW_THUMB_OVERLAP);
+    if (!(info = get_scroll_info_ptr( hwnd, bar, FALSE ))) return 0;
+    ret = info->minVal;
+    if (pixels > 0)
+    {
+        if (info->page)
+        {
+            thumb_size = muldiv( pixels, info->page, info->maxVal - info->minVal + 1 );
+            min_thumb_size = muldiv( SCROLL_MIN_THUMB, get_dpi_for_window( hwnd ), 96 );
+            if (thumb_size < min_thumb_size) thumb_size = min_thumb_size;
+        }
+        else thumb_size = get_system_metrics( SM_CXVSCROLL );
+
+        if ((pixels -= thumb_size) > 0)
+        {
+            pos = max( 0, pos - (get_system_metrics( SM_CXVSCROLL ) - SCROLL_ARROW_THUMB_OVERLAP ));
+            if (pos > pixels) pos = pixels;
+
+            if (!info->page)
+                range = info->maxVal - info->minVal;
+            else
+                range = info->maxVal - info->minVal - info->page + 1;
+
+            ret = info->minVal + muldiv( pos, range, pixels );
+        }
+    }
+
+    release_scroll_info_ptr( info );
+    return ret;
+}
+
+static POINT clip_scroll_pos( RECT *rect, POINT pt )
+{
+    if (pt.x < rect->left)
+        pt.x = rect->left;
+    else if (pt.x > rect->right)
+        pt.x = rect->right;
+
+    if (pt.y < rect->top)
+        pt.y = rect->top;
+    else if (pt.y > rect->bottom)
+        pt.y = rect->bottom;
+
+    return pt;
+}
+
+/***********************************************************************
+ *           handle_scroll_event
+ *
+ * Handle a mouse or timer event for the scrollbar.
+ * 'pt' is the location of the mouse event in client (for SB_CTL) or
+ * windows coordinates.
+ */
+void handle_scroll_event( HWND hwnd, int bar, UINT msg, POINT pt )
+{
+    /* Previous mouse position for timer events. */
+    static POINT prev_pt;
+    /* Thumb position when tracking started. */
+    static UINT track_thumb_pos;
+    /* Position in the scroll-bar of the last button-down event. */
+    static int last_click_pos;
+    /* Position in the scroll-bar of the last mouse event. */
+    static int last_mouse_pos;
+
+    int arrow_size, thumb_size, thumb_pos;
+    enum SCROLL_HITTEST hittest;
+    HWND owner_hwnd, ctl_hwnd;
+    struct scroll_info *info;
+    TRACKMOUSEEVENT tme;
+    BOOL vertical;
+    RECT rect;
+    HDC hdc;
+
+    if (!(info = get_scroll_info_ptr( hwnd, bar, FALSE ))) return;
+    release_scroll_info_ptr( info );
+
+    if (g_tracking_info.hit_test == SCROLL_NOWHERE &&
+        (msg != WM_LBUTTONDOWN && msg != WM_MOUSEMOVE && msg != WM_MOUSELEAVE &&
+         msg != WM_NCMOUSEMOVE && msg != WM_NCMOUSELEAVE))
+        return;
+
+    if (bar == SB_CTL && (get_window_long( hwnd, GWL_STYLE ) & (SBS_SIZEGRIP | SBS_SIZEBOX)))
+    {
+        switch (msg)
+        {
+        case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
+            NtUserHideCaret( hwnd ); /* hide caret while holding down LBUTTON */
+            NtUserSetCapture( hwnd );
+            prev_pt = pt;
+            g_tracking_info.hit_test = hittest = SCROLL_THUMB;
+            break;
+        case WM_MOUSEMOVE:
+            get_client_rect( get_parent( get_parent( hwnd )), &rect );
+            prev_pt = pt;
+            break;
+        case WM_LBUTTONUP:
+            release_capture();
+            g_tracking_info.hit_test = hittest = SCROLL_NOWHERE;
+            if (hwnd == get_focus()) NtUserShowCaret( hwnd );
+            break;
+        case WM_SYSTIMER:
+            pt = prev_pt;
+            break;
+        }
+        return;
+    }
+
+    hdc = NtUserGetDCEx( hwnd, 0, DCX_CACHE | (bar == SB_CTL ? 0 : DCX_WINDOW));
+    vertical = get_scroll_bar_rect( hwnd, bar, &rect, &arrow_size, &thumb_size, &thumb_pos );
+    owner_hwnd = bar == SB_CTL ? get_parent( hwnd ) : hwnd;
+    ctl_hwnd   = bar == SB_CTL ? hwnd : 0;
+
+    switch (msg)
+    {
+    case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
+        NtUserHideCaret( hwnd ); /* hide caret while holding down LBUTTON */
+        g_tracking_info.vertical = vertical;
+        g_tracking_info.hit_test = hittest = scroll_hit_test( hwnd, bar, pt, FALSE );
+        last_click_pos  = vertical ? pt.y - rect.top : pt.x - rect.left;
+        last_mouse_pos  = last_click_pos;
+        track_thumb_pos = thumb_pos;
+        prev_pt = pt;
+        if (bar == SB_CTL && (get_window_long( hwnd, GWL_STYLE ) & WS_TABSTOP))
+            NtUserSetFocus( hwnd );
+        NtUserSetCapture( hwnd );
+        break;
+
+    case WM_MOUSEMOVE:
+        hittest = scroll_hit_test( hwnd, bar, pt,
+                                   vertical == g_tracking_info.vertical && get_capture() == hwnd );
+        prev_pt = pt;
+
+        if (bar != SB_CTL)
+            break;
+
+        tme.cbSize = sizeof(tme);
+        tme.dwFlags = TME_QUERY;
+        NtUserTrackMouseEvent( &tme );
+        if (!(tme.dwFlags & TME_LEAVE) || tme.hwndTrack != hwnd)
+        {
+            tme.dwFlags = TME_LEAVE;
+            tme.hwndTrack = hwnd;
+            NtUserTrackMouseEvent( &tme );
+        }
+        break;
+
+    case WM_NCMOUSEMOVE:
+        hittest = scroll_hit_test( hwnd, bar, pt,
+                                   vertical == g_tracking_info.vertical && get_capture() == hwnd );
+        prev_pt = pt;
+
+        if (bar == SB_CTL)
+            break;
+
+        tme.cbSize = sizeof(tme);
+        tme.dwFlags = TME_QUERY;
+        NtUserTrackMouseEvent( &tme );
+        if (((tme.dwFlags & (TME_NONCLIENT | TME_LEAVE)) != (TME_NONCLIENT | TME_LEAVE)) ||
+            tme.hwndTrack != hwnd)
+        {
+            tme.dwFlags = TME_NONCLIENT | TME_LEAVE;
+            tme.hwndTrack = hwnd;
+            NtUserTrackMouseEvent( &tme );
+        }
+
+        break;
+
+    case WM_NCMOUSELEAVE:
+        if (bar == SB_CTL)
+            return;
+
+        hittest = SCROLL_NOWHERE;
+        break;
+
+    case WM_MOUSELEAVE:
+        if (bar != SB_CTL)
+            return;
+
+        hittest = SCROLL_NOWHERE;
+        break;
+
+    case WM_LBUTTONUP:
+        hittest = SCROLL_NOWHERE;
+        release_capture();
+        /* if scrollbar has focus, show back caret */
+        if (hwnd == get_focus()) NtUserShowCaret( hwnd );
+        break;
+
+    case WM_SYSTIMER:
+        pt = prev_pt;
+        hittest = scroll_hit_test( hwnd, bar, pt, FALSE );
+        break;
+
+    default:
+        return;  /* Should never happen */
+    }
+
+    TRACE( "Event: hwnd=%p bar=%d msg=%s pt=%d,%d hit=%d\n",
+           hwnd, bar, debugstr_msg_name( msg, hwnd ), pt.x, pt.y, hittest );
+
+    switch (g_tracking_info.hit_test)
+    {
+    case SCROLL_NOWHERE:  /* No tracking in progress */
+        /* For standard scroll bars, hovered state gets painted only when the scroll bar was
+         * previously painted by DefWinProc(). If an application handles WM_NCPAINT by itself, then
+         * the scrollbar shouldn't be repainted here to avoid overwriting the application painted
+         * content */
+        if (msg == WM_MOUSEMOVE || msg == WM_MOUSELEAVE ||
+            ((msg == WM_NCMOUSEMOVE || msg == WM_NCMOUSELEAVE) &&
+             is_standard_scroll_painted( hwnd, bar )))
+            draw_scroll_bar( hwnd, hdc, bar, hittest, &g_tracking_info, TRUE, TRUE );
+        break;
+
+    case SCROLL_TOP_ARROW:
+        draw_scroll_bar( hwnd, hdc, bar, hittest, &g_tracking_info, TRUE, FALSE );
+        if (hittest == g_tracking_info.hit_test)
+        {
+            if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
+            {
+                send_message( owner_hwnd, vertical ? WM_VSCROLL : WM_HSCROLL,
+                              SB_LINEUP, (LPARAM)ctl_hwnd );
+            }
+
+            NtUserSetSystemTimer( hwnd, SCROLL_TIMER,
+                                  msg == WM_LBUTTONDOWN ? SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY );
+        }
+        else NtUserKillSystemTimer( hwnd, SCROLL_TIMER );
+        break;
+
+    case SCROLL_TOP_RECT:
+        draw_scroll_bar( hwnd, hdc, bar, hittest, &g_tracking_info, FALSE, TRUE );
+        if (hittest == g_tracking_info.hit_test)
+        {
+            if (msg == WM_LBUTTONDOWN || msg == WM_SYSTIMER)
+            {
+                send_message( owner_hwnd, vertical ? WM_VSCROLL : WM_HSCROLL,
+                              SB_PAGEUP, (LPARAM)ctl_hwnd );
+            }
+            NtUserSetSystemTimer( hwnd, SCROLL_TIMER,
+                                  msg == WM_LBUTTONDOWN ? SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY );
+        }
+        else NtUserKillSystemTimer( hwnd, SCROLL_TIMER );
+        break;
+
+    case SCROLL_THUMB:
+        if (msg == WM_LBUTTONDOWN)
+        {
+            g_tracking_info.win = hwnd;
+            g_tracking_info.bar = bar;
+            g_tracking_info.thumb_pos = track_thumb_pos + last_mouse_pos - last_click_pos;
+            g_tracking_info.thumb_val = get_thumb_val( hwnd, bar, &rect, vertical,
+                                                       g_tracking_info.thumb_pos );
+            if (!scroll_moving_thumb)
+            {
+                scroll_moving_thumb = TRUE;
+                draw_scroll_bar( hwnd, hdc, bar, hittest, &g_tracking_info, FALSE, TRUE );
+            }
+        }
+        else if (msg == WM_LBUTTONUP)
+        {
+            draw_scroll_bar( hwnd, hdc, bar, SCROLL_NOWHERE, &g_tracking_info, FALSE, TRUE );
+        }
+        else if (msg == WM_MOUSEMOVE)
+        {
+            int pos;
+
+            if (!point_in_scroll_rect( &rect, pt, vertical )) pos = last_click_pos;
+            else
+            {
+                pt = clip_scroll_pos( &rect, pt );
+                pos = vertical ? pt.y - rect.top : pt.x - rect.left;
+            }
+            if (pos != last_mouse_pos || !scroll_moving_thumb)
+            {
+                last_mouse_pos = pos;
+                g_tracking_info.thumb_pos = track_thumb_pos + pos - last_click_pos;
+                g_tracking_info.thumb_val = get_thumb_val( hwnd, bar, &rect, vertical,
+                                                           g_tracking_info.thumb_pos );
+                send_message( owner_hwnd, vertical ? WM_VSCROLL : WM_HSCROLL,
+                              MAKEWPARAM( SB_THUMBTRACK, g_tracking_info.thumb_val ),
+                              (LPARAM)ctl_hwnd );
+                scroll_moving_thumb = TRUE;
+                draw_scroll_bar( hwnd, hdc, bar, hittest, &g_tracking_info, FALSE, TRUE );
+            }
+        }
+        break;
+
+    case SCROLL_BOTTOM_RECT:
+        draw_scroll_bar( hwnd, hdc, bar, hittest, &g_tracking_info, FALSE, TRUE );
+        if (hittest == g_tracking_info.hit_test)
+        {
+            if (msg == WM_LBUTTONDOWN || msg == WM_SYSTIMER)
+            {
+                send_message( owner_hwnd, vertical ? WM_VSCROLL : WM_HSCROLL,
+                              SB_PAGEDOWN, (LPARAM)ctl_hwnd );
+            }
+            NtUserSetSystemTimer( hwnd, SCROLL_TIMER,
+                                  msg == WM_LBUTTONDOWN ? SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY );
+        }
+        else NtUserKillSystemTimer( hwnd, SCROLL_TIMER );
+        break;
+
+    case SCROLL_BOTTOM_ARROW:
+        draw_scroll_bar( hwnd, hdc, bar, hittest, &g_tracking_info, TRUE, FALSE );
+        if (hittest == g_tracking_info.hit_test)
+        {
+            if (msg == WM_LBUTTONDOWN || msg == WM_SYSTIMER)
+            {
+                send_message( owner_hwnd, vertical ? WM_VSCROLL : WM_HSCROLL,
+                              SB_LINEDOWN, (LPARAM)ctl_hwnd );
+            }
+
+            NtUserSetSystemTimer( hwnd, SCROLL_TIMER,
+                                  msg == WM_LBUTTONDOWN ? SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY );
+        }
+        else NtUserKillSystemTimer( hwnd, SCROLL_TIMER );
+        break;
+    }
+
+    if (msg == WM_LBUTTONDOWN)
+    {
+
+        if (hittest == SCROLL_THUMB)
+        {
+            UINT val = get_thumb_val( hwnd, bar, &rect, vertical,
+                                      track_thumb_pos + last_mouse_pos - last_click_pos );
+            send_message( owner_hwnd, vertical ? WM_VSCROLL : WM_HSCROLL,
+                          MAKEWPARAM( SB_THUMBTRACK, val ), (LPARAM)ctl_hwnd );
+        }
+    }
+
+    if (msg == WM_LBUTTONUP)
+    {
+        hittest = g_tracking_info.hit_test;
+        g_tracking_info.hit_test = SCROLL_NOWHERE; /* Terminate tracking */
+
+        if (hittest == SCROLL_THUMB)
+        {
+            UINT val = get_thumb_val( hwnd, bar, &rect, vertical,
+                                      track_thumb_pos + last_mouse_pos - last_click_pos );
+            send_message( owner_hwnd, vertical ? WM_VSCROLL : WM_HSCROLL,
+                          MAKEWPARAM( SB_THUMBPOSITION, val ), (LPARAM)ctl_hwnd );
+        }
+        /* SB_ENDSCROLL doesn't report thumb position */
+        send_message( owner_hwnd, vertical ? WM_VSCROLL : WM_HSCROLL,
+                      SB_ENDSCROLL, (LPARAM)ctl_hwnd );
+
+        /* Terminate tracking */
+        g_tracking_info.win = 0;
+        scroll_moving_thumb = FALSE;
+        hittest = SCROLL_NOWHERE;
+        draw_scroll_bar( hwnd, hdc, bar, hittest, &g_tracking_info, TRUE, TRUE );
+    }
+
+    NtUserReleaseDC( hwnd, hdc );
+}
+
+/***********************************************************************
+ *           track_scroll_bar
+ *
+ * Track a mouse button press on a scroll-bar.
+ * pt is in screen-coordinates for non-client scroll bars.
+ */
+void track_scroll_bar( HWND hwnd, int scrollbar, POINT pt )
+{
+    MSG msg;
+    RECT rect;
+
+    if (scrollbar != SB_CTL)
+    {
+        get_window_rects( hwnd, COORDS_CLIENT, &rect, NULL, get_thread_dpi() );
+        screen_to_client( hwnd, &pt );
+        pt.x -= rect.left;
+        pt.y -= rect.top;
+    }
+    else
+        rect.left = rect.top = 0;
+
+    handle_scroll_event( hwnd, scrollbar, WM_LBUTTONDOWN, pt );
+
+    do
+    {
+        if (!NtUserGetMessage( &msg, 0, 0, 0 )) break;
+        if (NtUserCallMsgFilter( &msg, MSGF_SCROLLBAR )) continue;
+        if (msg.message == WM_LBUTTONUP ||
+            msg.message == WM_MOUSEMOVE ||
+            msg.message == WM_MOUSELEAVE ||
+            msg.message == WM_NCMOUSEMOVE ||
+            msg.message == WM_NCMOUSELEAVE ||
+            (msg.message == WM_SYSTIMER && msg.wParam == SCROLL_TIMER))
+        {
+            pt.x = (short)LOWORD( msg.lParam ) - rect.left;
+            pt.y = (short)HIWORD( msg.lParam ) - rect.top;
+            handle_scroll_event( hwnd, scrollbar, msg.message, pt );
+        }
+        else
+        {
+            NtUserTranslateMessage( &msg, 0 );
+            NtUserDispatchMessage( &msg );
+        }
+        if (!is_window( hwnd ))
+        {
+            release_capture();
+            break;
+        }
+    } while (msg.message != WM_LBUTTONUP && get_capture() == hwnd);
+}
+
+/***********************************************************************
+ *           validate_scroll_info
+ *
+ * Determine if the supplied SCROLLINFO struct is valid.
+ */
+static inline BOOL validate_scroll_info( const SCROLLINFO *info )
+{
+    return !(info->fMask & ~(SIF_ALL | SIF_DISABLENOSCROLL) ||
+             (info->cbSize != sizeof(*info) &&
+              info->cbSize != sizeof(*info) - sizeof(info->nTrackPos)));
+}
+
+
+BOOL get_scroll_info( HWND hwnd, int bar, SCROLLINFO *info )
+{
+    struct scroll_info *scroll;
+
+    /* handle invalid data structure */
+    if (!validate_scroll_info( info ) || !(scroll = get_scroll_info_ptr( hwnd, bar, FALSE )))
+        return FALSE;
+
+    /* fill in the desired scroll info structure */
+    if (info->fMask & SIF_PAGE) info->nPage = scroll->page;
+    if (info->fMask & SIF_POS) info->nPos = scroll->curVal;
+    if ((info->fMask & SIF_TRACKPOS) && (info->cbSize == sizeof(*info)))
+        info->nTrackPos = g_tracking_info.win == get_full_window_handle( hwnd ) ?
+            g_tracking_info.thumb_val : scroll->curVal;
+    if (info->fMask & SIF_RANGE)
+    {
+        info->nMin = scroll->minVal;
+        info->nMax = scroll->maxVal;
+    }
+    release_scroll_info_ptr( scroll );
+
+    TRACE( "cbSize %02x fMask %04x nMin %d nMax %d nPage %u nPos %d nTrackPos %d\n",
+           info->cbSize, info->fMask, info->nMin, info->nMax, info->nPage,
+           info->nPos, info->nTrackPos );
+
+    return (info->fMask & SIF_ALL) != 0;
+}
+
+static int set_scroll_info( HWND hwnd, int bar, const SCROLLINFO *info, BOOL redraw )
+{
+    struct scroll_info *scroll;
+    UINT new_flags;
+    int action = 0, ret;
+
+    /* handle invalid data structure */
+    if (!validate_scroll_info( info ) ||
+        !(scroll = get_scroll_info_ptr( hwnd, bar, TRUE )))
+        return 0;
+
+    if (TRACE_ON(scroll))
+    {
+        TRACE( "hwnd=%p bar=%d", hwnd, bar );
+        if (info->fMask & SIF_PAGE) TRACE( " page=%d", info->nPage );
+        if (info->fMask & SIF_POS) TRACE( " pos=%d", info->nPos );
+        if (info->fMask & SIF_RANGE) TRACE( " min=%d max=%d", info->nMin, info->nMax );
+        TRACE( "\n" );
+    }
+
+    /* Set the page size */
+    if ((info->fMask & SIF_PAGE) && scroll->page != info->nPage)
+    {
+        scroll->page = info->nPage;
+        action |= SA_SSI_REFRESH;
+    }
+
+    /* Set the scroll pos */
+    if ((info->fMask & SIF_POS) && scroll->curVal != info->nPos)
+    {
+        scroll->curVal = info->nPos;
+        action |= SA_SSI_REFRESH;
+    }
+
+    /* Set the scroll range */
+    if (info->fMask & SIF_RANGE)
+    {
+        /* Invalid range -> range is set to (0,0) */
+        if (info->nMin > info->nMax || (UINT)(info->nMax - info->nMin) >= 0x80000000)
+        {
+            action |= SA_SSI_REFRESH;
+            scroll->minVal = 0;
+            scroll->maxVal = 0;
+        }
+        else
+        {
+            if (scroll->minVal != info->nMin || scroll->maxVal != info->nMax)
+            {
+                action |= SA_SSI_REFRESH;
+                scroll->minVal = info->nMin;
+                scroll->maxVal = info->nMax;
+            }
+        }
+    }
+
+    /* Make sure the page size is valid */
+    if (scroll->page < 0) scroll->page = 0;
+    else if (scroll->page > scroll->maxVal - scroll->minVal + 1)
+        scroll->page = scroll->maxVal - scroll->minVal + 1;
+
+    /* Make sure the pos is inside the range */
+    if (scroll->curVal < scroll->minVal)
+        scroll->curVal = scroll->minVal;
+    else if (scroll->curVal > scroll->maxVal - max( scroll->page - 1, 0 ))
+        scroll->curVal = scroll->maxVal - max( scroll->page - 1, 0 );
+
+    TRACE( "    new values: page=%d pos=%d min=%d max=%d\n", scroll->page, scroll->curVal,
+           scroll->minVal, scroll->maxVal );
+
+    /* don't change the scrollbar state if SetScrollInfo is just called with SIF_DISABLENOSCROLL */
+    if(!(info->fMask & SIF_ALL)) goto done;
+
+    /* Check if the scrollbar should be hidden or disabled */
+    if (info->fMask & (SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL))
+    {
+        new_flags = scroll->flags;
+        if (scroll->minVal >= scroll->maxVal - max( scroll->page - 1, 0 ))
+        {
+            /* Hide or disable scroll-bar */
+            if (info->fMask & SIF_DISABLENOSCROLL)
+            {
+                new_flags = ESB_DISABLE_BOTH;
+                action |= SA_SSI_REFRESH;
+            }
+            else if (bar != SB_CTL && (action & SA_SSI_REFRESH))
+            {
+                action = SA_SSI_HIDE;
+            }
+        }
+        else if (info->fMask != SIF_PAGE)
+        {
+            /* Show and enable scroll-bar only if no page only changed. */
+            new_flags = ESB_ENABLE_BOTH;
+            if (bar != SB_CTL && (action & SA_SSI_REFRESH)) action |= SA_SSI_SHOW;
+        }
+
+        if (bar == SB_CTL && redraw && is_window_visible( hwnd ) &&
+            (new_flags == ESB_ENABLE_BOTH || new_flags == ESB_DISABLE_BOTH))
+        {
+            release_scroll_info_ptr( scroll );
+            enable_window( hwnd, new_flags == ESB_ENABLE_BOTH );
+            if (!(scroll = get_scroll_info_ptr( hwnd, bar, FALSE ))) return 0;
+        }
+
+        if (scroll->flags != new_flags) /* check arrow flags */
+        {
+            scroll->flags = new_flags;
+            action |= SA_SSI_REPAINT_ARROWS;
+        }
+    }
+
+done:
+    ret = scroll->curVal;
+    release_scroll_info_ptr( scroll );
+    if (action & SA_SSI_HIDE)
+        show_scroll_bar( hwnd, bar, FALSE, FALSE );
+    else
+    {
+        if (action & SA_SSI_SHOW && show_scroll_bar( hwnd, bar, TRUE, TRUE ))
+            return ret; /* NtUserSetWindowPos() already did the painting */
+
+        if (redraw)
+            refresh_scroll_bar( hwnd, bar, TRUE, TRUE );
+        else if (action & SA_SSI_REPAINT_ARROWS)
+            refresh_scroll_bar( hwnd, bar, TRUE, FALSE );
+    }
+
+    return ret; /* Return current position */
+}
+
+static BOOL get_scroll_bar_info( HWND hwnd, LONG id, SCROLLBARINFO *info )
+{
+    struct scroll_info *scroll;
+    int bar, dummy;
+    DWORD style = get_window_long( hwnd, GWL_STYLE );
+    BOOL pressed;
+    RECT rect;
+
+    switch (id)
+    {
+        case OBJID_CLIENT:  bar = SB_CTL; break;
+        case OBJID_HSCROLL: bar = SB_HORZ; break;
+        case OBJID_VSCROLL: bar = SB_VERT; break;
+        default: return FALSE;
+    }
+
+    /* handle invalid data structure */
+    if (info->cbSize != sizeof(*info)) return FALSE;
+
+    get_scroll_bar_rect( hwnd, bar, &info->rcScrollBar, &dummy,
+                         &info->dxyLineButton, &info->xyThumbTop );
+    /* rcScrollBar needs to be in screen coordinates */
+    get_window_rect( hwnd, &rect, get_thread_dpi() );
+    OffsetRect( &info->rcScrollBar, rect.left, rect.top );
+
+    info->xyThumbBottom = info->xyThumbTop + info->dxyLineButton;
+
+    if (!(scroll = get_scroll_info_ptr( hwnd, bar, TRUE ))) return FALSE;
+
+    /* Scroll bar state */
+    info->rgstate[0] = 0;
+    if ((bar == SB_HORZ && !(style & WS_HSCROLL)) ||
+        (bar == SB_VERT && !(style & WS_VSCROLL)))
+        info->rgstate[0] |= STATE_SYSTEM_INVISIBLE;
+    if (scroll->minVal >= scroll->maxVal - max( scroll->page - 1, 0 ))
+    {
+        if (!(info->rgstate[0] & STATE_SYSTEM_INVISIBLE))
+            info->rgstate[0] |= STATE_SYSTEM_UNAVAILABLE;
+        else
+            info->rgstate[0] |= STATE_SYSTEM_OFFSCREEN;
+    }
+    if (bar == SB_CTL && !is_window_enabled( hwnd ))
+        info->rgstate[0] |= STATE_SYSTEM_UNAVAILABLE;
+
+    pressed = (bar == SB_VERT) == g_tracking_info.vertical && get_capture() == hwnd;
+
+    /* Top/left arrow button state. MSDN says top/right, but I don't believe it */
+    info->rgstate[1] = 0;
+    if (pressed && g_tracking_info.hit_test == SCROLL_TOP_ARROW)
+        info->rgstate[1] |= STATE_SYSTEM_PRESSED;
+    if (scroll->flags & ESB_DISABLE_LTUP)
+        info->rgstate[1] |= STATE_SYSTEM_UNAVAILABLE;
+
+    /* Page up/left region state. MSDN says up/right, but I don't believe it */
+    info->rgstate[2] = 0;
+    if (scroll->curVal == scroll->minVal)
+        info->rgstate[2] |= STATE_SYSTEM_INVISIBLE;
+    if (pressed && g_tracking_info.hit_test == SCROLL_TOP_RECT)
+        info->rgstate[2] |= STATE_SYSTEM_PRESSED;
+
+    /* Thumb state */
+    info->rgstate[3] = 0;
+    if (pressed && g_tracking_info.hit_test == SCROLL_THUMB)
+        info->rgstate[3] |= STATE_SYSTEM_PRESSED;
+
+    /* Page down/right region state. MSDN says down/left, but I don't believe it */
+    info->rgstate[4] = 0;
+    if (scroll->curVal >= scroll->maxVal - 1)
+        info->rgstate[4] |= STATE_SYSTEM_INVISIBLE;
+    if (pressed && g_tracking_info.hit_test == SCROLL_BOTTOM_RECT)
+        info->rgstate[4] |= STATE_SYSTEM_PRESSED;
+
+    /* Bottom/right arrow button state. MSDN says bottom/left, but I don't believe it */
+    info->rgstate[5] = 0;
+    if (pressed && g_tracking_info.hit_test == SCROLL_BOTTOM_ARROW)
+        info->rgstate[5] |= STATE_SYSTEM_PRESSED;
+    if (scroll->flags & ESB_DISABLE_RTDN)
+        info->rgstate[5] |= STATE_SYSTEM_UNAVAILABLE;
+
+    release_scroll_info_ptr( scroll );
+    return TRUE;
+}
+
 static void create_scroll_bar( HWND hwnd, CREATESTRUCTW *create )
 {
     struct scroll_info *info = NULL;
@@ -256,6 +1127,31 @@ static void create_scroll_bar( HWND hwnd, CREATESTRUCTW *create )
     }
 }
 
+static int get_scroll_pos(HWND hwnd, int bar)
+{
+    struct scroll_info *scroll = get_scroll_info_ptr( hwnd, bar, FALSE );
+    int ret;
+    if (!scroll) return 0;
+    ret = scroll->curVal;
+    release_scroll_info_ptr( scroll );
+    return ret;
+}
+
+static BOOL set_scroll_range( HWND hwnd, int bar, int min_val, int max_val )
+{
+    struct scroll_info *scroll = get_scroll_info_ptr( hwnd, bar, FALSE );
+
+    TRACE( "hwnd=%p bar=%d min=%d max=%d\n", hwnd, bar, min_val, max_val );
+
+    if (scroll)
+    {
+        scroll->minVal = min_val;
+        scroll->maxVal = max_val;
+        release_scroll_info_ptr( scroll );
+    }
+    return TRUE;
+}
+
 LRESULT scroll_bar_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, BOOL ansi )
 {
     switch (msg)
@@ -264,6 +1160,49 @@ LRESULT scroll_bar_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara
         create_scroll_bar( hwnd, (CREATESTRUCTW *)lparam );
         return 0;
 
+    case WM_LBUTTONDBLCLK:
+    case WM_LBUTTONDOWN:
+        if (get_window_long( hwnd, GWL_STYLE ) & SBS_SIZEGRIP)
+        {
+            send_message( get_parent(hwnd), WM_SYSCOMMAND,
+                          SC_SIZE + ((get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) ?
+                                     WMSZ_BOTTOMLEFT : WMSZ_BOTTOMRIGHT), lparam );
+        }
+        else
+        {
+            POINT pt;
+            pt.x = (short)LOWORD( lparam );
+            pt.y = (short)HIWORD( lparam );
+            track_scroll_bar( hwnd, SB_CTL, pt );
+        }
+        return 0;
+
+    case WM_LBUTTONUP:
+    case WM_NCMOUSEMOVE:
+    case WM_NCMOUSELEAVE:
+    case WM_MOUSEMOVE:
+    case WM_MOUSELEAVE:
+    case WM_SYSTIMER:
+        {
+            POINT pt;
+            pt.x = (short)LOWORD( lparam );
+            pt.y = (short)HIWORD( lparam );
+            handle_scroll_event( hwnd, SB_CTL, msg, pt );
+        }
+        return 0;
+
+    case WM_ENABLE:
+        {
+            struct scroll_info *scroll;
+            if ((scroll = get_scroll_info_ptr( hwnd, SB_CTL, FALSE )))
+            {
+                scroll->flags = wparam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH;
+                release_scroll_info_ptr( scroll );
+                refresh_scroll_bar( hwnd, SB_CTL, TRUE, TRUE );
+            }
+        }
+        return 0;
+
     case WM_SETFOCUS:
         {
             /* Create a caret when a ScrollBar get focus */
@@ -312,6 +1251,34 @@ LRESULT scroll_bar_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara
     case WM_GETDLGCODE:
         return DLGC_WANTARROWS; /* Windows returns this value */
 
+    case WM_PAINT:
+        {
+            PAINTSTRUCT ps;
+            HDC hdc = wparam ? (HDC)wparam : NtUserBeginPaint( hwnd, &ps );
+            draw_scroll_bar( hwnd, hdc, SB_CTL, g_tracking_info.hit_test, &g_tracking_info, TRUE, TRUE );
+            if (!wparam) NtUserEndPaint( hwnd, &ps );
+        }
+        return 0;
+
+    case SBM_SETRANGEREDRAW:
+    case SBM_SETRANGE:
+        {
+            int old_pos = get_scroll_pos( hwnd, SB_CTL );
+            set_scroll_range( hwnd, SB_CTL, wparam, lparam );
+            if (msg == SBM_SETRANGEREDRAW) refresh_scroll_bar( hwnd, SB_CTL, TRUE, TRUE );
+            if (old_pos != get_scroll_pos( hwnd, SB_CTL )) return old_pos;
+        }
+        return 0;
+
+    case SBM_GETSCROLLINFO:
+        return get_scroll_info( hwnd, SB_CTL, (SCROLLINFO *)lparam );
+
+    case SBM_GETSCROLLBARINFO:
+        return get_scroll_bar_info( hwnd, OBJID_CLIENT, (SCROLLBARINFO *)lparam );
+
+    case SBM_SETSCROLLINFO:
+        return set_scroll_info( hwnd, SB_CTL, (SCROLLINFO *)lparam, wparam );
+
     default:
         return default_window_proc( hwnd, msg, wparam, lparam, ansi );
     }
@@ -341,3 +1308,66 @@ BOOL WINAPI NtUserShowScrollBar( HWND hwnd, INT bar, BOOL show )
     show_scroll_bar( hwnd, bar, bar == SB_VERT ? 0 : show, bar == SB_HORZ ? 0 : show );
     return TRUE;
 }
+
+/*************************************************************************
+ *           NtUserGetScrollBarInfo   (win32u.@)
+ */
+BOOL WINAPI NtUserGetScrollBarInfo( HWND hwnd, LONG id, SCROLLBARINFO *info )
+{
+    TRACE( "hwnd=%p id=%d info=%p\n", hwnd, id, info );
+
+    /* Refer OBJID_CLIENT requests to the window */
+    if (id == OBJID_CLIENT)
+        return send_message( hwnd, SBM_GETSCROLLBARINFO, 0, (LPARAM)info );
+    return get_scroll_bar_info( hwnd, id, info );
+}
+
+/*************************************************************************
+ *           NtUserEnableScrollBar   (win32u.@)
+ */
+BOOL WINAPI NtUserEnableScrollBar( HWND hwnd, UINT bar, UINT flags )
+{
+    struct scroll_info *scroll;
+    BOOL check_flags;
+
+    flags &= ESB_DISABLE_BOTH;
+
+    if (bar == SB_BOTH)
+    {
+        if (!(scroll = get_scroll_info_ptr( hwnd, SB_VERT, TRUE ))) return FALSE;
+        check_flags = scroll->flags == flags;
+        scroll->flags = flags;
+        release_scroll_info_ptr( scroll );
+        if (!check_flags) refresh_scroll_bar( hwnd, SB_VERT, TRUE, TRUE );
+        bar = SB_HORZ;
+    }
+    else
+        check_flags = bar != SB_CTL;
+
+    if (!(scroll = get_scroll_info_ptr( hwnd, bar, TRUE ))) return FALSE;
+    if (check_flags) check_flags = scroll->flags == flags;
+    scroll->flags = flags;
+    release_scroll_info_ptr( scroll );
+    if (check_flags) return FALSE;
+
+    if (bar == SB_CTL && (flags == ESB_DISABLE_BOTH || flags == ESB_ENABLE_BOTH))
+        NtUserEnableWindow( hwnd, flags == ESB_ENABLE_BOTH );
+
+    refresh_scroll_bar( hwnd, bar, TRUE, TRUE );
+    return TRUE;
+}
+
+
+/*************************************************************************
+ *           NtUserSetScrollInfo   (win32u.@)
+ */
+INT WINAPI NtUserSetScrollInfo( HWND hwnd, int bar, const SCROLLINFO *info, BOOL redraw )
+{
+    TRACE( "hwnd=%p bar=%d info=%p, redraw=%d\n", hwnd, bar, info, redraw );
+
+    /* Refer SB_CTL requests to the window */
+    if (bar == SB_CTL)
+        return send_message( hwnd, SBM_SETSCROLLINFO, redraw, (LPARAM)info );
+
+    return set_scroll_info( hwnd, bar, info, redraw );
+}
diff --git a/dlls/win32u/win32u.spec b/dlls/win32u/win32u.spec
index 428e8aadf1c..a2369364877 100644
--- a/dlls/win32u/win32u.spec
+++ b/dlls/win32u/win32u.spec
@@ -866,7 +866,7 @@
 @ stub NtUserEnableMouseInputForCursorSuppression
 @ stub NtUserEnableNonClientDpiScaling
 @ stub NtUserEnableResizeLayoutSynchronization
-@ stub NtUserEnableScrollBar
+@ stdcall NtUserEnableScrollBar(long long long)
 @ stub NtUserEnableSoftwareCursorForScreenCapture
 @ stub NtUserEnableTouchPad
 @ stub NtUserEnableWindowGDIScaledDpiMessage
@@ -991,7 +991,7 @@
 @ stdcall -syscall NtUserGetRegisteredRawInputDevices(ptr ptr long)
 @ stub NtUserGetRequiredCursorSizes
 @ stub NtUserGetResizeDCompositionSynchronizationObject
-@ stub NtUserGetScrollBarInfo
+@ stdcall NtUserGetScrollBarInfo(long long ptr)
 @ stub NtUserGetSharedWindowData
 @ stdcall -syscall NtUserGetSystemDpiForProcess(long)
 @ stdcall NtUserGetSystemMenu(long long)
@@ -1223,7 +1223,7 @@
 @ stub NtUserSetProcessUIAccessZorder
 @ stdcall -syscall NtUserSetProcessWindowStation(long)
 @ stdcall -syscall NtUserSetProp(long wstr ptr)
-@ stub NtUserSetScrollInfo
+@ stdcall NtUserSetScrollInfo(long long ptr long)
 @ stub NtUserSetSensorPresence
 @ stub NtUserSetSharedWindowData
 @ stub NtUserSetShellWindowEx
diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h
index 49c81ea3bef..a690acadd8b 100644
--- a/dlls/win32u/win32u_private.h
+++ b/dlls/win32u/win32u_private.h
@@ -223,6 +223,7 @@ struct unix_funcs
     DWORD    (WINAPI *pNtUserDrawMenuBarTemp)( HWND hwnd, HDC hdc, RECT *rect, HMENU handle, HFONT font );
     BOOL     (WINAPI *pNtUserEmptyClipboard)(void);
     BOOL     (WINAPI *pNtUserEnableMenuItem)( HMENU handle, UINT id, UINT flags );
+    BOOL     (WINAPI *pNtUserEnableScrollBar)( HWND hwnd, UINT bar, UINT flags );
     BOOL     (WINAPI *pNtUserEndDeferWindowPosEx)( HDWP hdwp, BOOL async );
     BOOL     (WINAPI *pNtUserEndPaint)( HWND hwnd, const PAINTSTRUCT *ps );
     NTSTATUS (WINAPI *pNtUserEnumDisplayDevices)( UNICODE_STRING *device, DWORD index,
@@ -248,6 +249,7 @@ struct unix_funcs
     BOOL     (WINAPI *pNtUserGetMessage)( MSG *msg, HWND hwnd, UINT first, UINT last );
     INT      (WINAPI *pNtUserGetPriorityClipboardFormat)( UINT *list, INT count );
     DWORD    (WINAPI *pNtUserGetQueueStatus)( UINT flags );
+    BOOL     (WINAPI *pNtUserGetScrollBarInfo)( HWND hwnd, LONG id, SCROLLBARINFO *info );
     HMENU    (WINAPI *pNtUserGetSystemMenu)( HWND hwnd, BOOL revert );
     BOOL     (WINAPI *pNtUserGetUpdateRect)( HWND hwnd, RECT *rect, BOOL erase );
     INT      (WINAPI *pNtUserGetUpdateRgn)( HWND hwnd, HRGN hrgn, BOOL erase );
@@ -300,6 +302,7 @@ struct unix_funcs
     BOOL     (WINAPI *pNtUserSetLayeredWindowAttributes)( HWND hwnd, COLORREF key, BYTE alpha, DWORD flags );
     BOOL     (WINAPI *pNtUserSetMenu)( HWND hwnd, HMENU menu );
     HWND     (WINAPI *pNtUserSetParent)( HWND hwnd, HWND parent );
+    INT      (WINAPI *pNtUserSetScrollInfo)( HWND hwnd, INT bar, const SCROLLINFO *info, BOOL redraw );
     BOOL     (WINAPI *pNtUserSetSysColors)( INT count, const INT *colors, const COLORREF *values );
     BOOL     (WINAPI *pNtUserSetSystemMenu)( HWND hwnd, HMENU menu );
     LONG     (WINAPI *pNtUserSetWindowLong)( HWND hwnd, INT offset, LONG newval, BOOL ansi );
@@ -441,9 +444,13 @@ extern BOOL process_rawinput_message( MSG *msg, UINT hw_id, const struct hardwar
 extern BOOL rawinput_device_get_usages( HANDLE handle, USHORT *usage_page, USHORT *usage ) DECLSPEC_HIDDEN;
 
 /* scroll.c */
+extern void draw_nc_scrollbar( HWND hwnd, HDC hdc, BOOL draw_horizontal, BOOL draw_vertical ) DECLSPEC_HIDDEN;
+extern BOOL get_scroll_info( HWND hwnd, INT bar, SCROLLINFO *info ) DECLSPEC_HIDDEN;
+extern void handle_scroll_event( HWND hwnd, INT bar, UINT msg, POINT pt ) DECLSPEC_HIDDEN;
 extern LRESULT scroll_bar_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
                                        BOOL ansi ) DECLSPEC_HIDDEN;
 extern void set_standard_scroll_painted( HWND hwnd, int bar, BOOL painted ) DECLSPEC_HIDDEN;
+extern void track_scroll_bar( HWND hwnd, int scrollbar, POINT pt ) DECLSPEC_HIDDEN;
 
 /* sysparams.c */
 extern BOOL enable_thunk_lock DECLSPEC_HIDDEN;
@@ -484,6 +491,7 @@ extern HDWP begin_defer_window_pos( INT count ) DECLSPEC_HIDDEN;
 extern BOOL client_to_screen( HWND hwnd, POINT *pt ) DECLSPEC_HIDDEN;
 extern void destroy_thread_windows(void) DECLSPEC_HIDDEN;
 extern LRESULT destroy_window( HWND hwnd ) DECLSPEC_HIDDEN;
+extern BOOL enable_window( HWND hwnd, BOOL enable ) DECLSPEC_HIDDEN;
 extern BOOL get_client_rect( HWND hwnd, RECT *rect ) DECLSPEC_HIDDEN;
 extern HWND get_desktop_window(void) DECLSPEC_HIDDEN;
 extern UINT get_dpi_for_window( HWND hwnd ) DECLSPEC_HIDDEN;
diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c
index 4a6b321d391..de4779aee59 100644
--- a/dlls/win32u/window.c
+++ b/dlls/win32u/window.c
@@ -812,7 +812,7 @@ BOOL is_window_unicode( HWND hwnd )
 }
 
 /* see EnableWindow */
-static BOOL enable_window( HWND hwnd, BOOL enable )
+BOOL enable_window( HWND hwnd, BOOL enable )
 {
     BOOL ret;
 
@@ -5442,6 +5442,12 @@ ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code )
     case NtUserCallHwndParam_GetClientRect:
         return get_client_rect( hwnd, (RECT *)param );
 
+    case NtUserCallHwndParam_GetScrollInfo:
+        {
+            struct get_scroll_info_params *params = (void *)param;
+            return get_scroll_info( hwnd, params->bar, params->info );
+        }
+
     case NtUserCallHwndParam_GetWindowInfo:
         return get_window_info( hwnd, (WINDOWINFO *)param );
 
@@ -5501,9 +5507,6 @@ ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code )
         return show_owned_popups( hwnd, param );
 
     /* temporary exports */
-    case NtUserIsWindowDrawable:
-        return is_window_drawable( hwnd, param );
-
     case NtUserSetWindowStyle:
         {
             STYLESTRUCT *style = (void *)param;
diff --git a/dlls/win32u/wrappers.c b/dlls/win32u/wrappers.c
index e7ab66cadcb..ba6fa9ea0a0 100644
--- a/dlls/win32u/wrappers.c
+++ b/dlls/win32u/wrappers.c
@@ -887,6 +887,12 @@ BOOL WINAPI NtUserEnableMenuItem( HMENU handle, UINT id, UINT flags )
     return unix_funcs->pNtUserEnableMenuItem( handle, id, flags );
 }
 
+BOOL WINAPI NtUserEnableScrollBar( HWND hwnd, UINT bar, UINT flags )
+{
+    if (!unix_funcs) return FALSE;
+    return unix_funcs->pNtUserEnableScrollBar( hwnd, bar, flags );
+}
+
 BOOL WINAPI NtUserEndDeferWindowPosEx( HDWP hdwp, BOOL async )
 {
     if (!unix_funcs) return FALSE;
@@ -1048,6 +1054,12 @@ DWORD WINAPI NtUserGetQueueStatus( UINT flags )
     return unix_funcs->pNtUserGetQueueStatus( flags );
 }
 
+BOOL WINAPI NtUserGetScrollBarInfo( HWND hwnd, LONG id, SCROLLBARINFO *info )
+{
+    if (!unix_funcs) return FALSE;
+    return unix_funcs->pNtUserGetScrollBarInfo( hwnd, id, info );
+}
+
 BOOL WINAPI NtUserGetUpdatedClipboardFormats( UINT *formats, UINT size, UINT *out_size )
 {
     if (!unix_funcs) return FALSE;
@@ -1274,6 +1286,12 @@ HWND WINAPI NtUserSetParent( HWND hwnd, HWND parent )
     return unix_funcs->pNtUserSetParent( hwnd, parent );
 }
 
+INT WINAPI NtUserSetScrollInfo( HWND hwnd, INT bar, const SCROLLINFO *info, BOOL redraw )
+{
+    if (!unix_funcs) return 0;
+    return unix_funcs->pNtUserSetScrollInfo( hwnd, bar, info, redraw );
+}
+
 BOOL WINAPI NtUserSetSysColors( INT count, const INT *colors, const COLORREF *values )
 {
     if (!unix_funcs) return FALSE;
diff --git a/include/ntuser.h b/include/ntuser.h
index 08a49cbd123..fecdbbbe253 100644
--- a/include/ntuser.h
+++ b/include/ntuser.h
@@ -33,6 +33,7 @@ enum
     NtUserCallWinProc,
     NtUserCallWindowsHook,
     NtUserCopyImage,
+    NtUserDrawScrollBar,
     NtUserDrawText,
     NtUserFreeCachedClipboardData,
     NtUserLoadDriver,
@@ -336,6 +337,24 @@ struct set_clipboard_params
     UINT   seqno;
 };
 
+/* NtUserDrawScrollBar params */
+struct draw_scroll_bar_params
+{
+    HWND hwnd;
+    HDC hdc;
+    INT bar;
+    UINT hit_test;
+    struct SCROLL_TRACKING_INFO tracking_info;
+    BOOL arrows;
+    BOOL interior;
+    RECT rect;
+    UINT enable_flags;
+    INT arrow_size;
+    INT thumb_pos;
+    INT thumb_size;
+    BOOL vertical;
+};
+
 /* internal messages codes */
 enum wine_internal_message
 {
@@ -559,6 +578,7 @@ BOOL    WINAPI NtUserDrawIconEx( HDC hdc, INT x0, INT y0, HICON icon, INT width,
 DWORD   WINAPI NtUserDrawMenuBarTemp( HWND hwnd, HDC hdc, RECT *rect, HMENU handle, HFONT font );
 BOOL    WINAPI NtUserEmptyClipboard(void);
 BOOL    WINAPI NtUserEnableMenuItem( HMENU handle, UINT id, UINT flags );
+BOOL    WINAPI NtUserEnableScrollBar( HWND hwnd, UINT bar, UINT flags );
 BOOL    WINAPI NtUserEndDeferWindowPosEx( HDWP hdwp, BOOL async );
 BOOL    WINAPI NtUserEndMenu(void);
 BOOL    WINAPI NtUserEndPaint( HWND hwnd, const PAINTSTRUCT *ps );
@@ -625,6 +645,7 @@ UINT    WINAPI NtUserGetRawInputData( HRAWINPUT rawinput, UINT command, void *da
 UINT    WINAPI NtUserGetRawInputDeviceInfo( HANDLE handle, UINT command, void *data, UINT *data_size );
 UINT    WINAPI NtUserGetRawInputDeviceList( RAWINPUTDEVICELIST *devices, UINT *device_count, UINT size );
 UINT    WINAPI NtUserGetRegisteredRawInputDevices( RAWINPUTDEVICE *devices, UINT *device_count, UINT size );
+BOOL    WINAPI NtUserGetScrollBarInfo( HWND hwnd, LONG id, SCROLLBARINFO *info );
 ULONG   WINAPI NtUserGetSystemDpiForProcess( HANDLE process );
 HMENU   WINAPI NtUserGetSystemMenu( HWND hwnd, BOOL revert );
 HDESK   WINAPI NtUserGetThreadDesktop( DWORD thread );
@@ -701,6 +722,7 @@ HWND    WINAPI NtUserSetParent( HWND hwnd, HWND parent );
 BOOL    WINAPI NtUserSetProcessDpiAwarenessContext( ULONG awareness, ULONG unknown );
 BOOL    WINAPI NtUserSetProcessWindowStation( HWINSTA handle );
 BOOL    WINAPI NtUserSetProp( HWND hwnd, const WCHAR *str, HANDLE handle );
+INT     WINAPI NtUserSetScrollInfo( HWND hwnd, INT bar, const SCROLLINFO *info, BOOL redraw );
 BOOL    WINAPI NtUserSetSysColors( INT count, const INT *colors, const COLORREF *values );
 BOOL    WINAPI NtUserSetSystemMenu( HWND hwnd, HMENU menu );
 UINT_PTR WINAPI NtUserSetSystemTimer( HWND hwnd, UINT_PTR id, UINT timeout );
@@ -1085,6 +1107,7 @@ enum
     NtUserCallHwndParam_GetClassLongPtrW,
     NtUserCallHwndParam_GetClassWord,
     NtUserCallHwndParam_GetClientRect,
+    NtUserCallHwndParam_GetScrollInfo,
     NtUserCallHwndParam_GetWindowInfo,
     NtUserCallHwndParam_GetWindowLongA,
     NtUserCallHwndParam_GetWindowLongW,
@@ -1104,7 +1127,6 @@ enum
     NtUserCallHwndParam_SetWindowPixelFormat,
     NtUserCallHwndParam_ShowOwnedPopups,
     /* temporary exports */
-    NtUserIsWindowDrawable,
     NtUserSetWindowStyle,
     NtUserSpyGetMsgName,
 };
@@ -1149,6 +1171,18 @@ static inline BOOL NtUserGetClientRect( HWND hwnd, RECT *rect )
     return NtUserCallHwndParam( hwnd, (UINT_PTR)rect, NtUserCallHwndParam_GetClientRect );
 }
 
+struct get_scroll_info_params
+{
+    int bar;
+    SCROLLINFO *info;
+};
+
+static inline BOOL NtUserGetScrollInfo( HWND hwnd, INT bar, SCROLLINFO *info )
+{
+    struct get_scroll_info_params params = { .bar = bar, .info = info };
+    return NtUserCallHwndParam( hwnd, (UINT_PTR)&params, NtUserCallHwndParam_GetScrollInfo );
+}
+
 static inline BOOL NtUserGetWindowInfo( HWND hwnd, WINDOWINFO *info )
 {
     return NtUserCallHwndParam( hwnd, (UINT_PTR)info, NtUserCallHwndParam_GetWindowInfo );
-- 
GitLab


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



More information about the wine-devel mailing list