[Win32k]
[reactos.git] / reactos / win32ss / user / ntuser / scrollex.c
1 /*
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)
7 */
8
9 #include <win32k.h>
10
11 DBG_DEFAULT_CHANNEL(UserPainting);
12
13 static
14 HWND FASTCALL
15 co_IntFixCaret(PWND Window, RECTL *lprc, UINT flags)
16 {
17 PDESKTOP Desktop;
18 PTHRDCARETINFO CaretInfo;
19 PTHREADINFO pti;
20 PUSER_MESSAGE_QUEUE ActiveMessageQueue;
21 HWND hWndCaret;
22 PWND WndCaret;
23
24 ASSERT_REFS_CO(Window);
25
26 pti = PsGetCurrentThreadWin32Thread();
27 Desktop = pti->rpdesk;
28 ActiveMessageQueue = Desktop->ActiveMessageQueue;
29 if (!ActiveMessageQueue) return 0;
30 CaretInfo = ActiveMessageQueue->CaretInfo;
31 hWndCaret = CaretInfo->hWnd;
32
33 WndCaret = ValidateHwndNoErr(hWndCaret);
34
35 // FIXME: Check for WndCaret can be NULL
36 if (WndCaret == Window ||
37 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(Window, WndCaret)))
38 {
39 POINT pt, FromOffset, ToOffset;
40 RECTL rcCaret;
41
42 pt.x = CaretInfo->Pos.x;
43 pt.y = CaretInfo->Pos.y;
44 IntGetClientOrigin(WndCaret, &FromOffset);
45 IntGetClientOrigin(Window, &ToOffset);
46 rcCaret.left = pt.x;
47 rcCaret.top = pt.y;
48 rcCaret.right = pt.x + CaretInfo->Size.cx;
49 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
50 if (RECTL_bIntersectRect(lprc, lprc, &rcCaret))
51 {
52 co_UserHideCaret(0);
53 lprc->left = pt.x;
54 lprc->top = pt.y;
55 return hWndCaret;
56 }
57 }
58
59 return 0;
60 }
61
62 /*
63 Old GetUpdateRgn, for scrolls, see above note.
64 */
65 INT FASTCALL
66 co_IntGetUpdateRgn(PWND Window, PREGION Rgn, BOOL bErase)
67 {
68 int RegionType;
69 RECTL Rect;
70 PREGION UpdateRgn;
71
72 ASSERT_REFS_CO(Window);
73
74 if (bErase)
75 {
76 co_IntPaintWindows(Window, RDW_NOCHILDREN, FALSE);
77 }
78
79 Window->state &= ~WNDS_UPDATEDIRTY;
80
81 if (Window->hrgnUpdate == NULL)
82 {
83 REGION_SetRectRgn(Rgn, 0, 0, 0, 0);
84 return NULLREGION;
85 }
86
87 UpdateRgn = REGION_LockRgn(Window->hrgnUpdate);
88 if (!UpdateRgn)
89 return ERROR;
90
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);
97
98 return RegionType;
99 }
100
101 static
102 INT FASTCALL
103 UserScrollDC(
104 HDC hDC,
105 INT dx,
106 INT dy,
107 const RECTL *prcScroll,
108 const RECTL *prcClip,
109 HRGN hrgnUpdate,
110 PREGION RgnUpdate,
111 RECTL *prcUpdate)
112 {
113 PDC pDC;
114 RECTL rcScroll, rcClip, rcSrc, rcDst;
115 INT Result;
116
117 if (GdiGetClipBox(hDC, &rcClip) == ERROR)
118 {
119 ERR("GdiGetClipBox failed for HDC %p\n", hDC);
120 return ERROR;
121 }
122
123 rcScroll = rcClip;
124 if (prcClip)
125 {
126 RECTL_bIntersectRect(&rcClip, &rcClip, prcClip);
127 }
128
129 if (prcScroll)
130 {
131 rcScroll = *prcScroll;
132 RECTL_bIntersectRect(&rcSrc, &rcClip, prcScroll);
133 }
134 else
135 {
136 rcSrc = rcClip;
137 }
138
139 rcDst = rcSrc;
140 RECTL_vOffsetRect(&rcDst, dx, dy);
141 RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
142
143 if (!NtGdiBitBlt( hDC,
144 rcDst.left,
145 rcDst.top,
146 rcDst.right - rcDst.left,
147 rcDst.bottom - rcDst.top,
148 hDC,
149 rcDst.left - dx,
150 rcDst.top - dy,
151 SRCCOPY,
152 0,
153 0))
154 {
155 return ERROR;
156 }
157
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)
161 {
162 PREGION RgnOwn, RgnTmp;
163
164 pDC = DC_LockDc(hDC);
165 if (!pDC)
166 {
167 return ERROR;
168 }
169
170 if (hrgnUpdate)
171 {
172 NT_ASSERT(RgnUpdate == NULL);
173 RgnUpdate = REGION_LockRgn(hrgnUpdate);
174 if (!RgnUpdate)
175 {
176 DC_UnlockDc(pDC);
177 return ERROR;
178 }
179 }
180
181 /* Begin with the shifted and then clipped scroll rect */
182 rcDst = rcScroll;
183 RECTL_vOffsetRect(&rcDst, dx, dy);
184 RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
185 if (RgnUpdate)
186 {
187 RgnOwn = RgnUpdate;
188 REGION_SetRectRgn(RgnOwn, rcDst.left, rcDst.top, rcDst.right, rcDst.bottom);
189 }
190 else
191 {
192 RgnOwn = IntSysCreateRectpRgnIndirect(&rcDst);
193 }
194
195 /* Add the source rect */
196 RgnTmp = IntSysCreateRectpRgnIndirect(&rcSrc);
197 IntGdiCombineRgn(RgnOwn, RgnOwn, RgnTmp, RGN_OR);
198
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);
203
204 /* DO NOT Unlock DC while messing with prgnVis! */
205 DC_UnlockDc(pDC);
206
207 REGION_Delete(RgnTmp);
208
209 if (prcUpdate)
210 {
211 REGION_GetRgnBox(RgnOwn, prcUpdate);
212 }
213
214 if (hrgnUpdate)
215 {
216 REGION_UnlockRgn(RgnUpdate);
217 }
218 else if (!RgnUpdate)
219 {
220 REGION_Delete(RgnOwn);
221 }
222 }
223 else
224 Result = NULLREGION;
225
226 return Result;
227 }
228
229 DWORD
230 FASTCALL
231 IntScrollWindowEx(
232 PWND Window,
233 INT dx,
234 INT dy,
235 const RECT *prcScroll,
236 const RECT *prcClip,
237 HRGN hrgnUpdate,
238 LPRECT prcUpdate,
239 UINT flags)
240 {
241 RECTL rcScroll, rcClip, rcCaret;
242 INT Result;
243 PWND CaretWnd;
244 HDC hDC;
245 PREGION RgnUpdate = NULL, RgnTemp, RgnWinupd = NULL;
246 HWND hwndCaret;
247 DWORD dcxflags = 0;
248 int rdw_flags;
249 USER_REFERENCE_ENTRY CaretRef;
250
251 IntGetClientRect(Window, &rcClip);
252
253 if (prcScroll)
254 {
255 RECTL_bIntersectRect(&rcScroll, &rcClip, prcScroll);
256 }
257 else
258 rcScroll = rcClip;
259
260 if (prcClip)
261 {
262 RECTL_bIntersectRect(&rcClip, &rcClip, prcClip);
263 }
264
265 if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
266 (dx == 0 && dy == 0))
267 {
268 return NULLREGION;
269 }
270
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);
274 if(!RgnUpdate)
275 {
276 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
277 return ERROR;
278 }
279
280 if (hrgnUpdate)
281 {
282 RgnTemp = REGION_LockRgn(hrgnUpdate);
283 if (!RgnTemp)
284 {
285 EngSetLastError(ERROR_INVALID_HANDLE);
286 return ERROR;
287 }
288 IntGdiCombineRgn(RgnUpdate, RgnTemp, NULL, RGN_COPY);
289 REGION_UnlockRgn(RgnTemp);
290 }
291
292 /* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
293 if (flags & SW_SCROLLWNDDCE)
294 {
295 dcxflags = DCX_USESTYLE;
296
297 if (!(Window->pcls->style & (CS_OWNDC|CS_CLASSDC)))
298 dcxflags |= DCX_CACHE; // AH??? wine~ If not Powned or with Class go Cheap!
299
300 if (flags & SW_SCROLLCHILDREN && Window->style & WS_CLIPCHILDREN)
301 dcxflags |= DCX_CACHE|DCX_NOCLIPCHILDREN;
302 }
303 else
304 {
305 /* So in this case ScrollWindowEx uses Cache DC. */
306 dcxflags = DCX_CACHE|DCX_USESTYLE;
307 if (flags & SW_SCROLLCHILDREN) dcxflags |= DCX_NOCLIPCHILDREN;
308 }
309
310 hDC = UserGetDCEx(Window, 0, dcxflags);
311 if (!hDC)
312 {
313 /* FIXME: SetLastError? */
314 return ERROR;
315 }
316
317 rdw_flags = (flags & SW_ERASE) && (flags & SW_INVALIDATE) ? RDW_INVALIDATE | RDW_ERASE : RDW_INVALIDATE ;
318
319 rcCaret = rcScroll;
320 hwndCaret = co_IntFixCaret(Window, &rcCaret, flags);
321
322 Result = UserScrollDC( hDC,
323 dx,
324 dy,
325 &rcScroll,
326 &rcClip,
327 NULL,
328 RgnUpdate,
329 prcUpdate);
330
331 UserReleaseDC(Window, hDC, FALSE);
332
333 /*
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.
336 */
337
338 RgnTemp = IntSysCreateRectpRgn(0, 0, 0, 0);
339 if (!RgnTemp)
340 {
341 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
342 return ERROR;
343 }
344
345 if (co_IntGetUpdateRgn(Window, RgnTemp, FALSE) != NULLREGION)
346 {
347 PREGION RgnClip = IntSysCreateRectpRgnIndirect(&rcClip);
348 if (RgnClip)
349 {
350 if (hrgnUpdate)
351 {
352 RgnWinupd = IntSysCreateRectpRgn( 0, 0, 0, 0);
353 IntGdiCombineRgn( RgnWinupd, RgnTemp, 0, RGN_COPY);
354 }
355
356 REGION_bOffsetRgn(RgnTemp, dx, dy);
357
358 IntGdiCombineRgn(RgnTemp, RgnTemp, RgnClip, RGN_AND);
359
360 if (hrgnUpdate)
361 IntGdiCombineRgn( RgnWinupd, RgnWinupd, RgnTemp, RGN_OR );
362
363 co_UserRedrawWindow(Window, NULL, RgnTemp, rdw_flags );
364
365 REGION_Delete(RgnClip);
366 }
367 }
368 REGION_Delete(RgnTemp);
369
370 if (flags & SW_SCROLLCHILDREN)
371 {
372 PWND Child;
373 RECTL rcChild;
374 POINT ClientOrigin;
375 USER_REFERENCE_ENTRY WndRef;
376 RECTL rcDummy;
377 LPARAM lParam;
378
379 IntGetClientOrigin(Window, &ClientOrigin);
380
381 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
382 {
383 rcChild = Child->rcWindow;
384 rcChild.left -= ClientOrigin.x;
385 rcChild.top -= ClientOrigin.y;
386 rcChild.right -= ClientOrigin.x;
387 rcChild.bottom -= ClientOrigin.y;
388
389 if (!prcScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll))
390 {
391 UserRefObjectCo(Child, &WndRef);
392
393 if (Window->spwndParent == UserGetDesktopWindow()) // Window->spwndParent->fnid == FNID_DESKTOP )
394 lParam = MAKELONG(Child->rcClient.left, Child->rcClient.top);
395 else
396 lParam = MAKELONG(rcChild.left + dx, rcChild.top + dy);
397
398 /* wine sends WM_POSCHANGING, WM_POSCHANGED messages */
399 /* windows sometimes a WM_MOVE */
400 co_IntSendMessage(UserHMGetHandle(Child), WM_MOVE, 0, lParam);
401
402 UserDerefObjectCo(Child);
403 }
404 }
405 }
406
407 if (flags & (SW_INVALIDATE | SW_ERASE))
408 {
409 co_UserRedrawWindow( Window,
410 NULL,
411 RgnUpdate,
412 rdw_flags | /* HACK */
413 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : RDW_NOCHILDREN) );
414 }
415
416 if (hwndCaret && (CaretWnd = UserGetWindowObject(hwndCaret)))
417 {
418 UserRefObjectCo(CaretWnd, &CaretRef);
419
420 co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
421 co_UserShowCaret(CaretWnd);
422
423 UserDerefObjectCo(CaretWnd);
424 }
425
426 if (hrgnUpdate && (Result != ERROR))
427 {
428 /* Give everything back to the caller */
429 RgnTemp = REGION_LockRgn(hrgnUpdate);
430 /* The handle should still be valid */
431 ASSERT(RgnTemp);
432 if (RgnWinupd)
433 IntGdiCombineRgn(RgnTemp, RgnUpdate, RgnWinupd, RGN_OR);
434 else
435 IntGdiCombineRgn(RgnTemp, RgnUpdate, NULL, RGN_COPY);
436 REGION_UnlockRgn(RgnTemp);
437 }
438
439 if (RgnWinupd)
440 {
441 REGION_Delete(RgnWinupd);
442 }
443
444 if (RgnUpdate)
445 {
446 REGION_Delete(RgnUpdate);
447 }
448
449 return Result;
450 }
451
452
453 BOOL FASTCALL
454 IntScrollWindow(PWND pWnd,
455 int dx,
456 int dy,
457 CONST RECT *lpRect,
458 CONST RECT *prcClip)
459 {
460 return IntScrollWindowEx( pWnd, dx, dy, lpRect, prcClip, 0, NULL,
461 (lpRect ? 0 : SW_SCROLLCHILDREN) | (SW_ERASE|SW_INVALIDATE|SW_SCROLLWNDDCE)) != ERROR;
462 }
463
464 /*
465 * NtUserScrollDC
466 *
467 * Status
468 * @implemented
469 */
470 BOOL APIENTRY
471 NtUserScrollDC(
472 HDC hDC,
473 INT dx,
474 INT dy,
475 const RECT *prcUnsafeScroll,
476 const RECT *prcUnsafeClip,
477 HRGN hrgnUpdate,
478 LPRECT prcUnsafeUpdate)
479 {
480 DECLARE_RETURN(DWORD);
481 RECTL rcScroll, rcClip, rcUpdate;
482 NTSTATUS Status = STATUS_SUCCESS;
483 DWORD Result;
484
485 TRACE("Enter NtUserScrollDC\n");
486 UserEnterExclusive();
487
488 _SEH2_TRY
489 {
490 if (prcUnsafeScroll)
491 {
492 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
493 rcScroll = *prcUnsafeScroll;
494 }
495 if (prcUnsafeClip)
496 {
497 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
498 rcClip = *prcUnsafeClip;
499 }
500 if (prcUnsafeUpdate)
501 {
502 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
503 }
504 }
505 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
506 {
507 Status = _SEH2_GetExceptionCode();
508 }
509 _SEH2_END
510 if (!NT_SUCCESS(Status))
511 {
512 SetLastNtError(Status);
513 RETURN(FALSE);
514 }
515
516 Result = UserScrollDC( hDC,
517 dx,
518 dy,
519 prcUnsafeScroll? &rcScroll : 0,
520 prcUnsafeClip? &rcClip : 0,
521 hrgnUpdate,
522 NULL,
523 prcUnsafeUpdate? &rcUpdate : NULL);
524 if(Result == ERROR)
525 {
526 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
527 RETURN(FALSE);
528 }
529
530 if (prcUnsafeUpdate)
531 {
532 _SEH2_TRY
533 {
534 *prcUnsafeUpdate = rcUpdate;
535 }
536 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
537 {
538 Status = _SEH2_GetExceptionCode();
539 }
540 _SEH2_END
541 if (!NT_SUCCESS(Status))
542 {
543 /* FIXME: SetLastError? */
544 /* FIXME: correct? We have already scrolled! */
545 RETURN(FALSE);
546 }
547 }
548
549 RETURN(TRUE);
550
551 CLEANUP:
552 TRACE("Leave NtUserScrollDC, ret=%lu\n",_ret_);
553 UserLeave();
554 END_CLEANUP;
555 }
556
557 /*
558 * NtUserScrollWindowEx
559 *
560 * Status
561 * @implemented
562 */
563
564 DWORD APIENTRY
565 NtUserScrollWindowEx(
566 HWND hWnd,
567 INT dx,
568 INT dy,
569 const RECT *prcUnsafeScroll,
570 const RECT *prcUnsafeClip,
571 HRGN hrgnUpdate,
572 LPRECT prcUnsafeUpdate,
573 UINT flags)
574 {
575 RECTL rcScroll, rcClip, rcCaret, rcUpdate;
576 INT Result;
577 PWND Window = NULL, CaretWnd;
578 HDC hDC;
579 PREGION RgnUpdate = NULL, RgnTemp, RgnWinupd = NULL;
580 HWND hwndCaret;
581 DWORD dcxflags = 0;
582 int rdw_flags;
583 NTSTATUS Status = STATUS_SUCCESS;
584 DECLARE_RETURN(DWORD);
585 USER_REFERENCE_ENTRY Ref, CaretRef;
586
587 TRACE("Enter NtUserScrollWindowEx\n");
588 UserEnterExclusive();
589
590 Window = UserGetWindowObject(hWnd);
591 if (!Window || !IntIsWindowDrawable(Window))
592 {
593 Window = NULL; /* prevent deref at cleanup */
594 RETURN( ERROR);
595 }
596 UserRefObjectCo(Window, &Ref);
597
598 IntGetClientRect(Window, &rcClip);
599
600 _SEH2_TRY
601 {
602 if (prcUnsafeScroll)
603 {
604 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
605 RECTL_bIntersectRect(&rcScroll, &rcClip, prcUnsafeScroll);
606 }
607 else
608 rcScroll = rcClip;
609
610 if (prcUnsafeClip)
611 {
612 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
613 RECTL_bIntersectRect(&rcClip, &rcClip, prcUnsafeClip);
614 }
615 }
616 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
617 {
618 Status = _SEH2_GetExceptionCode();
619 }
620 _SEH2_END
621
622 if (!NT_SUCCESS(Status))
623 {
624 SetLastNtError(Status);
625 RETURN(ERROR);
626 }
627
628 if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
629 (dx == 0 && dy == 0))
630 {
631 RETURN(NULLREGION);
632 }
633
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);
637 if(!RgnUpdate)
638 {
639 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
640 RETURN(ERROR);
641 }
642
643 if (hrgnUpdate)
644 {
645 RgnTemp = REGION_LockRgn(hrgnUpdate);
646 if (!RgnTemp)
647 {
648 EngSetLastError(ERROR_INVALID_HANDLE);
649 RETURN(ERROR);
650 }
651 IntGdiCombineRgn(RgnUpdate, RgnTemp, NULL, RGN_COPY);
652 REGION_UnlockRgn(RgnTemp);
653 }
654
655 /* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
656 if (flags & SW_SCROLLWNDDCE)
657 {
658 dcxflags = DCX_USESTYLE;
659
660 if (!(Window->pcls->style & (CS_OWNDC|CS_CLASSDC)))
661 dcxflags |= DCX_CACHE; // AH??? wine~ If not Powned or with Class go Cheap!
662
663 if (flags & SW_SCROLLCHILDREN && Window->style & WS_CLIPCHILDREN)
664 dcxflags |= DCX_CACHE|DCX_NOCLIPCHILDREN;
665 }
666 else
667 {
668 /* So in this case ScrollWindowEx uses Cache DC. */
669 dcxflags = DCX_CACHE|DCX_USESTYLE;
670 if (flags & SW_SCROLLCHILDREN) dcxflags |= DCX_NOCLIPCHILDREN;
671 }
672
673 hDC = UserGetDCEx(Window, 0, dcxflags);
674 if (!hDC)
675 {
676 /* FIXME: SetLastError? */
677 RETURN(ERROR);
678 }
679
680 rdw_flags = (flags & SW_ERASE) && (flags & SW_INVALIDATE) ? RDW_INVALIDATE | RDW_ERASE : RDW_INVALIDATE ;
681
682 rcCaret = rcScroll;
683 hwndCaret = co_IntFixCaret(Window, &rcCaret, flags);
684
685 Result = UserScrollDC( hDC,
686 dx,
687 dy,
688 &rcScroll,
689 &rcClip,
690 NULL,
691 RgnUpdate,
692 prcUnsafeUpdate? &rcUpdate : NULL);
693
694 UserReleaseDC(Window, hDC, FALSE);
695
696 /*
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.
699 */
700
701 RgnTemp = IntSysCreateRectpRgn(0, 0, 0, 0);
702 if (!RgnTemp)
703 {
704 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
705 RETURN(ERROR);
706 }
707
708 if (co_IntGetUpdateRgn(Window, RgnTemp, FALSE) != NULLREGION)
709 {
710 PREGION RgnClip = IntSysCreateRectpRgnIndirect(&rcClip);
711 if (RgnClip)
712 {
713 if (hrgnUpdate)
714 {
715 RgnWinupd = IntSysCreateRectpRgn( 0, 0, 0, 0);
716 IntGdiCombineRgn( RgnWinupd, RgnTemp, 0, RGN_COPY);
717 }
718
719 REGION_bOffsetRgn(RgnTemp, dx, dy);
720
721 IntGdiCombineRgn(RgnTemp, RgnTemp, RgnClip, RGN_AND);
722
723 if (hrgnUpdate)
724 IntGdiCombineRgn( RgnWinupd, RgnWinupd, RgnTemp, RGN_OR );
725
726 co_UserRedrawWindow(Window, NULL, RgnTemp, rdw_flags );
727
728 REGION_Delete(RgnClip);
729 }
730 }
731 REGION_Delete(RgnTemp);
732
733 if (flags & SW_SCROLLCHILDREN)
734 {
735 PWND Child;
736 RECTL rcChild;
737 POINT ClientOrigin;
738 USER_REFERENCE_ENTRY WndRef;
739 RECTL rcDummy;
740 LPARAM lParam;
741
742 IntGetClientOrigin(Window, &ClientOrigin);
743
744 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
745 {
746 rcChild = Child->rcWindow;
747 rcChild.left -= ClientOrigin.x;
748 rcChild.top -= ClientOrigin.y;
749 rcChild.right -= ClientOrigin.x;
750 rcChild.bottom -= ClientOrigin.y;
751
752 if (!prcUnsafeScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll))
753 {
754 UserRefObjectCo(Child, &WndRef);
755
756 if (Window->spwndParent == UserGetDesktopWindow()) // Window->spwndParent->fnid == FNID_DESKTOP )
757 lParam = MAKELONG(Child->rcClient.left, Child->rcClient.top);
758 else
759 lParam = MAKELONG(rcChild.left + dx, rcChild.top + dy);
760
761 /* wine sends WM_POSCHANGING, WM_POSCHANGED messages */
762 /* windows sometimes a WM_MOVE */
763 co_IntSendMessage(UserHMGetHandle(Child), WM_MOVE, 0, lParam);
764
765 UserDerefObjectCo(Child);
766 }
767 }
768 }
769
770 if (flags & (SW_INVALIDATE | SW_ERASE))
771 {
772 co_UserRedrawWindow( Window,
773 NULL,
774 RgnUpdate,
775 rdw_flags | /* HACK */
776 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : RDW_NOCHILDREN) );
777 }
778
779 if (hwndCaret && (CaretWnd = UserGetWindowObject(hwndCaret)))
780 {
781 UserRefObjectCo(CaretWnd, &CaretRef);
782
783 co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
784 co_UserShowCaret(CaretWnd);
785
786 UserDerefObjectCo(CaretWnd);
787 }
788
789 if (prcUnsafeUpdate)
790 {
791 _SEH2_TRY
792 {
793 /* Probe here, to not fail on invalid pointer before scrolling */
794 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
795 *prcUnsafeUpdate = rcUpdate;
796 }
797 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
798 {
799 Status = _SEH2_GetExceptionCode();
800 }
801 _SEH2_END
802
803 if (!NT_SUCCESS(Status))
804 {
805 SetLastNtError(Status);
806 RETURN(ERROR);
807 }
808 }
809
810 RETURN(Result);
811
812 CLEANUP:
813 if (hrgnUpdate && (_ret_ != ERROR))
814 {
815 /* Give everything back to the caller */
816 RgnTemp = REGION_LockRgn(hrgnUpdate);
817 /* The handle should still be valid */
818 ASSERT(RgnTemp);
819 if (RgnWinupd)
820 IntGdiCombineRgn(RgnTemp, RgnUpdate, RgnWinupd, RGN_OR);
821 else
822 IntGdiCombineRgn(RgnTemp, RgnUpdate, NULL, RGN_COPY);
823 REGION_UnlockRgn(RgnTemp);
824 }
825
826 if (RgnWinupd)
827 {
828 REGION_Delete(RgnWinupd);
829 }
830
831 if (RgnUpdate)
832 {
833 REGION_Delete(RgnUpdate);
834 }
835
836 if (Window)
837 UserDerefObjectCo(Window);
838
839 TRACE("Leave NtUserScrollWindowEx, ret=%lu\n",_ret_);
840 UserLeave();
841 END_CLEANUP;
842 }
843
844
845 /* EOF */