[Win32k]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / painting.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window painting function
5 * FILE: subsys/win32k/ntuser/painting.c
6 * PROGRAMER: Filip Navara (xnavara@volny.cz)
7 * REVISION HISTORY:
8 * 06/06/2001 Created (?)
9 * 18/11/2003 Complete rewrite
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <w32k.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* PRIVATE FUNCTIONS **********************************************************/
20
21 /**
22 * @name IntIntersectWithParents
23 *
24 * Intersect window rectangle with all parent client rectangles.
25 *
26 * @param Child
27 * Pointer to child window to start intersecting from.
28 * @param WindowRect
29 * Pointer to rectangle that we want to intersect in screen
30 * coordinates on input and intersected rectangle on output (if TRUE
31 * is returned).
32 *
33 * @return
34 * If any parent is minimized or invisible or the resulting rectangle
35 * is empty then FALSE is returned. Otherwise TRUE is returned.
36 */
37
38 BOOL FASTCALL
39 IntIntersectWithParents(PWINDOW_OBJECT Child, RECTL *WindowRect)
40 {
41 PWINDOW_OBJECT ParentWindow;
42 PWND ParentWnd;
43
44 ParentWindow = Child->spwndParent;
45 while (ParentWindow != NULL)
46 {
47 ParentWnd = ParentWindow->Wnd;
48 if (!(ParentWnd->style & WS_VISIBLE) ||
49 (ParentWnd->style & WS_MINIMIZE))
50 {
51 return FALSE;
52 }
53
54 if (!RECTL_bIntersectRect(WindowRect, WindowRect, &ParentWnd->rcClient))
55 {
56 return FALSE;
57 }
58
59 /* FIXME: Layered windows. */
60
61 ParentWindow = ParentWindow->spwndParent;
62 }
63
64 return TRUE;
65 }
66
67 BOOL FASTCALL
68 IntValidateParent(PWINDOW_OBJECT Child, HRGN hValidateRgn, BOOL Recurse)
69 {
70 PWINDOW_OBJECT ParentWindow = Child->spwndParent;
71 PWND ParentWnd;
72
73 while (ParentWindow)
74 {
75 ParentWnd = ParentWindow->Wnd;
76 if (ParentWnd->style & WS_CLIPCHILDREN)
77 break;
78
79 if (ParentWindow->UpdateRegion != 0)
80 {
81 if (Recurse)
82 return FALSE;
83
84 IntInvalidateWindows(ParentWindow, hValidateRgn,
85 RDW_VALIDATE | RDW_NOCHILDREN);
86 }
87
88 ParentWindow = ParentWindow->spwndParent;
89 }
90
91 return TRUE;
92 }
93
94 /**
95 * @name IntCalcWindowRgn
96 *
97 * Get a window or client region.
98 */
99
100 HRGN FASTCALL
101 IntCalcWindowRgn(PWINDOW_OBJECT Window, BOOL Client)
102 {
103 PWND Wnd;
104 HRGN hRgnWindow;
105 UINT RgnType;
106
107 Wnd = Window->Wnd;
108 if (Client)
109 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Wnd->rcClient);
110 else
111 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Wnd->rcWindow);
112
113 if (Window->WindowRegion != NULL && !(Wnd->style & WS_MINIMIZE))
114 {
115 NtGdiOffsetRgn(hRgnWindow,
116 -Wnd->rcWindow.left,
117 -Wnd->rcWindow.top);
118 RgnType = NtGdiCombineRgn(hRgnWindow, hRgnWindow, Window->WindowRegion, RGN_AND);
119 NtGdiOffsetRgn(hRgnWindow,
120 Wnd->rcWindow.left,
121 Wnd->rcWindow.top);
122 }
123
124 return hRgnWindow;
125 }
126
127 /**
128 * @name IntGetNCUpdateRgn
129 *
130 * Get non-client update region of a window and optionally validate it.
131 *
132 * @param Window
133 * Pointer to window to get the NC update region from.
134 * @param Validate
135 * Set to TRUE to force validating the NC update region.
136 *
137 * @return
138 * Handle to NC update region. The caller is responsible for deleting
139 * it.
140 */
141
142 HRGN FASTCALL
143 IntGetNCUpdateRgn(PWINDOW_OBJECT Window, BOOL Validate)
144 {
145 HRGN hRgnNonClient;
146 HRGN hRgnWindow;
147 UINT RgnType;
148
149 if (Window->UpdateRegion != NULL &&
150 Window->UpdateRegion != (HRGN)1)
151 {
152 hRgnNonClient = IntCalcWindowRgn(Window, FALSE);
153
154 /*
155 * If region creation fails it's safe to fallback to whole
156 * window region.
157 */
158 if (hRgnNonClient == NULL)
159 {
160 return (HRGN)1;
161 }
162
163 hRgnWindow = IntCalcWindowRgn(Window, TRUE);
164 if (hRgnWindow == NULL)
165 {
166 GreDeleteObject(hRgnNonClient);
167 return (HRGN)1;
168 }
169
170 RgnType = NtGdiCombineRgn(hRgnNonClient, hRgnNonClient,
171 hRgnWindow, RGN_DIFF);
172 if (RgnType == ERROR)
173 {
174 GreDeleteObject(hRgnWindow);
175 GreDeleteObject(hRgnNonClient);
176 return (HRGN)1;
177 }
178 else if (RgnType == NULLREGION)
179 {
180 GreDeleteObject(hRgnWindow);
181 GreDeleteObject(hRgnNonClient);
182 return NULL;
183 }
184
185 /*
186 * Remove the nonclient region from the standard update region if
187 * we were asked for it.
188 */
189
190 if (Validate)
191 {
192 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
193 hRgnWindow, RGN_AND) == NULLREGION)
194 {
195 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
196 GreDeleteObject(Window->UpdateRegion);
197 Window->UpdateRegion = NULL;
198 if (!(Window->state & WINDOWOBJECT_NEED_INTERNALPAINT))
199 MsqDecPaintCountQueue(Window->MessageQueue);
200 }
201 }
202
203 GreDeleteObject(hRgnWindow);
204
205 return hRgnNonClient;
206 }
207 else
208 {
209 return Window->UpdateRegion;
210 }
211 }
212
213 /*
214 * IntPaintWindows
215 *
216 * Internal function used by IntRedrawWindow.
217 */
218
219 VOID FASTCALL
220 co_IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags, BOOL Recurse)
221 {
222 HDC hDC;
223 HWND hWnd = Window->hSelf;
224 HRGN TempRegion;
225 PWND Wnd;
226
227 Wnd = Window->Wnd;
228
229 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
230 {
231 if (Window->UpdateRegion)
232 {
233 if (!IntValidateParent(Window, Window->UpdateRegion, Recurse))
234 return;
235 }
236
237 if (Flags & RDW_UPDATENOW)
238 {
239 if (Window->UpdateRegion != NULL ||
240 Window->state & WINDOWOBJECT_NEED_INTERNALPAINT)
241 {
242 co_IntSendMessage(hWnd, WM_PAINT, 0, 0);
243 }
244 }
245 else
246 {
247 if (Window->state & WINDOWOBJECT_NEED_NCPAINT)
248 {
249 TempRegion = IntGetNCUpdateRgn(Window, TRUE);
250 Window->state &= ~WINDOWOBJECT_NEED_NCPAINT;
251 MsqDecPaintCountQueue(Window->MessageQueue);
252 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)TempRegion, 0);
253 if ((HANDLE) 1 != TempRegion && NULL != TempRegion)
254 {
255 /* NOTE: The region can already be deleted! */
256 GDIOBJ_FreeObjByHandle(TempRegion, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
257 }
258 }
259
260 if (Window->state & WINDOWOBJECT_NEED_ERASEBKGND)
261 {
262 if (Window->UpdateRegion)
263 {
264 hDC = UserGetDCEx(Window, Window->UpdateRegion,
265 DCX_CACHE | DCX_USESTYLE |
266 DCX_INTERSECTRGN | DCX_KEEPCLIPRGN);
267 if (co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
268 {
269 Window->state &= ~WINDOWOBJECT_NEED_ERASEBKGND;
270 }
271 UserReleaseDC(Window, hDC, FALSE);
272 }
273 }
274 }
275 }
276
277 /*
278 * Check that the window is still valid at this point
279 */
280 if (!IntIsWindow(hWnd))
281 {
282 return;
283 }
284
285 /*
286 * Paint child windows.
287 */
288 if (!(Flags & RDW_NOCHILDREN) && !(Wnd->style & WS_MINIMIZE) &&
289 ((Flags & RDW_ALLCHILDREN) || !(Wnd->style & WS_CLIPCHILDREN)))
290 {
291 HWND *List, *phWnd;
292
293 if ((List = IntWinListChildren(Window)))
294 {
295 /* FIXME: Handle WS_EX_TRANSPARENT */
296 for (phWnd = List; *phWnd; ++phWnd)
297 {
298 Window = UserGetWindowObject(*phWnd);
299 Wnd = Window->Wnd;
300 if (Window && (Wnd->style & WS_VISIBLE))
301 {
302 USER_REFERENCE_ENTRY Ref;
303 UserRefObjectCo(Window, &Ref);
304 co_IntPaintWindows(Window, Flags, TRUE);
305 UserDerefObjectCo(Window);
306 }
307 }
308 ExFreePool(List);
309 }
310 }
311 }
312
313 /*
314 * IntInvalidateWindows
315 *
316 * Internal function used by IntRedrawWindow.
317 */
318
319 VOID FASTCALL
320 IntInvalidateWindows(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags)
321 {
322 INT RgnType;
323 PWND Wnd;
324 BOOL HadPaintMessage, HadNCPaintMessage;
325 BOOL HasPaintMessage, HasNCPaintMessage;
326
327 Wnd = Window->Wnd;
328
329 /*
330 * If the nonclient is not to be redrawn, clip the region to the client
331 * rect
332 */
333 if (0 != (Flags & RDW_INVALIDATE) && 0 == (Flags & RDW_FRAME))
334 {
335 HRGN hRgnClient;
336
337 hRgnClient = UnsafeIntCreateRectRgnIndirect(&Window->Wnd->rcClient);
338 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnClient, RGN_AND);
339 GreDeleteObject(hRgnClient);
340 }
341
342 /*
343 * Clip the given region with window rectangle (or region)
344 */
345
346 if (!Window->WindowRegion || (Wnd->style & WS_MINIMIZE))
347 {
348 HRGN hRgnWindow;
349
350 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->Wnd->rcWindow);
351 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
352 GreDeleteObject(hRgnWindow);
353 }
354 else
355 {
356 NtGdiOffsetRgn(hRgn,
357 -Wnd->rcWindow.left,
358 -Wnd->rcWindow.top);
359 RgnType = NtGdiCombineRgn(hRgn, hRgn, Window->WindowRegion, RGN_AND);
360 NtGdiOffsetRgn(hRgn,
361 Wnd->rcWindow.left,
362 Wnd->rcWindow.top);
363 }
364
365 /*
366 * Save current state of pending updates
367 */
368
369 HadPaintMessage = Window->UpdateRegion != NULL ||
370 Window->state & WINDOWOBJECT_NEED_INTERNALPAINT;
371 HadNCPaintMessage = Window->state & WINDOWOBJECT_NEED_NCPAINT;
372
373 /*
374 * Update the region and flags
375 */
376
377 if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
378 {
379 if (Window->UpdateRegion == NULL)
380 {
381 Window->UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
382 GDIOBJ_SetOwnership(Window->UpdateRegion, NULL);
383 }
384
385 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
386 hRgn, RGN_OR) == NULLREGION)
387 {
388 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
389 GreDeleteObject(Window->UpdateRegion);
390 Window->UpdateRegion = NULL;
391 }
392
393 if (Flags & RDW_FRAME)
394 Window->state |= WINDOWOBJECT_NEED_NCPAINT;
395 if (Flags & RDW_ERASE)
396 Window->state |= WINDOWOBJECT_NEED_ERASEBKGND;
397
398 Flags |= RDW_FRAME;
399 }
400
401 if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
402 {
403 if (Window->UpdateRegion != NULL)
404 {
405 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
406 hRgn, RGN_DIFF) == NULLREGION)
407 {
408 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
409 GreDeleteObject(Window->UpdateRegion);
410 Window->UpdateRegion = NULL;
411 }
412 }
413
414 if (Window->UpdateRegion == NULL)
415 Window->state &= ~WINDOWOBJECT_NEED_ERASEBKGND;
416 if (Flags & RDW_NOFRAME)
417 Window->state &= ~WINDOWOBJECT_NEED_NCPAINT;
418 if (Flags & RDW_NOERASE)
419 Window->state &= ~WINDOWOBJECT_NEED_ERASEBKGND;
420 }
421
422 if (Flags & RDW_INTERNALPAINT)
423 {
424 Window->state |= WINDOWOBJECT_NEED_INTERNALPAINT;
425 }
426
427 if (Flags & RDW_NOINTERNALPAINT)
428 {
429 Window->state &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
430 }
431
432 /*
433 * Process children if needed
434 */
435
436 if (!(Flags & RDW_NOCHILDREN) && !(Wnd->style & WS_MINIMIZE) &&
437 ((Flags & RDW_ALLCHILDREN) || !(Wnd->style & WS_CLIPCHILDREN)))
438 {
439 PWINDOW_OBJECT Child;
440
441 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
442 {
443 if (Child->Wnd->style & WS_VISIBLE)
444 {
445 /*
446 * Recursive call to update children UpdateRegion
447 */
448 HRGN hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
449 NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
450 IntInvalidateWindows(Child, hRgnTemp, Flags);
451 GreDeleteObject(hRgnTemp);
452 }
453
454 }
455 }
456
457 /*
458 * Fake post paint messages to window message queue if needed
459 */
460
461 HasPaintMessage = Window->UpdateRegion != NULL ||
462 Window->state & WINDOWOBJECT_NEED_INTERNALPAINT;
463 HasNCPaintMessage = Window->state & WINDOWOBJECT_NEED_NCPAINT;
464
465 if (HasPaintMessage != HadPaintMessage)
466 {
467 if (HadPaintMessage)
468 MsqDecPaintCountQueue(Window->MessageQueue);
469 else
470 MsqIncPaintCountQueue(Window->MessageQueue);
471 }
472
473 if (HasNCPaintMessage != HadNCPaintMessage)
474 {
475 if (HadNCPaintMessage)
476 MsqDecPaintCountQueue(Window->MessageQueue);
477 else
478 MsqIncPaintCountQueue(Window->MessageQueue);
479 }
480
481 }
482
483 /*
484 * IntIsWindowDrawable
485 *
486 * Remarks
487 * Window is drawable when it is visible and all parents are not
488 * minimized.
489 */
490
491 BOOL FASTCALL
492 IntIsWindowDrawable(PWINDOW_OBJECT Window)
493 {
494 PWINDOW_OBJECT WndObject;
495 PWND Wnd;
496
497 for (WndObject = Window; WndObject != NULL; WndObject = WndObject->spwndParent)
498 {
499 Wnd = WndObject->Wnd;
500 if (!(Wnd->style & WS_VISIBLE) ||
501 ((Wnd->style & WS_MINIMIZE) && (WndObject != Window)))
502 {
503 return FALSE;
504 }
505 }
506
507 return TRUE;
508 }
509
510 /*
511 * IntRedrawWindow
512 *
513 * Internal version of NtUserRedrawWindow that takes WINDOW_OBJECT as
514 * first parameter.
515 */
516
517 BOOL FASTCALL
518 co_UserRedrawWindow(PWINDOW_OBJECT Window, const RECTL* UpdateRect, HRGN UpdateRgn,
519 ULONG Flags)
520 {
521 HRGN hRgn = NULL;
522
523 /*
524 * Step 1.
525 * Validation of passed parameters.
526 */
527
528 if (!IntIsWindowDrawable(Window) ||
529 (Flags & (RDW_VALIDATE | RDW_INVALIDATE)) ==
530 (RDW_VALIDATE | RDW_INVALIDATE))
531 {
532 return FALSE;
533 }
534
535 /*
536 * Step 2.
537 * Transform the parameters UpdateRgn and UpdateRect into
538 * a region hRgn specified in screen coordinates.
539 */
540
541 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE))
542 {
543 if (UpdateRgn != NULL)
544 {
545 hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
546 if (NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY) == NULLREGION)
547 {
548 GreDeleteObject(hRgn);
549 hRgn = NULL;
550 }
551 else
552 NtGdiOffsetRgn(hRgn, Window->Wnd->rcClient.left, Window->Wnd->rcClient.top);
553 }
554 else if (UpdateRect != NULL)
555 {
556 if (!RECTL_bIsEmptyRect(UpdateRect))
557 {
558 hRgn = UnsafeIntCreateRectRgnIndirect((RECTL *)UpdateRect);
559 NtGdiOffsetRgn(hRgn, Window->Wnd->rcClient.left, Window->Wnd->rcClient.top);
560 }
561 }
562 else if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
563 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
564 {
565 if (!RECTL_bIsEmptyRect(&Window->Wnd->rcWindow))
566 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->Wnd->rcWindow);
567 }
568 else
569 {
570 if (!RECTL_bIsEmptyRect(&Window->Wnd->rcClient))
571 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->Wnd->rcClient);
572 }
573 }
574
575 /*
576 * Step 3.
577 * Adjust the window update region depending on hRgn and flags.
578 */
579
580 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT) &&
581 hRgn != NULL)
582 {
583 IntInvalidateWindows(Window, hRgn, Flags);
584 }
585
586 /*
587 * Step 4.
588 * Repaint and erase windows if needed.
589 */
590
591 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
592 {
593 co_IntPaintWindows(Window, Flags, FALSE);
594 }
595
596 /*
597 * Step 5.
598 * Cleanup ;-)
599 */
600
601 if (hRgn != NULL)
602 {
603 GreDeleteObject(hRgn);
604 }
605
606 return TRUE;
607 }
608
609 BOOL FASTCALL
610 IntIsWindowDirty(PWINDOW_OBJECT Window)
611 {
612 PWND Wnd = Window->Wnd;
613 return (Wnd->style & WS_VISIBLE) &&
614 ((Window->UpdateRegion != NULL) ||
615 (Window->state & WINDOWOBJECT_NEED_INTERNALPAINT) ||
616 (Window->state & WINDOWOBJECT_NEED_NCPAINT));
617 }
618
619 HWND FASTCALL
620 IntFindWindowToRepaint(PWINDOW_OBJECT Window, PTHREADINFO Thread)
621 {
622 HWND hChild;
623 PWINDOW_OBJECT TempWindow;
624 PWND Wnd, TempWnd;
625
626 for (; Window != NULL; Window = Window->spwndNext)
627 {
628 Wnd = Window->Wnd;
629 if (IntWndBelongsToThread(Window, Thread) &&
630 IntIsWindowDirty(Window))
631 {
632 /* Make sure all non-transparent siblings are already drawn. */
633 if (Wnd->ExStyle & WS_EX_TRANSPARENT)
634 {
635 for (TempWindow = Window->spwndNext; TempWindow != NULL;
636 TempWindow = TempWindow->spwndNext)
637 {
638 TempWnd = TempWindow->Wnd;
639 if (!(TempWnd->ExStyle & WS_EX_TRANSPARENT) &&
640 IntWndBelongsToThread(TempWindow, Thread) &&
641 IntIsWindowDirty(TempWindow))
642 {
643 return TempWindow->hSelf;
644 }
645 }
646 }
647
648 return Window->hSelf;
649 }
650
651 if (Window->spwndChild)
652 {
653 hChild = IntFindWindowToRepaint(Window->spwndChild, Thread);
654 if (hChild != NULL)
655 return hChild;
656 }
657 }
658
659 return NULL;
660 }
661
662 BOOL FASTCALL
663 IntGetPaintMessage(PWINDOW_OBJECT Window, UINT MsgFilterMin, UINT MsgFilterMax,
664 PTHREADINFO Thread, MSG *Message, BOOL Remove)
665 {
666 PUSER_MESSAGE_QUEUE MessageQueue = (PUSER_MESSAGE_QUEUE)Thread->MessageQueue;
667
668 if (!MessageQueue->PaintCount)
669 return FALSE;
670
671 if ((MsgFilterMin != 0 || MsgFilterMax != 0) &&
672 (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT))
673 return FALSE;
674
675 Message->hwnd = IntFindWindowToRepaint(UserGetDesktopWindow(), PsGetCurrentThreadWin32Thread());
676
677 if (Message->hwnd == NULL)
678 {
679 DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found!\n");
680 /* Hack to stop spamming the debuglog ! */
681 MessageQueue->PaintCount = 0;
682 return FALSE;
683 }
684
685 if (Window != NULL && Message->hwnd != Window->hSelf)
686 return FALSE;
687
688 Message->message = WM_PAINT;
689 Message->wParam = Message->lParam = 0;
690
691 return TRUE;
692 }
693
694 static
695 HWND FASTCALL
696 co_IntFixCaret(PWINDOW_OBJECT Window, RECTL *lprc, UINT flags)
697 {
698 PDESKTOP Desktop;
699 PTHRDCARETINFO CaretInfo;
700 HWND hWndCaret;
701 PWINDOW_OBJECT WndCaret;
702
703 ASSERT_REFS_CO(Window);
704
705 Desktop = ((PTHREADINFO)PsGetCurrentThread()->Tcb.Win32Thread)->Desktop;
706 CaretInfo = ((PUSER_MESSAGE_QUEUE)Desktop->ActiveMessageQueue)->CaretInfo;
707 hWndCaret = CaretInfo->hWnd;
708
709 WndCaret = UserGetWindowObject(hWndCaret);
710
711 //fix: check for WndCaret can be null
712 if (WndCaret == Window ||
713 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(Window, WndCaret)))
714 {
715 POINT pt, FromOffset, ToOffset, Offset;
716 RECTL rcCaret;
717
718 pt.x = CaretInfo->Pos.x;
719 pt.y = CaretInfo->Pos.y;
720 IntGetClientOrigin(WndCaret, &FromOffset);
721 IntGetClientOrigin(Window, &ToOffset);
722 Offset.x = FromOffset.x - ToOffset.x;
723 Offset.y = FromOffset.y - ToOffset.y;
724 rcCaret.left = pt.x;
725 rcCaret.top = pt.y;
726 rcCaret.right = pt.x + CaretInfo->Size.cx;
727 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
728 if (RECTL_bIntersectRect(lprc, lprc, &rcCaret))
729 {
730 co_UserHideCaret(0);
731 lprc->left = pt.x;
732 lprc->top = pt.y;
733 return hWndCaret;
734 }
735 }
736
737 return 0;
738 }
739
740 /* PUBLIC FUNCTIONS ***********************************************************/
741
742 /*
743 * NtUserBeginPaint
744 *
745 * Status
746 * @implemented
747 */
748
749 HDC APIENTRY
750 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
751 {
752 PWINDOW_OBJECT Window = NULL;
753 PAINTSTRUCT Ps;
754 NTSTATUS Status;
755 DECLARE_RETURN(HDC);
756 USER_REFERENCE_ENTRY Ref;
757 PWND Wnd;
758
759 DPRINT("Enter NtUserBeginPaint\n");
760 UserEnterExclusive();
761
762 if (!(Window = UserGetWindowObject(hWnd)))
763 {
764 RETURN( NULL);
765 }
766
767 UserRefObjectCo(Window, &Ref);
768
769 Wnd = Window->Wnd;
770
771 co_UserHideCaret(Window);
772
773 if (Window->state & WINDOWOBJECT_NEED_NCPAINT)
774 {
775 HRGN hRgn;
776
777 hRgn = IntGetNCUpdateRgn(Window, FALSE);
778 Window->state &= ~WINDOWOBJECT_NEED_NCPAINT;
779 MsqDecPaintCountQueue(Window->MessageQueue);
780 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);
781 if (hRgn != (HANDLE)1 && hRgn != NULL)
782 {
783 /* NOTE: The region can already by deleted! */
784 GDIOBJ_FreeObjByHandle(hRgn, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
785 }
786 }
787
788 RtlZeroMemory(&Ps, sizeof(PAINTSTRUCT));
789
790 Ps.hdc = UserGetDCEx(Window, Window->UpdateRegion, DCX_INTERSECTRGN | DCX_USESTYLE);
791 if (!Ps.hdc)
792 {
793 RETURN(NULL);
794 }
795
796 if (Window->UpdateRegion != NULL)
797 {
798 MsqDecPaintCountQueue(Window->MessageQueue);
799 GdiGetClipBox(Ps.hdc, &Ps.rcPaint);
800 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
801 /* The region is part of the dc now and belongs to the process! */
802 Window->UpdateRegion = NULL;
803 }
804 else
805 {
806 if (Window->state & WINDOWOBJECT_NEED_INTERNALPAINT)
807 MsqDecPaintCountQueue(Window->MessageQueue);
808
809 IntGetClientRect(Window, &Ps.rcPaint);
810 }
811
812 Window->state &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
813
814 if (Window->state & WINDOWOBJECT_NEED_ERASEBKGND)
815 {
816 Window->state &= ~WINDOWOBJECT_NEED_ERASEBKGND;
817 Ps.fErase = !co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)Ps.hdc, 0);
818 }
819 else
820 {
821 Ps.fErase = FALSE;
822 }
823 if (Window->UpdateRegion)
824 {
825 if (!(Wnd->style & WS_CLIPCHILDREN))
826 {
827 PWINDOW_OBJECT Child;
828 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
829 {
830 IntInvalidateWindows(Child, Window->UpdateRegion, RDW_FRAME | RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
831 }
832 }
833 }
834
835 Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
836 if (! NT_SUCCESS(Status))
837 {
838 SetLastNtError(Status);
839 RETURN(NULL);
840 }
841
842 RETURN(Ps.hdc);
843
844 CLEANUP:
845 if (Window) UserDerefObjectCo(Window);
846
847 DPRINT("Leave NtUserBeginPaint, ret=%i\n",_ret_);
848 UserLeave();
849 END_CLEANUP;
850
851 }
852
853 /*
854 * NtUserEndPaint
855 *
856 * Status
857 * @implemented
858 */
859
860 BOOL APIENTRY
861 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* pUnsafePs)
862 {
863 NTSTATUS Status = STATUS_SUCCESS;
864 PWINDOW_OBJECT Window;
865 DECLARE_RETURN(BOOL);
866 USER_REFERENCE_ENTRY Ref;
867 HDC hdc = NULL;
868
869 DPRINT("Enter NtUserEndPaint\n");
870 UserEnterExclusive();
871
872 if (!(Window = UserGetWindowObject(hWnd)))
873 {
874 RETURN(FALSE);
875 }
876
877 _SEH2_TRY
878 {
879 ProbeForRead(pUnsafePs, sizeof(*pUnsafePs), 1);
880 hdc = pUnsafePs->hdc;
881 }
882 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
883 {
884 Status = _SEH2_GetExceptionCode();
885 }
886 _SEH2_END
887 if (!NT_SUCCESS(Status))
888 {
889 RETURN(FALSE);
890 }
891
892 UserReleaseDC(Window, hdc, TRUE);
893
894 UserRefObjectCo(Window, &Ref);
895 co_UserShowCaret(Window);
896 UserDerefObjectCo(Window);
897
898 RETURN(TRUE);
899
900 CLEANUP:
901 DPRINT("Leave NtUserEndPaint, ret=%i\n",_ret_);
902 UserLeave();
903 END_CLEANUP;
904 }
905
906
907 INT FASTCALL
908 co_UserGetUpdateRgn(PWINDOW_OBJECT Window, HRGN hRgn, BOOL bErase)
909 {
910 int RegionType;
911 RECTL Rect;
912
913 ASSERT_REFS_CO(Window);
914
915 if (Window->UpdateRegion == NULL)
916 {
917 RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
918 }
919 else
920 {
921 Rect = Window->Wnd->rcClient;
922 IntIntersectWithParents(Window, &Rect);
923 NtGdiSetRectRgn(hRgn, Rect.left, Rect.top, Rect.right, Rect.bottom);
924 RegionType = NtGdiCombineRgn(hRgn, hRgn, Window->UpdateRegion, RGN_AND);
925 NtGdiOffsetRgn(hRgn, -Window->Wnd->rcClient.left, -Window->Wnd->rcClient.top);
926 }
927
928 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
929 {
930 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
931 }
932
933 return RegionType;
934 }
935
936 /*
937 * NtUserGetUpdateRgn
938 *
939 * Status
940 * @implemented
941 */
942
943 INT APIENTRY
944 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
945 {
946 DECLARE_RETURN(INT);
947 PWINDOW_OBJECT Window;
948 INT ret;
949 USER_REFERENCE_ENTRY Ref;
950
951 DPRINT("Enter NtUserGetUpdateRgn\n");
952 UserEnterExclusive();
953
954 if (!(Window = UserGetWindowObject(hWnd)))
955 {
956 RETURN(ERROR);
957 }
958
959 UserRefObjectCo(Window, &Ref);
960 ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
961 UserDerefObjectCo(Window);
962
963 RETURN(ret);
964
965 CLEANUP:
966 DPRINT("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
967 UserLeave();
968 END_CLEANUP;
969 }
970
971 /*
972 * NtUserGetUpdateRect
973 *
974 * Status
975 * @implemented
976 */
977
978 BOOL APIENTRY
979 NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
980 {
981 PWINDOW_OBJECT Window;
982 RECTL Rect;
983 INT RegionType;
984 PROSRGNDATA RgnData;
985 NTSTATUS Status;
986 DECLARE_RETURN(BOOL);
987
988 DPRINT("Enter NtUserGetUpdateRect\n");
989 UserEnterExclusive();
990
991 if (!(Window = UserGetWindowObject(hWnd)))
992 {
993 RETURN(FALSE);
994 }
995
996 if (Window->UpdateRegion == NULL)
997 {
998 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
999 }
1000 else
1001 {
1002 /* Get the update region bounding box. */
1003 if (Window->UpdateRegion == (HRGN)1)
1004 {
1005 Rect = Window->Wnd->rcClient;
1006 }
1007 else
1008 {
1009 RgnData = REGION_LockRgn(Window->UpdateRegion);
1010 ASSERT(RgnData != NULL);
1011 RegionType = REGION_GetRgnBox(RgnData, &Rect);
1012 REGION_UnlockRgn(RgnData);
1013
1014 if (RegionType != ERROR && RegionType != NULLREGION)
1015 RECTL_bIntersectRect(&Rect, &Rect, &Window->Wnd->rcClient);
1016 }
1017
1018 if (IntIntersectWithParents(Window, &Rect))
1019 {
1020 RECTL_vOffsetRect(&Rect,
1021 -Window->Wnd->rcClient.left,
1022 -Window->Wnd->rcClient.top);
1023 } else
1024 {
1025 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
1026 }
1027 }
1028
1029 if (bErase && !RECTL_bIsEmptyRect(&Rect))
1030 {
1031 USER_REFERENCE_ENTRY Ref;
1032 UserRefObjectCo(Window, &Ref);
1033 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
1034 UserDerefObjectCo(Window);
1035 }
1036
1037 if (UnsafeRect != NULL)
1038 {
1039 Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECTL));
1040 if (!NT_SUCCESS(Status))
1041 {
1042 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1043 RETURN(FALSE);
1044 }
1045 }
1046
1047 RETURN(!RECTL_bIsEmptyRect(&Rect));
1048
1049 CLEANUP:
1050 DPRINT("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
1051 UserLeave();
1052 END_CLEANUP;
1053 }
1054
1055 /*
1056 * NtUserRedrawWindow
1057 *
1058 * Status
1059 * @implemented
1060 */
1061
1062 BOOL APIENTRY
1063 NtUserRedrawWindow(HWND hWnd, CONST RECT *lprcUpdate, HRGN hrgnUpdate,
1064 UINT flags)
1065 {
1066 RECTL SafeUpdateRect;
1067 NTSTATUS Status;
1068 PWINDOW_OBJECT Wnd;
1069 DECLARE_RETURN(BOOL);
1070 USER_REFERENCE_ENTRY Ref;
1071
1072 DPRINT("Enter NtUserRedrawWindow\n");
1073 UserEnterExclusive();
1074
1075 if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
1076 {
1077 RETURN( FALSE);
1078 }
1079
1080 if (lprcUpdate != NULL)
1081 {
1082 Status = MmCopyFromCaller(&SafeUpdateRect, lprcUpdate,
1083 sizeof(RECTL));
1084
1085 if (!NT_SUCCESS(Status))
1086 {
1087 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1088 RETURN( FALSE);
1089 }
1090 }
1091
1092 UserRefObjectCo(Wnd, &Ref);
1093
1094 Status = co_UserRedrawWindow(Wnd, NULL == lprcUpdate ? NULL : &SafeUpdateRect,
1095 hrgnUpdate, flags);
1096
1097 UserDerefObjectCo(Wnd);
1098
1099 if (!NT_SUCCESS(Status))
1100 {
1101 /* IntRedrawWindow fails only in case that flags are invalid */
1102 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1103 RETURN( FALSE);
1104 }
1105
1106 RETURN( TRUE);
1107
1108 CLEANUP:
1109 DPRINT("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
1110 UserLeave();
1111 END_CLEANUP;
1112 }
1113
1114
1115
1116 static
1117 INT FASTCALL
1118 UserScrollDC(HDC hDC, INT dx, INT dy, const RECTL *prcScroll,
1119 const RECTL *prcClip, HRGN hrgnUpdate, RECTL *prcUpdate)
1120 {
1121 PDC pDC;
1122 RECTL rcScroll, rcClip, rcSrc, rcDst;
1123 INT Result;
1124
1125 GdiGetClipBox(hDC, &rcClip);
1126 rcScroll = rcClip;
1127 if (prcClip)
1128 {
1129 RECTL_bIntersectRect(&rcClip, &rcClip, prcClip);
1130 }
1131
1132 if (prcScroll)
1133 {
1134 rcScroll = *prcScroll;
1135 RECTL_bIntersectRect(&rcSrc, &rcClip, prcScroll);
1136 }
1137 else
1138 {
1139 rcSrc = rcClip;
1140 }
1141
1142 rcDst = rcSrc;
1143 RECTL_vOffsetRect(&rcDst, dx, dy);
1144 RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
1145
1146 if (!NtGdiBitBlt(hDC, rcDst.left, rcDst.top,
1147 rcDst.right - rcDst.left, rcDst.bottom - rcDst.top,
1148 hDC, rcDst.left - dx, rcDst.top - dy, SRCCOPY, 0, 0))
1149 {
1150 return ERROR;
1151 }
1152
1153 /* Calculate the region that was invalidated by moving or
1154 could not be copied, because it was not visible */
1155 if (hrgnUpdate || prcUpdate)
1156 {
1157 HRGN hrgnOwn, hrgnVisible, hrgnTmp;
1158
1159 pDC = DC_LockDc(hDC);
1160 if (!pDC)
1161 {
1162 return FALSE;
1163 }
1164 hrgnVisible = pDC->rosdc.hVisRgn; // pDC->w.hGCClipRgn?
1165 DC_UnlockDc(pDC);
1166
1167 /* Begin with the shifted and then clipped scroll rect */
1168 rcDst = rcScroll;
1169 RECTL_vOffsetRect(&rcDst, dx, dy);
1170 RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
1171 if (hrgnUpdate)
1172 {
1173 hrgnOwn = hrgnUpdate;
1174 if (!NtGdiSetRectRgn(hrgnOwn, rcDst.left, rcDst.top, rcDst.right, rcDst.bottom))
1175 {
1176 return ERROR;
1177 }
1178 }
1179 else
1180 {
1181 hrgnOwn = UnsafeIntCreateRectRgnIndirect(&rcDst);
1182 }
1183
1184 /* Add the source rect */
1185 hrgnTmp = UnsafeIntCreateRectRgnIndirect(&rcSrc);
1186 NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_OR);
1187
1188 /* Substract the part of the dest that was visible in source */
1189 NtGdiCombineRgn(hrgnTmp, hrgnTmp, hrgnVisible, RGN_AND);
1190 NtGdiOffsetRgn(hrgnTmp, dx, dy);
1191 Result = NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_DIFF);
1192
1193 GreDeleteObject(hrgnTmp);
1194
1195 if (prcUpdate)
1196 {
1197 IntGdiGetRgnBox(hrgnOwn, prcUpdate);
1198 }
1199
1200 if (!hrgnUpdate)
1201 {
1202 GreDeleteObject(hrgnOwn);
1203 }
1204 }
1205 else
1206 Result = NULLREGION;
1207
1208 return Result;
1209 }
1210
1211
1212
1213
1214 /*
1215 * NtUserScrollDC
1216 *
1217 * Status
1218 * @implemented
1219 */
1220
1221 BOOL APIENTRY
1222 NtUserScrollDC(HDC hDC, INT dx, INT dy, const RECT *prcUnsafeScroll,
1223 const RECT *prcUnsafeClip, HRGN hrgnUpdate, LPRECT prcUnsafeUpdate)
1224 {
1225 DECLARE_RETURN(DWORD);
1226 RECTL rcScroll, rcClip, rcUpdate;
1227 NTSTATUS Status = STATUS_SUCCESS;
1228 DWORD Result;
1229
1230 DPRINT("Enter NtUserScrollDC\n");
1231 UserEnterExclusive();
1232
1233 _SEH2_TRY
1234 {
1235 if (prcUnsafeScroll)
1236 {
1237 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1238 rcScroll = *prcUnsafeScroll;
1239 }
1240 if (prcUnsafeClip)
1241 {
1242 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1243 rcClip = *prcUnsafeClip;
1244 }
1245 if (prcUnsafeUpdate)
1246 {
1247 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1248 }
1249 }
1250 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1251 {
1252 Status = _SEH2_GetExceptionCode();
1253 }
1254 _SEH2_END
1255 if (!NT_SUCCESS(Status))
1256 {
1257 SetLastNtError(Status);
1258 RETURN(FALSE);
1259 }
1260
1261 Result = UserScrollDC(hDC, dx, dy,
1262 prcUnsafeScroll? &rcScroll : 0,
1263 prcUnsafeClip? &rcClip : 0, hrgnUpdate,
1264 prcUnsafeUpdate? &rcUpdate : NULL);
1265 if(Result == ERROR)
1266 {
1267 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
1268 RETURN(FALSE);
1269 }
1270
1271 if (prcUnsafeUpdate)
1272 {
1273 _SEH2_TRY
1274 {
1275 *prcUnsafeUpdate = rcUpdate;
1276 }
1277 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1278 {
1279 Status = _SEH2_GetExceptionCode();
1280 }
1281 _SEH2_END
1282 if (!NT_SUCCESS(Status))
1283 {
1284 /* FIXME: SetLastError? */
1285 /* FIXME: correct? We have already scrolled! */
1286 RETURN(FALSE);
1287 }
1288 }
1289
1290 RETURN(TRUE);
1291
1292 CLEANUP:
1293 DPRINT("Leave NtUserScrollDC, ret=%i\n",_ret_);
1294 UserLeave();
1295 END_CLEANUP;
1296 }
1297
1298 /*
1299 * NtUserScrollWindowEx
1300 *
1301 * Status
1302 * @implemented
1303 */
1304
1305 DWORD APIENTRY
1306 NtUserScrollWindowEx(HWND hWnd, INT dx, INT dy, const RECT *prcUnsafeScroll,
1307 const RECT *prcUnsafeClip, HRGN hrgnUpdate, LPRECT prcUnsafeUpdate, UINT flags)
1308 {
1309 RECTL rcScroll, rcClip, rcCaret, rcUpdate;
1310 INT Result;
1311 PWINDOW_OBJECT Window = NULL, CaretWnd;
1312 HDC hDC;
1313 HRGN hrgnOwn = NULL, hrgnTemp;
1314 HWND hwndCaret;
1315 NTSTATUS Status = STATUS_SUCCESS;
1316 DECLARE_RETURN(DWORD);
1317 USER_REFERENCE_ENTRY Ref, CaretRef;
1318
1319 DPRINT("Enter NtUserScrollWindowEx\n");
1320 UserEnterExclusive();
1321
1322 Window = UserGetWindowObject(hWnd);
1323 if (!Window || !IntIsWindowDrawable(Window))
1324 {
1325 Window = NULL; /* prevent deref at cleanup */
1326 RETURN( ERROR);
1327 }
1328 UserRefObjectCo(Window, &Ref);
1329
1330 IntGetClientRect(Window, &rcClip);
1331
1332 _SEH2_TRY
1333 {
1334 if (prcUnsafeScroll)
1335 {
1336 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1337 RECTL_bIntersectRect(&rcScroll, &rcClip, prcUnsafeScroll);
1338 }
1339 else
1340 rcScroll = rcClip;
1341
1342 if (prcUnsafeClip)
1343 {
1344 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1345 RECTL_bIntersectRect(&rcClip, &rcClip, prcUnsafeClip);
1346 }
1347 }
1348 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1349 {
1350 Status = _SEH2_GetExceptionCode();
1351 }
1352 _SEH2_END
1353
1354 if (!NT_SUCCESS(Status))
1355 {
1356 SetLastNtError(Status);
1357 RETURN(ERROR);
1358 }
1359
1360 if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
1361 (dx == 0 && dy == 0))
1362 {
1363 RETURN(NULLREGION);
1364 }
1365
1366 if (hrgnUpdate)
1367 hrgnOwn = hrgnUpdate;
1368 else
1369 hrgnOwn = NtGdiCreateRectRgn(0, 0, 0, 0);
1370
1371 hDC = UserGetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE);
1372 if (!hDC)
1373 {
1374 /* FIXME: SetLastError? */
1375 RETURN(ERROR);
1376 }
1377
1378 rcCaret = rcScroll;
1379 hwndCaret = co_IntFixCaret(Window, &rcCaret, flags);
1380
1381 Result = UserScrollDC(hDC, dx, dy, &rcScroll, &rcClip, hrgnOwn, prcUnsafeUpdate? &rcUpdate : NULL);
1382 UserReleaseDC(Window, hDC, FALSE);
1383
1384 /*
1385 * Take into account the fact that some damage may have occurred during
1386 * the scroll.
1387 */
1388
1389 hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
1390 if (co_UserGetUpdateRgn(Window, hrgnTemp, FALSE) != NULLREGION)
1391 {
1392 HRGN hrgnClip = UnsafeIntCreateRectRgnIndirect(&rcClip);
1393 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1394 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1395 co_UserRedrawWindow(Window, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1396 GreDeleteObject(hrgnClip);
1397 }
1398 GreDeleteObject(hrgnTemp);
1399
1400 if (flags & SW_SCROLLCHILDREN)
1401 {
1402 PWINDOW_OBJECT Child;
1403 RECTL rcChild;
1404 POINT ClientOrigin;
1405 USER_REFERENCE_ENTRY WndRef;
1406 RECTL rcDummy;
1407
1408 IntGetClientOrigin(Window, &ClientOrigin);
1409 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
1410 {
1411 rcChild = Child->Wnd->rcWindow;
1412 rcChild.left -= ClientOrigin.x;
1413 rcChild.top -= ClientOrigin.y;
1414 rcChild.right -= ClientOrigin.x;
1415 rcChild.bottom -= ClientOrigin.y;
1416
1417 if (! prcUnsafeScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll))
1418 {
1419 UserRefObjectCo(Child, &WndRef);
1420 co_WinPosSetWindowPos(Child, 0, rcChild.left + dx, rcChild.top + dy, 0, 0,
1421 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1422 SWP_NOREDRAW);
1423 UserDerefObjectCo(Child);
1424 }
1425 }
1426 }
1427
1428 if (flags & (SW_INVALIDATE | SW_ERASE))
1429 {
1430 co_UserRedrawWindow(Window, NULL, hrgnOwn, RDW_INVALIDATE | RDW_ERASE |
1431 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1432 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1433 }
1434
1435 if ((CaretWnd = UserGetWindowObject(hwndCaret)))
1436 {
1437 UserRefObjectCo(CaretWnd, &CaretRef);
1438
1439 co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
1440 co_UserShowCaret(CaretWnd);
1441
1442 UserDerefObjectCo(CaretWnd);
1443 }
1444
1445 if (prcUnsafeUpdate)
1446 {
1447 _SEH2_TRY
1448 {
1449 /* Probe here, to not fail on invalid pointer before scrolling */
1450 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1451 *prcUnsafeUpdate = rcUpdate;
1452 }
1453 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1454 {
1455 Status = _SEH2_GetExceptionCode();
1456 }
1457 _SEH2_END
1458
1459 if (!NT_SUCCESS(Status))
1460 {
1461 SetLastNtError(Status);
1462 RETURN(ERROR);
1463 }
1464 }
1465
1466 RETURN(Result);
1467
1468 CLEANUP:
1469 if (hrgnOwn && !hrgnUpdate)
1470 {
1471 GreDeleteObject(hrgnOwn);
1472 }
1473
1474 if (Window)
1475 UserDerefObjectCo(Window);
1476
1477 DPRINT("Leave NtUserScrollWindowEx, ret=%i\n",_ret_);
1478 UserLeave();
1479 END_CLEANUP;
1480 }
1481
1482
1483 BOOL
1484 UserDrawSysMenuButton(
1485 PWINDOW_OBJECT pWnd,
1486 HDC hDc,
1487 RECTL *lpRc,
1488 BOOL Down)
1489 {
1490 HICON hIcon;
1491 PCURICON_OBJECT pIcon;
1492
1493 ASSERT(pWnd && lpRc);
1494
1495 /* Get the icon to draw. We don't care about WM_GETICON here. */
1496
1497 hIcon = pWnd->Wnd->pcls->hIconSm;
1498
1499 if(!hIcon)
1500 {
1501 DPRINT("Wnd class has no small icon.\n");
1502 hIcon = pWnd->Wnd->pcls->hIcon;
1503 }
1504
1505 if(!hIcon)
1506 {
1507 DPRINT("Wnd class hasn't any icon.\n");
1508 //FIXME: Draw "winlogo" icon.
1509 return FALSE;
1510 }
1511
1512 if(!(pIcon = UserGetCurIconObject(hIcon)))
1513 {
1514 DPRINT1("UserGetCurIconObject() failed!\n");
1515 return FALSE;
1516 }
1517
1518 return UserDrawIconEx(hDc, lpRc->left, lpRc->top, pIcon,
1519 UserGetSystemMetrics(SM_CXSMICON),
1520 UserGetSystemMetrics(SM_CYSMICON),
1521 0, NULL, DI_NORMAL);
1522 }
1523
1524 BOOL
1525 UserDrawCaptionText(HDC hDc,
1526 const PUNICODE_STRING Text,
1527 const RECTL *lpRc,
1528 UINT uFlags)
1529 {
1530 HFONT hOldFont = NULL, hFont = NULL;
1531 COLORREF OldTextColor;
1532 NONCLIENTMETRICSW nclm;
1533 NTSTATUS Status;
1534 #ifndef NDEBUG
1535 INT i;
1536 DPRINT("%s:", __FUNCTION__);
1537 for(i = 0; i < Text->Length/sizeof(WCHAR); i++)
1538 DbgPrint("%C", Text->Buffer[i]);
1539 DbgPrint(", %d\n", Text->Length/sizeof(WCHAR));
1540 #endif
1541
1542 nclm.cbSize = sizeof(nclm);
1543 if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS,
1544 sizeof(NONCLIENTMETRICS), &nclm, 0))
1545 {
1546 DPRINT1("%s: UserSystemParametersInfo() failed!\n", __FUNCTION__);
1547 return FALSE;
1548 }
1549
1550 IntGdiSetBkMode(hDc, TRANSPARENT);
1551
1552 if(uFlags & DC_SMALLCAP)
1553 Status = TextIntCreateFontIndirect(&nclm.lfSmCaptionFont, &hFont);
1554 else Status = TextIntCreateFontIndirect(&nclm.lfCaptionFont, &hFont);
1555
1556 if(!NT_SUCCESS(Status))
1557 {
1558 DPRINT1("%s: TextIntCreateFontIndirect() failed! Status: 0x%x\n",
1559 __FUNCTION__, Status);
1560 return FALSE;
1561 }
1562
1563 hOldFont = NtGdiSelectFont(hDc, hFont);
1564 if(!hOldFont)
1565 {
1566 DPRINT1("%s: SelectFont() failed!\n", __FUNCTION__);
1567 GreDeleteObject(hFont);
1568 return FALSE;
1569 }
1570
1571 if(uFlags & DC_INBUTTON)
1572 OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT));
1573 else OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(uFlags & DC_ACTIVE
1574 ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
1575
1576 //FIXME: If string doesn't fit to rc, truncate it and add ellipsis.
1577
1578 GreExtTextOutW(hDc, lpRc->left,
1579 lpRc->top, 0, NULL, Text->Buffer,
1580 Text->Length/sizeof(WCHAR), NULL, 0);
1581
1582 IntGdiSetTextColor(hDc, OldTextColor);
1583 NtGdiSelectFont(hDc, hOldFont);
1584 GreDeleteObject(hFont);
1585
1586 return TRUE;
1587 }
1588
1589 BOOL UserDrawCaption(
1590 PWINDOW_OBJECT pWnd,
1591 HDC hDc,
1592 RECTL *lpRc,
1593 HFONT hFont,
1594 HICON hIcon,
1595 const PUNICODE_STRING str,
1596 UINT uFlags)
1597 {
1598 BOOL Ret = FALSE;
1599 HBITMAP hMemBmp = NULL, hOldBmp = NULL;
1600 HBRUSH hOldBrush = NULL;
1601 HDC hMemDc = NULL;
1602 ULONG Height;
1603 UINT VCenter = 0, Padding = 0;
1604 RECTL r = *lpRc;
1605 LONG ButtonWidth, IconWidth;
1606 BOOL HasIcon;
1607 PWND Wnd = NULL;
1608
1609 //ASSERT(pWnd != NULL);
1610
1611 if (pWnd)
1612 Wnd = pWnd->Wnd;
1613
1614 RECTL_vMakeWellOrdered(lpRc);
1615 hMemBmp = NtGdiCreateCompatibleBitmap(hDc,
1616 lpRc->right - lpRc->left,
1617 lpRc->bottom - lpRc->top);
1618
1619 if(!hMemBmp)
1620 {
1621 DPRINT1("%s: NtGdiCreateCompatibleBitmap() failed!\n", __FUNCTION__);
1622 return FALSE;
1623 }
1624
1625 hMemDc = NtGdiCreateCompatibleDC(hDc);
1626 if(!hMemDc)
1627 {
1628 DPRINT1("%s: NtGdiCreateCompatibleDC() failed!\n", __FUNCTION__);
1629 goto cleanup;
1630 }
1631
1632 hOldBmp = NtGdiSelectBitmap(hMemDc, hMemBmp);
1633 if(!hOldBmp)
1634 {
1635 DPRINT1("%s: NtGdiSelectBitmap() failed!\n", __FUNCTION__);
1636 goto cleanup;
1637 }
1638
1639 Height = UserGetSystemMetrics(SM_CYCAPTION) - 1;
1640 VCenter = (lpRc->bottom - lpRc->top) / 2;
1641 Padding = VCenter - (Height / 2);
1642
1643 if ((!hIcon) && (Wnd != NULL))
1644 {
1645 HasIcon = (uFlags & DC_ICON) && (Wnd->style & WS_SYSMENU)
1646 && !(uFlags & DC_SMALLCAP) && !(Wnd->ExStyle & WS_EX_DLGMODALFRAME)
1647 && !(Wnd->ExStyle & WS_EX_TOOLWINDOW);
1648 }
1649 else
1650 HasIcon = (BOOL) hIcon;
1651
1652 IconWidth = UserGetSystemMetrics(SM_CXSIZE) + Padding;
1653
1654 r.left = Padding;
1655 r.right = r.left + (lpRc->right - lpRc->left);
1656 r.top = Padding;
1657 r.bottom = r.top + (Height / 2);
1658
1659 // Draw the caption background
1660 if(uFlags & DC_INBUTTON)
1661 {
1662 hOldBrush = NtGdiSelectBrush(hMemDc,
1663 IntGetSysColorBrush(COLOR_3DFACE));
1664
1665 if(!hOldBrush)
1666 {
1667 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1668 goto cleanup;
1669 }
1670
1671 if(!NtGdiPatBlt(hMemDc, 0, 0,
1672 lpRc->right - lpRc->left,
1673 lpRc->bottom - lpRc->top,
1674 PATCOPY))
1675 {
1676 DPRINT1("%s: NtGdiPatBlt() failed!\n", __FUNCTION__);
1677 goto cleanup;
1678 }
1679
1680 if(HasIcon) r.left+=IconWidth;
1681 }
1682 else
1683 {
1684 r.right = (lpRc->right - lpRc->left);
1685 if(uFlags & DC_SMALLCAP)
1686 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1687 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1688
1689 hOldBrush = NtGdiSelectBrush(hMemDc,
1690 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1691 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION));
1692
1693 if(!hOldBrush)
1694 {
1695 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1696 goto cleanup;
1697 }
1698
1699 if(HasIcon && (uFlags & DC_GRADIENT))
1700 {
1701 NtGdiPatBlt(hMemDc, 0, 0,
1702 IconWidth+1,
1703 lpRc->bottom - lpRc->top,
1704 PATCOPY);
1705 r.left+=IconWidth;
1706 }
1707 else
1708 {
1709 NtGdiPatBlt(hMemDc, 0, 0,
1710 lpRc->right - lpRc->left,
1711 lpRc->bottom - lpRc->top,
1712 PATCOPY);
1713 }
1714
1715 if(uFlags & DC_GRADIENT)
1716 {
1717 static GRADIENT_RECT gcap = {0, 1};
1718 TRIVERTEX vert[2];
1719 COLORREF Colors[2];
1720 PDC pMemDc;
1721
1722 if (Wnd != NULL)
1723 {
1724 if(Wnd->style & WS_SYSMENU)
1725 {
1726 r.right -= 3 + ButtonWidth;
1727 if(!(uFlags & DC_SMALLCAP))
1728 {
1729 if(Wnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1730 r.right -= 2 + 2 * ButtonWidth;
1731 else r.right -= 2;
1732 r.right -= 2;
1733 }
1734
1735 //Draw buttons background
1736 if(!NtGdiSelectBrush(hMemDc,
1737 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1738 COLOR_GRADIENTACTIVECAPTION:COLOR_GRADIENTINACTIVECAPTION)))
1739 {
1740 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1741 goto cleanup;
1742 }
1743
1744 NtGdiPatBlt(hMemDc,
1745 r.right,
1746 0,
1747 lpRc->right - lpRc->left - r.right,
1748 lpRc->bottom - lpRc->top,
1749 PATCOPY);
1750 }
1751 }
1752
1753 Colors[0] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1754 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION);
1755
1756 Colors[1] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1757 COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION);
1758
1759 vert[0].x = r.left;
1760 vert[0].y = 0;
1761 vert[0].Red = (WORD)Colors[0]<<8;
1762 vert[0].Green = (WORD)Colors[0] & 0xFF00;
1763 vert[0].Blue = (WORD)(Colors[0]>>8) & 0xFF00;
1764 vert[0].Alpha = 0;
1765
1766 vert[1].x = r.right;
1767 vert[1].y = lpRc->bottom - lpRc->top;
1768 vert[1].Red = (WORD)Colors[1]<<8;
1769 vert[1].Green = (WORD)Colors[1] & 0xFF00;
1770 vert[1].Blue = (WORD)(Colors[1]>>8) & 0xFF00;
1771 vert[1].Alpha = 0;
1772
1773 pMemDc = DC_LockDc(hMemDc);
1774 if(!pMemDc)
1775 {
1776 DPRINT1("%s: Can't lock dc!\n", __FUNCTION__);
1777 goto cleanup;
1778 }
1779
1780 if(!IntGdiGradientFill(pMemDc, vert, 2, &gcap,
1781 1, GRADIENT_FILL_RECT_H))
1782 {
1783 DPRINT1("%s: IntGdiGradientFill() failed!\n", __FUNCTION__);
1784 }
1785
1786 DC_UnlockDc(pMemDc);
1787 } //if(uFlags & DC_GRADIENT)
1788 }
1789
1790 if(HasIcon)
1791 {
1792 r.top ++;
1793 r.left -= --IconWidth;
1794 /* FIXME: Draw the Icon when pWnd == NULL but hIcon is valid */
1795 if (pWnd != NULL)
1796 UserDrawSysMenuButton(pWnd, hMemDc, &r, FALSE);
1797
1798 r.left += IconWidth;
1799 r.top --;
1800 }
1801
1802 r.top ++;
1803 r.left += 2;
1804
1805 r.bottom = r.top + Height;
1806
1807 if((uFlags & DC_TEXT))
1808 {
1809 if(!(uFlags & DC_GRADIENT))
1810 {
1811 r.right = (lpRc->right - lpRc->left);
1812
1813 if(uFlags & DC_SMALLCAP)
1814 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1815 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1816
1817 if ((Wnd != NULL) && (Wnd->style & WS_SYSMENU))
1818 {
1819 r.right -= 3 + ButtonWidth;
1820 if(! (uFlags & DC_SMALLCAP))
1821 {
1822 if(Wnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1823 r.right -= 2 + 2 * ButtonWidth;
1824 else r.right -= 2;
1825 r.right -= 2;
1826 }
1827 }
1828 }
1829
1830 /* FIXME: hFont isn't handled */
1831 if (str)
1832 UserDrawCaptionText(hMemDc, str, &r, uFlags);
1833 else if (pWnd != NULL)
1834 UserDrawCaptionText(hMemDc, &pWnd->Wnd->strName, &r, uFlags);
1835 }
1836
1837 if(!NtGdiBitBlt(hDc, lpRc->left, lpRc->top,
1838 lpRc->right - lpRc->left, lpRc->bottom - lpRc->top,
1839 hMemDc, 0, 0, SRCCOPY, 0, 0))
1840 {
1841 DPRINT1("%s: NtGdiBitBlt() failed!\n", __FUNCTION__);
1842 goto cleanup;
1843 }
1844
1845 Ret = TRUE;
1846
1847 cleanup:
1848 if (hOldBrush) NtGdiSelectBrush(hMemDc, hOldBrush);
1849 if (hOldBmp) NtGdiSelectBitmap(hMemDc, hOldBmp);
1850 if (hMemBmp) GreDeleteObject(hMemBmp);
1851 if (hMemDc) NtGdiDeleteObjectApp(hMemDc);
1852
1853 return Ret;
1854 }
1855
1856 INT
1857 FASTCALL
1858 UserRealizePalette(HDC hdc)
1859 {
1860 HWND hWnd;
1861 DWORD Ret;
1862
1863 Ret = IntGdiRealizePalette(hdc);
1864 if (Ret) // There was a change.
1865 {
1866 hWnd = IntWindowFromDC(hdc);
1867 if (hWnd) // Send broadcast if dc is associated with a window.
1868 { // FYI: Thread locked in CallOneParam.
1869 co_IntSendMessage((HWND)HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0);
1870 }
1871 }
1872 return Ret;
1873 }
1874
1875 BOOL
1876 APIENTRY
1877 NtUserDrawCaptionTemp(
1878 HWND hWnd,
1879 HDC hDC,
1880 LPCRECT lpRc,
1881 HFONT hFont,
1882 HICON hIcon,
1883 const PUNICODE_STRING str,
1884 UINT uFlags)
1885 {
1886 PWINDOW_OBJECT pWnd = NULL;
1887 UNICODE_STRING SafeStr = {0};
1888 NTSTATUS Status = STATUS_SUCCESS;
1889 RECTL SafeRect;
1890 BOOL Ret;
1891
1892 UserEnterExclusive();
1893
1894 if (hWnd != NULL)
1895 {
1896 if(!(pWnd = UserGetWindowObject(hWnd)))
1897 {
1898 UserLeave();
1899 return FALSE;
1900 }
1901 }
1902
1903 _SEH2_TRY
1904 {
1905 ProbeForRead(lpRc, sizeof(RECTL), sizeof(ULONG));
1906 RtlCopyMemory(&SafeRect, lpRc, sizeof(RECTL));
1907 if (str != NULL)
1908 {
1909 SafeStr = ProbeForReadUnicodeString(str);
1910 if (SafeStr.Length != 0)
1911 {
1912 ProbeForRead( SafeStr.Buffer,
1913 SafeStr.Length,
1914 sizeof(WCHAR));
1915 }
1916 }
1917 }
1918 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1919 {
1920 Status = _SEH2_GetExceptionCode();
1921 }
1922 _SEH2_END;
1923
1924 if (Status != STATUS_SUCCESS)
1925 {
1926 SetLastNtError(Status);
1927 UserLeave();
1928 return FALSE;
1929 }
1930
1931 if (str != NULL)
1932 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags);
1933 else
1934 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags);
1935
1936 UserLeave();
1937 return Ret;
1938 }
1939
1940 BOOL
1941 APIENTRY
1942 NtUserDrawCaption(HWND hWnd,
1943 HDC hDC,
1944 LPCRECT lpRc,
1945 UINT uFlags)
1946 {
1947 return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
1948 }
1949
1950 BOOL
1951 APIENTRY
1952 NtUserInvalidateRect(
1953 HWND hWnd,
1954 CONST RECT *lpUnsafeRect,
1955 BOOL bErase)
1956 {
1957 return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
1958 }
1959
1960 BOOL
1961 APIENTRY
1962 NtUserInvalidateRgn(
1963 HWND hWnd,
1964 HRGN hRgn,
1965 BOOL bErase)
1966 {
1967 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
1968 }
1969
1970 /* EOF */