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
)
17 PTHRDCARETINFO CaretInfo
;
19 PUSER_MESSAGE_QUEUE ThreadQueue
;
23 ASSERT_REFS_CO(Window
);
25 pti
= PsGetCurrentThreadWin32Thread();
26 ThreadQueue
= pti
->MessageQueue
;
27 CaretInfo
= &ThreadQueue
->CaretInfo
;
28 hWndCaret
= CaretInfo
->hWnd
;
30 WndCaret
= ValidateHwndNoErr(hWndCaret
);
32 // FIXME: Check for WndCaret can be NULL
33 if (WndCaret
== Window
||
34 ((flags
& SW_SCROLLCHILDREN
) && IntIsChildWindow(Window
, WndCaret
)))
36 POINT pt
, FromOffset
, ToOffset
;
39 pt
.x
= CaretInfo
->Pos
.x
;
40 pt
.y
= CaretInfo
->Pos
.y
;
41 IntGetClientOrigin(WndCaret
, &FromOffset
);
42 IntGetClientOrigin(Window
, &ToOffset
);
45 rcCaret
.right
= pt
.x
+ CaretInfo
->Size
.cx
;
46 rcCaret
.bottom
= pt
.y
+ CaretInfo
->Size
.cy
;
47 if (RECTL_bIntersectRect(lprc
, lprc
, &rcCaret
))
60 Old GetUpdateRgn, for scrolls, see above note.
63 co_IntGetUpdateRgn(PWND Window
, PREGION Rgn
, BOOL bErase
)
69 ASSERT_REFS_CO(Window
);
73 co_IntPaintWindows(Window
, RDW_NOCHILDREN
, FALSE
);
76 Window
->state
&= ~WNDS_UPDATEDIRTY
;
78 if (Window
->hrgnUpdate
== NULL
)
80 REGION_SetRectRgn(Rgn
, 0, 0, 0, 0);
84 UpdateRgn
= REGION_LockRgn(Window
->hrgnUpdate
);
88 Rect
= Window
->rcClient
;
89 IntIntersectWithParents(Window
, &Rect
);
90 REGION_SetRectRgn(Rgn
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
91 RegionType
= IntGdiCombineRgn(Rgn
, Rgn
, UpdateRgn
, RGN_AND
);
92 REGION_bOffsetRgn(Rgn
, -Window
->rcClient
.left
, -Window
->rcClient
.top
);
93 REGION_UnlockRgn(UpdateRgn
);
104 const RECTL
*prcScroll
,
105 const RECTL
*prcClip
,
111 RECTL rcScroll
, rcClip
, rcSrc
, rcDst
;
114 if (GdiGetClipBox(hDC
, &rcClip
) == ERROR
)
116 ERR("GdiGetClipBox failed for HDC %p\n", hDC
);
123 RECTL_bIntersectRect(&rcClip
, &rcClip
, prcClip
);
128 rcScroll
= *prcScroll
;
129 RECTL_bIntersectRect(&rcSrc
, &rcClip
, prcScroll
);
137 RECTL_vOffsetRect(&rcDst
, dx
, dy
);
138 RECTL_bIntersectRect(&rcDst
, &rcDst
, &rcClip
);
140 if (!NtGdiBitBlt( hDC
,
143 rcDst
.right
- rcDst
.left
,
144 rcDst
.bottom
- rcDst
.top
,
155 /* Calculate the region that was invalidated by moving or
156 could not be copied, because it was not visible */
157 if (RgnUpdate
|| hrgnUpdate
|| prcUpdate
)
159 PREGION RgnOwn
, RgnTmp
;
161 pDC
= DC_LockDc(hDC
);
169 NT_ASSERT(RgnUpdate
== NULL
);
170 RgnUpdate
= REGION_LockRgn(hrgnUpdate
);
178 /* Begin with the shifted and then clipped scroll rect */
180 RECTL_vOffsetRect(&rcDst
, dx
, dy
);
181 RECTL_bIntersectRect(&rcDst
, &rcDst
, &rcClip
);
185 REGION_SetRectRgn(RgnOwn
, rcDst
.left
, rcDst
.top
, rcDst
.right
, rcDst
.bottom
);
189 RgnOwn
= IntSysCreateRectpRgnIndirect(&rcDst
);
192 /* Add the source rect */
193 RgnTmp
= IntSysCreateRectpRgnIndirect(&rcSrc
);
194 IntGdiCombineRgn(RgnOwn
, RgnOwn
, RgnTmp
, RGN_OR
);
196 /* Substract the part of the dest that was visible in source */
197 IntGdiCombineRgn(RgnTmp
, RgnTmp
, pDC
->prgnVis
, RGN_AND
);
198 REGION_bOffsetRgn(RgnTmp
, dx
, dy
);
199 Result
= IntGdiCombineRgn(RgnOwn
, RgnOwn
, RgnTmp
, RGN_DIFF
);
201 /* DO NOT Unlock DC while messing with prgnVis! */
204 REGION_Delete(RgnTmp
);
208 REGION_GetRgnBox(RgnOwn
, prcUpdate
);
213 REGION_UnlockRgn(RgnUpdate
);
217 REGION_Delete(RgnOwn
);
232 const RECT
*prcScroll
,
239 RECTL rcScroll
, rcClip
, rcCaret
;
242 PREGION RgnUpdate
= NULL
, RgnTemp
, RgnWinupd
= NULL
;
246 USER_REFERENCE_ENTRY CaretRef
;
248 if (!Window
|| !IntIsWindowDrawable(Window
))
253 IntGetClientRect(Window
, &rcClip
);
256 RECTL_bIntersectRect(&rcScroll
, &rcClip
, prcScroll
);
261 RECTL_bIntersectRect(&rcClip
, &rcClip
, prcClip
);
263 if (rcClip
.right
<= rcClip
.left
|| rcClip
.bottom
<= rcClip
.top
||
264 (dx
== 0 && dy
== 0))
269 /* We must use a copy of the region, as we can't hold an exclusive lock
270 * on it while doing callouts to user-mode */
271 RgnUpdate
= IntSysCreateRectpRgn(0, 0, 0, 0);
274 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
280 RgnTemp
= REGION_LockRgn(hrgnUpdate
);
283 EngSetLastError(ERROR_INVALID_HANDLE
);
287 IntGdiCombineRgn(RgnUpdate
, RgnTemp
, NULL
, RGN_COPY
);
288 REGION_UnlockRgn(RgnTemp
);
291 /* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
292 if (flags
& SW_SCROLLWNDDCE
)
294 dcxflags
= DCX_USESTYLE
;
296 if (!(Window
->pcls
->style
& (CS_OWNDC
|CS_CLASSDC
)))
297 dcxflags
|= DCX_CACHE
; // AH??? wine~ If not Powned or with Class go Cheap!
299 if (flags
& SW_SCROLLCHILDREN
&& Window
->style
& WS_CLIPCHILDREN
)
300 dcxflags
|= DCX_CACHE
|DCX_NOCLIPCHILDREN
;
304 /* So in this case ScrollWindowEx uses Cache DC. */
305 dcxflags
= DCX_CACHE
|DCX_USESTYLE
;
306 if (flags
& SW_SCROLLCHILDREN
) dcxflags
|= DCX_NOCLIPCHILDREN
;
309 hDC
= UserGetDCEx(Window
, 0, dcxflags
);
312 /* 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
);
346 if (co_IntGetUpdateRgn(Window
, RgnTemp
, FALSE
) != NULLREGION
)
348 PREGION RgnClip
= IntSysCreateRectpRgnIndirect(&rcClip
);
353 RgnWinupd
= IntSysCreateRectpRgn(0, 0, 0, 0);
354 // FIXME: What to do if RgnWinupd == NULL??
355 IntGdiCombineRgn( RgnWinupd
, RgnTemp
, 0, RGN_COPY
);
358 REGION_bOffsetRgn(RgnTemp
, dx
, dy
);
360 IntGdiCombineRgn(RgnTemp
, RgnTemp
, RgnClip
, RGN_AND
);
363 IntGdiCombineRgn( RgnWinupd
, RgnWinupd
, RgnTemp
, RGN_OR
);
365 co_UserRedrawWindow(Window
, NULL
, RgnTemp
, rdw_flags
);
367 REGION_Delete(RgnClip
);
370 REGION_Delete(RgnTemp
);
372 if (flags
& SW_SCROLLCHILDREN
)
377 USER_REFERENCE_ENTRY WndRef
;
381 IntGetClientOrigin(Window
, &ClientOrigin
);
383 for (Child
= Window
->spwndChild
; Child
; Child
= Child
->spwndNext
)
385 rcChild
= Child
->rcWindow
;
386 RECTL_vOffsetRect(&rcChild
, -ClientOrigin
.x
, -ClientOrigin
.y
);
388 if (!prcScroll
|| RECTL_bIntersectRect(&rcDummy
, &rcChild
, &rcScroll
))
390 UserRefObjectCo(Child
, &WndRef
);
392 if (UserIsDesktopWindow(Window
->spwndParent
))
393 lParam
= MAKELONG(Child
->rcClient
.left
, Child
->rcClient
.top
);
395 lParam
= MAKELONG(rcChild
.left
+ dx
, rcChild
.top
+ dy
);
397 /* wine sends WM_POSCHANGING, WM_POSCHANGED messages */
398 /* windows sometimes a WM_MOVE */
399 co_IntSendMessage(UserHMGetHandle(Child
), WM_MOVE
, 0, lParam
);
401 UserDerefObjectCo(Child
);
406 if (flags
& (SW_INVALIDATE
| SW_ERASE
))
408 co_UserRedrawWindow( Window
,
411 rdw_flags
| /* HACK */
412 ((flags
& SW_SCROLLCHILDREN
) ? RDW_ALLCHILDREN
: RDW_NOCHILDREN
) );
415 if (hwndCaret
&& (CaretWnd
= UserGetWindowObject(hwndCaret
)))
417 UserRefObjectCo(CaretWnd
, &CaretRef
);
419 co_IntSetCaretPos(rcCaret
.left
+ dx
, rcCaret
.top
+ dy
);
420 co_UserShowCaret(CaretWnd
);
422 UserDerefObjectCo(CaretWnd
);
425 if (hrgnUpdate
&& (Result
!= ERROR
))
427 /* Give everything back to the caller */
428 RgnTemp
= REGION_LockRgn(hrgnUpdate
);
429 /* The handle should still be valid */
432 IntGdiCombineRgn(RgnTemp
, RgnUpdate
, RgnWinupd
, RGN_OR
);
434 IntGdiCombineRgn(RgnTemp
, RgnUpdate
, NULL
, RGN_COPY
);
435 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
);
482 NTSTATUS Status
= STATUS_SUCCESS
;
483 RECTL rcScroll
, rcClip
, rcUpdate
;
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();
511 if (!NT_SUCCESS(Status
))
513 SetLastNtError(Status
);
517 Result
= UserScrollDC( hDC
,
520 prcUnsafeScroll
? &rcScroll
: NULL
,
521 prcUnsafeClip
? &rcClip
: NULL
,
524 prcUnsafeUpdate
? &rcUpdate
: NULL
);
527 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
535 *prcUnsafeUpdate
= rcUpdate
;
537 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
539 Status
= _SEH2_GetExceptionCode();
543 if (!NT_SUCCESS(Status
))
545 /* FIXME: SetLastError? */
546 /* FIXME: correct? We have already scrolled! */
554 TRACE("Leave NtUserScrollDC, ret=%lu\n",_ret_
);
560 * NtUserScrollWindowEx
567 NtUserScrollWindowEx(
571 const RECT
*prcUnsafeScroll
,
572 const RECT
*prcUnsafeClip
,
574 LPRECT prcUnsafeUpdate
,
577 DECLARE_RETURN(DWORD
);
579 NTSTATUS Status
= STATUS_SUCCESS
;
581 RECTL rcScroll
, rcClip
, rcUpdate
;
582 USER_REFERENCE_ENTRY Ref
;
584 TRACE("Enter NtUserScrollWindowEx\n");
585 UserEnterExclusive();
587 Window
= UserGetWindowObject(hWnd
);
588 if (!Window
|| !IntIsWindowDrawable(Window
))
590 Window
= NULL
; /* prevent deref at cleanup */
593 UserRefObjectCo(Window
, &Ref
);
599 ProbeForRead(prcUnsafeScroll
, sizeof(*prcUnsafeScroll
), 1);
600 rcScroll
= *prcUnsafeScroll
;
605 ProbeForRead(prcUnsafeClip
, sizeof(*prcUnsafeClip
), 1);
606 rcClip
= *prcUnsafeClip
;
609 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
611 Status
= _SEH2_GetExceptionCode();
615 if (!NT_SUCCESS(Status
))
617 SetLastNtError(Status
);
621 Result
= IntScrollWindowEx(Window
,
623 prcUnsafeScroll
? &rcScroll
: NULL
,
624 prcUnsafeClip
? &rcClip
: NULL
,
626 prcUnsafeUpdate
? &rcUpdate
: NULL
,
633 /* Probe here, to not fail on invalid pointer before scrolling */
634 ProbeForWrite(prcUnsafeUpdate
, sizeof(*prcUnsafeUpdate
), 1);
635 *prcUnsafeUpdate
= rcUpdate
;
637 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
639 Status
= _SEH2_GetExceptionCode();
643 if (!NT_SUCCESS(Status
))
645 SetLastNtError(Status
);
654 UserDerefObjectCo(Window
);
656 TRACE("Leave NtUserScrollWindowEx, ret=%lu\n",_ret_
);