2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Window scrolling function
5 * FILE: win32ss/user/ntuser/scrollex.c
6 * PROGRAMER: Filip Navara (xnavara@volny.cz)
11 DBG_DEFAULT_CHANNEL(UserPainting
);
15 co_IntFixCaret(PWND Window
, RECTL
*lprc
, UINT flags
)
18 PTHRDCARETINFO CaretInfo
;
20 PUSER_MESSAGE_QUEUE ActiveMessageQueue
;
24 ASSERT_REFS_CO(Window
);
26 pti
= PsGetCurrentThreadWin32Thread();
27 Desktop
= pti
->rpdesk
;
28 ActiveMessageQueue
= Desktop
->ActiveMessageQueue
;
29 if (!ActiveMessageQueue
) return 0;
30 CaretInfo
= &ActiveMessageQueue
->CaretInfo
;
31 hWndCaret
= CaretInfo
->hWnd
;
33 WndCaret
= ValidateHwndNoErr(hWndCaret
);
35 // FIXME: Check for WndCaret can be NULL
36 if (WndCaret
== Window
||
37 ((flags
& SW_SCROLLCHILDREN
) && IntIsChildWindow(Window
, WndCaret
)))
39 POINT pt
, FromOffset
, ToOffset
;
42 pt
.x
= CaretInfo
->Pos
.x
;
43 pt
.y
= CaretInfo
->Pos
.y
;
44 IntGetClientOrigin(WndCaret
, &FromOffset
);
45 IntGetClientOrigin(Window
, &ToOffset
);
48 rcCaret
.right
= pt
.x
+ CaretInfo
->Size
.cx
;
49 rcCaret
.bottom
= pt
.y
+ CaretInfo
->Size
.cy
;
50 if (RECTL_bIntersectRect(lprc
, lprc
, &rcCaret
))
63 Old GetUpdateRgn, for scrolls, see above note.
66 co_IntGetUpdateRgn(PWND Window
, PREGION Rgn
, BOOL bErase
)
72 ASSERT_REFS_CO(Window
);
76 co_IntPaintWindows(Window
, RDW_NOCHILDREN
, FALSE
);
79 Window
->state
&= ~WNDS_UPDATEDIRTY
;
81 if (Window
->hrgnUpdate
== NULL
)
83 REGION_SetRectRgn(Rgn
, 0, 0, 0, 0);
87 UpdateRgn
= REGION_LockRgn(Window
->hrgnUpdate
);
91 Rect
= Window
->rcClient
;
92 IntIntersectWithParents(Window
, &Rect
);
93 REGION_SetRectRgn(Rgn
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
94 RegionType
= IntGdiCombineRgn(Rgn
, Rgn
, UpdateRgn
, RGN_AND
);
95 REGION_bOffsetRgn(Rgn
, -Window
->rcClient
.left
, -Window
->rcClient
.top
);
96 REGION_UnlockRgn(UpdateRgn
);
107 const RECTL
*prcScroll
,
108 const RECTL
*prcClip
,
114 RECTL rcScroll
, rcClip
, rcSrc
, rcDst
;
117 if (GdiGetClipBox(hDC
, &rcClip
) == ERROR
)
119 ERR("GdiGetClipBox failed for HDC %p\n", hDC
);
126 RECTL_bIntersectRect(&rcClip
, &rcClip
, prcClip
);
131 rcScroll
= *prcScroll
;
132 RECTL_bIntersectRect(&rcSrc
, &rcClip
, prcScroll
);
140 RECTL_vOffsetRect(&rcDst
, dx
, dy
);
141 RECTL_bIntersectRect(&rcDst
, &rcDst
, &rcClip
);
143 if (!NtGdiBitBlt( hDC
,
146 rcDst
.right
- rcDst
.left
,
147 rcDst
.bottom
- rcDst
.top
,
158 /* Calculate the region that was invalidated by moving or
159 could not be copied, because it was not visible */
160 if (RgnUpdate
|| hrgnUpdate
|| prcUpdate
)
162 PREGION RgnOwn
, RgnTmp
;
164 pDC
= DC_LockDc(hDC
);
172 NT_ASSERT(RgnUpdate
== NULL
);
173 RgnUpdate
= REGION_LockRgn(hrgnUpdate
);
181 /* Begin with the shifted and then clipped scroll rect */
183 RECTL_vOffsetRect(&rcDst
, dx
, dy
);
184 RECTL_bIntersectRect(&rcDst
, &rcDst
, &rcClip
);
188 REGION_SetRectRgn(RgnOwn
, rcDst
.left
, rcDst
.top
, rcDst
.right
, rcDst
.bottom
);
192 RgnOwn
= IntSysCreateRectpRgnIndirect(&rcDst
);
195 /* Add the source rect */
196 RgnTmp
= IntSysCreateRectpRgnIndirect(&rcSrc
);
197 IntGdiCombineRgn(RgnOwn
, RgnOwn
, RgnTmp
, RGN_OR
);
199 /* Substract the part of the dest that was visible in source */
200 IntGdiCombineRgn(RgnTmp
, RgnTmp
, pDC
->prgnVis
, RGN_AND
);
201 REGION_bOffsetRgn(RgnTmp
, dx
, dy
);
202 Result
= IntGdiCombineRgn(RgnOwn
, RgnOwn
, RgnTmp
, RGN_DIFF
);
204 /* DO NOT Unlock DC while messing with prgnVis! */
207 REGION_Delete(RgnTmp
);
211 REGION_GetRgnBox(RgnOwn
, prcUpdate
);
216 REGION_UnlockRgn(RgnUpdate
);
220 REGION_Delete(RgnOwn
);
235 const RECT
*prcScroll
,
241 RECTL rcScroll
, rcClip
, rcCaret
;
245 PREGION RgnUpdate
= NULL
, RgnTemp
, RgnWinupd
= NULL
;
249 USER_REFERENCE_ENTRY CaretRef
;
251 IntGetClientRect(Window
, &rcClip
);
255 RECTL_bIntersectRect(&rcScroll
, &rcClip
, prcScroll
);
262 RECTL_bIntersectRect(&rcClip
, &rcClip
, prcClip
);
265 if (rcClip
.right
<= rcClip
.left
|| rcClip
.bottom
<= rcClip
.top
||
266 (dx
== 0 && dy
== 0))
271 /* We must use a copy of the region, as we can't hold an exclusive lock
272 * on it while doing callouts to user-mode */
273 RgnUpdate
= IntSysCreateRectpRgn(0, 0, 0, 0);
276 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
282 RgnTemp
= REGION_LockRgn(hrgnUpdate
);
285 EngSetLastError(ERROR_INVALID_HANDLE
);
288 IntGdiCombineRgn(RgnUpdate
, RgnTemp
, NULL
, RGN_COPY
);
289 REGION_UnlockRgn(RgnTemp
);
292 /* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
293 if (flags
& SW_SCROLLWNDDCE
)
295 dcxflags
= DCX_USESTYLE
;
297 if (!(Window
->pcls
->style
& (CS_OWNDC
|CS_CLASSDC
)))
298 dcxflags
|= DCX_CACHE
; // AH??? wine~ If not Powned or with Class go Cheap!
300 if (flags
& SW_SCROLLCHILDREN
&& Window
->style
& WS_CLIPCHILDREN
)
301 dcxflags
|= DCX_CACHE
|DCX_NOCLIPCHILDREN
;
305 /* So in this case ScrollWindowEx uses Cache DC. */
306 dcxflags
= DCX_CACHE
|DCX_USESTYLE
;
307 if (flags
& SW_SCROLLCHILDREN
) dcxflags
|= DCX_NOCLIPCHILDREN
;
310 hDC
= UserGetDCEx(Window
, 0, dcxflags
);
313 /* FIXME: SetLastError? */
317 rdw_flags
= (flags
& SW_ERASE
) && (flags
& SW_INVALIDATE
) ? RDW_INVALIDATE
| RDW_ERASE
: RDW_INVALIDATE
;
320 hwndCaret
= co_IntFixCaret(Window
, &rcCaret
, flags
);
322 Result
= UserScrollDC( hDC
,
331 UserReleaseDC(Window
, hDC
, FALSE
);
334 * Take into account the fact that some damage may have occurred during
335 * the scroll. Keep a copy in hrgnWinupd to be added to hrngUpdate at the end.
338 RgnTemp
= IntSysCreateRectpRgn(0, 0, 0, 0);
341 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
345 if (co_IntGetUpdateRgn(Window
, RgnTemp
, FALSE
) != NULLREGION
)
347 PREGION RgnClip
= IntSysCreateRectpRgnIndirect(&rcClip
);
352 RgnWinupd
= IntSysCreateRectpRgn( 0, 0, 0, 0);
353 IntGdiCombineRgn( RgnWinupd
, RgnTemp
, 0, RGN_COPY
);
356 REGION_bOffsetRgn(RgnTemp
, dx
, dy
);
358 IntGdiCombineRgn(RgnTemp
, RgnTemp
, RgnClip
, RGN_AND
);
361 IntGdiCombineRgn( RgnWinupd
, RgnWinupd
, RgnTemp
, RGN_OR
);
363 co_UserRedrawWindow(Window
, NULL
, RgnTemp
, rdw_flags
);
365 REGION_Delete(RgnClip
);
368 REGION_Delete(RgnTemp
);
370 if (flags
& SW_SCROLLCHILDREN
)
375 USER_REFERENCE_ENTRY WndRef
;
379 IntGetClientOrigin(Window
, &ClientOrigin
);
381 for (Child
= Window
->spwndChild
; Child
; Child
= Child
->spwndNext
)
383 rcChild
= Child
->rcWindow
;
384 rcChild
.left
-= ClientOrigin
.x
;
385 rcChild
.top
-= ClientOrigin
.y
;
386 rcChild
.right
-= ClientOrigin
.x
;
387 rcChild
.bottom
-= ClientOrigin
.y
;
389 if (!prcScroll
|| RECTL_bIntersectRect(&rcDummy
, &rcChild
, &rcScroll
))
391 UserRefObjectCo(Child
, &WndRef
);
393 if (Window
->spwndParent
== UserGetDesktopWindow()) // Window->spwndParent->fnid == FNID_DESKTOP )
394 lParam
= MAKELONG(Child
->rcClient
.left
, Child
->rcClient
.top
);
396 lParam
= MAKELONG(rcChild
.left
+ dx
, rcChild
.top
+ dy
);
398 /* wine sends WM_POSCHANGING, WM_POSCHANGED messages */
399 /* windows sometimes a WM_MOVE */
400 co_IntSendMessage(UserHMGetHandle(Child
), WM_MOVE
, 0, lParam
);
402 UserDerefObjectCo(Child
);
407 if (flags
& (SW_INVALIDATE
| SW_ERASE
))
409 co_UserRedrawWindow( Window
,
412 rdw_flags
| /* HACK */
413 ((flags
& SW_SCROLLCHILDREN
) ? RDW_ALLCHILDREN
: RDW_NOCHILDREN
) );
416 if (hwndCaret
&& (CaretWnd
= UserGetWindowObject(hwndCaret
)))
418 UserRefObjectCo(CaretWnd
, &CaretRef
);
420 co_IntSetCaretPos(rcCaret
.left
+ dx
, rcCaret
.top
+ dy
);
421 co_UserShowCaret(CaretWnd
);
423 UserDerefObjectCo(CaretWnd
);
426 if (hrgnUpdate
&& (Result
!= ERROR
))
428 /* Give everything back to the caller */
429 RgnTemp
= REGION_LockRgn(hrgnUpdate
);
430 /* The handle should still be valid */
433 IntGdiCombineRgn(RgnTemp
, RgnUpdate
, RgnWinupd
, RGN_OR
);
435 IntGdiCombineRgn(RgnTemp
, RgnUpdate
, NULL
, RGN_COPY
);
436 REGION_UnlockRgn(RgnTemp
);
441 REGION_Delete(RgnWinupd
);
446 REGION_Delete(RgnUpdate
);
454 IntScrollWindow(PWND pWnd
,
460 return IntScrollWindowEx( pWnd
, dx
, dy
, lpRect
, prcClip
, 0, NULL
,
461 (lpRect
? 0 : SW_SCROLLCHILDREN
) | (SW_ERASE
|SW_INVALIDATE
|SW_SCROLLWNDDCE
)) != ERROR
;
475 const RECT
*prcUnsafeScroll
,
476 const RECT
*prcUnsafeClip
,
478 LPRECT prcUnsafeUpdate
)
480 DECLARE_RETURN(DWORD
);
481 RECTL rcScroll
, rcClip
, rcUpdate
;
482 NTSTATUS Status
= STATUS_SUCCESS
;
485 TRACE("Enter NtUserScrollDC\n");
486 UserEnterExclusive();
492 ProbeForRead(prcUnsafeScroll
, sizeof(*prcUnsafeScroll
), 1);
493 rcScroll
= *prcUnsafeScroll
;
497 ProbeForRead(prcUnsafeClip
, sizeof(*prcUnsafeClip
), 1);
498 rcClip
= *prcUnsafeClip
;
502 ProbeForWrite(prcUnsafeUpdate
, sizeof(*prcUnsafeUpdate
), 1);
505 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
507 Status
= _SEH2_GetExceptionCode();
510 if (!NT_SUCCESS(Status
))
512 SetLastNtError(Status
);
516 Result
= UserScrollDC( hDC
,
519 prcUnsafeScroll
? &rcScroll
: 0,
520 prcUnsafeClip
? &rcClip
: 0,
523 prcUnsafeUpdate
? &rcUpdate
: NULL
);
526 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
534 *prcUnsafeUpdate
= rcUpdate
;
536 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
538 Status
= _SEH2_GetExceptionCode();
541 if (!NT_SUCCESS(Status
))
543 /* FIXME: SetLastError? */
544 /* FIXME: correct? We have already scrolled! */
552 TRACE("Leave NtUserScrollDC, ret=%lu\n",_ret_
);
558 * NtUserScrollWindowEx
565 NtUserScrollWindowEx(
569 const RECT
*prcUnsafeScroll
,
570 const RECT
*prcUnsafeClip
,
572 LPRECT prcUnsafeUpdate
,
575 RECTL rcScroll
, rcClip
, rcCaret
, rcUpdate
;
577 PWND Window
= NULL
, CaretWnd
;
579 PREGION RgnUpdate
= NULL
, RgnTemp
, RgnWinupd
= NULL
;
583 NTSTATUS Status
= STATUS_SUCCESS
;
584 DECLARE_RETURN(DWORD
);
585 USER_REFERENCE_ENTRY Ref
, CaretRef
;
587 TRACE("Enter NtUserScrollWindowEx\n");
588 UserEnterExclusive();
590 Window
= UserGetWindowObject(hWnd
);
591 if (!Window
|| !IntIsWindowDrawable(Window
))
593 Window
= NULL
; /* prevent deref at cleanup */
596 UserRefObjectCo(Window
, &Ref
);
598 IntGetClientRect(Window
, &rcClip
);
604 ProbeForRead(prcUnsafeScroll
, sizeof(*prcUnsafeScroll
), 1);
605 RECTL_bIntersectRect(&rcScroll
, &rcClip
, prcUnsafeScroll
);
612 ProbeForRead(prcUnsafeClip
, sizeof(*prcUnsafeClip
), 1);
613 RECTL_bIntersectRect(&rcClip
, &rcClip
, prcUnsafeClip
);
616 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
618 Status
= _SEH2_GetExceptionCode();
622 if (!NT_SUCCESS(Status
))
624 SetLastNtError(Status
);
628 if (rcClip
.right
<= rcClip
.left
|| rcClip
.bottom
<= rcClip
.top
||
629 (dx
== 0 && dy
== 0))
634 /* We must use a copy of the region, as we can't hold an exclusive lock
635 * on it while doing callouts to user-mode */
636 RgnUpdate
= IntSysCreateRectpRgn(0, 0, 0, 0);
639 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
645 RgnTemp
= REGION_LockRgn(hrgnUpdate
);
648 EngSetLastError(ERROR_INVALID_HANDLE
);
651 IntGdiCombineRgn(RgnUpdate
, RgnTemp
, NULL
, RGN_COPY
);
652 REGION_UnlockRgn(RgnTemp
);
655 /* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
656 if (flags
& SW_SCROLLWNDDCE
)
658 dcxflags
= DCX_USESTYLE
;
660 if (!(Window
->pcls
->style
& (CS_OWNDC
|CS_CLASSDC
)))
661 dcxflags
|= DCX_CACHE
; // AH??? wine~ If not Powned or with Class go Cheap!
663 if (flags
& SW_SCROLLCHILDREN
&& Window
->style
& WS_CLIPCHILDREN
)
664 dcxflags
|= DCX_CACHE
|DCX_NOCLIPCHILDREN
;
668 /* So in this case ScrollWindowEx uses Cache DC. */
669 dcxflags
= DCX_CACHE
|DCX_USESTYLE
;
670 if (flags
& SW_SCROLLCHILDREN
) dcxflags
|= DCX_NOCLIPCHILDREN
;
673 hDC
= UserGetDCEx(Window
, 0, dcxflags
);
676 /* FIXME: SetLastError? */
680 rdw_flags
= (flags
& SW_ERASE
) && (flags
& SW_INVALIDATE
) ? RDW_INVALIDATE
| RDW_ERASE
: RDW_INVALIDATE
;
683 hwndCaret
= co_IntFixCaret(Window
, &rcCaret
, flags
);
685 Result
= UserScrollDC( hDC
,
692 prcUnsafeUpdate
? &rcUpdate
: NULL
);
694 UserReleaseDC(Window
, hDC
, FALSE
);
697 * Take into account the fact that some damage may have occurred during
698 * the scroll. Keep a copy in hrgnWinupd to be added to hrngUpdate at the end.
701 RgnTemp
= IntSysCreateRectpRgn(0, 0, 0, 0);
704 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
708 if (co_IntGetUpdateRgn(Window
, RgnTemp
, FALSE
) != NULLREGION
)
710 PREGION RgnClip
= IntSysCreateRectpRgnIndirect(&rcClip
);
715 RgnWinupd
= IntSysCreateRectpRgn( 0, 0, 0, 0);
716 IntGdiCombineRgn( RgnWinupd
, RgnTemp
, 0, RGN_COPY
);
719 REGION_bOffsetRgn(RgnTemp
, dx
, dy
);
721 IntGdiCombineRgn(RgnTemp
, RgnTemp
, RgnClip
, RGN_AND
);
724 IntGdiCombineRgn( RgnWinupd
, RgnWinupd
, RgnTemp
, RGN_OR
);
726 co_UserRedrawWindow(Window
, NULL
, RgnTemp
, rdw_flags
);
728 REGION_Delete(RgnClip
);
731 REGION_Delete(RgnTemp
);
733 if (flags
& SW_SCROLLCHILDREN
)
738 USER_REFERENCE_ENTRY WndRef
;
742 IntGetClientOrigin(Window
, &ClientOrigin
);
744 for (Child
= Window
->spwndChild
; Child
; Child
= Child
->spwndNext
)
746 rcChild
= Child
->rcWindow
;
747 rcChild
.left
-= ClientOrigin
.x
;
748 rcChild
.top
-= ClientOrigin
.y
;
749 rcChild
.right
-= ClientOrigin
.x
;
750 rcChild
.bottom
-= ClientOrigin
.y
;
752 if (!prcUnsafeScroll
|| RECTL_bIntersectRect(&rcDummy
, &rcChild
, &rcScroll
))
754 UserRefObjectCo(Child
, &WndRef
);
756 if (Window
->spwndParent
== UserGetDesktopWindow()) // Window->spwndParent->fnid == FNID_DESKTOP )
757 lParam
= MAKELONG(Child
->rcClient
.left
, Child
->rcClient
.top
);
759 lParam
= MAKELONG(rcChild
.left
+ dx
, rcChild
.top
+ dy
);
761 /* wine sends WM_POSCHANGING, WM_POSCHANGED messages */
762 /* windows sometimes a WM_MOVE */
763 co_IntSendMessage(UserHMGetHandle(Child
), WM_MOVE
, 0, lParam
);
765 UserDerefObjectCo(Child
);
770 if (flags
& (SW_INVALIDATE
| SW_ERASE
))
772 co_UserRedrawWindow( Window
,
775 rdw_flags
| /* HACK */
776 ((flags
& SW_SCROLLCHILDREN
) ? RDW_ALLCHILDREN
: RDW_NOCHILDREN
) );
779 if (hwndCaret
&& (CaretWnd
= UserGetWindowObject(hwndCaret
)))
781 UserRefObjectCo(CaretWnd
, &CaretRef
);
783 co_IntSetCaretPos(rcCaret
.left
+ dx
, rcCaret
.top
+ dy
);
784 co_UserShowCaret(CaretWnd
);
786 UserDerefObjectCo(CaretWnd
);
793 /* Probe here, to not fail on invalid pointer before scrolling */
794 ProbeForWrite(prcUnsafeUpdate
, sizeof(*prcUnsafeUpdate
), 1);
795 *prcUnsafeUpdate
= rcUpdate
;
797 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
799 Status
= _SEH2_GetExceptionCode();
803 if (!NT_SUCCESS(Status
))
805 SetLastNtError(Status
);
813 if (hrgnUpdate
&& (_ret_
!= ERROR
))
815 /* Give everything back to the caller */
816 RgnTemp
= REGION_LockRgn(hrgnUpdate
);
817 /* The handle should still be valid */
820 IntGdiCombineRgn(RgnTemp
, RgnUpdate
, RgnWinupd
, RGN_OR
);
822 IntGdiCombineRgn(RgnTemp
, RgnUpdate
, NULL
, RGN_COPY
);
823 REGION_UnlockRgn(RgnTemp
);
828 REGION_Delete(RgnWinupd
);
833 REGION_Delete(RgnUpdate
);
837 UserDerefObjectCo(Window
);
839 TRACE("Leave NtUserScrollWindowEx, ret=%lu\n",_ret_
);