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