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
);
240 const RECT
*prcUnsafeScroll
,
241 const RECT
*prcUnsafeClip
,
243 LPRECT prcUnsafeUpdate
)
245 DECLARE_RETURN(DWORD
);
246 RECTL rcScroll
, rcClip
, rcUpdate
;
247 NTSTATUS Status
= STATUS_SUCCESS
;
250 TRACE("Enter NtUserScrollDC\n");
251 UserEnterExclusive();
257 ProbeForRead(prcUnsafeScroll
, sizeof(*prcUnsafeScroll
), 1);
258 rcScroll
= *prcUnsafeScroll
;
262 ProbeForRead(prcUnsafeClip
, sizeof(*prcUnsafeClip
), 1);
263 rcClip
= *prcUnsafeClip
;
267 ProbeForWrite(prcUnsafeUpdate
, sizeof(*prcUnsafeUpdate
), 1);
270 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
272 Status
= _SEH2_GetExceptionCode();
275 if (!NT_SUCCESS(Status
))
277 SetLastNtError(Status
);
281 Result
= UserScrollDC( hDC
,
284 prcUnsafeScroll
? &rcScroll
: 0,
285 prcUnsafeClip
? &rcClip
: 0,
288 prcUnsafeUpdate
? &rcUpdate
: NULL
);
291 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
299 *prcUnsafeUpdate
= rcUpdate
;
301 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
303 Status
= _SEH2_GetExceptionCode();
306 if (!NT_SUCCESS(Status
))
308 /* FIXME: SetLastError? */
309 /* FIXME: correct? We have already scrolled! */
317 TRACE("Leave NtUserScrollDC, ret=%lu\n",_ret_
);
323 * NtUserScrollWindowEx
330 NtUserScrollWindowEx(
334 const RECT
*prcUnsafeScroll
,
335 const RECT
*prcUnsafeClip
,
337 LPRECT prcUnsafeUpdate
,
340 RECTL rcScroll
, rcClip
, rcCaret
, rcUpdate
;
342 PWND Window
= NULL
, CaretWnd
;
344 PREGION RgnUpdate
= NULL
, RgnTemp
, RgnWinupd
= NULL
;
348 NTSTATUS Status
= STATUS_SUCCESS
;
349 DECLARE_RETURN(DWORD
);
350 USER_REFERENCE_ENTRY Ref
, CaretRef
;
352 TRACE("Enter NtUserScrollWindowEx\n");
353 UserEnterExclusive();
355 Window
= UserGetWindowObject(hWnd
);
356 if (!Window
|| !IntIsWindowDrawable(Window
))
358 Window
= NULL
; /* prevent deref at cleanup */
361 UserRefObjectCo(Window
, &Ref
);
363 IntGetClientRect(Window
, &rcClip
);
369 ProbeForRead(prcUnsafeScroll
, sizeof(*prcUnsafeScroll
), 1);
370 RECTL_bIntersectRect(&rcScroll
, &rcClip
, prcUnsafeScroll
);
377 ProbeForRead(prcUnsafeClip
, sizeof(*prcUnsafeClip
), 1);
378 RECTL_bIntersectRect(&rcClip
, &rcClip
, prcUnsafeClip
);
381 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
383 Status
= _SEH2_GetExceptionCode();
387 if (!NT_SUCCESS(Status
))
389 SetLastNtError(Status
);
393 if (rcClip
.right
<= rcClip
.left
|| rcClip
.bottom
<= rcClip
.top
||
394 (dx
== 0 && dy
== 0))
399 /* We must use a copy of the region, as we can't hold an exclusive lock
400 * on it while doing callouts to user-mode */
401 RgnUpdate
= IntSysCreateRectpRgn(0, 0, 0, 0);
404 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
410 RgnTemp
= REGION_LockRgn(hrgnUpdate
);
413 EngSetLastError(ERROR_INVALID_HANDLE
);
416 IntGdiCombineRgn(RgnUpdate
, RgnTemp
, NULL
, RGN_COPY
);
417 REGION_UnlockRgn(RgnTemp
);
420 /* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
421 if (flags
& SW_SCROLLWNDDCE
)
423 dcxflags
= DCX_USESTYLE
;
425 if (!(Window
->pcls
->style
& (CS_OWNDC
|CS_CLASSDC
)))
426 dcxflags
|= DCX_CACHE
; // AH??? wine~ If not Powned or with Class go Cheap!
428 if (flags
& SW_SCROLLCHILDREN
&& Window
->style
& WS_CLIPCHILDREN
)
429 dcxflags
|= DCX_CACHE
|DCX_NOCLIPCHILDREN
;
433 /* So in this case ScrollWindowEx uses Cache DC. */
434 dcxflags
= DCX_CACHE
|DCX_USESTYLE
;
435 if (flags
& SW_SCROLLCHILDREN
) dcxflags
|= DCX_NOCLIPCHILDREN
;
438 hDC
= UserGetDCEx(Window
, 0, dcxflags
);
441 /* FIXME: SetLastError? */
445 rdw_flags
= (flags
& SW_ERASE
) && (flags
& SW_INVALIDATE
) ? RDW_INVALIDATE
| RDW_ERASE
: RDW_INVALIDATE
;
448 hwndCaret
= co_IntFixCaret(Window
, &rcCaret
, flags
);
450 Result
= UserScrollDC( hDC
,
457 prcUnsafeUpdate
? &rcUpdate
: NULL
);
459 UserReleaseDC(Window
, hDC
, FALSE
);
462 * Take into account the fact that some damage may have occurred during
463 * the scroll. Keep a copy in hrgnWinupd to be added to hrngUpdate at the end.
466 RgnTemp
= IntSysCreateRectpRgn(0, 0, 0, 0);
469 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
473 if (co_IntGetUpdateRgn(Window
, RgnTemp
, FALSE
) != NULLREGION
)
475 PREGION RgnClip
= IntSysCreateRectpRgnIndirect(&rcClip
);
480 RgnWinupd
= IntSysCreateRectpRgn( 0, 0, 0, 0);
481 IntGdiCombineRgn( RgnWinupd
, RgnTemp
, 0, RGN_COPY
);
484 REGION_bOffsetRgn(RgnTemp
, dx
, dy
);
486 IntGdiCombineRgn(RgnTemp
, RgnTemp
, RgnClip
, RGN_AND
);
489 IntGdiCombineRgn( RgnWinupd
, RgnWinupd
, RgnTemp
, RGN_OR
);
491 co_UserRedrawWindow(Window
, NULL
, RgnTemp
, rdw_flags
);
493 REGION_Delete(RgnClip
);
496 REGION_Delete(RgnTemp
);
498 if (flags
& SW_SCROLLCHILDREN
)
503 USER_REFERENCE_ENTRY WndRef
;
507 IntGetClientOrigin(Window
, &ClientOrigin
);
509 for (Child
= Window
->spwndChild
; Child
; Child
= Child
->spwndNext
)
511 rcChild
= Child
->rcWindow
;
512 rcChild
.left
-= ClientOrigin
.x
;
513 rcChild
.top
-= ClientOrigin
.y
;
514 rcChild
.right
-= ClientOrigin
.x
;
515 rcChild
.bottom
-= ClientOrigin
.y
;
517 if (!prcUnsafeScroll
|| RECTL_bIntersectRect(&rcDummy
, &rcChild
, &rcScroll
))
519 UserRefObjectCo(Child
, &WndRef
);
521 if (Window
->spwndParent
== UserGetDesktopWindow()) // Window->spwndParent->fnid == FNID_DESKTOP )
522 lParam
= MAKELONG(Child
->rcClient
.left
, Child
->rcClient
.top
);
524 lParam
= MAKELONG(rcChild
.left
+ dx
, rcChild
.top
+ dy
);
526 /* wine sends WM_POSCHANGING, WM_POSCHANGED messages */
527 /* windows sometimes a WM_MOVE */
528 co_IntSendMessage(UserHMGetHandle(Child
), WM_MOVE
, 0, lParam
);
530 UserDerefObjectCo(Child
);
535 if (flags
& (SW_INVALIDATE
| SW_ERASE
))
537 co_UserRedrawWindow( Window
,
540 rdw_flags
| /* HACK */
541 ((flags
& SW_SCROLLCHILDREN
) ? RDW_ALLCHILDREN
: RDW_NOCHILDREN
) );
544 if (hwndCaret
&& (CaretWnd
= UserGetWindowObject(hwndCaret
)))
546 UserRefObjectCo(CaretWnd
, &CaretRef
);
548 co_IntSetCaretPos(rcCaret
.left
+ dx
, rcCaret
.top
+ dy
);
549 co_UserShowCaret(CaretWnd
);
551 UserDerefObjectCo(CaretWnd
);
558 /* Probe here, to not fail on invalid pointer before scrolling */
559 ProbeForWrite(prcUnsafeUpdate
, sizeof(*prcUnsafeUpdate
), 1);
560 *prcUnsafeUpdate
= rcUpdate
;
562 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
564 Status
= _SEH2_GetExceptionCode();
568 if (!NT_SUCCESS(Status
))
570 SetLastNtError(Status
);
578 if (hrgnUpdate
&& (_ret_
!= ERROR
))
580 /* Give everything back to the caller */
581 RgnTemp
= REGION_LockRgn(hrgnUpdate
);
582 /* The handle should still be valid */
585 IntGdiCombineRgn(RgnTemp
, RgnUpdate
, RgnWinupd
, RGN_OR
);
587 IntGdiCombineRgn(RgnTemp
, RgnUpdate
, NULL
, RGN_COPY
);
588 REGION_UnlockRgn(RgnTemp
);
593 REGION_Delete(RgnWinupd
);
598 REGION_Delete(RgnUpdate
);
602 UserDerefObjectCo(Window
);
604 TRACE("Leave NtUserScrollWindowEx, ret=%lu\n",_ret_
);