[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 GreDeleteObject(hRgnNonClient);
161 return HRGN_WINDOW;
162 }
163
164 RgnType = NtGdiCombineRgn(hRgnNonClient, hRgnNonClient,
165 hRgnWindow, RGN_DIFF);
166 if (RgnType == ERROR)
167 {
168 GreDeleteObject(hRgnWindow);
169 GreDeleteObject(hRgnNonClient);
170 return HRGN_WINDOW;
171 }
172 else if (RgnType == NULLREGION)
173 {
174 GreDeleteObject(hRgnWindow);
175 GreDeleteObject(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 GreDeleteObject(Window->hrgnUpdate);
191 Window->hrgnUpdate = NULL;
192 if (!(Window->state & WNDS_INTERNALPAINT))
193 MsqDecPaintCountQueue(Window->head.pti->MessageQueue);
194 }
195 }
196
197 GreDeleteObject(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 }
246
247 if (Wnd->state & WNDS_SENDERASEBACKGROUND)
248 {
249 if (Wnd->hrgnUpdate)
250 {
251 hDC = UserGetDCEx( Wnd,
252 Wnd->hrgnUpdate,
253 DCX_CACHE|DCX_USESTYLE|DCX_INTERSECTRGN|DCX_KEEPCLIPRGN);
254
255 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
256 // Kill the loop, so Clear before we send.
257 if (!co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
258 {
259 Wnd->state |= (WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
260 }
261 UserReleaseDC(Wnd, hDC, FALSE);
262 }
263 }
264 }
265 }
266
267 /*
268 * Check that the window is still valid at this point
269 */
270 if (!IntIsWindow(hWnd))
271 {
272 return;
273 }
274
275 /*
276 * Paint child windows.
277 */
278 if (!(Flags & RDW_NOCHILDREN) &&
279 !(Wnd->style & WS_MINIMIZE) &&
280 ((Flags & RDW_ALLCHILDREN) || !(Wnd->style & WS_CLIPCHILDREN)) )
281 {
282 HWND *List, *phWnd;
283
284 if ((List = IntWinListChildren(Wnd)))
285 {
286 /* FIXME: Handle WS_EX_TRANSPARENT */
287 for (phWnd = List; *phWnd; ++phWnd)
288 {
289 Wnd = UserGetWindowObject(*phWnd);
290 if (Wnd && (Wnd->style & WS_VISIBLE))
291 {
292 USER_REFERENCE_ENTRY Ref;
293 UserRefObjectCo(Wnd, &Ref);
294 co_IntPaintWindows(Wnd, Flags, TRUE);
295 UserDerefObjectCo(Wnd);
296 }
297 }
298 ExFreePool(List);
299 }
300 }
301 }
302
303 /*
304 * IntInvalidateWindows
305 *
306 * Internal function used by IntRedrawWindow, UserRedrawDesktop,
307 * co_WinPosSetWindowPos, IntValidateParent, co_UserRedrawWindow.
308 */
309 VOID FASTCALL
310 IntInvalidateWindows(PWND Wnd, HRGN hRgn, ULONG Flags)
311 {
312 INT RgnType;
313 BOOL HadPaintMessage, HadNCPaintMessage;
314 BOOL HasPaintMessage, HasNCPaintMessage;
315
316 DPRINT("IntInvalidateWindows start\n");
317 /*
318 * If the nonclient is not to be redrawn, clip the region to the client
319 * rect
320 */
321 if (0 != (Flags & RDW_INVALIDATE) && 0 == (Flags & RDW_FRAME))
322 {
323 HRGN hRgnClient;
324
325 hRgnClient = IntSysCreateRectRgnIndirect(&Wnd->rcClient);
326 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnClient, RGN_AND);
327 GreDeleteObject(hRgnClient);
328 }
329
330 /*
331 * Clip the given region with window rectangle (or region)
332 */
333
334 if (!Wnd->hrgnClip || (Wnd->style & WS_MINIMIZE))
335 {
336 HRGN hRgnWindow;
337
338 hRgnWindow = IntSysCreateRectRgnIndirect(&Wnd->rcWindow);
339 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
340 GreDeleteObject(hRgnWindow);
341 }
342 else
343 {
344 NtGdiOffsetRgn( hRgn,
345 -Wnd->rcWindow.left,
346 -Wnd->rcWindow.top);
347 RgnType = NtGdiCombineRgn(hRgn, hRgn, Wnd->hrgnClip, RGN_AND);
348 NtGdiOffsetRgn( hRgn,
349 Wnd->rcWindow.left,
350 Wnd->rcWindow.top);
351 }
352
353 /*
354 * Save current state of pending updates
355 */
356
357 HadPaintMessage = Wnd->hrgnUpdate != NULL ||
358 Wnd->state & WNDS_INTERNALPAINT;
359 HadNCPaintMessage = Wnd->state & WNDS_SENDNCPAINT;
360
361 /*
362 * Update the region and flags
363 */
364
365 if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
366 {
367 if (Wnd->hrgnUpdate == NULL)
368 {
369 Wnd->hrgnUpdate = IntSysCreateRectRgn(0, 0, 0, 0);
370 IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_PUBLIC);
371 }
372
373 if (NtGdiCombineRgn(Wnd->hrgnUpdate, Wnd->hrgnUpdate,
374 hRgn, RGN_OR) == NULLREGION)
375 {
376 IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
377 GreDeleteObject(Wnd->hrgnUpdate);
378 Wnd->hrgnUpdate = NULL;
379 }
380
381 if (Flags & RDW_FRAME)
382 Wnd->state |= WNDS_SENDNCPAINT;
383 if (Flags & RDW_ERASE)
384 Wnd->state |= WNDS_SENDERASEBACKGROUND;
385
386 Flags |= RDW_FRAME;
387 }
388
389 if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
390 {
391 if (Wnd->hrgnUpdate != NULL)
392 {
393 if (NtGdiCombineRgn(Wnd->hrgnUpdate, Wnd->hrgnUpdate,
394 hRgn, RGN_DIFF) == NULLREGION)
395 {
396 IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
397 GreDeleteObject(Wnd->hrgnUpdate);
398 Wnd->hrgnUpdate = NULL;
399 }
400 }
401
402 if (Wnd->hrgnUpdate == NULL)
403 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
404 if (Flags & RDW_NOFRAME)
405 Wnd->state &= ~WNDS_SENDNCPAINT;
406 if (Flags & RDW_NOERASE)
407 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
408 }
409
410 if (Flags & RDW_INTERNALPAINT)
411 {
412 Wnd->state |= WNDS_INTERNALPAINT;
413 }
414
415 if (Flags & RDW_NOINTERNALPAINT)
416 {
417 Wnd->state &= ~WNDS_INTERNALPAINT;
418 }
419
420 /*
421 * Process children if needed
422 */
423
424 if (!(Flags & RDW_NOCHILDREN) && !(Wnd->style & WS_MINIMIZE) &&
425 ((Flags & RDW_ALLCHILDREN) || !(Wnd->style & WS_CLIPCHILDREN)))
426 {
427 PWND Child;
428
429 for (Child = Wnd->spwndChild; Child; Child = Child->spwndNext)
430 {
431 if (Child->style & WS_VISIBLE)
432 {
433 /*
434 * Recursive call to update children hrgnUpdate
435 */
436 HRGN hRgnTemp = IntSysCreateRectRgn(0, 0, 0, 0);
437 NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
438 IntInvalidateWindows(Child, hRgnTemp, Flags);
439 GreDeleteObject(hRgnTemp);
440 }
441
442 }
443 }
444
445 /*
446 * Fake post paint messages to window message queue if needed
447 */
448
449 HasPaintMessage = Wnd->hrgnUpdate != NULL ||
450 Wnd->state & WNDS_INTERNALPAINT;
451 HasNCPaintMessage = Wnd->state & WNDS_SENDNCPAINT;
452
453 if (HasPaintMessage != HadPaintMessage)
454 {
455 if (HadPaintMessage)
456 MsqDecPaintCountQueue(Wnd->head.pti->MessageQueue);
457 else
458 MsqIncPaintCountQueue(Wnd->head.pti->MessageQueue);
459 }
460
461 if (HasNCPaintMessage != HadNCPaintMessage)
462 {
463 if (HadNCPaintMessage)
464 MsqDecPaintCountQueue(Wnd->head.pti->MessageQueue);
465 else
466 MsqIncPaintCountQueue(Wnd->head.pti->MessageQueue);
467 }
468 DPRINT("IntInvalidateWindows exit\n");
469 }
470
471 /*
472 * IntIsWindowDrawable
473 *
474 * Remarks
475 * Window is drawable when it is visible and all parents are not
476 * minimized.
477 */
478
479 BOOL FASTCALL
480 IntIsWindowDrawable(PWND Wnd)
481 {
482 PWND WndObject;
483
484 for (WndObject = Wnd; WndObject != NULL; WndObject = WndObject->spwndParent)
485 {
486 if ( WndObject->state2 & WNDS2_INDESTROY ||
487 WndObject->state & WNDS_DESTROYED ||
488 !WndObject ||
489 !(WndObject->style & WS_VISIBLE) ||
490 ((WndObject->style & WS_MINIMIZE) && (WndObject != Wnd)))
491 {
492 return FALSE;
493 }
494 }
495
496 return TRUE;
497 }
498
499 /*
500 * IntRedrawWindow
501 *
502 * Internal version of NtUserRedrawWindow that takes WND as
503 * first parameter.
504 */
505
506 BOOL FASTCALL
507 co_UserRedrawWindow(
508 PWND Window,
509 const RECTL* UpdateRect,
510 HRGN UpdateRgn,
511 ULONG Flags)
512 {
513 HRGN hRgn = NULL;
514 DPRINT("co_UserRedrawWindow start\n");
515
516 /*
517 * Step 1.
518 * Validation of passed parameters.
519 */
520
521 if (!IntIsWindowDrawable(Window))
522 {
523 return TRUE; // Just do nothing!!!
524 }
525
526 /*
527 * Step 2.
528 * Transform the parameters UpdateRgn and UpdateRect into
529 * a region hRgn specified in screen coordinates.
530 */
531
532 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE)) // Both are OKAY!
533 {
534 if (UpdateRgn != NULL)
535 {
536 hRgn = IntSysCreateRectRgn(0, 0, 0, 0);
537 if (NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY) == NULLREGION)
538 {
539 GreDeleteObject(hRgn);
540 hRgn = NULL;
541 }
542 else
543 NtGdiOffsetRgn(hRgn, Window->rcClient.left, Window->rcClient.top);
544 }
545 else if (UpdateRect != NULL)
546 {
547 if (!RECTL_bIsEmptyRect(UpdateRect))
548 {
549 hRgn = IntSysCreateRectRgnIndirect((RECTL *)UpdateRect);
550 NtGdiOffsetRgn(hRgn, Window->rcClient.left, Window->rcClient.top);
551 }
552 }
553 else if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
554 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
555 {
556 if (!RECTL_bIsEmptyRect(&Window->rcWindow))
557 hRgn = IntSysCreateRectRgnIndirect(&Window->rcWindow);
558 }
559 else
560 {
561 if (!RECTL_bIsEmptyRect(&Window->rcClient))
562 hRgn = IntSysCreateRectRgnIndirect(&Window->rcClient);
563 }
564 }
565
566 /*
567 * Step 3.
568 * Adjust the window update region depending on hRgn and flags.
569 */
570
571 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT) &&
572 hRgn != NULL)
573 {
574 IntInvalidateWindows(Window, hRgn, Flags);
575 }
576
577 /*
578 * Step 4.
579 * Repaint and erase windows if needed.
580 */
581
582 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
583 {
584 co_IntPaintWindows(Window, Flags, FALSE);
585 }
586
587 /*
588 * Step 5.
589 * Cleanup ;-)
590 */
591
592 if (hRgn != NULL)
593 {
594 GreDeleteObject(hRgn);
595 }
596 DPRINT("co_UserRedrawWindow exit\n");
597
598 return TRUE;
599 }
600
601 BOOL FASTCALL
602 IntIsWindowDirty(PWND Wnd)
603 {
604 return (Wnd->style & WS_VISIBLE) &&
605 ((Wnd->hrgnUpdate != NULL) ||
606 (Wnd->state & WNDS_INTERNALPAINT) ||
607 (Wnd->state & WNDS_SENDNCPAINT));
608 }
609
610 HWND FASTCALL
611 IntFindWindowToRepaint(PWND Window, PTHREADINFO Thread)
612 {
613 HWND hChild;
614 PWND TempWindow;
615
616 for (; Window != NULL; Window = Window->spwndNext)
617 {
618 if (IntWndBelongsToThread(Window, Thread) &&
619 IntIsWindowDirty(Window))
620 {
621 /* Make sure all non-transparent siblings are already drawn. */
622 if (Window->ExStyle & WS_EX_TRANSPARENT)
623 {
624 for (TempWindow = Window->spwndNext; TempWindow != NULL;
625 TempWindow = TempWindow->spwndNext)
626 {
627 if (!(TempWindow->ExStyle & WS_EX_TRANSPARENT) &&
628 IntWndBelongsToThread(TempWindow, Thread) &&
629 IntIsWindowDirty(TempWindow))
630 {
631 return TempWindow->head.h;
632 }
633 }
634 }
635
636 return Window->head.h;
637 }
638
639 if (Window->spwndChild)
640 {
641 hChild = IntFindWindowToRepaint(Window->spwndChild, Thread);
642 if (hChild != NULL)
643 return hChild;
644 }
645 }
646
647 return NULL;
648 }
649
650 BOOL FASTCALL
651 IntGetPaintMessage(
652 PWND Window,
653 UINT MsgFilterMin,
654 UINT MsgFilterMax,
655 PTHREADINFO Thread,
656 MSG *Message,
657 BOOL Remove)
658 {
659 if (!Thread->cPaintsReady)
660 return FALSE;
661
662 if ((MsgFilterMin != 0 || MsgFilterMax != 0) &&
663 (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT))
664 return FALSE;
665
666 Message->hwnd = IntFindWindowToRepaint(UserGetDesktopWindow(), PsGetCurrentThreadWin32Thread());
667
668 if (Message->hwnd == NULL)
669 {
670 DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found! Counts %d\n",Thread->cPaintsReady);
671 /* Hack to stop spamming the debuglog ! */
672 Thread->cPaintsReady = 0;
673 return FALSE;
674 }
675
676 if (Window != NULL && Message->hwnd != Window->head.h)
677 return FALSE;
678
679 Message->message = WM_PAINT;
680 Message->wParam = Message->lParam = 0;
681
682 return TRUE;
683 }
684
685 static
686 HWND FASTCALL
687 co_IntFixCaret(PWND Window, RECTL *lprc, UINT flags)
688 {
689 PDESKTOP Desktop;
690 PTHRDCARETINFO CaretInfo;
691 PTHREADINFO pti;
692 PUSER_MESSAGE_QUEUE ActiveMessageQueue;
693 HWND hWndCaret;
694 PWND WndCaret;
695
696 ASSERT_REFS_CO(Window);
697
698 pti = PsGetCurrentThreadWin32Thread();
699 Desktop = pti->rpdesk;
700 ActiveMessageQueue = Desktop->ActiveMessageQueue;
701 if (!ActiveMessageQueue) return 0;
702 CaretInfo = ActiveMessageQueue->CaretInfo;
703 hWndCaret = CaretInfo->hWnd;
704
705 WndCaret = UserGetWindowObject(hWndCaret);
706
707 //fix: check for WndCaret can be null
708 if (WndCaret == Window ||
709 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(Window, WndCaret)))
710 {
711 POINT pt, FromOffset, ToOffset;
712 RECTL rcCaret;
713
714 pt.x = CaretInfo->Pos.x;
715 pt.y = CaretInfo->Pos.y;
716 IntGetClientOrigin(WndCaret, &FromOffset);
717 IntGetClientOrigin(Window, &ToOffset);
718 rcCaret.left = pt.x;
719 rcCaret.top = pt.y;
720 rcCaret.right = pt.x + CaretInfo->Size.cx;
721 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
722 if (RECTL_bIntersectRect(lprc, lprc, &rcCaret))
723 {
724 co_UserHideCaret(0);
725 lprc->left = pt.x;
726 lprc->top = pt.y;
727 return hWndCaret;
728 }
729 }
730
731 return 0;
732 }
733
734 BOOL
735 FASTCALL
736 IntPrintWindow(
737 PWND pwnd,
738 HDC hdcBlt,
739 UINT nFlags)
740 {
741 HDC hdcSrc;
742 INT cx, cy, xSrc, ySrc;
743
744 if ( nFlags & PW_CLIENTONLY)
745 {
746 cx = pwnd->rcClient.right - pwnd->rcClient.left;
747 cy = pwnd->rcClient.bottom - pwnd->rcClient.top;
748 xSrc = pwnd->rcClient.left - pwnd->rcWindow.left;
749 ySrc = pwnd->rcClient.top - pwnd->rcWindow.top;
750 }
751 else
752 {
753 cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
754 cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
755 xSrc = 0;
756 ySrc = 0;
757 }
758
759 // TODO: Setup Redirection for Print.
760 return FALSE;
761
762 /* Update the window just incase. */
763 co_IntPaintWindows( pwnd, RDW_ERASENOW|RDW_UPDATENOW, FALSE);
764
765 hdcSrc = UserGetDCEx( pwnd, NULL, DCX_CACHE|DCX_WINDOW);
766 /* Print window to printer context. */
767 NtGdiBitBlt( hdcBlt,
768 0,
769 0,
770 cx,
771 cy,
772 hdcSrc,
773 xSrc,
774 ySrc,
775 SRCCOPY,
776 0,
777 0);
778
779 UserReleaseDC( pwnd, hdcSrc, FALSE);
780
781 // TODO: Release Redirection from Print.
782
783 return TRUE;
784 }
785
786 /* PUBLIC FUNCTIONS ***********************************************************/
787
788 /*
789 * NtUserBeginPaint
790 *
791 * Status
792 * @implemented
793 */
794
795 HDC APIENTRY
796 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
797 {
798 PWND Window = NULL;
799 PAINTSTRUCT Ps;
800 NTSTATUS Status;
801 DECLARE_RETURN(HDC);
802 USER_REFERENCE_ENTRY Ref;
803
804 DPRINT("Enter NtUserBeginPaint\n");
805 UserEnterExclusive();
806
807 if (!(Window = UserGetWindowObject(hWnd)))
808 {
809 RETURN( NULL);
810 }
811
812 UserRefObjectCo(Window, &Ref);
813
814 co_UserHideCaret(Window);
815
816 if (Window->state & WNDS_SENDNCPAINT)
817 {
818 HRGN hRgn;
819
820 hRgn = IntGetNCUpdateRgn(Window, FALSE);
821 Window->state &= ~WNDS_SENDNCPAINT;
822 MsqDecPaintCountQueue(Window->head.pti->MessageQueue);
823 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);
824 if (hRgn != HRGN_WINDOW && hRgn != NULL && GreIsHandleValid(hRgn))
825 {
826 /* NOTE: The region can already by deleted! */
827 GreDeleteObject(hRgn);
828 }
829 }
830
831 RtlZeroMemory(&Ps, sizeof(PAINTSTRUCT));
832
833 Ps.hdc = UserGetDCEx( Window,
834 Window->hrgnUpdate,
835 DCX_INTERSECTRGN | DCX_USESTYLE);
836 if (!Ps.hdc)
837 {
838 RETURN(NULL);
839 }
840
841 if (Window->hrgnUpdate != NULL)
842 {
843 MsqDecPaintCountQueue(Window->head.pti->MessageQueue);
844 GdiGetClipBox(Ps.hdc, &Ps.rcPaint);
845 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
846 /* The region is part of the dc now and belongs to the process! */
847 Window->hrgnUpdate = NULL;
848 }
849 else
850 {
851 if (Window->state & WNDS_INTERNALPAINT)
852 MsqDecPaintCountQueue(Window->head.pti->MessageQueue);
853
854 IntGetClientRect(Window, &Ps.rcPaint);
855 }
856
857 Window->state &= ~WNDS_INTERNALPAINT;
858
859 if (Window->state & WNDS_SENDERASEBACKGROUND)
860 {
861 Window->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
862 Ps.fErase = !co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)Ps.hdc, 0);
863 if ( Ps.fErase )
864 {
865 Window->state |= (WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
866 }
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, hrgnTmp;
1234 PREGION prgnTmp;
1235
1236 pDC = DC_LockDc(hDC);
1237 if (!pDC)
1238 {
1239 return FALSE;
1240 }
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 DC_UnlockDc(pDC);
1252 return ERROR;
1253 }
1254 }
1255 else
1256 {
1257 hrgnOwn = IntSysCreateRectRgnIndirect(&rcDst);
1258 }
1259
1260 /* Add the source rect */
1261 hrgnTmp = IntSysCreateRectRgnIndirect(&rcSrc);
1262 NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_OR);
1263
1264 /* Substract the part of the dest that was visible in source */
1265 prgnTmp = RGNOBJAPI_Lock(hrgnTmp, NULL);
1266 IntGdiCombineRgn(prgnTmp, prgnTmp, pDC->prgnVis, RGN_AND);
1267 RGNOBJAPI_Unlock(prgnTmp);
1268 NtGdiOffsetRgn(hrgnTmp, dx, dy);
1269 Result = NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_DIFF);
1270
1271 /* DO NOT Unlock DC while messing with prgnVis! */
1272 DC_UnlockDc(pDC);
1273
1274 GreDeleteObject(hrgnTmp);
1275
1276 if (prcUpdate)
1277 {
1278 IntGdiGetRgnBox(hrgnOwn, prcUpdate);
1279 }
1280
1281 if (!hrgnUpdate)
1282 {
1283 GreDeleteObject(hrgnOwn);
1284 }
1285 }
1286 else
1287 Result = NULLREGION;
1288
1289 return Result;
1290 }
1291
1292 /*
1293 * NtUserScrollDC
1294 *
1295 * Status
1296 * @implemented
1297 */
1298 BOOL APIENTRY
1299 NtUserScrollDC(
1300 HDC hDC,
1301 INT dx,
1302 INT dy,
1303 const RECT *prcUnsafeScroll,
1304 const RECT *prcUnsafeClip,
1305 HRGN hrgnUpdate,
1306 LPRECT prcUnsafeUpdate)
1307 {
1308 DECLARE_RETURN(DWORD);
1309 RECTL rcScroll, rcClip, rcUpdate;
1310 NTSTATUS Status = STATUS_SUCCESS;
1311 DWORD Result;
1312
1313 DPRINT("Enter NtUserScrollDC\n");
1314 UserEnterExclusive();
1315
1316 _SEH2_TRY
1317 {
1318 if (prcUnsafeScroll)
1319 {
1320 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1321 rcScroll = *prcUnsafeScroll;
1322 }
1323 if (prcUnsafeClip)
1324 {
1325 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1326 rcClip = *prcUnsafeClip;
1327 }
1328 if (prcUnsafeUpdate)
1329 {
1330 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1331 }
1332 }
1333 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1334 {
1335 Status = _SEH2_GetExceptionCode();
1336 }
1337 _SEH2_END
1338 if (!NT_SUCCESS(Status))
1339 {
1340 SetLastNtError(Status);
1341 RETURN(FALSE);
1342 }
1343
1344 Result = UserScrollDC( hDC,
1345 dx,
1346 dy,
1347 prcUnsafeScroll? &rcScroll : 0,
1348 prcUnsafeClip? &rcClip : 0,
1349 hrgnUpdate,
1350 prcUnsafeUpdate? &rcUpdate : NULL);
1351 if(Result == ERROR)
1352 {
1353 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
1354 RETURN(FALSE);
1355 }
1356
1357 if (prcUnsafeUpdate)
1358 {
1359 _SEH2_TRY
1360 {
1361 *prcUnsafeUpdate = rcUpdate;
1362 }
1363 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1364 {
1365 Status = _SEH2_GetExceptionCode();
1366 }
1367 _SEH2_END
1368 if (!NT_SUCCESS(Status))
1369 {
1370 /* FIXME: SetLastError? */
1371 /* FIXME: correct? We have already scrolled! */
1372 RETURN(FALSE);
1373 }
1374 }
1375
1376 RETURN(TRUE);
1377
1378 CLEANUP:
1379 DPRINT("Leave NtUserScrollDC, ret=%i\n",_ret_);
1380 UserLeave();
1381 END_CLEANUP;
1382 }
1383
1384 /*
1385 * NtUserScrollWindowEx
1386 *
1387 * Status
1388 * @implemented
1389 */
1390
1391 DWORD APIENTRY
1392 NtUserScrollWindowEx(
1393 HWND hWnd,
1394 INT dx,
1395 INT dy,
1396 const RECT *prcUnsafeScroll,
1397 const RECT *prcUnsafeClip,
1398 HRGN hrgnUpdate,
1399 LPRECT prcUnsafeUpdate,
1400 UINT flags)
1401 {
1402 RECTL rcScroll, rcClip, rcCaret, rcUpdate;
1403 INT Result;
1404 PWND Window = NULL, CaretWnd;
1405 HDC hDC;
1406 HRGN hrgnOwn = NULL, hrgnTemp;
1407 HWND hwndCaret;
1408 NTSTATUS Status = STATUS_SUCCESS;
1409 DECLARE_RETURN(DWORD);
1410 USER_REFERENCE_ENTRY Ref, CaretRef;
1411
1412 DPRINT("Enter NtUserScrollWindowEx\n");
1413 UserEnterExclusive();
1414
1415 Window = UserGetWindowObject(hWnd);
1416 if (!Window || !IntIsWindowDrawable(Window))
1417 {
1418 Window = NULL; /* prevent deref at cleanup */
1419 RETURN( ERROR);
1420 }
1421 UserRefObjectCo(Window, &Ref);
1422
1423 IntGetClientRect(Window, &rcClip);
1424
1425 _SEH2_TRY
1426 {
1427 if (prcUnsafeScroll)
1428 {
1429 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1430 RECTL_bIntersectRect(&rcScroll, &rcClip, prcUnsafeScroll);
1431 }
1432 else
1433 rcScroll = rcClip;
1434
1435 if (prcUnsafeClip)
1436 {
1437 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1438 RECTL_bIntersectRect(&rcClip, &rcClip, prcUnsafeClip);
1439 }
1440 }
1441 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1442 {
1443 Status = _SEH2_GetExceptionCode();
1444 }
1445 _SEH2_END
1446
1447 if (!NT_SUCCESS(Status))
1448 {
1449 SetLastNtError(Status);
1450 RETURN(ERROR);
1451 }
1452
1453 if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
1454 (dx == 0 && dy == 0))
1455 {
1456 RETURN(NULLREGION);
1457 }
1458
1459 if (hrgnUpdate)
1460 hrgnOwn = hrgnUpdate;
1461 else
1462 hrgnOwn = IntSysCreateRectRgn(0, 0, 0, 0);
1463
1464 hDC = UserGetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE);
1465 if (!hDC)
1466 {
1467 /* FIXME: SetLastError? */
1468 RETURN(ERROR);
1469 }
1470
1471 rcCaret = rcScroll;
1472 hwndCaret = co_IntFixCaret(Window, &rcCaret, flags);
1473
1474 Result = UserScrollDC( hDC,
1475 dx,
1476 dy,
1477 &rcScroll,
1478 &rcClip,
1479 hrgnOwn,
1480 prcUnsafeUpdate? &rcUpdate : NULL);
1481
1482 UserReleaseDC(Window, hDC, FALSE);
1483
1484 /*
1485 * Take into account the fact that some damage may have occurred during
1486 * the scroll.
1487 */
1488
1489 hrgnTemp = IntSysCreateRectRgn(0, 0, 0, 0);
1490 if (co_UserGetUpdateRgn(Window, hrgnTemp, FALSE) != NULLREGION)
1491 {
1492 HRGN hrgnClip = IntSysCreateRectRgnIndirect(&rcClip);
1493 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1494 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1495 co_UserRedrawWindow(Window, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1496 GreDeleteObject(hrgnClip);
1497 }
1498 GreDeleteObject(hrgnTemp);
1499
1500 if (flags & SW_SCROLLCHILDREN)
1501 {
1502 PWND Child;
1503 RECTL rcChild;
1504 POINT ClientOrigin;
1505 USER_REFERENCE_ENTRY WndRef;
1506 RECTL rcDummy;
1507
1508 IntGetClientOrigin(Window, &ClientOrigin);
1509 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
1510 {
1511 rcChild = Child->rcWindow;
1512 rcChild.left -= ClientOrigin.x;
1513 rcChild.top -= ClientOrigin.y;
1514 rcChild.right -= ClientOrigin.x;
1515 rcChild.bottom -= ClientOrigin.y;
1516
1517 if (! prcUnsafeScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll))
1518 {
1519 UserRefObjectCo(Child, &WndRef);
1520 co_WinPosSetWindowPos(Child, 0, rcChild.left + dx, rcChild.top + dy, 0, 0,
1521 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1522 SWP_NOREDRAW);
1523 UserDerefObjectCo(Child);
1524 }
1525 }
1526 }
1527
1528 if (flags & (SW_INVALIDATE | SW_ERASE))
1529 {
1530 co_UserRedrawWindow(Window, NULL, hrgnOwn, RDW_INVALIDATE | RDW_ERASE |
1531 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1532 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1533 }
1534
1535 if ((CaretWnd = UserGetWindowObject(hwndCaret)))
1536 {
1537 UserRefObjectCo(CaretWnd, &CaretRef);
1538
1539 co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
1540 co_UserShowCaret(CaretWnd);
1541
1542 UserDerefObjectCo(CaretWnd);
1543 }
1544
1545 if (prcUnsafeUpdate)
1546 {
1547 _SEH2_TRY
1548 {
1549 /* Probe here, to not fail on invalid pointer before scrolling */
1550 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1551 *prcUnsafeUpdate = rcUpdate;
1552 }
1553 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1554 {
1555 Status = _SEH2_GetExceptionCode();
1556 }
1557 _SEH2_END
1558
1559 if (!NT_SUCCESS(Status))
1560 {
1561 SetLastNtError(Status);
1562 RETURN(ERROR);
1563 }
1564 }
1565
1566 RETURN(Result);
1567
1568 CLEANUP:
1569 if (hrgnOwn && !hrgnUpdate)
1570 {
1571 GreDeleteObject(hrgnOwn);
1572 }
1573
1574 if (Window)
1575 UserDerefObjectCo(Window);
1576
1577 DPRINT("Leave NtUserScrollWindowEx, ret=%i\n",_ret_);
1578 UserLeave();
1579 END_CLEANUP;
1580 }
1581
1582
1583 BOOL
1584 UserDrawSysMenuButton(
1585 PWND pWnd,
1586 HDC hDc,
1587 RECTL *lpRc,
1588 BOOL Down)
1589 {
1590 HICON hIcon;
1591 PCURICON_OBJECT pIcon;
1592
1593 ASSERT(pWnd && lpRc);
1594
1595 /* Get the icon to draw. We don't care about WM_GETICON here. */
1596
1597 hIcon = pWnd->pcls->hIconSm;
1598
1599 if(!hIcon)
1600 {
1601 DPRINT("Wnd class has no small icon.\n");
1602 hIcon = pWnd->pcls->hIcon;
1603 }
1604
1605 if(!hIcon)
1606 {
1607 DPRINT("Wnd class hasn't any icon.\n");
1608 //FIXME: Draw "winlogo" icon.
1609 return FALSE;
1610 }
1611
1612 if(!(pIcon = UserGetCurIconObject(hIcon)))
1613 {
1614 DPRINT1("UserGetCurIconObject() failed!\n");
1615 return FALSE;
1616 }
1617
1618 return UserDrawIconEx(hDc, lpRc->left, lpRc->top, pIcon,
1619 UserGetSystemMetrics(SM_CXSMICON),
1620 UserGetSystemMetrics(SM_CYSMICON),
1621 0, NULL, DI_NORMAL);
1622 }
1623
1624 BOOL
1625 UserDrawCaptionText(
1626 HDC hDc,
1627 const PUNICODE_STRING Text,
1628 const RECTL *lpRc,
1629 UINT uFlags)
1630 {
1631 HFONT hOldFont = NULL, hFont = NULL;
1632 COLORREF OldTextColor;
1633 NONCLIENTMETRICSW nclm;
1634 NTSTATUS Status;
1635 #ifndef NDEBUG
1636 INT i;
1637 DPRINT("%s:", __FUNCTION__);
1638 for(i = 0; i < Text->Length/sizeof(WCHAR); i++)
1639 DbgPrint("%C", Text->Buffer[i]);
1640 DbgPrint(", %d\n", Text->Length/sizeof(WCHAR));
1641 #endif
1642
1643 nclm.cbSize = sizeof(nclm);
1644 if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS,
1645 sizeof(NONCLIENTMETRICS), &nclm, 0))
1646 {
1647 DPRINT1("%s: UserSystemParametersInfo() failed!\n", __FUNCTION__);
1648 return FALSE;
1649 }
1650
1651 IntGdiSetBkMode(hDc, TRANSPARENT);
1652
1653 if(uFlags & DC_SMALLCAP)
1654 Status = TextIntCreateFontIndirect(&nclm.lfSmCaptionFont, &hFont);
1655 else Status = TextIntCreateFontIndirect(&nclm.lfCaptionFont, &hFont);
1656
1657 if(!NT_SUCCESS(Status))
1658 {
1659 DPRINT1("%s: TextIntCreateFontIndirect() failed! Status: 0x%x\n",
1660 __FUNCTION__, Status);
1661 return FALSE;
1662 }
1663
1664 hOldFont = NtGdiSelectFont(hDc, hFont);
1665 if(!hOldFont)
1666 {
1667 DPRINT1("%s: SelectFont() failed!\n", __FUNCTION__);
1668 GreDeleteObject(hFont);
1669 return FALSE;
1670 }
1671
1672 if(uFlags & DC_INBUTTON)
1673 OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT));
1674 else OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(uFlags & DC_ACTIVE
1675 ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
1676
1677 //FIXME: If string doesn't fit to rc, truncate it and add ellipsis.
1678
1679 GreExtTextOutW(hDc, lpRc->left,
1680 lpRc->top, 0, NULL, Text->Buffer,
1681 Text->Length/sizeof(WCHAR), NULL, 0);
1682
1683 IntGdiSetTextColor(hDc, OldTextColor);
1684 NtGdiSelectFont(hDc, hOldFont);
1685 GreDeleteObject(hFont);
1686
1687 return TRUE;
1688 }
1689
1690 BOOL UserDrawCaption(
1691 PWND pWnd,
1692 HDC hDc,
1693 RECTL *lpRc,
1694 HFONT hFont,
1695 HICON hIcon,
1696 const PUNICODE_STRING str,
1697 UINT uFlags)
1698 {
1699 BOOL Ret = FALSE;
1700 HBITMAP hMemBmp = NULL, hOldBmp = NULL;
1701 HBRUSH hOldBrush = NULL;
1702 HDC hMemDc = NULL;
1703 ULONG Height;
1704 UINT VCenter = 0, Padding = 0;
1705 RECTL r = *lpRc;
1706 LONG ButtonWidth, IconWidth;
1707 BOOL HasIcon;
1708
1709 //ASSERT(pWnd != NULL);
1710
1711 RECTL_vMakeWellOrdered(lpRc);
1712 hMemBmp = NtGdiCreateCompatibleBitmap(hDc,
1713 lpRc->right - lpRc->left,
1714 lpRc->bottom - lpRc->top);
1715
1716 if(!hMemBmp)
1717 {
1718 DPRINT1("%s: NtGdiCreateCompatibleBitmap() failed!\n", __FUNCTION__);
1719 return FALSE;
1720 }
1721
1722 hMemDc = NtGdiCreateCompatibleDC(hDc);
1723 if(!hMemDc)
1724 {
1725 DPRINT1("%s: NtGdiCreateCompatibleDC() failed!\n", __FUNCTION__);
1726 goto cleanup;
1727 }
1728
1729 hOldBmp = NtGdiSelectBitmap(hMemDc, hMemBmp);
1730 if(!hOldBmp)
1731 {
1732 DPRINT1("%s: NtGdiSelectBitmap() failed!\n", __FUNCTION__);
1733 goto cleanup;
1734 }
1735
1736 Height = UserGetSystemMetrics(SM_CYCAPTION) - 1;
1737 VCenter = (lpRc->bottom - lpRc->top) / 2;
1738 Padding = VCenter - (Height / 2);
1739
1740 if ((!hIcon) && (pWnd != NULL))
1741 {
1742 HasIcon = (uFlags & DC_ICON) && (pWnd->style & WS_SYSMENU)
1743 && !(uFlags & DC_SMALLCAP) && !(pWnd->ExStyle & WS_EX_DLGMODALFRAME)
1744 && !(pWnd->ExStyle & WS_EX_TOOLWINDOW);
1745 }
1746 else
1747 HasIcon = (hIcon != 0);
1748
1749 IconWidth = UserGetSystemMetrics(SM_CXSIZE) + Padding;
1750
1751 r.left = Padding;
1752 r.right = r.left + (lpRc->right - lpRc->left);
1753 r.top = Padding;
1754 r.bottom = r.top + (Height / 2);
1755
1756 // Draw the caption background
1757 if(uFlags & DC_INBUTTON)
1758 {
1759 hOldBrush = NtGdiSelectBrush(hMemDc,
1760 IntGetSysColorBrush(COLOR_3DFACE));
1761
1762 if(!hOldBrush)
1763 {
1764 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1765 goto cleanup;
1766 }
1767
1768 if(!NtGdiPatBlt(hMemDc, 0, 0,
1769 lpRc->right - lpRc->left,
1770 lpRc->bottom - lpRc->top,
1771 PATCOPY))
1772 {
1773 DPRINT1("%s: NtGdiPatBlt() failed!\n", __FUNCTION__);
1774 goto cleanup;
1775 }
1776
1777 if(HasIcon) r.left+=IconWidth;
1778 }
1779 else
1780 {
1781 r.right = (lpRc->right - lpRc->left);
1782 if(uFlags & DC_SMALLCAP)
1783 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1784 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1785
1786 hOldBrush = NtGdiSelectBrush(hMemDc,
1787 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1788 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION));
1789
1790 if(!hOldBrush)
1791 {
1792 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1793 goto cleanup;
1794 }
1795
1796 if(HasIcon && (uFlags & DC_GRADIENT))
1797 {
1798 NtGdiPatBlt(hMemDc, 0, 0,
1799 IconWidth+1,
1800 lpRc->bottom - lpRc->top,
1801 PATCOPY);
1802 r.left+=IconWidth;
1803 }
1804 else
1805 {
1806 NtGdiPatBlt(hMemDc, 0, 0,
1807 lpRc->right - lpRc->left,
1808 lpRc->bottom - lpRc->top,
1809 PATCOPY);
1810 }
1811
1812 if(uFlags & DC_GRADIENT)
1813 {
1814 static GRADIENT_RECT gcap = {0, 1};
1815 TRIVERTEX vert[2];
1816 COLORREF Colors[2];
1817
1818 if (pWnd != NULL)
1819 {
1820 if(pWnd->style & WS_SYSMENU)
1821 {
1822 r.right -= 3 + ButtonWidth;
1823 if(!(uFlags & DC_SMALLCAP))
1824 {
1825 if(pWnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1826 r.right -= 2 + 2 * ButtonWidth;
1827 else r.right -= 2;
1828 r.right -= 2;
1829 }
1830
1831 //Draw buttons background
1832 if(!NtGdiSelectBrush(hMemDc,
1833 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1834 COLOR_GRADIENTACTIVECAPTION:COLOR_GRADIENTINACTIVECAPTION)))
1835 {
1836 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1837 goto cleanup;
1838 }
1839
1840 NtGdiPatBlt(hMemDc,
1841 r.right,
1842 0,
1843 lpRc->right - lpRc->left - r.right,
1844 lpRc->bottom - lpRc->top,
1845 PATCOPY);
1846 }
1847 }
1848
1849 Colors[0] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1850 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION);
1851
1852 Colors[1] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1853 COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION);
1854
1855 vert[0].x = r.left;
1856 vert[0].y = 0;
1857 vert[0].Red = (WORD)Colors[0]<<8;
1858 vert[0].Green = (WORD)Colors[0] & 0xFF00;
1859 vert[0].Blue = (WORD)(Colors[0]>>8) & 0xFF00;
1860 vert[0].Alpha = 0;
1861
1862 vert[1].x = r.right;
1863 vert[1].y = lpRc->bottom - lpRc->top;
1864 vert[1].Red = (WORD)Colors[1]<<8;
1865 vert[1].Green = (WORD)Colors[1] & 0xFF00;
1866 vert[1].Blue = (WORD)(Colors[1]>>8) & 0xFF00;
1867 vert[1].Alpha = 0;
1868
1869 if(!GreGradientFill(hMemDc, vert, 2, &gcap,
1870 1, GRADIENT_FILL_RECT_H))
1871 {
1872 DPRINT1("%s: IntGdiGradientFill() failed!\n", __FUNCTION__);
1873 }
1874
1875 } //if(uFlags & DC_GRADIENT)
1876 }
1877
1878 if(HasIcon)
1879 {
1880 r.top ++;
1881 r.left -= --IconWidth;
1882 /* FIXME: Draw the Icon when pWnd == NULL but hIcon is valid */
1883 if (pWnd != NULL)
1884 UserDrawSysMenuButton(pWnd, hMemDc, &r, FALSE);
1885
1886 r.left += IconWidth;
1887 r.top --;
1888 }
1889
1890 r.top ++;
1891 r.left += 2;
1892
1893 r.bottom = r.top + Height;
1894
1895 if((uFlags & DC_TEXT))
1896 {
1897 if(!(uFlags & DC_GRADIENT))
1898 {
1899 r.right = (lpRc->right - lpRc->left);
1900
1901 if(uFlags & DC_SMALLCAP)
1902 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1903 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1904
1905 if ((pWnd != NULL) && (pWnd->style & WS_SYSMENU))
1906 {
1907 r.right -= 3 + ButtonWidth;
1908 if(! (uFlags & DC_SMALLCAP))
1909 {
1910 if(pWnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1911 r.right -= 2 + 2 * ButtonWidth;
1912 else r.right -= 2;
1913 r.right -= 2;
1914 }
1915 }
1916 }
1917
1918 /* FIXME: hFont isn't handled */
1919 if (str)
1920 UserDrawCaptionText(hMemDc, str, &r, uFlags);
1921 else if (pWnd != NULL)
1922 {
1923 UNICODE_STRING ustr;
1924 ustr.Buffer = pWnd->strName.Buffer;
1925 ustr.Length = pWnd->strName.Length;
1926 ustr.MaximumLength = pWnd->strName.MaximumLength;
1927 UserDrawCaptionText(hMemDc, &ustr, &r, uFlags);
1928 }
1929 }
1930
1931 if(!NtGdiBitBlt(hDc, lpRc->left, lpRc->top,
1932 lpRc->right - lpRc->left, lpRc->bottom - lpRc->top,
1933 hMemDc, 0, 0, SRCCOPY, 0, 0))
1934 {
1935 DPRINT1("%s: NtGdiBitBlt() failed!\n", __FUNCTION__);
1936 goto cleanup;
1937 }
1938
1939 Ret = TRUE;
1940
1941 cleanup:
1942 if (hOldBrush) NtGdiSelectBrush(hMemDc, hOldBrush);
1943 if (hOldBmp) NtGdiSelectBitmap(hMemDc, hOldBmp);
1944 if (hMemBmp) GreDeleteObject(hMemBmp);
1945 if (hMemDc) NtGdiDeleteObjectApp(hMemDc);
1946
1947 return Ret;
1948 }
1949
1950 INT
1951 FASTCALL
1952 UserRealizePalette(HDC hdc)
1953 {
1954 HWND hWnd;
1955 DWORD Ret;
1956
1957 Ret = IntGdiRealizePalette(hdc);
1958 if (Ret) // There was a change.
1959 {
1960 hWnd = IntWindowFromDC(hdc);
1961 if (hWnd) // Send broadcast if dc is associated with a window.
1962 { // FYI: Thread locked in CallOneParam.
1963 UserSendNotifyMessage((HWND)HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0);
1964 }
1965 }
1966 return Ret;
1967 }
1968
1969 BOOL
1970 APIENTRY
1971 NtUserDrawCaptionTemp(
1972 HWND hWnd,
1973 HDC hDC,
1974 LPCRECT lpRc,
1975 HFONT hFont,
1976 HICON hIcon,
1977 const PUNICODE_STRING str,
1978 UINT uFlags)
1979 {
1980 PWND pWnd = NULL;
1981 UNICODE_STRING SafeStr = {0};
1982 NTSTATUS Status = STATUS_SUCCESS;
1983 RECTL SafeRect;
1984 BOOL Ret;
1985
1986 UserEnterExclusive();
1987
1988 if (hWnd != NULL)
1989 {
1990 if(!(pWnd = UserGetWindowObject(hWnd)))
1991 {
1992 UserLeave();
1993 return FALSE;
1994 }
1995 }
1996
1997 _SEH2_TRY
1998 {
1999 ProbeForRead(lpRc, sizeof(RECTL), sizeof(ULONG));
2000 RtlCopyMemory(&SafeRect, lpRc, sizeof(RECTL));
2001 if (str != NULL)
2002 {
2003 SafeStr = ProbeForReadUnicodeString(str);
2004 if (SafeStr.Length != 0)
2005 {
2006 ProbeForRead( SafeStr.Buffer,
2007 SafeStr.Length,
2008 sizeof(WCHAR));
2009 }
2010 }
2011 }
2012 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2013 {
2014 Status = _SEH2_GetExceptionCode();
2015 }
2016 _SEH2_END;
2017
2018 if (Status != STATUS_SUCCESS)
2019 {
2020 SetLastNtError(Status);
2021 UserLeave();
2022 return FALSE;
2023 }
2024
2025 if (str != NULL)
2026 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags);
2027 else
2028 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags);
2029
2030 UserLeave();
2031 return Ret;
2032 }
2033
2034 BOOL
2035 APIENTRY
2036 NtUserDrawCaption(HWND hWnd,
2037 HDC hDC,
2038 LPCRECT lpRc,
2039 UINT uFlags)
2040 {
2041 return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
2042 }
2043
2044 BOOL
2045 APIENTRY
2046 NtUserInvalidateRect(
2047 HWND hWnd,
2048 CONST RECT *lpUnsafeRect,
2049 BOOL bErase)
2050 {
2051 return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2052 }
2053
2054 BOOL
2055 APIENTRY
2056 NtUserInvalidateRgn(
2057 HWND hWnd,
2058 HRGN hRgn,
2059 BOOL bErase)
2060 {
2061 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2062 }
2063
2064 BOOL
2065 APIENTRY
2066 NtUserPrintWindow(
2067 HWND hwnd,
2068 HDC hdcBlt,
2069 UINT nFlags)
2070 {
2071 PWND Window;
2072 BOOL Ret = FALSE;
2073
2074 UserEnterExclusive();
2075
2076 if (hwnd)
2077 {
2078 Window = UserGetWindowObject(hwnd);
2079 // TODO: Add Desktop and MessageBox check via FNID's.
2080 if ( Window )
2081 {
2082 /* Validate flags and check it as a mask for 0 or 1. */
2083 if ( (nFlags & PW_CLIENTONLY) == nFlags)
2084 Ret = IntPrintWindow( Window, hdcBlt, nFlags);
2085 else
2086 EngSetLastError(ERROR_INVALID_PARAMETER);
2087 }
2088 }
2089
2090 UserLeave();
2091 return Ret;
2092 }
2093
2094 /* ValidateRect gets redirected to NtUserValidateRect:
2095 http://blog.csdn.net/ntdll/archive/2005/10/19/509299.aspx */
2096 BOOL
2097 APIENTRY
2098 NtUserValidateRect(
2099 HWND hWnd,
2100 const RECT *lpRect)
2101 {
2102 if (hWnd)
2103 {
2104 return NtUserRedrawWindow(hWnd, lpRect, NULL, RDW_VALIDATE );
2105 }
2106 return NtUserRedrawWindow(hWnd, lpRect, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ERASENOW|RDW_ALLCHILDREN);
2107 }
2108
2109 /* EOF */