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