[NtUser] Fix Theme Non Client Painting.
[reactos.git] / 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 PTHRDCARETINFO CaretInfo;
18 PTHREADINFO pti;
19 PUSER_MESSAGE_QUEUE ThreadQueue;
20 HWND hWndCaret;
21 PWND WndCaret;
22
23 ASSERT_REFS_CO(Window);
24
25 pti = PsGetCurrentThreadWin32Thread();
26 ThreadQueue = pti->MessageQueue;
27 CaretInfo = &ThreadQueue->CaretInfo;
28 hWndCaret = CaretInfo->hWnd;
29
30 WndCaret = ValidateHwndNoErr(hWndCaret);
31
32 // FIXME: Check for WndCaret can be NULL
33 if (WndCaret == Window ||
34 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(Window, WndCaret)))
35 {
36 POINT pt, FromOffset, ToOffset;
37 RECTL rcCaret;
38
39 pt.x = CaretInfo->Pos.x;
40 pt.y = CaretInfo->Pos.y;
41 IntGetClientOrigin(WndCaret, &FromOffset);
42 IntGetClientOrigin(Window, &ToOffset);
43 rcCaret.left = pt.x;
44 rcCaret.top = pt.y;
45 rcCaret.right = pt.x + CaretInfo->Size.cx;
46 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
47 if (RECTL_bIntersectRect(lprc, lprc, &rcCaret))
48 {
49 co_UserHideCaret(0);
50 lprc->left = pt.x;
51 lprc->top = pt.y;
52 return hWndCaret;
53 }
54 }
55
56 return 0;
57 }
58
59 /*
60 Old GetUpdateRgn, for scrolls, see above note.
61 */
62 INT FASTCALL
63 co_IntGetUpdateRgn(PWND Window, PREGION Rgn, BOOL bErase)
64 {
65 int RegionType;
66 RECTL Rect;
67 PREGION UpdateRgn;
68
69 ASSERT_REFS_CO(Window);
70
71 if (bErase)
72 {
73 co_IntPaintWindows(Window, RDW_NOCHILDREN, FALSE);
74 }
75
76 Window->state &= ~WNDS_UPDATEDIRTY;
77
78 if (Window->hrgnUpdate == NULL)
79 {
80 REGION_SetRectRgn(Rgn, 0, 0, 0, 0);
81 return NULLREGION;
82 }
83
84 UpdateRgn = REGION_LockRgn(Window->hrgnUpdate);
85 if (!UpdateRgn)
86 return ERROR;
87
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);
94
95 return RegionType;
96 }
97
98 static
99 INT FASTCALL
100 UserScrollDC(
101 HDC hDC,
102 INT dx,
103 INT dy,
104 const RECTL *prcScroll,
105 const RECTL *prcClip,
106 HRGN hrgnUpdate,
107 PREGION RgnUpdate,
108 RECTL *prcUpdate)
109 {
110 PDC pDC;
111 RECTL rcScroll, rcClip, rcSrc, rcDst;
112 INT Result;
113
114 if (GdiGetClipBox(hDC, &rcClip) == ERROR)
115 {
116 ERR("GdiGetClipBox failed for HDC %p\n", hDC);
117 return ERROR;
118 }
119
120 rcScroll = rcClip;
121 if (prcClip)
122 {
123 RECTL_bIntersectRect(&rcClip, &rcClip, prcClip);
124 }
125
126 if (prcScroll)
127 {
128 rcScroll = *prcScroll;
129 RECTL_bIntersectRect(&rcSrc, &rcClip, prcScroll);
130 }
131 else
132 {
133 rcSrc = rcClip;
134 }
135
136 rcDst = rcSrc;
137 RECTL_vOffsetRect(&rcDst, dx, dy);
138 RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
139
140 if (!NtGdiBitBlt( hDC,
141 rcDst.left,
142 rcDst.top,
143 rcDst.right - rcDst.left,
144 rcDst.bottom - rcDst.top,
145 hDC,
146 rcDst.left - dx,
147 rcDst.top - dy,
148 SRCCOPY,
149 0,
150 0))
151 {
152 return ERROR;
153 }
154
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)
158 {
159 PREGION RgnOwn, RgnTmp;
160
161 pDC = DC_LockDc(hDC);
162 if (!pDC)
163 {
164 return ERROR;
165 }
166
167 if (hrgnUpdate)
168 {
169 NT_ASSERT(RgnUpdate == NULL);
170 RgnUpdate = REGION_LockRgn(hrgnUpdate);
171 if (!RgnUpdate)
172 {
173 DC_UnlockDc(pDC);
174 return ERROR;
175 }
176 }
177
178 /* Begin with the shifted and then clipped scroll rect */
179 rcDst = rcScroll;
180 RECTL_vOffsetRect(&rcDst, dx, dy);
181 RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
182 if (RgnUpdate)
183 {
184 RgnOwn = RgnUpdate;
185 REGION_SetRectRgn(RgnOwn, rcDst.left, rcDst.top, rcDst.right, rcDst.bottom);
186 }
187 else
188 {
189 RgnOwn = IntSysCreateRectpRgnIndirect(&rcDst);
190 }
191
192 /* Add the source rect */
193 RgnTmp = IntSysCreateRectpRgnIndirect(&rcSrc);
194 IntGdiCombineRgn(RgnOwn, RgnOwn, RgnTmp, RGN_OR);
195
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);
200
201 /* DO NOT Unlock DC while messing with prgnVis! */
202 DC_UnlockDc(pDC);
203
204 REGION_Delete(RgnTmp);
205
206 if (prcUpdate)
207 {
208 REGION_GetRgnBox(RgnOwn, prcUpdate);
209 }
210
211 if (hrgnUpdate)
212 {
213 REGION_UnlockRgn(RgnUpdate);
214 }
215 else if (!RgnUpdate)
216 {
217 REGION_Delete(RgnOwn);
218 }
219 }
220 else
221 Result = NULLREGION;
222
223 return Result;
224 }
225
226 DWORD
227 FASTCALL
228 IntScrollWindowEx(
229 PWND Window,
230 INT dx,
231 INT dy,
232 const RECT *prcScroll,
233 const RECT *prcClip,
234 HRGN hrgnUpdate,
235 LPRECT prcUpdate,
236 UINT flags)
237 {
238 INT Result;
239 RECTL rcScroll, rcClip, rcCaret;
240 PWND CaretWnd;
241 HDC hDC;
242 PREGION RgnUpdate = NULL, RgnTemp, RgnWinupd = NULL;
243 HWND hwndCaret;
244 DWORD dcxflags = 0;
245 int rdw_flags;
246 USER_REFERENCE_ENTRY CaretRef;
247
248 if (!Window || !IntIsWindowDrawable(Window))
249 {
250 return ERROR;
251 }
252
253 IntGetClientRect(Window, &rcClip);
254
255 if (prcScroll)
256 RECTL_bIntersectRect(&rcScroll, &rcClip, prcScroll);
257 else
258 rcScroll = rcClip;
259
260 if (prcClip)
261 RECTL_bIntersectRect(&rcClip, &rcClip, prcClip);
262
263 if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
264 (dx == 0 && dy == 0))
265 {
266 return NULLREGION;
267 }
268
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);
272 if(!RgnUpdate)
273 {
274 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
275 return ERROR;
276 }
277
278 if (hrgnUpdate)
279 {
280 RgnTemp = REGION_LockRgn(hrgnUpdate);
281 if (!RgnTemp)
282 {
283 EngSetLastError(ERROR_INVALID_HANDLE);
284 Result = ERROR;
285 goto Cleanup;
286 }
287 IntGdiCombineRgn(RgnUpdate, RgnTemp, NULL, RGN_COPY);
288 REGION_UnlockRgn(RgnTemp);
289 }
290
291 /* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
292 if (flags & SW_SCROLLWNDDCE)
293 {
294 dcxflags = DCX_USESTYLE;
295
296 if (!(Window->pcls->style & (CS_OWNDC|CS_CLASSDC)))
297 dcxflags |= DCX_CACHE; // AH??? wine~ If not Powned or with Class go Cheap!
298
299 if (flags & SW_SCROLLCHILDREN && Window->style & WS_CLIPCHILDREN)
300 dcxflags |= DCX_CACHE|DCX_NOCLIPCHILDREN;
301 }
302 else
303 {
304 /* So in this case ScrollWindowEx uses Cache DC. */
305 dcxflags = DCX_CACHE|DCX_USESTYLE;
306 if (flags & SW_SCROLLCHILDREN) dcxflags |= DCX_NOCLIPCHILDREN;
307 }
308
309 hDC = UserGetDCEx(Window, 0, dcxflags);
310 if (!hDC)
311 {
312 /* FIXME: SetLastError? */
313 Result = ERROR;
314 goto Cleanup;
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 Result = ERROR;
343 goto Cleanup;
344 }
345
346 if (co_IntGetUpdateRgn(Window, RgnTemp, FALSE) != NULLREGION)
347 {
348 PREGION RgnClip = IntSysCreateRectpRgnIndirect(&rcClip);
349 if (RgnClip)
350 {
351 if (hrgnUpdate)
352 {
353 RgnWinupd = IntSysCreateRectpRgn(0, 0, 0, 0);
354 // FIXME: What to do if RgnWinupd == NULL??
355 IntGdiCombineRgn( RgnWinupd, RgnTemp, 0, RGN_COPY);
356 }
357
358 REGION_bOffsetRgn(RgnTemp, dx, dy);
359
360 IntGdiCombineRgn(RgnTemp, RgnTemp, RgnClip, RGN_AND);
361
362 if (hrgnUpdate)
363 IntGdiCombineRgn( RgnWinupd, RgnWinupd, RgnTemp, RGN_OR );
364
365 co_UserRedrawWindow(Window, NULL, RgnTemp, rdw_flags );
366
367 REGION_Delete(RgnClip);
368 }
369 }
370 REGION_Delete(RgnTemp);
371
372 if (flags & SW_SCROLLCHILDREN)
373 {
374 PWND Child;
375 RECTL rcChild;
376 POINT ClientOrigin;
377 USER_REFERENCE_ENTRY WndRef;
378 RECTL rcDummy;
379 LPARAM lParam;
380
381 IntGetClientOrigin(Window, &ClientOrigin);
382
383 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
384 {
385 rcChild = Child->rcWindow;
386 RECTL_vOffsetRect(&rcChild, -ClientOrigin.x, -ClientOrigin.y);
387
388 if (!prcScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll))
389 {
390 UserRefObjectCo(Child, &WndRef);
391
392 if (UserIsDesktopWindow(Window->spwndParent))
393 lParam = MAKELONG(Child->rcClient.left, Child->rcClient.top);
394 else
395 lParam = MAKELONG(rcChild.left + dx, rcChild.top + dy);
396
397 /* wine sends WM_POSCHANGING, WM_POSCHANGED messages */
398 /* windows sometimes a WM_MOVE */
399 co_IntSendMessage(UserHMGetHandle(Child), WM_MOVE, 0, lParam);
400
401 UserDerefObjectCo(Child);
402 }
403 }
404 }
405
406 if (flags & (SW_INVALIDATE | SW_ERASE))
407 {
408 co_UserRedrawWindow( Window,
409 NULL,
410 RgnUpdate,
411 rdw_flags | /* HACK */
412 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : RDW_NOCHILDREN) );
413 }
414
415 if (hwndCaret && (CaretWnd = UserGetWindowObject(hwndCaret)))
416 {
417 UserRefObjectCo(CaretWnd, &CaretRef);
418
419 co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
420 co_UserShowCaret(CaretWnd);
421
422 UserDerefObjectCo(CaretWnd);
423 }
424
425 if (hrgnUpdate && (Result != ERROR))
426 {
427 /* Give everything back to the caller */
428 RgnTemp = REGION_LockRgn(hrgnUpdate);
429 /* The handle should still be valid */
430 ASSERT(RgnTemp);
431 if (RgnWinupd)
432 IntGdiCombineRgn(RgnTemp, RgnUpdate, RgnWinupd, RGN_OR);
433 else
434 IntGdiCombineRgn(RgnTemp, RgnUpdate, NULL, RGN_COPY);
435 REGION_UnlockRgn(RgnTemp);
436 }
437
438 Cleanup:
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 DWORD Result;
482 NTSTATUS Status = STATUS_SUCCESS;
483 RECTL rcScroll, rcClip, rcUpdate;
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
511 if (!NT_SUCCESS(Status))
512 {
513 SetLastNtError(Status);
514 RETURN(FALSE);
515 }
516
517 Result = UserScrollDC( hDC,
518 dx,
519 dy,
520 prcUnsafeScroll ? &rcScroll : NULL,
521 prcUnsafeClip ? &rcClip : NULL,
522 hrgnUpdate,
523 NULL,
524 prcUnsafeUpdate ? &rcUpdate : NULL);
525 if(Result == ERROR)
526 {
527 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
528 RETURN(FALSE);
529 }
530
531 if (prcUnsafeUpdate)
532 {
533 _SEH2_TRY
534 {
535 *prcUnsafeUpdate = rcUpdate;
536 }
537 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
538 {
539 Status = _SEH2_GetExceptionCode();
540 }
541 _SEH2_END;
542
543 if (!NT_SUCCESS(Status))
544 {
545 /* FIXME: SetLastError? */
546 /* FIXME: correct? We have already scrolled! */
547 RETURN(FALSE);
548 }
549 }
550
551 RETURN(TRUE);
552
553 CLEANUP:
554 TRACE("Leave NtUserScrollDC, ret=%lu\n",_ret_);
555 UserLeave();
556 END_CLEANUP;
557 }
558
559 /*
560 * NtUserScrollWindowEx
561 *
562 * Status
563 * @implemented
564 */
565
566 DWORD APIENTRY
567 NtUserScrollWindowEx(
568 HWND hWnd,
569 INT dx,
570 INT dy,
571 const RECT *prcUnsafeScroll,
572 const RECT *prcUnsafeClip,
573 HRGN hrgnUpdate,
574 LPRECT prcUnsafeUpdate,
575 UINT flags)
576 {
577 DECLARE_RETURN(DWORD);
578 INT Result;
579 NTSTATUS Status = STATUS_SUCCESS;
580 PWND Window = NULL;
581 RECTL rcScroll, rcClip, rcUpdate;
582 USER_REFERENCE_ENTRY Ref;
583
584 TRACE("Enter NtUserScrollWindowEx\n");
585 UserEnterExclusive();
586
587 Window = UserGetWindowObject(hWnd);
588 if (!Window || !IntIsWindowDrawable(Window))
589 {
590 Window = NULL; /* prevent deref at cleanup */
591 RETURN(ERROR);
592 }
593 UserRefObjectCo(Window, &Ref);
594
595 _SEH2_TRY
596 {
597 if (prcUnsafeScroll)
598 {
599 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
600 rcScroll = *prcUnsafeScroll;
601 }
602
603 if (prcUnsafeClip)
604 {
605 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
606 rcClip = *prcUnsafeClip;
607 }
608 }
609 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
610 {
611 Status = _SEH2_GetExceptionCode();
612 }
613 _SEH2_END;
614
615 if (!NT_SUCCESS(Status))
616 {
617 SetLastNtError(Status);
618 RETURN(ERROR);
619 }
620
621 Result = IntScrollWindowEx(Window,
622 dx, dy,
623 prcUnsafeScroll ? &rcScroll : NULL,
624 prcUnsafeClip ? &rcClip : NULL,
625 hrgnUpdate,
626 prcUnsafeUpdate ? &rcUpdate : NULL,
627 flags);
628
629 if (prcUnsafeUpdate)
630 {
631 _SEH2_TRY
632 {
633 /* Probe here, to not fail on invalid pointer before scrolling */
634 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
635 *prcUnsafeUpdate = rcUpdate;
636 }
637 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
638 {
639 Status = _SEH2_GetExceptionCode();
640 }
641 _SEH2_END;
642
643 if (!NT_SUCCESS(Status))
644 {
645 SetLastNtError(Status);
646 RETURN(ERROR);
647 }
648 }
649
650 RETURN(Result);
651
652 CLEANUP:
653 if (Window)
654 UserDerefObjectCo(Window);
655
656 TRACE("Leave NtUserScrollWindowEx, ret=%lu\n",_ret_);
657 UserLeave();
658 END_CLEANUP;
659 }
660
661 /* EOF */