[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 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 NtGdiCombineRgn(hrgnTmp, hrgnTmp, hrgnVisible, RGN_AND);
1266 NtGdiOffsetRgn(hrgnTmp, dx, dy);
1267 Result = NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_DIFF);
1268
1269 /* DO NOT Unlock DC while messing with prgnVis! */
1270 DC_UnlockDc(pDC);
1271
1272 REGION_FreeRgnByHandle(hrgnTmp);
1273
1274 if (prcUpdate)
1275 {
1276 IntGdiGetRgnBox(hrgnOwn, prcUpdate);
1277 }
1278
1279 if (!hrgnUpdate)
1280 {
1281 REGION_FreeRgnByHandle(hrgnOwn);
1282 }
1283 }
1284 else
1285 Result = NULLREGION;
1286
1287 return Result;
1288 }
1289
1290 /*
1291 * NtUserScrollDC
1292 *
1293 * Status
1294 * @implemented
1295 */
1296 BOOL APIENTRY
1297 NtUserScrollDC(
1298 HDC hDC,
1299 INT dx,
1300 INT dy,
1301 const RECT *prcUnsafeScroll,
1302 const RECT *prcUnsafeClip,
1303 HRGN hrgnUpdate,
1304 LPRECT prcUnsafeUpdate)
1305 {
1306 DECLARE_RETURN(DWORD);
1307 RECTL rcScroll, rcClip, rcUpdate;
1308 NTSTATUS Status = STATUS_SUCCESS;
1309 DWORD Result;
1310
1311 DPRINT("Enter NtUserScrollDC\n");
1312 UserEnterExclusive();
1313
1314 _SEH2_TRY
1315 {
1316 if (prcUnsafeScroll)
1317 {
1318 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1319 rcScroll = *prcUnsafeScroll;
1320 }
1321 if (prcUnsafeClip)
1322 {
1323 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1324 rcClip = *prcUnsafeClip;
1325 }
1326 if (prcUnsafeUpdate)
1327 {
1328 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1329 }
1330 }
1331 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1332 {
1333 Status = _SEH2_GetExceptionCode();
1334 }
1335 _SEH2_END
1336 if (!NT_SUCCESS(Status))
1337 {
1338 SetLastNtError(Status);
1339 RETURN(FALSE);
1340 }
1341
1342 Result = UserScrollDC( hDC,
1343 dx,
1344 dy,
1345 prcUnsafeScroll? &rcScroll : 0,
1346 prcUnsafeClip? &rcClip : 0,
1347 hrgnUpdate,
1348 prcUnsafeUpdate? &rcUpdate : NULL);
1349 if(Result == ERROR)
1350 {
1351 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
1352 RETURN(FALSE);
1353 }
1354
1355 if (prcUnsafeUpdate)
1356 {
1357 _SEH2_TRY
1358 {
1359 *prcUnsafeUpdate = rcUpdate;
1360 }
1361 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1362 {
1363 Status = _SEH2_GetExceptionCode();
1364 }
1365 _SEH2_END
1366 if (!NT_SUCCESS(Status))
1367 {
1368 /* FIXME: SetLastError? */
1369 /* FIXME: correct? We have already scrolled! */
1370 RETURN(FALSE);
1371 }
1372 }
1373
1374 RETURN(TRUE);
1375
1376 CLEANUP:
1377 DPRINT("Leave NtUserScrollDC, ret=%i\n",_ret_);
1378 UserLeave();
1379 END_CLEANUP;
1380 }
1381
1382 /*
1383 * NtUserScrollWindowEx
1384 *
1385 * Status
1386 * @implemented
1387 */
1388
1389 DWORD APIENTRY
1390 NtUserScrollWindowEx(
1391 HWND hWnd,
1392 INT dx,
1393 INT dy,
1394 const RECT *prcUnsafeScroll,
1395 const RECT *prcUnsafeClip,
1396 HRGN hrgnUpdate,
1397 LPRECT prcUnsafeUpdate,
1398 UINT flags)
1399 {
1400 RECTL rcScroll, rcClip, rcCaret, rcUpdate;
1401 INT Result;
1402 PWND Window = NULL, CaretWnd;
1403 HDC hDC;
1404 HRGN hrgnOwn = NULL, hrgnTemp;
1405 HWND hwndCaret;
1406 NTSTATUS Status = STATUS_SUCCESS;
1407 DECLARE_RETURN(DWORD);
1408 USER_REFERENCE_ENTRY Ref, CaretRef;
1409
1410 DPRINT("Enter NtUserScrollWindowEx\n");
1411 UserEnterExclusive();
1412
1413 Window = UserGetWindowObject(hWnd);
1414 if (!Window || !IntIsWindowDrawable(Window))
1415 {
1416 Window = NULL; /* prevent deref at cleanup */
1417 RETURN( ERROR);
1418 }
1419 UserRefObjectCo(Window, &Ref);
1420
1421 IntGetClientRect(Window, &rcClip);
1422
1423 _SEH2_TRY
1424 {
1425 if (prcUnsafeScroll)
1426 {
1427 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1428 RECTL_bIntersectRect(&rcScroll, &rcClip, prcUnsafeScroll);
1429 }
1430 else
1431 rcScroll = rcClip;
1432
1433 if (prcUnsafeClip)
1434 {
1435 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1436 RECTL_bIntersectRect(&rcClip, &rcClip, prcUnsafeClip);
1437 }
1438 }
1439 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1440 {
1441 Status = _SEH2_GetExceptionCode();
1442 }
1443 _SEH2_END
1444
1445 if (!NT_SUCCESS(Status))
1446 {
1447 SetLastNtError(Status);
1448 RETURN(ERROR);
1449 }
1450
1451 if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
1452 (dx == 0 && dy == 0))
1453 {
1454 RETURN(NULLREGION);
1455 }
1456
1457 if (hrgnUpdate)
1458 hrgnOwn = hrgnUpdate;
1459 else
1460 hrgnOwn = IntSysCreateRectRgn(0, 0, 0, 0);
1461
1462 hDC = UserGetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE);
1463 if (!hDC)
1464 {
1465 /* FIXME: SetLastError? */
1466 RETURN(ERROR);
1467 }
1468
1469 rcCaret = rcScroll;
1470 hwndCaret = co_IntFixCaret(Window, &rcCaret, flags);
1471
1472 Result = UserScrollDC( hDC,
1473 dx,
1474 dy,
1475 &rcScroll,
1476 &rcClip,
1477 hrgnOwn,
1478 prcUnsafeUpdate? &rcUpdate : NULL);
1479
1480 UserReleaseDC(Window, hDC, FALSE);
1481
1482 /*
1483 * Take into account the fact that some damage may have occurred during
1484 * the scroll.
1485 */
1486
1487 hrgnTemp = IntSysCreateRectRgn(0, 0, 0, 0);
1488 if (co_UserGetUpdateRgn(Window, hrgnTemp, FALSE) != NULLREGION)
1489 {
1490 HRGN hrgnClip = IntSysCreateRectRgnIndirect(&rcClip);
1491 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1492 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1493 co_UserRedrawWindow(Window, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1494 REGION_FreeRgnByHandle(hrgnClip);
1495 }
1496 REGION_FreeRgnByHandle(hrgnTemp);
1497
1498 if (flags & SW_SCROLLCHILDREN)
1499 {
1500 PWND Child;
1501 RECTL rcChild;
1502 POINT ClientOrigin;
1503 USER_REFERENCE_ENTRY WndRef;
1504 RECTL rcDummy;
1505
1506 IntGetClientOrigin(Window, &ClientOrigin);
1507 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
1508 {
1509 rcChild = Child->rcWindow;
1510 rcChild.left -= ClientOrigin.x;
1511 rcChild.top -= ClientOrigin.y;
1512 rcChild.right -= ClientOrigin.x;
1513 rcChild.bottom -= ClientOrigin.y;
1514
1515 if (! prcUnsafeScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll))
1516 {
1517 UserRefObjectCo(Child, &WndRef);
1518 co_WinPosSetWindowPos(Child, 0, rcChild.left + dx, rcChild.top + dy, 0, 0,
1519 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1520 SWP_NOREDRAW);
1521 UserDerefObjectCo(Child);
1522 }
1523 }
1524 }
1525
1526 if (flags & (SW_INVALIDATE | SW_ERASE))
1527 {
1528 co_UserRedrawWindow(Window, NULL, hrgnOwn, RDW_INVALIDATE | RDW_ERASE |
1529 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1530 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1531 }
1532
1533 if ((CaretWnd = UserGetWindowObject(hwndCaret)))
1534 {
1535 UserRefObjectCo(CaretWnd, &CaretRef);
1536
1537 co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
1538 co_UserShowCaret(CaretWnd);
1539
1540 UserDerefObjectCo(CaretWnd);
1541 }
1542
1543 if (prcUnsafeUpdate)
1544 {
1545 _SEH2_TRY
1546 {
1547 /* Probe here, to not fail on invalid pointer before scrolling */
1548 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1549 *prcUnsafeUpdate = rcUpdate;
1550 }
1551 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1552 {
1553 Status = _SEH2_GetExceptionCode();
1554 }
1555 _SEH2_END
1556
1557 if (!NT_SUCCESS(Status))
1558 {
1559 SetLastNtError(Status);
1560 RETURN(ERROR);
1561 }
1562 }
1563
1564 RETURN(Result);
1565
1566 CLEANUP:
1567 if (hrgnOwn && !hrgnUpdate)
1568 {
1569 REGION_FreeRgnByHandle(hrgnOwn);
1570 }
1571
1572 if (Window)
1573 UserDerefObjectCo(Window);
1574
1575 DPRINT("Leave NtUserScrollWindowEx, ret=%i\n",_ret_);
1576 UserLeave();
1577 END_CLEANUP;
1578 }
1579
1580
1581 BOOL
1582 UserDrawSysMenuButton(
1583 PWND pWnd,
1584 HDC hDc,
1585 RECTL *lpRc,
1586 BOOL Down)
1587 {
1588 HICON hIcon;
1589 PCURICON_OBJECT pIcon;
1590
1591 ASSERT(pWnd && lpRc);
1592
1593 /* Get the icon to draw. We don't care about WM_GETICON here. */
1594
1595 hIcon = pWnd->pcls->hIconSm;
1596
1597 if(!hIcon)
1598 {
1599 DPRINT("Wnd class has no small icon.\n");
1600 hIcon = pWnd->pcls->hIcon;
1601 }
1602
1603 if(!hIcon)
1604 {
1605 DPRINT("Wnd class hasn't any icon.\n");
1606 //FIXME: Draw "winlogo" icon.
1607 return FALSE;
1608 }
1609
1610 if(!(pIcon = UserGetCurIconObject(hIcon)))
1611 {
1612 DPRINT1("UserGetCurIconObject() failed!\n");
1613 return FALSE;
1614 }
1615
1616 return UserDrawIconEx(hDc, lpRc->left, lpRc->top, pIcon,
1617 UserGetSystemMetrics(SM_CXSMICON),
1618 UserGetSystemMetrics(SM_CYSMICON),
1619 0, NULL, DI_NORMAL);
1620 }
1621
1622 BOOL
1623 UserDrawCaptionText(
1624 HDC hDc,
1625 const PUNICODE_STRING Text,
1626 const RECTL *lpRc,
1627 UINT uFlags)
1628 {
1629 HFONT hOldFont = NULL, hFont = NULL;
1630 COLORREF OldTextColor;
1631 NONCLIENTMETRICSW nclm;
1632 NTSTATUS Status;
1633 #ifndef NDEBUG
1634 INT i;
1635 DPRINT("%s:", __FUNCTION__);
1636 for(i = 0; i < Text->Length/sizeof(WCHAR); i++)
1637 DbgPrint("%C", Text->Buffer[i]);
1638 DbgPrint(", %d\n", Text->Length/sizeof(WCHAR));
1639 #endif
1640
1641 nclm.cbSize = sizeof(nclm);
1642 if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS,
1643 sizeof(NONCLIENTMETRICS), &nclm, 0))
1644 {
1645 DPRINT1("%s: UserSystemParametersInfo() failed!\n", __FUNCTION__);
1646 return FALSE;
1647 }
1648
1649 IntGdiSetBkMode(hDc, TRANSPARENT);
1650
1651 if(uFlags & DC_SMALLCAP)
1652 Status = TextIntCreateFontIndirect(&nclm.lfSmCaptionFont, &hFont);
1653 else Status = TextIntCreateFontIndirect(&nclm.lfCaptionFont, &hFont);
1654
1655 if(!NT_SUCCESS(Status))
1656 {
1657 DPRINT1("%s: TextIntCreateFontIndirect() failed! Status: 0x%x\n",
1658 __FUNCTION__, Status);
1659 return FALSE;
1660 }
1661
1662 hOldFont = NtGdiSelectFont(hDc, hFont);
1663 if(!hOldFont)
1664 {
1665 DPRINT1("%s: SelectFont() failed!\n", __FUNCTION__);
1666 GreDeleteObject(hFont);
1667 return FALSE;
1668 }
1669
1670 if(uFlags & DC_INBUTTON)
1671 OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT));
1672 else OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(uFlags & DC_ACTIVE
1673 ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
1674
1675 //FIXME: If string doesn't fit to rc, truncate it and add ellipsis.
1676
1677 GreExtTextOutW(hDc, lpRc->left,
1678 lpRc->top, 0, NULL, Text->Buffer,
1679 Text->Length/sizeof(WCHAR), NULL, 0);
1680
1681 IntGdiSetTextColor(hDc, OldTextColor);
1682 NtGdiSelectFont(hDc, hOldFont);
1683 GreDeleteObject(hFont);
1684
1685 return TRUE;
1686 }
1687
1688 BOOL UserDrawCaption(
1689 PWND pWnd,
1690 HDC hDc,
1691 RECTL *lpRc,
1692 HFONT hFont,
1693 HICON hIcon,
1694 const PUNICODE_STRING str,
1695 UINT uFlags)
1696 {
1697 BOOL Ret = FALSE;
1698 HBITMAP hMemBmp = NULL, hOldBmp = NULL;
1699 HBRUSH hOldBrush = NULL;
1700 HDC hMemDc = NULL;
1701 ULONG Height;
1702 UINT VCenter = 0, Padding = 0;
1703 RECTL r = *lpRc;
1704 LONG ButtonWidth, IconWidth;
1705 BOOL HasIcon;
1706
1707 //ASSERT(pWnd != NULL);
1708
1709 RECTL_vMakeWellOrdered(lpRc);
1710 hMemBmp = NtGdiCreateCompatibleBitmap(hDc,
1711 lpRc->right - lpRc->left,
1712 lpRc->bottom - lpRc->top);
1713
1714 if(!hMemBmp)
1715 {
1716 DPRINT1("%s: NtGdiCreateCompatibleBitmap() failed!\n", __FUNCTION__);
1717 return FALSE;
1718 }
1719
1720 hMemDc = NtGdiCreateCompatibleDC(hDc);
1721 if(!hMemDc)
1722 {
1723 DPRINT1("%s: NtGdiCreateCompatibleDC() failed!\n", __FUNCTION__);
1724 goto cleanup;
1725 }
1726
1727 hOldBmp = NtGdiSelectBitmap(hMemDc, hMemBmp);
1728 if(!hOldBmp)
1729 {
1730 DPRINT1("%s: NtGdiSelectBitmap() failed!\n", __FUNCTION__);
1731 goto cleanup;
1732 }
1733
1734 Height = UserGetSystemMetrics(SM_CYCAPTION) - 1;
1735 VCenter = (lpRc->bottom - lpRc->top) / 2;
1736 Padding = VCenter - (Height / 2);
1737
1738 if ((!hIcon) && (pWnd != NULL))
1739 {
1740 HasIcon = (uFlags & DC_ICON) && (pWnd->style & WS_SYSMENU)
1741 && !(uFlags & DC_SMALLCAP) && !(pWnd->ExStyle & WS_EX_DLGMODALFRAME)
1742 && !(pWnd->ExStyle & WS_EX_TOOLWINDOW);
1743 }
1744 else
1745 HasIcon = (hIcon != 0);
1746
1747 IconWidth = UserGetSystemMetrics(SM_CXSIZE) + Padding;
1748
1749 r.left = Padding;
1750 r.right = r.left + (lpRc->right - lpRc->left);
1751 r.top = Padding;
1752 r.bottom = r.top + (Height / 2);
1753
1754 // Draw the caption background
1755 if(uFlags & DC_INBUTTON)
1756 {
1757 hOldBrush = NtGdiSelectBrush(hMemDc,
1758 IntGetSysColorBrush(COLOR_3DFACE));
1759
1760 if(!hOldBrush)
1761 {
1762 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1763 goto cleanup;
1764 }
1765
1766 if(!NtGdiPatBlt(hMemDc, 0, 0,
1767 lpRc->right - lpRc->left,
1768 lpRc->bottom - lpRc->top,
1769 PATCOPY))
1770 {
1771 DPRINT1("%s: NtGdiPatBlt() failed!\n", __FUNCTION__);
1772 goto cleanup;
1773 }
1774
1775 if(HasIcon) r.left+=IconWidth;
1776 }
1777 else
1778 {
1779 r.right = (lpRc->right - lpRc->left);
1780 if(uFlags & DC_SMALLCAP)
1781 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1782 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1783
1784 hOldBrush = NtGdiSelectBrush(hMemDc,
1785 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1786 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION));
1787
1788 if(!hOldBrush)
1789 {
1790 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1791 goto cleanup;
1792 }
1793
1794 if(HasIcon && (uFlags & DC_GRADIENT))
1795 {
1796 NtGdiPatBlt(hMemDc, 0, 0,
1797 IconWidth+1,
1798 lpRc->bottom - lpRc->top,
1799 PATCOPY);
1800 r.left+=IconWidth;
1801 }
1802 else
1803 {
1804 NtGdiPatBlt(hMemDc, 0, 0,
1805 lpRc->right - lpRc->left,
1806 lpRc->bottom - lpRc->top,
1807 PATCOPY);
1808 }
1809
1810 if(uFlags & DC_GRADIENT)
1811 {
1812 static GRADIENT_RECT gcap = {0, 1};
1813 TRIVERTEX vert[2];
1814 COLORREF Colors[2];
1815
1816 if (pWnd != NULL)
1817 {
1818 if(pWnd->style & WS_SYSMENU)
1819 {
1820 r.right -= 3 + ButtonWidth;
1821 if(!(uFlags & DC_SMALLCAP))
1822 {
1823 if(pWnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1824 r.right -= 2 + 2 * ButtonWidth;
1825 else r.right -= 2;
1826 r.right -= 2;
1827 }
1828
1829 //Draw buttons background
1830 if(!NtGdiSelectBrush(hMemDc,
1831 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1832 COLOR_GRADIENTACTIVECAPTION:COLOR_GRADIENTINACTIVECAPTION)))
1833 {
1834 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1835 goto cleanup;
1836 }
1837
1838 NtGdiPatBlt(hMemDc,
1839 r.right,
1840 0,
1841 lpRc->right - lpRc->left - r.right,
1842 lpRc->bottom - lpRc->top,
1843 PATCOPY);
1844 }
1845 }
1846
1847 Colors[0] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1848 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION);
1849
1850 Colors[1] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1851 COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION);
1852
1853 vert[0].x = r.left;
1854 vert[0].y = 0;
1855 vert[0].Red = (WORD)Colors[0]<<8;
1856 vert[0].Green = (WORD)Colors[0] & 0xFF00;
1857 vert[0].Blue = (WORD)(Colors[0]>>8) & 0xFF00;
1858 vert[0].Alpha = 0;
1859
1860 vert[1].x = r.right;
1861 vert[1].y = lpRc->bottom - lpRc->top;
1862 vert[1].Red = (WORD)Colors[1]<<8;
1863 vert[1].Green = (WORD)Colors[1] & 0xFF00;
1864 vert[1].Blue = (WORD)(Colors[1]>>8) & 0xFF00;
1865 vert[1].Alpha = 0;
1866
1867 if(!GreGradientFill(hMemDc, vert, 2, &gcap,
1868 1, GRADIENT_FILL_RECT_H))
1869 {
1870 DPRINT1("%s: IntGdiGradientFill() failed!\n", __FUNCTION__);
1871 }
1872
1873 } //if(uFlags & DC_GRADIENT)
1874 }
1875
1876 if(HasIcon)
1877 {
1878 r.top ++;
1879 r.left -= --IconWidth;
1880 /* FIXME: Draw the Icon when pWnd == NULL but hIcon is valid */
1881 if (pWnd != NULL)
1882 UserDrawSysMenuButton(pWnd, hMemDc, &r, FALSE);
1883
1884 r.left += IconWidth;
1885 r.top --;
1886 }
1887
1888 r.top ++;
1889 r.left += 2;
1890
1891 r.bottom = r.top + Height;
1892
1893 if((uFlags & DC_TEXT))
1894 {
1895 if(!(uFlags & DC_GRADIENT))
1896 {
1897 r.right = (lpRc->right - lpRc->left);
1898
1899 if(uFlags & DC_SMALLCAP)
1900 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1901 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1902
1903 if ((pWnd != NULL) && (pWnd->style & WS_SYSMENU))
1904 {
1905 r.right -= 3 + ButtonWidth;
1906 if(! (uFlags & DC_SMALLCAP))
1907 {
1908 if(pWnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1909 r.right -= 2 + 2 * ButtonWidth;
1910 else r.right -= 2;
1911 r.right -= 2;
1912 }
1913 }
1914 }
1915
1916 /* FIXME: hFont isn't handled */
1917 if (str)
1918 UserDrawCaptionText(hMemDc, str, &r, uFlags);
1919 else if (pWnd != NULL)
1920 {
1921 UNICODE_STRING ustr;
1922 ustr.Buffer = pWnd->strName.Buffer;
1923 ustr.Length = pWnd->strName.Length;
1924 ustr.MaximumLength = pWnd->strName.MaximumLength;
1925 UserDrawCaptionText(hMemDc, &ustr, &r, uFlags);
1926 }
1927 }
1928
1929 if(!NtGdiBitBlt(hDc, lpRc->left, lpRc->top,
1930 lpRc->right - lpRc->left, lpRc->bottom - lpRc->top,
1931 hMemDc, 0, 0, SRCCOPY, 0, 0))
1932 {
1933 DPRINT1("%s: NtGdiBitBlt() failed!\n", __FUNCTION__);
1934 goto cleanup;
1935 }
1936
1937 Ret = TRUE;
1938
1939 cleanup:
1940 if (hOldBrush) NtGdiSelectBrush(hMemDc, hOldBrush);
1941 if (hOldBmp) NtGdiSelectBitmap(hMemDc, hOldBmp);
1942 if (hMemBmp) GreDeleteObject(hMemBmp);
1943 if (hMemDc) NtGdiDeleteObjectApp(hMemDc);
1944
1945 return Ret;
1946 }
1947
1948 INT
1949 FASTCALL
1950 UserRealizePalette(HDC hdc)
1951 {
1952 HWND hWnd;
1953 DWORD Ret;
1954
1955 Ret = IntGdiRealizePalette(hdc);
1956 if (Ret) // There was a change.
1957 {
1958 hWnd = IntWindowFromDC(hdc);
1959 if (hWnd) // Send broadcast if dc is associated with a window.
1960 { // FYI: Thread locked in CallOneParam.
1961 UserSendNotifyMessage((HWND)HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0);
1962 }
1963 }
1964 return Ret;
1965 }
1966
1967 BOOL
1968 APIENTRY
1969 NtUserDrawCaptionTemp(
1970 HWND hWnd,
1971 HDC hDC,
1972 LPCRECT lpRc,
1973 HFONT hFont,
1974 HICON hIcon,
1975 const PUNICODE_STRING str,
1976 UINT uFlags)
1977 {
1978 PWND pWnd = NULL;
1979 UNICODE_STRING SafeStr = {0};
1980 NTSTATUS Status = STATUS_SUCCESS;
1981 RECTL SafeRect;
1982 BOOL Ret;
1983
1984 UserEnterExclusive();
1985
1986 if (hWnd != NULL)
1987 {
1988 if(!(pWnd = UserGetWindowObject(hWnd)))
1989 {
1990 UserLeave();
1991 return FALSE;
1992 }
1993 }
1994
1995 _SEH2_TRY
1996 {
1997 ProbeForRead(lpRc, sizeof(RECTL), sizeof(ULONG));
1998 RtlCopyMemory(&SafeRect, lpRc, sizeof(RECTL));
1999 if (str != NULL)
2000 {
2001 SafeStr = ProbeForReadUnicodeString(str);
2002 if (SafeStr.Length != 0)
2003 {
2004 ProbeForRead( SafeStr.Buffer,
2005 SafeStr.Length,
2006 sizeof(WCHAR));
2007 }
2008 }
2009 }
2010 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2011 {
2012 Status = _SEH2_GetExceptionCode();
2013 }
2014 _SEH2_END;
2015
2016 if (Status != STATUS_SUCCESS)
2017 {
2018 SetLastNtError(Status);
2019 UserLeave();
2020 return FALSE;
2021 }
2022
2023 if (str != NULL)
2024 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags);
2025 else
2026 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags);
2027
2028 UserLeave();
2029 return Ret;
2030 }
2031
2032 BOOL
2033 APIENTRY
2034 NtUserDrawCaption(HWND hWnd,
2035 HDC hDC,
2036 LPCRECT lpRc,
2037 UINT uFlags)
2038 {
2039 return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
2040 }
2041
2042 BOOL
2043 APIENTRY
2044 NtUserInvalidateRect(
2045 HWND hWnd,
2046 CONST RECT *lpUnsafeRect,
2047 BOOL bErase)
2048 {
2049 return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2050 }
2051
2052 BOOL
2053 APIENTRY
2054 NtUserInvalidateRgn(
2055 HWND hWnd,
2056 HRGN hRgn,
2057 BOOL bErase)
2058 {
2059 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2060 }
2061
2062 BOOL
2063 APIENTRY
2064 NtUserPrintWindow(
2065 HWND hwnd,
2066 HDC hdcBlt,
2067 UINT nFlags)
2068 {
2069 PWND Window;
2070 BOOL Ret = FALSE;
2071
2072 UserEnterExclusive();
2073
2074 if (hwnd)
2075 {
2076 Window = UserGetWindowObject(hwnd);
2077 // TODO: Add Desktop and MessageBox check via FNID's.
2078 if ( Window )
2079 {
2080 /* Validate flags and check it as a mask for 0 or 1. */
2081 if ( (nFlags & PW_CLIENTONLY) == nFlags)
2082 Ret = IntPrintWindow( Window, hdcBlt, nFlags);
2083 else
2084 EngSetLastError(ERROR_INVALID_PARAMETER);
2085 }
2086 }
2087
2088 UserLeave();
2089 return Ret;
2090 }
2091
2092 /* ValidateRect gets redirected to NtUserValidateRect:
2093 http://blog.csdn.net/ntdll/archive/2005/10/19/509299.aspx */
2094 BOOL
2095 APIENTRY
2096 NtUserValidateRect(
2097 HWND hWnd,
2098 const RECT *lpRect)
2099 {
2100 if (hWnd)
2101 {
2102 return NtUserRedrawWindow(hWnd, lpRect, NULL, RDW_VALIDATE );
2103 }
2104 return NtUserRedrawWindow(hWnd, lpRect, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ERASENOW|RDW_ALLCHILDREN);
2105 }
2106
2107 /* EOF */