[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)1)
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)1;
155 }
156
157 hRgnWindow = IntCalcWindowRgn(Window, TRUE);
158 if (hRgnWindow == NULL)
159 {
160 REGION_FreeRgnByHandle(hRgnNonClient);
161 return (HRGN)1;
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)1;
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.
311 */
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 HWND hWndCaret;
696 PWND WndCaret;
697
698 ASSERT_REFS_CO(Window);
699
700 Desktop = ((PTHREADINFO)PsGetCurrentThread()->Tcb.Win32Thread)->rpdesk;
701 CaretInfo = ((PUSER_MESSAGE_QUEUE)Desktop->ActiveMessageQueue)->CaretInfo;
702 hWndCaret = CaretInfo->hWnd;
703
704 WndCaret = UserGetWindowObject(hWndCaret);
705
706 //fix: check for WndCaret can be null
707 if (WndCaret == Window ||
708 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(Window, WndCaret)))
709 {
710 POINT pt, FromOffset, ToOffset;
711 RECTL rcCaret;
712
713 pt.x = CaretInfo->Pos.x;
714 pt.y = CaretInfo->Pos.y;
715 IntGetClientOrigin(WndCaret, &FromOffset);
716 IntGetClientOrigin(Window, &ToOffset);
717 rcCaret.left = pt.x;
718 rcCaret.top = pt.y;
719 rcCaret.right = pt.x + CaretInfo->Size.cx;
720 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
721 if (RECTL_bIntersectRect(lprc, lprc, &rcCaret))
722 {
723 co_UserHideCaret(0);
724 lprc->left = pt.x;
725 lprc->top = pt.y;
726 return hWndCaret;
727 }
728 }
729
730 return 0;
731 }
732
733 BOOL
734 FASTCALL
735 IntPrintWindow(
736 PWND pwnd,
737 HDC hdcBlt,
738 UINT nFlags)
739 {
740 HDC hdcSrc;
741 INT cx, cy, xSrc, ySrc;
742
743 if ( nFlags & PW_CLIENTONLY)
744 {
745 cx = pwnd->rcClient.right - pwnd->rcClient.left;
746 cy = pwnd->rcClient.bottom - pwnd->rcClient.top;
747 xSrc = pwnd->rcClient.left - pwnd->rcWindow.left;
748 ySrc = pwnd->rcClient.top - pwnd->rcWindow.top;
749 }
750 else
751 {
752 cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
753 cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
754 xSrc = 0;
755 ySrc = 0;
756 }
757
758 // TODO: Setup Redirection for Print.
759 return FALSE;
760
761 /* Update the window just incase. */
762 co_IntPaintWindows( pwnd, RDW_ERASENOW|RDW_UPDATENOW, FALSE);
763
764 hdcSrc = UserGetDCEx( pwnd, NULL, DCX_CACHE|DCX_WINDOW);
765 /* Print window to printer context. */
766 NtGdiBitBlt( hdcBlt,
767 0,
768 0,
769 cx,
770 cy,
771 hdcSrc,
772 xSrc,
773 ySrc,
774 SRCCOPY,
775 0,
776 0);
777
778 UserReleaseDC( pwnd, hdcSrc, FALSE);
779
780 // TODO: Release Redirection from Print.
781
782 return TRUE;
783 }
784
785 /* PUBLIC FUNCTIONS ***********************************************************/
786
787 /*
788 * NtUserBeginPaint
789 *
790 * Status
791 * @implemented
792 */
793
794 HDC APIENTRY
795 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
796 {
797 PWND Window = NULL;
798 PAINTSTRUCT Ps;
799 NTSTATUS Status;
800 DECLARE_RETURN(HDC);
801 USER_REFERENCE_ENTRY Ref;
802
803 DPRINT("Enter NtUserBeginPaint\n");
804 UserEnterExclusive();
805
806 if (!(Window = UserGetWindowObject(hWnd)))
807 {
808 RETURN( NULL);
809 }
810
811 UserRefObjectCo(Window, &Ref);
812
813 co_UserHideCaret(Window);
814
815 if (Window->state & WNDS_SENDNCPAINT)
816 {
817 HRGN hRgn;
818
819 hRgn = IntGetNCUpdateRgn(Window, FALSE);
820 Window->state &= ~WNDS_SENDNCPAINT;
821 MsqDecPaintCountQueue(Window->head.pti->MessageQueue);
822 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);
823 if (hRgn != (HANDLE)1 && hRgn != NULL)
824 {
825 /* NOTE: The region can already by deleted! */
826 GDIOBJ_FreeObjByHandle(hRgn, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
827 }
828 }
829
830 RtlZeroMemory(&Ps, sizeof(PAINTSTRUCT));
831
832 Ps.hdc = UserGetDCEx( Window,
833 Window->hrgnUpdate,
834 DCX_INTERSECTRGN | DCX_USESTYLE);
835 if (!Ps.hdc)
836 {
837 RETURN(NULL);
838 }
839
840 if (Window->hrgnUpdate != NULL)
841 {
842 MsqDecPaintCountQueue(Window->head.pti->MessageQueue);
843 GdiGetClipBox(Ps.hdc, &Ps.rcPaint);
844 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
845 /* The region is part of the dc now and belongs to the process! */
846 Window->hrgnUpdate = NULL;
847 }
848 else
849 {
850 if (Window->state & WNDS_INTERNALPAINT)
851 MsqDecPaintCountQueue(Window->head.pti->MessageQueue);
852
853 IntGetClientRect(Window, &Ps.rcPaint);
854 }
855
856 Window->state &= ~WNDS_INTERNALPAINT;
857
858 if (Window->state & WNDS_ERASEBACKGROUND)
859 {
860 Window->state &= ~WNDS_ERASEBACKGROUND;
861 Ps.fErase = !co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)Ps.hdc, 0);
862 }
863 else
864 {
865 Ps.fErase = FALSE;
866 }
867 if (Window->hrgnUpdate)
868 {
869 if (!(Window->style & WS_CLIPCHILDREN))
870 {
871 PWND Child;
872 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
873 {
874 IntInvalidateWindows(Child, Window->hrgnUpdate, RDW_FRAME | RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
875 }
876 }
877 }
878
879 Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
880 if (! NT_SUCCESS(Status))
881 {
882 SetLastNtError(Status);
883 RETURN(NULL);
884 }
885
886 RETURN(Ps.hdc);
887
888 CLEANUP:
889 if (Window) UserDerefObjectCo(Window);
890
891 DPRINT("Leave NtUserBeginPaint, ret=%i\n",_ret_);
892 UserLeave();
893 END_CLEANUP;
894
895 }
896
897 /*
898 * NtUserEndPaint
899 *
900 * Status
901 * @implemented
902 */
903
904 BOOL APIENTRY
905 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* pUnsafePs)
906 {
907 NTSTATUS Status = STATUS_SUCCESS;
908 PWND Window;
909 DECLARE_RETURN(BOOL);
910 USER_REFERENCE_ENTRY Ref;
911 HDC hdc = NULL;
912
913 DPRINT("Enter NtUserEndPaint\n");
914 UserEnterExclusive();
915
916 if (!(Window = UserGetWindowObject(hWnd)))
917 {
918 RETURN(FALSE);
919 }
920
921 _SEH2_TRY
922 {
923 ProbeForRead(pUnsafePs, sizeof(*pUnsafePs), 1);
924 hdc = pUnsafePs->hdc;
925 }
926 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
927 {
928 Status = _SEH2_GetExceptionCode();
929 }
930 _SEH2_END
931 if (!NT_SUCCESS(Status))
932 {
933 RETURN(FALSE);
934 }
935
936 UserReleaseDC(Window, hdc, TRUE);
937
938 UserRefObjectCo(Window, &Ref);
939 co_UserShowCaret(Window);
940 UserDerefObjectCo(Window);
941
942 RETURN(TRUE);
943
944 CLEANUP:
945 DPRINT("Leave NtUserEndPaint, ret=%i\n",_ret_);
946 UserLeave();
947 END_CLEANUP;
948 }
949
950
951 INT FASTCALL
952 co_UserGetUpdateRgn(PWND Window, HRGN hRgn, BOOL bErase)
953 {
954 int RegionType;
955 RECTL Rect;
956
957 ASSERT_REFS_CO(Window);
958
959 if (Window->hrgnUpdate == NULL)
960 {
961 RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
962 }
963 else
964 {
965 Rect = Window->rcClient;
966 IntIntersectWithParents(Window, &Rect);
967 NtGdiSetRectRgn(hRgn, Rect.left, Rect.top, Rect.right, Rect.bottom);
968 RegionType = NtGdiCombineRgn(hRgn, hRgn, Window->hrgnUpdate, RGN_AND);
969 NtGdiOffsetRgn(hRgn, -Window->rcClient.left, -Window->rcClient.top);
970 }
971
972 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
973 {
974 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
975 }
976
977 return RegionType;
978 }
979
980 /*
981 * NtUserGetUpdateRgn
982 *
983 * Status
984 * @implemented
985 */
986
987 INT APIENTRY
988 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
989 {
990 DECLARE_RETURN(INT);
991 PWND Window;
992 INT ret;
993 USER_REFERENCE_ENTRY Ref;
994
995 DPRINT("Enter NtUserGetUpdateRgn\n");
996 UserEnterExclusive();
997
998 if (!(Window = UserGetWindowObject(hWnd)))
999 {
1000 RETURN(ERROR);
1001 }
1002
1003 UserRefObjectCo(Window, &Ref);
1004 ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
1005 UserDerefObjectCo(Window);
1006
1007 RETURN(ret);
1008
1009 CLEANUP:
1010 DPRINT("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
1011 UserLeave();
1012 END_CLEANUP;
1013 }
1014
1015 /*
1016 * NtUserGetUpdateRect
1017 *
1018 * Status
1019 * @implemented
1020 */
1021
1022 BOOL APIENTRY
1023 NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
1024 {
1025 PWND Window;
1026 RECTL Rect;
1027 INT RegionType;
1028 PROSRGNDATA RgnData;
1029 NTSTATUS Status;
1030 DECLARE_RETURN(BOOL);
1031
1032 DPRINT("Enter NtUserGetUpdateRect\n");
1033 UserEnterExclusive();
1034
1035 if (!(Window = UserGetWindowObject(hWnd)))
1036 {
1037 RETURN(FALSE);
1038 }
1039
1040 if (Window->hrgnUpdate == NULL)
1041 {
1042 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
1043 }
1044 else
1045 {
1046 /* Get the update region bounding box. */
1047 if (Window->hrgnUpdate == (HRGN)1)
1048 {
1049 Rect = Window->rcClient;
1050 }
1051 else
1052 {
1053 RgnData = RGNOBJAPI_Lock(Window->hrgnUpdate, NULL);
1054 ASSERT(RgnData != NULL);
1055 RegionType = REGION_GetRgnBox(RgnData, &Rect);
1056 RGNOBJAPI_Unlock(RgnData);
1057
1058 if (RegionType != ERROR && RegionType != NULLREGION)
1059 RECTL_bIntersectRect(&Rect, &Rect, &Window->rcClient);
1060 }
1061
1062 if (IntIntersectWithParents(Window, &Rect))
1063 {
1064 RECTL_vOffsetRect(&Rect,
1065 -Window->rcClient.left,
1066 -Window->rcClient.top);
1067 } else
1068 {
1069 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
1070 }
1071 }
1072
1073 if (bErase && !RECTL_bIsEmptyRect(&Rect))
1074 {
1075 USER_REFERENCE_ENTRY Ref;
1076 UserRefObjectCo(Window, &Ref);
1077 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
1078 UserDerefObjectCo(Window);
1079 }
1080
1081 if (UnsafeRect != NULL)
1082 {
1083 Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECTL));
1084 if (!NT_SUCCESS(Status))
1085 {
1086 EngSetLastError(ERROR_INVALID_PARAMETER);
1087 RETURN(FALSE);
1088 }
1089 }
1090
1091 RETURN(!RECTL_bIsEmptyRect(&Rect));
1092
1093 CLEANUP:
1094 DPRINT("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
1095 UserLeave();
1096 END_CLEANUP;
1097 }
1098
1099 /*
1100 * NtUserRedrawWindow
1101 *
1102 * Status
1103 * @implemented
1104 */
1105
1106 BOOL APIENTRY
1107 NtUserRedrawWindow(
1108 HWND hWnd,
1109 CONST RECT *lprcUpdate,
1110 HRGN hrgnUpdate,
1111 UINT flags)
1112 {
1113 RECTL SafeUpdateRect;
1114 PWND Wnd;
1115 BOOL Ret;
1116 USER_REFERENCE_ENTRY Ref;
1117 NTSTATUS Status = STATUS_SUCCESS;
1118 DECLARE_RETURN(BOOL);
1119
1120 DPRINT("Enter NtUserRedrawWindow\n");
1121 UserEnterExclusive();
1122
1123 if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
1124 {
1125 RETURN( FALSE);
1126 }
1127
1128 if (lprcUpdate)
1129 {
1130 _SEH2_TRY
1131 {
1132 ProbeForRead(lprcUpdate, sizeof(RECTL), 1);
1133 RtlCopyMemory(&SafeUpdateRect, lprcUpdate, sizeof(RECTL));
1134 }
1135 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1136 {
1137 Status = _SEH2_GetExceptionCode();
1138 }
1139 _SEH2_END
1140 if (!NT_SUCCESS(Status))
1141 {
1142 EngSetLastError(RtlNtStatusToDosError(Status));
1143 RETURN( FALSE);
1144 }
1145 }
1146
1147 if ( flags & ~(RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE|
1148 RDW_NOERASE|RDW_NOFRAME|RDW_NOINTERNALPAINT|RDW_VALIDATE|
1149 RDW_ERASENOW|RDW_UPDATENOW|RDW_ALLCHILDREN|RDW_NOCHILDREN) )
1150 {
1151 /* RedrawWindow fails only in case that flags are invalid */
1152 EngSetLastError(ERROR_INVALID_FLAGS);
1153 RETURN( FALSE);
1154 }
1155
1156 UserRefObjectCo(Wnd, &Ref);
1157
1158 Ret = co_UserRedrawWindow( Wnd,
1159 lprcUpdate ? &SafeUpdateRect : NULL,
1160 hrgnUpdate,
1161 flags);
1162
1163 UserDerefObjectCo(Wnd);
1164
1165 RETURN( Ret);
1166
1167 CLEANUP:
1168 DPRINT("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
1169 UserLeave();
1170 END_CLEANUP;
1171 }
1172
1173 static
1174 INT FASTCALL
1175 UserScrollDC(
1176 HDC hDC,
1177 INT dx,
1178 INT dy,
1179 const RECTL *prcScroll,
1180 const RECTL *prcClip,
1181 HRGN hrgnUpdate,
1182 RECTL *prcUpdate)
1183 {
1184 PDC pDC;
1185 RECTL rcScroll, rcClip, rcSrc, rcDst;
1186 INT Result;
1187
1188 GdiGetClipBox(hDC, &rcClip);
1189 rcScroll = rcClip;
1190 if (prcClip)
1191 {
1192 RECTL_bIntersectRect(&rcClip, &rcClip, prcClip);
1193 }
1194
1195 if (prcScroll)
1196 {
1197 rcScroll = *prcScroll;
1198 RECTL_bIntersectRect(&rcSrc, &rcClip, prcScroll);
1199 }
1200 else
1201 {
1202 rcSrc = rcClip;
1203 }
1204
1205 rcDst = rcSrc;
1206 RECTL_vOffsetRect(&rcDst, dx, dy);
1207 RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
1208
1209 if (!NtGdiBitBlt( hDC,
1210 rcDst.left,
1211 rcDst.top,
1212 rcDst.right - rcDst.left,
1213 rcDst.bottom - rcDst.top,
1214 hDC,
1215 rcDst.left - dx,
1216 rcDst.top - dy,
1217 SRCCOPY,
1218 0,
1219 0))
1220 {
1221 return ERROR;
1222 }
1223
1224 /* Calculate the region that was invalidated by moving or
1225 could not be copied, because it was not visible */
1226 if (hrgnUpdate || prcUpdate)
1227 {
1228 HRGN hrgnOwn, hrgnVisible, hrgnTmp;
1229
1230 pDC = DC_LockDc(hDC);
1231 if (!pDC)
1232 {
1233 return FALSE;
1234 }
1235 hrgnVisible = ((PROSRGNDATA)pDC->prgnVis)->BaseObject.hHmgr; // pDC->prgnRao?
1236
1237 /* Begin with the shifted and then clipped scroll rect */
1238 rcDst = rcScroll;
1239 RECTL_vOffsetRect(&rcDst, dx, dy);
1240 RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
1241 if (hrgnUpdate)
1242 {
1243 hrgnOwn = hrgnUpdate;
1244 if (!NtGdiSetRectRgn(hrgnOwn, rcDst.left, rcDst.top, rcDst.right, rcDst.bottom))
1245 {
1246 return ERROR;
1247 }
1248 }
1249 else
1250 {
1251 hrgnOwn = IntSysCreateRectRgnIndirect(&rcDst);
1252 }
1253
1254 /* Add the source rect */
1255 hrgnTmp = IntSysCreateRectRgnIndirect(&rcSrc);
1256 NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_OR);
1257
1258 /* Substract the part of the dest that was visible in source */
1259 NtGdiCombineRgn(hrgnTmp, hrgnTmp, hrgnVisible, RGN_AND);
1260 NtGdiOffsetRgn(hrgnTmp, dx, dy);
1261 Result = NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_DIFF);
1262
1263 /* DO NOT Unlock DC while messing with prgnVis! */
1264 DC_UnlockDc(pDC);
1265
1266 REGION_FreeRgnByHandle(hrgnTmp);
1267
1268 if (prcUpdate)
1269 {
1270 IntGdiGetRgnBox(hrgnOwn, prcUpdate);
1271 }
1272
1273 if (!hrgnUpdate)
1274 {
1275 REGION_FreeRgnByHandle(hrgnOwn);
1276 }
1277 }
1278 else
1279 Result = NULLREGION;
1280
1281 return Result;
1282 }
1283
1284 /*
1285 * NtUserScrollDC
1286 *
1287 * Status
1288 * @implemented
1289 */
1290 BOOL APIENTRY
1291 NtUserScrollDC(
1292 HDC hDC,
1293 INT dx,
1294 INT dy,
1295 const RECT *prcUnsafeScroll,
1296 const RECT *prcUnsafeClip,
1297 HRGN hrgnUpdate,
1298 LPRECT prcUnsafeUpdate)
1299 {
1300 DECLARE_RETURN(DWORD);
1301 RECTL rcScroll, rcClip, rcUpdate;
1302 NTSTATUS Status = STATUS_SUCCESS;
1303 DWORD Result;
1304
1305 DPRINT("Enter NtUserScrollDC\n");
1306 UserEnterExclusive();
1307
1308 _SEH2_TRY
1309 {
1310 if (prcUnsafeScroll)
1311 {
1312 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1313 rcScroll = *prcUnsafeScroll;
1314 }
1315 if (prcUnsafeClip)
1316 {
1317 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1318 rcClip = *prcUnsafeClip;
1319 }
1320 if (prcUnsafeUpdate)
1321 {
1322 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1323 }
1324 }
1325 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1326 {
1327 Status = _SEH2_GetExceptionCode();
1328 }
1329 _SEH2_END
1330 if (!NT_SUCCESS(Status))
1331 {
1332 SetLastNtError(Status);
1333 RETURN(FALSE);
1334 }
1335
1336 Result = UserScrollDC( hDC,
1337 dx,
1338 dy,
1339 prcUnsafeScroll? &rcScroll : 0,
1340 prcUnsafeClip? &rcClip : 0,
1341 hrgnUpdate,
1342 prcUnsafeUpdate? &rcUpdate : NULL);
1343 if(Result == ERROR)
1344 {
1345 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
1346 RETURN(FALSE);
1347 }
1348
1349 if (prcUnsafeUpdate)
1350 {
1351 _SEH2_TRY
1352 {
1353 *prcUnsafeUpdate = rcUpdate;
1354 }
1355 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1356 {
1357 Status = _SEH2_GetExceptionCode();
1358 }
1359 _SEH2_END
1360 if (!NT_SUCCESS(Status))
1361 {
1362 /* FIXME: SetLastError? */
1363 /* FIXME: correct? We have already scrolled! */
1364 RETURN(FALSE);
1365 }
1366 }
1367
1368 RETURN(TRUE);
1369
1370 CLEANUP:
1371 DPRINT("Leave NtUserScrollDC, ret=%i\n",_ret_);
1372 UserLeave();
1373 END_CLEANUP;
1374 }
1375
1376 /*
1377 * NtUserScrollWindowEx
1378 *
1379 * Status
1380 * @implemented
1381 */
1382
1383 DWORD APIENTRY
1384 NtUserScrollWindowEx(
1385 HWND hWnd,
1386 INT dx,
1387 INT dy,
1388 const RECT *prcUnsafeScroll,
1389 const RECT *prcUnsafeClip,
1390 HRGN hrgnUpdate,
1391 LPRECT prcUnsafeUpdate,
1392 UINT flags)
1393 {
1394 RECTL rcScroll, rcClip, rcCaret, rcUpdate;
1395 INT Result;
1396 PWND Window = NULL, CaretWnd;
1397 HDC hDC;
1398 HRGN hrgnOwn = NULL, hrgnTemp;
1399 HWND hwndCaret;
1400 NTSTATUS Status = STATUS_SUCCESS;
1401 DECLARE_RETURN(DWORD);
1402 USER_REFERENCE_ENTRY Ref, CaretRef;
1403
1404 DPRINT("Enter NtUserScrollWindowEx\n");
1405 UserEnterExclusive();
1406
1407 Window = UserGetWindowObject(hWnd);
1408 if (!Window || !IntIsWindowDrawable(Window))
1409 {
1410 Window = NULL; /* prevent deref at cleanup */
1411 RETURN( ERROR);
1412 }
1413 UserRefObjectCo(Window, &Ref);
1414
1415 IntGetClientRect(Window, &rcClip);
1416
1417 _SEH2_TRY
1418 {
1419 if (prcUnsafeScroll)
1420 {
1421 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1422 RECTL_bIntersectRect(&rcScroll, &rcClip, prcUnsafeScroll);
1423 }
1424 else
1425 rcScroll = rcClip;
1426
1427 if (prcUnsafeClip)
1428 {
1429 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1430 RECTL_bIntersectRect(&rcClip, &rcClip, prcUnsafeClip);
1431 }
1432 }
1433 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1434 {
1435 Status = _SEH2_GetExceptionCode();
1436 }
1437 _SEH2_END
1438
1439 if (!NT_SUCCESS(Status))
1440 {
1441 SetLastNtError(Status);
1442 RETURN(ERROR);
1443 }
1444
1445 if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
1446 (dx == 0 && dy == 0))
1447 {
1448 RETURN(NULLREGION);
1449 }
1450
1451 if (hrgnUpdate)
1452 hrgnOwn = hrgnUpdate;
1453 else
1454 hrgnOwn = IntSysCreateRectRgn(0, 0, 0, 0);
1455
1456 hDC = UserGetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE);
1457 if (!hDC)
1458 {
1459 /* FIXME: SetLastError? */
1460 RETURN(ERROR);
1461 }
1462
1463 rcCaret = rcScroll;
1464 hwndCaret = co_IntFixCaret(Window, &rcCaret, flags);
1465
1466 Result = UserScrollDC( hDC,
1467 dx,
1468 dy,
1469 &rcScroll,
1470 &rcClip,
1471 hrgnOwn,
1472 prcUnsafeUpdate? &rcUpdate : NULL);
1473
1474 UserReleaseDC(Window, hDC, FALSE);
1475
1476 /*
1477 * Take into account the fact that some damage may have occurred during
1478 * the scroll.
1479 */
1480
1481 hrgnTemp = IntSysCreateRectRgn(0, 0, 0, 0);
1482 if (co_UserGetUpdateRgn(Window, hrgnTemp, FALSE) != NULLREGION)
1483 {
1484 HRGN hrgnClip = IntSysCreateRectRgnIndirect(&rcClip);
1485 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1486 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1487 co_UserRedrawWindow(Window, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1488 REGION_FreeRgnByHandle(hrgnClip);
1489 }
1490 REGION_FreeRgnByHandle(hrgnTemp);
1491
1492 if (flags & SW_SCROLLCHILDREN)
1493 {
1494 PWND Child;
1495 RECTL rcChild;
1496 POINT ClientOrigin;
1497 USER_REFERENCE_ENTRY WndRef;
1498 RECTL rcDummy;
1499
1500 IntGetClientOrigin(Window, &ClientOrigin);
1501 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
1502 {
1503 rcChild = Child->rcWindow;
1504 rcChild.left -= ClientOrigin.x;
1505 rcChild.top -= ClientOrigin.y;
1506 rcChild.right -= ClientOrigin.x;
1507 rcChild.bottom -= ClientOrigin.y;
1508
1509 if (! prcUnsafeScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll))
1510 {
1511 UserRefObjectCo(Child, &WndRef);
1512 co_WinPosSetWindowPos(Child, 0, rcChild.left + dx, rcChild.top + dy, 0, 0,
1513 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1514 SWP_NOREDRAW);
1515 UserDerefObjectCo(Child);
1516 }
1517 }
1518 }
1519
1520 if (flags & (SW_INVALIDATE | SW_ERASE))
1521 {
1522 co_UserRedrawWindow(Window, NULL, hrgnOwn, RDW_INVALIDATE | RDW_ERASE |
1523 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1524 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1525 }
1526
1527 if ((CaretWnd = UserGetWindowObject(hwndCaret)))
1528 {
1529 UserRefObjectCo(CaretWnd, &CaretRef);
1530
1531 co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
1532 co_UserShowCaret(CaretWnd);
1533
1534 UserDerefObjectCo(CaretWnd);
1535 }
1536
1537 if (prcUnsafeUpdate)
1538 {
1539 _SEH2_TRY
1540 {
1541 /* Probe here, to not fail on invalid pointer before scrolling */
1542 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1543 *prcUnsafeUpdate = rcUpdate;
1544 }
1545 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1546 {
1547 Status = _SEH2_GetExceptionCode();
1548 }
1549 _SEH2_END
1550
1551 if (!NT_SUCCESS(Status))
1552 {
1553 SetLastNtError(Status);
1554 RETURN(ERROR);
1555 }
1556 }
1557
1558 RETURN(Result);
1559
1560 CLEANUP:
1561 if (hrgnOwn && !hrgnUpdate)
1562 {
1563 REGION_FreeRgnByHandle(hrgnOwn);
1564 }
1565
1566 if (Window)
1567 UserDerefObjectCo(Window);
1568
1569 DPRINT("Leave NtUserScrollWindowEx, ret=%i\n",_ret_);
1570 UserLeave();
1571 END_CLEANUP;
1572 }
1573
1574
1575 BOOL
1576 UserDrawSysMenuButton(
1577 PWND pWnd,
1578 HDC hDc,
1579 RECTL *lpRc,
1580 BOOL Down)
1581 {
1582 HICON hIcon;
1583 PCURICON_OBJECT pIcon;
1584
1585 ASSERT(pWnd && lpRc);
1586
1587 /* Get the icon to draw. We don't care about WM_GETICON here. */
1588
1589 hIcon = pWnd->pcls->hIconSm;
1590
1591 if(!hIcon)
1592 {
1593 DPRINT("Wnd class has no small icon.\n");
1594 hIcon = pWnd->pcls->hIcon;
1595 }
1596
1597 if(!hIcon)
1598 {
1599 DPRINT("Wnd class hasn't any icon.\n");
1600 //FIXME: Draw "winlogo" icon.
1601 return FALSE;
1602 }
1603
1604 if(!(pIcon = UserGetCurIconObject(hIcon)))
1605 {
1606 DPRINT1("UserGetCurIconObject() failed!\n");
1607 return FALSE;
1608 }
1609
1610 return UserDrawIconEx(hDc, lpRc->left, lpRc->top, pIcon,
1611 UserGetSystemMetrics(SM_CXSMICON),
1612 UserGetSystemMetrics(SM_CYSMICON),
1613 0, NULL, DI_NORMAL);
1614 }
1615
1616 BOOL
1617 UserDrawCaptionText(
1618 HDC hDc,
1619 const PUNICODE_STRING Text,
1620 const RECTL *lpRc,
1621 UINT uFlags)
1622 {
1623 HFONT hOldFont = NULL, hFont = NULL;
1624 COLORREF OldTextColor;
1625 NONCLIENTMETRICSW nclm;
1626 NTSTATUS Status;
1627 #ifndef NDEBUG
1628 INT i;
1629 DPRINT("%s:", __FUNCTION__);
1630 for(i = 0; i < Text->Length/sizeof(WCHAR); i++)
1631 DbgPrint("%C", Text->Buffer[i]);
1632 DbgPrint(", %d\n", Text->Length/sizeof(WCHAR));
1633 #endif
1634
1635 nclm.cbSize = sizeof(nclm);
1636 if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS,
1637 sizeof(NONCLIENTMETRICS), &nclm, 0))
1638 {
1639 DPRINT1("%s: UserSystemParametersInfo() failed!\n", __FUNCTION__);
1640 return FALSE;
1641 }
1642
1643 IntGdiSetBkMode(hDc, TRANSPARENT);
1644
1645 if(uFlags & DC_SMALLCAP)
1646 Status = TextIntCreateFontIndirect(&nclm.lfSmCaptionFont, &hFont);
1647 else Status = TextIntCreateFontIndirect(&nclm.lfCaptionFont, &hFont);
1648
1649 if(!NT_SUCCESS(Status))
1650 {
1651 DPRINT1("%s: TextIntCreateFontIndirect() failed! Status: 0x%x\n",
1652 __FUNCTION__, Status);
1653 return FALSE;
1654 }
1655
1656 hOldFont = NtGdiSelectFont(hDc, hFont);
1657 if(!hOldFont)
1658 {
1659 DPRINT1("%s: SelectFont() failed!\n", __FUNCTION__);
1660 GreDeleteObject(hFont);
1661 return FALSE;
1662 }
1663
1664 if(uFlags & DC_INBUTTON)
1665 OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT));
1666 else OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(uFlags & DC_ACTIVE
1667 ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
1668
1669 //FIXME: If string doesn't fit to rc, truncate it and add ellipsis.
1670
1671 GreExtTextOutW(hDc, lpRc->left,
1672 lpRc->top, 0, NULL, Text->Buffer,
1673 Text->Length/sizeof(WCHAR), NULL, 0);
1674
1675 IntGdiSetTextColor(hDc, OldTextColor);
1676 NtGdiSelectFont(hDc, hOldFont);
1677 GreDeleteObject(hFont);
1678
1679 return TRUE;
1680 }
1681
1682 BOOL UserDrawCaption(
1683 PWND pWnd,
1684 HDC hDc,
1685 RECTL *lpRc,
1686 HFONT hFont,
1687 HICON hIcon,
1688 const PUNICODE_STRING str,
1689 UINT uFlags)
1690 {
1691 BOOL Ret = FALSE;
1692 HBITMAP hMemBmp = NULL, hOldBmp = NULL;
1693 HBRUSH hOldBrush = NULL;
1694 HDC hMemDc = NULL;
1695 ULONG Height;
1696 UINT VCenter = 0, Padding = 0;
1697 RECTL r = *lpRc;
1698 LONG ButtonWidth, IconWidth;
1699 BOOL HasIcon;
1700
1701 //ASSERT(pWnd != NULL);
1702
1703 RECTL_vMakeWellOrdered(lpRc);
1704 hMemBmp = NtGdiCreateCompatibleBitmap(hDc,
1705 lpRc->right - lpRc->left,
1706 lpRc->bottom - lpRc->top);
1707
1708 if(!hMemBmp)
1709 {
1710 DPRINT1("%s: NtGdiCreateCompatibleBitmap() failed!\n", __FUNCTION__);
1711 return FALSE;
1712 }
1713
1714 hMemDc = NtGdiCreateCompatibleDC(hDc);
1715 if(!hMemDc)
1716 {
1717 DPRINT1("%s: NtGdiCreateCompatibleDC() failed!\n", __FUNCTION__);
1718 goto cleanup;
1719 }
1720
1721 hOldBmp = NtGdiSelectBitmap(hMemDc, hMemBmp);
1722 if(!hOldBmp)
1723 {
1724 DPRINT1("%s: NtGdiSelectBitmap() failed!\n", __FUNCTION__);
1725 goto cleanup;
1726 }
1727
1728 Height = UserGetSystemMetrics(SM_CYCAPTION) - 1;
1729 VCenter = (lpRc->bottom - lpRc->top) / 2;
1730 Padding = VCenter - (Height / 2);
1731
1732 if ((!hIcon) && (pWnd != NULL))
1733 {
1734 HasIcon = (uFlags & DC_ICON) && (pWnd->style & WS_SYSMENU)
1735 && !(uFlags & DC_SMALLCAP) && !(pWnd->ExStyle & WS_EX_DLGMODALFRAME)
1736 && !(pWnd->ExStyle & WS_EX_TOOLWINDOW);
1737 }
1738 else
1739 HasIcon = (hIcon != 0);
1740
1741 IconWidth = UserGetSystemMetrics(SM_CXSIZE) + Padding;
1742
1743 r.left = Padding;
1744 r.right = r.left + (lpRc->right - lpRc->left);
1745 r.top = Padding;
1746 r.bottom = r.top + (Height / 2);
1747
1748 // Draw the caption background
1749 if(uFlags & DC_INBUTTON)
1750 {
1751 hOldBrush = NtGdiSelectBrush(hMemDc,
1752 IntGetSysColorBrush(COLOR_3DFACE));
1753
1754 if(!hOldBrush)
1755 {
1756 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1757 goto cleanup;
1758 }
1759
1760 if(!NtGdiPatBlt(hMemDc, 0, 0,
1761 lpRc->right - lpRc->left,
1762 lpRc->bottom - lpRc->top,
1763 PATCOPY))
1764 {
1765 DPRINT1("%s: NtGdiPatBlt() failed!\n", __FUNCTION__);
1766 goto cleanup;
1767 }
1768
1769 if(HasIcon) r.left+=IconWidth;
1770 }
1771 else
1772 {
1773 r.right = (lpRc->right - lpRc->left);
1774 if(uFlags & DC_SMALLCAP)
1775 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1776 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1777
1778 hOldBrush = NtGdiSelectBrush(hMemDc,
1779 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1780 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION));
1781
1782 if(!hOldBrush)
1783 {
1784 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1785 goto cleanup;
1786 }
1787
1788 if(HasIcon && (uFlags & DC_GRADIENT))
1789 {
1790 NtGdiPatBlt(hMemDc, 0, 0,
1791 IconWidth+1,
1792 lpRc->bottom - lpRc->top,
1793 PATCOPY);
1794 r.left+=IconWidth;
1795 }
1796 else
1797 {
1798 NtGdiPatBlt(hMemDc, 0, 0,
1799 lpRc->right - lpRc->left,
1800 lpRc->bottom - lpRc->top,
1801 PATCOPY);
1802 }
1803
1804 if(uFlags & DC_GRADIENT)
1805 {
1806 static GRADIENT_RECT gcap = {0, 1};
1807 TRIVERTEX vert[2];
1808 COLORREF Colors[2];
1809
1810 if (pWnd != NULL)
1811 {
1812 if(pWnd->style & WS_SYSMENU)
1813 {
1814 r.right -= 3 + ButtonWidth;
1815 if(!(uFlags & DC_SMALLCAP))
1816 {
1817 if(pWnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1818 r.right -= 2 + 2 * ButtonWidth;
1819 else r.right -= 2;
1820 r.right -= 2;
1821 }
1822
1823 //Draw buttons background
1824 if(!NtGdiSelectBrush(hMemDc,
1825 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1826 COLOR_GRADIENTACTIVECAPTION:COLOR_GRADIENTINACTIVECAPTION)))
1827 {
1828 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1829 goto cleanup;
1830 }
1831
1832 NtGdiPatBlt(hMemDc,
1833 r.right,
1834 0,
1835 lpRc->right - lpRc->left - r.right,
1836 lpRc->bottom - lpRc->top,
1837 PATCOPY);
1838 }
1839 }
1840
1841 Colors[0] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1842 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION);
1843
1844 Colors[1] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1845 COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION);
1846
1847 vert[0].x = r.left;
1848 vert[0].y = 0;
1849 vert[0].Red = (WORD)Colors[0]<<8;
1850 vert[0].Green = (WORD)Colors[0] & 0xFF00;
1851 vert[0].Blue = (WORD)(Colors[0]>>8) & 0xFF00;
1852 vert[0].Alpha = 0;
1853
1854 vert[1].x = r.right;
1855 vert[1].y = lpRc->bottom - lpRc->top;
1856 vert[1].Red = (WORD)Colors[1]<<8;
1857 vert[1].Green = (WORD)Colors[1] & 0xFF00;
1858 vert[1].Blue = (WORD)(Colors[1]>>8) & 0xFF00;
1859 vert[1].Alpha = 0;
1860
1861 if(!GreGradientFill(hMemDc, vert, 2, &gcap,
1862 1, GRADIENT_FILL_RECT_H))
1863 {
1864 DPRINT1("%s: IntGdiGradientFill() failed!\n", __FUNCTION__);
1865 }
1866
1867 } //if(uFlags & DC_GRADIENT)
1868 }
1869
1870 if(HasIcon)
1871 {
1872 r.top ++;
1873 r.left -= --IconWidth;
1874 /* FIXME: Draw the Icon when pWnd == NULL but hIcon is valid */
1875 if (pWnd != NULL)
1876 UserDrawSysMenuButton(pWnd, hMemDc, &r, FALSE);
1877
1878 r.left += IconWidth;
1879 r.top --;
1880 }
1881
1882 r.top ++;
1883 r.left += 2;
1884
1885 r.bottom = r.top + Height;
1886
1887 if((uFlags & DC_TEXT))
1888 {
1889 if(!(uFlags & DC_GRADIENT))
1890 {
1891 r.right = (lpRc->right - lpRc->left);
1892
1893 if(uFlags & DC_SMALLCAP)
1894 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1895 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1896
1897 if ((pWnd != NULL) && (pWnd->style & WS_SYSMENU))
1898 {
1899 r.right -= 3 + ButtonWidth;
1900 if(! (uFlags & DC_SMALLCAP))
1901 {
1902 if(pWnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1903 r.right -= 2 + 2 * ButtonWidth;
1904 else r.right -= 2;
1905 r.right -= 2;
1906 }
1907 }
1908 }
1909
1910 /* FIXME: hFont isn't handled */
1911 if (str)
1912 UserDrawCaptionText(hMemDc, str, &r, uFlags);
1913 else if (pWnd != NULL)
1914 {
1915 UNICODE_STRING ustr;
1916 ustr.Buffer = pWnd->strName.Buffer;
1917 ustr.Length = pWnd->strName.Length;
1918 ustr.MaximumLength = pWnd->strName.MaximumLength;
1919 UserDrawCaptionText(hMemDc, &ustr, &r, uFlags);
1920 }
1921 }
1922
1923 if(!NtGdiBitBlt(hDc, lpRc->left, lpRc->top,
1924 lpRc->right - lpRc->left, lpRc->bottom - lpRc->top,
1925 hMemDc, 0, 0, SRCCOPY, 0, 0))
1926 {
1927 DPRINT1("%s: NtGdiBitBlt() failed!\n", __FUNCTION__);
1928 goto cleanup;
1929 }
1930
1931 Ret = TRUE;
1932
1933 cleanup:
1934 if (hOldBrush) NtGdiSelectBrush(hMemDc, hOldBrush);
1935 if (hOldBmp) NtGdiSelectBitmap(hMemDc, hOldBmp);
1936 if (hMemBmp) GreDeleteObject(hMemBmp);
1937 if (hMemDc) NtGdiDeleteObjectApp(hMemDc);
1938
1939 return Ret;
1940 }
1941
1942 INT
1943 FASTCALL
1944 UserRealizePalette(HDC hdc)
1945 {
1946 HWND hWnd;
1947 DWORD Ret;
1948
1949 Ret = IntGdiRealizePalette(hdc);
1950 if (Ret) // There was a change.
1951 {
1952 hWnd = IntWindowFromDC(hdc);
1953 if (hWnd) // Send broadcast if dc is associated with a window.
1954 { // FYI: Thread locked in CallOneParam.
1955 UserSendNotifyMessage((HWND)HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0);
1956 }
1957 }
1958 return Ret;
1959 }
1960
1961 BOOL
1962 APIENTRY
1963 NtUserDrawCaptionTemp(
1964 HWND hWnd,
1965 HDC hDC,
1966 LPCRECT lpRc,
1967 HFONT hFont,
1968 HICON hIcon,
1969 const PUNICODE_STRING str,
1970 UINT uFlags)
1971 {
1972 PWND pWnd = NULL;
1973 UNICODE_STRING SafeStr = {0};
1974 NTSTATUS Status = STATUS_SUCCESS;
1975 RECTL SafeRect;
1976 BOOL Ret;
1977
1978 UserEnterExclusive();
1979
1980 if (hWnd != NULL)
1981 {
1982 if(!(pWnd = UserGetWindowObject(hWnd)))
1983 {
1984 UserLeave();
1985 return FALSE;
1986 }
1987 }
1988
1989 _SEH2_TRY
1990 {
1991 ProbeForRead(lpRc, sizeof(RECTL), sizeof(ULONG));
1992 RtlCopyMemory(&SafeRect, lpRc, sizeof(RECTL));
1993 if (str != NULL)
1994 {
1995 SafeStr = ProbeForReadUnicodeString(str);
1996 if (SafeStr.Length != 0)
1997 {
1998 ProbeForRead( SafeStr.Buffer,
1999 SafeStr.Length,
2000 sizeof(WCHAR));
2001 }
2002 }
2003 }
2004 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2005 {
2006 Status = _SEH2_GetExceptionCode();
2007 }
2008 _SEH2_END;
2009
2010 if (Status != STATUS_SUCCESS)
2011 {
2012 SetLastNtError(Status);
2013 UserLeave();
2014 return FALSE;
2015 }
2016
2017 if (str != NULL)
2018 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags);
2019 else
2020 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags);
2021
2022 UserLeave();
2023 return Ret;
2024 }
2025
2026 BOOL
2027 APIENTRY
2028 NtUserDrawCaption(HWND hWnd,
2029 HDC hDC,
2030 LPCRECT lpRc,
2031 UINT uFlags)
2032 {
2033 return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
2034 }
2035
2036 BOOL
2037 APIENTRY
2038 NtUserInvalidateRect(
2039 HWND hWnd,
2040 CONST RECT *lpUnsafeRect,
2041 BOOL bErase)
2042 {
2043 return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2044 }
2045
2046 BOOL
2047 APIENTRY
2048 NtUserInvalidateRgn(
2049 HWND hWnd,
2050 HRGN hRgn,
2051 BOOL bErase)
2052 {
2053 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2054 }
2055
2056 BOOL
2057 APIENTRY
2058 NtUserPrintWindow(
2059 HWND hwnd,
2060 HDC hdcBlt,
2061 UINT nFlags)
2062 {
2063 PWND Window;
2064 BOOL Ret = FALSE;
2065
2066 UserEnterExclusive();
2067
2068 if (hwnd)
2069 {
2070 Window = UserGetWindowObject(hwnd);
2071 // TODO: Add Desktop and MessageBox check via FNID's.
2072 if ( Window )
2073 {
2074 /* Validate flags and check it as a mask for 0 or 1. */
2075 if ( (nFlags & PW_CLIENTONLY) == nFlags)
2076 Ret = IntPrintWindow( Window, hdcBlt, nFlags);
2077 else
2078 EngSetLastError(ERROR_INVALID_PARAMETER);
2079 }
2080 }
2081
2082 UserLeave();
2083 return Ret;
2084 }
2085
2086 /* ValidateRect gets redirected to NtUserValidateRect:
2087 http://blog.csdn.net/ntdll/archive/2005/10/19/509299.aspx */
2088 BOOL
2089 APIENTRY
2090 NtUserValidateRect(
2091 HWND hWnd,
2092 const RECT *lpRect)
2093 {
2094 if (hWnd)
2095 {
2096 return NtUserRedrawWindow(hWnd, lpRect, NULL, RDW_VALIDATE );
2097 }
2098 return NtUserRedrawWindow(hWnd, lpRect, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ERASENOW|RDW_ALLCHILDREN);
2099 }
2100
2101 /* EOF */