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
,
242 RECTL rcScroll
, rcClip
, rcCaret
;
245 PREGION RgnUpdate
= NULL
, RgnTemp
, RgnWinupd
= NULL
;
249 USER_REFERENCE_ENTRY CaretRef
;
251 if (!Window
|| !IntIsWindowDrawable(Window
))
256 IntGetClientRect(Window
, &rcClip
);
259 RECTL_bIntersectRect(&rcScroll
, &rcClip
, prcScroll
);
264 RECTL_bIntersectRect(&rcClip
, &rcClip
, prcClip
);
266 if (rcClip
.right
<= rcClip
.left
|| rcClip
.bottom
<= rcClip
.top
||
267 (dx
== 0 && dy
== 0))
272 /* We must use a copy of the region, as we can't hold an exclusive lock
273 * on it while doing callouts to user-mode */
274 RgnUpdate
= IntSysCreateRectpRgn(0, 0, 0, 0);
277 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
283 RgnTemp
= REGION_LockRgn(hrgnUpdate
);
286 EngSetLastError(ERROR_INVALID_HANDLE
);
290 IntGdiCombineRgn(RgnUpdate
, RgnTemp
, NULL
, RGN_COPY
);
291 REGION_UnlockRgn(RgnTemp
);
294 /* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
295 if (flags
& SW_SCROLLWNDDCE
)
297 dcxflags
= DCX_USESTYLE
;
299 if (!(Window
->pcls
->style
& (CS_OWNDC
|CS_CLASSDC
)))
300 dcxflags
|= DCX_CACHE
; // AH??? wine~ If not Powned or with Class go Cheap!
302 if (flags
& SW_SCROLLCHILDREN
&& Window
->style
& WS_CLIPCHILDREN
)
303 dcxflags
|= DCX_CACHE
|DCX_NOCLIPCHILDREN
;
307 /* So in this case ScrollWindowEx uses Cache DC. */
308 dcxflags
= DCX_CACHE
|DCX_USESTYLE
;
309 if (flags
& SW_SCROLLCHILDREN
) dcxflags
|= DCX_NOCLIPCHILDREN
;
312 hDC
= UserGetDCEx(Window
, 0, dcxflags
);
315 /* FIXME: SetLastError? */
320 rdw_flags
= (flags
& SW_ERASE
) && (flags
& SW_INVALIDATE
) ? RDW_INVALIDATE
| RDW_ERASE
: RDW_INVALIDATE
;
323 hwndCaret
= co_IntFixCaret(Window
, &rcCaret
, flags
);
325 Result
= UserScrollDC( hDC
,
334 UserReleaseDC(Window
, hDC
, FALSE
);
337 * Take into account the fact that some damage may have occurred during
338 * the scroll. Keep a copy in hrgnWinupd to be added to hrngUpdate at the end.
341 RgnTemp
= IntSysCreateRectpRgn(0, 0, 0, 0);
344 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
349 if (co_IntGetUpdateRgn(Window
, RgnTemp
, FALSE
) != NULLREGION
)
351 PREGION RgnClip
= IntSysCreateRectpRgnIndirect(&rcClip
);
356 RgnWinupd
= IntSysCreateRectpRgn(0, 0, 0, 0);
357 // FIXME: What to do if RgnWinupd == NULL??
358 IntGdiCombineRgn( RgnWinupd
, RgnTemp
, 0, RGN_COPY
);
361 REGION_bOffsetRgn(RgnTemp
, dx
, dy
);
363 IntGdiCombineRgn(RgnTemp
, RgnTemp
, RgnClip
, RGN_AND
);
366 IntGdiCombineRgn( RgnWinupd
, RgnWinupd
, RgnTemp
, RGN_OR
);
368 co_UserRedrawWindow(Window
, NULL
, RgnTemp
, rdw_flags
);
370 REGION_Delete(RgnClip
);
373 REGION_Delete(RgnTemp
);
375 if (flags
& SW_SCROLLCHILDREN
)
380 USER_REFERENCE_ENTRY WndRef
;
384 IntGetClientOrigin(Window
, &ClientOrigin
);
386 for (Child
= Window
->spwndChild
; Child
; Child
= Child
->spwndNext
)
388 rcChild
= Child
->rcWindow
;
389 RECTL_vOffsetRect(&rcChild
, -ClientOrigin
.x
, -ClientOrigin
.y
);
391 if (!prcScroll
|| RECTL_bIntersectRect(&rcDummy
, &rcChild
, &rcScroll
))
393 UserRefObjectCo(Child
, &WndRef
);
395 if (UserIsDesktopWindow(Window
->spwndParent
))
396 lParam
= MAKELONG(Child
->rcClient
.left
, Child
->rcClient
.top
);
398 lParam
= MAKELONG(rcChild
.left
+ dx
, rcChild
.top
+ dy
);
400 /* wine sends WM_POSCHANGING, WM_POSCHANGED messages */
401 /* windows sometimes a WM_MOVE */
402 co_IntSendMessage(UserHMGetHandle(Child
), WM_MOVE
, 0, lParam
);
404 UserDerefObjectCo(Child
);
409 if (flags
& (SW_INVALIDATE
| SW_ERASE
))
411 co_UserRedrawWindow( Window
,
414 rdw_flags
| /* HACK */
415 ((flags
& SW_SCROLLCHILDREN
) ? RDW_ALLCHILDREN
: RDW_NOCHILDREN
) );
418 if (hwndCaret
&& (CaretWnd
= UserGetWindowObject(hwndCaret
)))
420 UserRefObjectCo(CaretWnd
, &CaretRef
);
422 co_IntSetCaretPos(rcCaret
.left
+ dx
, rcCaret
.top
+ dy
);
423 co_UserShowCaret(CaretWnd
);
425 UserDerefObjectCo(CaretWnd
);
428 if (hrgnUpdate
&& (Result
!= ERROR
))
430 /* Give everything back to the caller */
431 RgnTemp
= REGION_LockRgn(hrgnUpdate
);
432 /* The handle should still be valid */
435 IntGdiCombineRgn(RgnTemp
, RgnUpdate
, RgnWinupd
, RGN_OR
);
437 IntGdiCombineRgn(RgnTemp
, RgnUpdate
, NULL
, RGN_COPY
);
438 REGION_UnlockRgn(RgnTemp
);
444 REGION_Delete(RgnWinupd
);
449 REGION_Delete(RgnUpdate
);
457 IntScrollWindow(PWND pWnd
,
463 return IntScrollWindowEx( pWnd
, dx
, dy
, lpRect
, prcClip
, 0, NULL
,
464 (lpRect
? 0 : SW_SCROLLCHILDREN
) | (SW_ERASE
|SW_INVALIDATE
|SW_SCROLLWNDDCE
)) != ERROR
;
478 const RECT
*prcUnsafeScroll
,
479 const RECT
*prcUnsafeClip
,
481 LPRECT prcUnsafeUpdate
)
483 DECLARE_RETURN(DWORD
);
485 NTSTATUS Status
= STATUS_SUCCESS
;
486 RECTL rcScroll
, rcClip
, rcUpdate
;
488 TRACE("Enter NtUserScrollDC\n");
489 UserEnterExclusive();
495 ProbeForRead(prcUnsafeScroll
, sizeof(*prcUnsafeScroll
), 1);
496 rcScroll
= *prcUnsafeScroll
;
500 ProbeForRead(prcUnsafeClip
, sizeof(*prcUnsafeClip
), 1);
501 rcClip
= *prcUnsafeClip
;
505 ProbeForWrite(prcUnsafeUpdate
, sizeof(*prcUnsafeUpdate
), 1);
508 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
510 Status
= _SEH2_GetExceptionCode();
514 if (!NT_SUCCESS(Status
))
516 SetLastNtError(Status
);
520 Result
= UserScrollDC( hDC
,
523 prcUnsafeScroll
? &rcScroll
: NULL
,
524 prcUnsafeClip
? &rcClip
: NULL
,
527 prcUnsafeUpdate
? &rcUpdate
: NULL
);
530 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
538 *prcUnsafeUpdate
= rcUpdate
;
540 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
542 Status
= _SEH2_GetExceptionCode();
546 if (!NT_SUCCESS(Status
))
548 /* FIXME: SetLastError? */
549 /* FIXME: correct? We have already scrolled! */
557 TRACE("Leave NtUserScrollDC, ret=%lu\n",_ret_
);
563 * NtUserScrollWindowEx
570 NtUserScrollWindowEx(
574 const RECT
*prcUnsafeScroll
,
575 const RECT
*prcUnsafeClip
,
577 LPRECT prcUnsafeUpdate
,
580 DECLARE_RETURN(DWORD
);
582 NTSTATUS Status
= STATUS_SUCCESS
;
584 RECTL rcScroll
, rcClip
, rcUpdate
;
585 USER_REFERENCE_ENTRY Ref
;
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
);
602 ProbeForRead(prcUnsafeScroll
, sizeof(*prcUnsafeScroll
), 1);
603 rcScroll
= *prcUnsafeScroll
;
608 ProbeForRead(prcUnsafeClip
, sizeof(*prcUnsafeClip
), 1);
609 rcClip
= *prcUnsafeClip
;
612 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
614 Status
= _SEH2_GetExceptionCode();
618 if (!NT_SUCCESS(Status
))
620 SetLastNtError(Status
);
624 Result
= IntScrollWindowEx(Window
,
626 prcUnsafeScroll
? &rcScroll
: NULL
,
627 prcUnsafeClip
? &rcClip
: NULL
,
629 prcUnsafeUpdate
? &rcUpdate
: NULL
,
636 /* Probe here, to not fail on invalid pointer before scrolling */
637 ProbeForWrite(prcUnsafeUpdate
, sizeof(*prcUnsafeUpdate
), 1);
638 *prcUnsafeUpdate
= rcUpdate
;
640 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
642 Status
= _SEH2_GetExceptionCode();
646 if (!NT_SUCCESS(Status
))
648 SetLastNtError(Status
);
657 UserDerefObjectCo(Window
);
659 TRACE("Leave NtUserScrollWindowEx, ret=%lu\n",_ret_
);