xref: /reactos/win32ss/user/ntuser/painting.c (revision 5cadc268)
1 /*
2  *  COPYRIGHT:        See COPYING in the top level directory
3  *  PROJECT:          ReactOS Win32k subsystem
4  *  PURPOSE:          Window painting function
5  *  FILE:             win32ss/user/ntuser/painting.c
6  *  PROGRAMER:        Filip Navara (xnavara@volny.cz)
7  */
8 
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserPainting);
11 
12 BOOL UserExtTextOutW(HDC hdc, INT x, INT y, UINT flags, PRECTL lprc,
13                      LPCWSTR lpString, UINT count);
14 
15 /* PRIVATE FUNCTIONS **********************************************************/
16 
17 /**
18  * @name IntIntersectWithParents
19  *
20  * Intersect window rectangle with all parent client rectangles.
21  *
22  * @param Child
23  *        Pointer to child window to start intersecting from.
24  * @param WindowRect
25  *        Pointer to rectangle that we want to intersect in screen
26  *        coordinates on input and intersected rectangle on output (if TRUE
27  *        is returned).
28  *
29  * @return
30  *    If any parent is minimized or invisible or the resulting rectangle
31  *    is empty then FALSE is returned. Otherwise TRUE is returned.
32  */
33 
34 BOOL FASTCALL
35 IntIntersectWithParents(PWND Child, RECTL *WindowRect)
36 {
37    PWND ParentWnd;
38 
39    if (Child->ExStyle & WS_EX_REDIRECTED)
40       return TRUE;
41 
42    ParentWnd = Child->spwndParent;
43    while (ParentWnd != NULL)
44    {
45       if (!(ParentWnd->style & WS_VISIBLE)  ||
46            (ParentWnd->style & WS_MINIMIZE) ||
47           !RECTL_bIntersectRect(WindowRect, WindowRect, &ParentWnd->rcClient) )
48       {
49          return FALSE;
50       }
51 
52       if (ParentWnd->ExStyle & WS_EX_REDIRECTED)
53          return TRUE;
54 
55       ParentWnd = ParentWnd->spwndParent;
56    }
57 
58    return TRUE;
59 }
60 
61 BOOL FASTCALL
62 IntValidateParents(PWND Child, BOOL Recurse)
63 {
64    RECTL ParentRect, Rect;
65    BOOL Start, Ret = TRUE;
66    PWND ParentWnd = Child;
67    PREGION Rgn = NULL;
68 
69    if (ParentWnd->style & WS_CHILD)
70    {
71       do
72          ParentWnd = ParentWnd->spwndParent;
73       while (ParentWnd->style & WS_CHILD);
74    }
75 
76    // No pending nonclient paints.
77    if (!(ParentWnd->state & WNDS_SYNCPAINTPENDING)) Recurse = FALSE;
78 
79    Start = TRUE;
80    ParentWnd = Child->spwndParent;
81    while (ParentWnd)
82    {
83       if (ParentWnd->style & WS_CLIPCHILDREN)
84          break;
85 
86       if (ParentWnd->hrgnUpdate != 0)
87       {
88          if (Recurse)
89          {
90             Ret = FALSE;
91             break;
92          }
93          // Start with child clipping.
94          if (Start)
95          {
96             Start = FALSE;
97 
98             Rect = Child->rcWindow;
99 
100             if (!IntIntersectWithParents(Child, &Rect)) break;
101 
102             Rgn = IntSysCreateRectpRgnIndirect(&Rect);
103 
104             if (Child->hrgnClip)
105             {
106                PREGION RgnClip = REGION_LockRgn(Child->hrgnClip);
107                IntGdiCombineRgn(Rgn, Rgn, RgnClip, RGN_AND);
108                REGION_UnlockRgn(RgnClip);
109             }
110          }
111 
112          ParentRect = ParentWnd->rcWindow;
113 
114          if (!IntIntersectWithParents(ParentWnd, &ParentRect)) break;
115 
116          IntInvalidateWindows( ParentWnd,
117                                Rgn,
118                                RDW_VALIDATE | RDW_NOCHILDREN | RDW_NOUPDATEDIRTY);
119       }
120       ParentWnd = ParentWnd->spwndParent;
121    }
122 
123    if (Rgn) REGION_Delete(Rgn);
124 
125    return Ret;
126 }
127 
128 /*
129   Synchronize painting to the top-level windows of other threads.
130 */
131 VOID FASTCALL
132 IntSendSyncPaint(PWND Wnd, ULONG Flags)
133 {
134    PTHREADINFO ptiCur, ptiWnd;
135    PUSER_SENT_MESSAGE Message;
136    PLIST_ENTRY Entry;
137    BOOL bSend = TRUE;
138 
139    ptiWnd = Wnd->head.pti;
140    ptiCur = PsGetCurrentThreadWin32Thread();
141    /*
142       Not the current thread, Wnd is in send Nonclient paint also in send erase background and it is visiable.
143    */
144    if ( Wnd->head.pti != ptiCur &&
145         Wnd->state & WNDS_SENDNCPAINT &&
146         Wnd->state & WNDS_SENDERASEBACKGROUND &&
147         Wnd->style & WS_VISIBLE)
148    {
149       // For testing, if you see this, break out the Champagne and have a party!
150       TRACE("SendSyncPaint Wnd in State!\n");
151       if (!IsListEmpty(&ptiWnd->SentMessagesListHead))
152       {
153          // Scan sent queue messages to see if we received sync paint messages.
154          Entry = ptiWnd->SentMessagesListHead.Flink;
155          Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
156          do
157          {
158             ERR("LOOP it\n");
159             if (Message->Msg.message == WM_SYNCPAINT &&
160                 Message->Msg.hwnd == UserHMGetHandle(Wnd))
161             {  // Already received so exit out.
162                 ERR("SendSyncPaint Found one in the Sent Msg Queue!\n");
163                 bSend = FALSE;
164                 break;
165             }
166             Entry = Message->ListEntry.Flink;
167             Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
168          }
169          while (Entry != &ptiWnd->SentMessagesListHead);
170       }
171       if (bSend)
172       {
173          TRACE("Sending WM_SYNCPAINT\n");
174          // This message has no parameters. But it does! Pass Flags along.
175          co_IntSendMessageNoWait(UserHMGetHandle(Wnd), WM_SYNCPAINT, Flags, 0);
176          Wnd->state |= WNDS_SYNCPAINTPENDING;
177       }
178    }
179 
180    // Send to all the children if this is the desktop window.
181    if (UserIsDesktopWindow(Wnd))
182    {
183       if ( Flags & RDW_ALLCHILDREN ||
184           ( !(Flags & RDW_NOCHILDREN) && Wnd->style & WS_CLIPCHILDREN))
185       {
186          PWND spwndChild = Wnd->spwndChild;
187          while(spwndChild)
188          {
189             if ( spwndChild->style & WS_CHILD &&
190                  spwndChild->head.pti != ptiCur)
191             {
192                spwndChild = spwndChild->spwndNext;
193                continue;
194             }
195             IntSendSyncPaint( spwndChild, Flags );
196             spwndChild = spwndChild->spwndNext;
197          }
198       }
199    }
200 }
201 
202 /*
203  * @name IntCalcWindowRgn
204  *
205  * Get a window or client region.
206  */
207 
208 HRGN FASTCALL
209 IntCalcWindowRgn(PWND Wnd, BOOL Client)
210 {
211    HRGN hRgnWindow;
212 
213    if (Client)
214    {
215       hRgnWindow = NtGdiCreateRectRgn(
216           Wnd->rcClient.left,
217           Wnd->rcClient.top,
218           Wnd->rcClient.right,
219           Wnd->rcClient.bottom);
220    }
221    else
222    {
223       hRgnWindow = NtGdiCreateRectRgn(
224           Wnd->rcWindow.left,
225           Wnd->rcWindow.top,
226           Wnd->rcWindow.right,
227           Wnd->rcWindow.bottom);
228    }
229 
230    if (Wnd->hrgnClip != NULL && !(Wnd->style & WS_MINIMIZE))
231    {
232       NtGdiOffsetRgn(hRgnWindow,
233          -Wnd->rcWindow.left,
234          -Wnd->rcWindow.top);
235       NtGdiCombineRgn(hRgnWindow, hRgnWindow, Wnd->hrgnClip, RGN_AND);
236       NtGdiOffsetRgn(hRgnWindow,
237          Wnd->rcWindow.left,
238          Wnd->rcWindow.top);
239    }
240 
241    return hRgnWindow;
242 }
243 
244 /*
245  * @name IntGetNCUpdateRgn
246  *
247  * Get non-client update region of a window and optionally validate it.
248  *
249  * @param Window
250  *        Pointer to window to get the NC update region from.
251  * @param Validate
252  *        Set to TRUE to force validating the NC update region.
253  *
254  * @return
255  *    Handle to NC update region. The caller is responsible for deleting
256  *    it.
257  */
258 
259 HRGN FASTCALL
260 IntGetNCUpdateRgn(PWND Window, BOOL Validate)
261 {
262    HRGN hRgnNonClient;
263    HRGN hRgnWindow;
264    UINT RgnType, NcType;
265    RECT update;
266 
267    if (Window->hrgnUpdate != NULL &&
268        Window->hrgnUpdate != HRGN_WINDOW)
269    {
270       hRgnNonClient = IntCalcWindowRgn(Window, FALSE);
271 
272       /*
273        * If region creation fails it's safe to fallback to whole
274        * window region.
275        */
276       if (hRgnNonClient == NULL)
277       {
278          return HRGN_WINDOW;
279       }
280 
281       hRgnWindow = IntCalcWindowRgn(Window, TRUE);
282       if (hRgnWindow == NULL)
283       {
284          GreDeleteObject(hRgnNonClient);
285          return HRGN_WINDOW;
286       }
287 
288       NcType = IntGdiGetRgnBox(hRgnNonClient, &update);
289 
290       RgnType = NtGdiCombineRgn(hRgnNonClient, hRgnNonClient, hRgnWindow, RGN_DIFF);
291 
292       if (RgnType == ERROR)
293       {
294          GreDeleteObject(hRgnWindow);
295          GreDeleteObject(hRgnNonClient);
296          return HRGN_WINDOW;
297       }
298       else if (RgnType == NULLREGION)
299       {
300          GreDeleteObject(hRgnWindow);
301          GreDeleteObject(hRgnNonClient);
302          Window->state &= ~WNDS_UPDATEDIRTY;
303          return NULL;
304       }
305 
306       /*
307        * Remove the nonclient region from the standard update region if
308        * we were asked for it.
309        */
310 
311       if (Validate)
312       {
313          if (NtGdiCombineRgn(Window->hrgnUpdate, Window->hrgnUpdate, hRgnWindow, RGN_AND) == NULLREGION)
314          {
315             IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
316             GreDeleteObject(Window->hrgnUpdate);
317             Window->state &= ~WNDS_UPDATEDIRTY;
318             Window->hrgnUpdate = NULL;
319             if (!(Window->state & WNDS_INTERNALPAINT))
320                MsqDecPaintCountQueue(Window->head.pti);
321          }
322       }
323 
324       /* check if update rgn contains complete nonclient area */
325       if (NcType == SIMPLEREGION)
326       {
327          RECT window;
328          IntGetWindowRect( Window, &window );
329 
330          if (IntEqualRect( &window, &update ))
331          {
332             GreDeleteObject(hRgnNonClient);
333             hRgnNonClient = HRGN_WINDOW;
334          }
335       }
336 
337       GreDeleteObject(hRgnWindow);
338 
339       return hRgnNonClient;
340    }
341    else
342    {
343       return Window->hrgnUpdate;
344    }
345 }
346 
347 VOID FASTCALL
348 IntSendNCPaint(PWND pWnd, HRGN hRgn)
349 {
350    pWnd->state &= ~WNDS_SENDNCPAINT;
351 
352    if ( pWnd == GetW32ThreadInfo()->MessageQueue->spwndActive &&
353        !(pWnd->state & WNDS_ACTIVEFRAME))
354    {
355       pWnd->state |= WNDS_ACTIVEFRAME;
356       pWnd->state &= ~WNDS_NONCPAINT;
357       hRgn = HRGN_WINDOW;
358    }
359 
360    if (pWnd->state2 & WNDS2_FORCEFULLNCPAINTCLIPRGN)
361    {
362       pWnd->state2 &= ~WNDS2_FORCEFULLNCPAINTCLIPRGN;
363       hRgn = HRGN_WINDOW;
364    }
365 
366    if (hRgn) co_IntSendMessage(UserHMGetHandle(pWnd), WM_NCPAINT, (WPARAM)hRgn, 0);
367 }
368 
369 VOID FASTCALL
370 IntSendChildNCPaint(PWND pWnd)
371 {
372     pWnd = pWnd->spwndChild;
373     while (pWnd)
374     {
375         if ((pWnd->hrgnUpdate == NULL) && (pWnd->state & WNDS_SENDNCPAINT))
376         {
377             PWND Next;
378             USER_REFERENCE_ENTRY Ref;
379 
380             /* Reference, IntSendNCPaint leaves win32k */
381             UserRefObjectCo(pWnd, &Ref);
382             IntSendNCPaint(pWnd, HRGN_WINDOW);
383 
384             /* Make sure to grab next one before dereferencing/freeing */
385             Next = pWnd->spwndNext;
386             UserDerefObjectCo(pWnd);
387             pWnd = Next;
388         }
389         else
390         {
391             pWnd = pWnd->spwndNext;
392         }
393     }
394 }
395 
396 /*
397  * IntPaintWindows
398  *
399  * Internal function used by IntRedrawWindow.
400  */
401 
402 VOID FASTCALL
403 co_IntPaintWindows(PWND Wnd, ULONG Flags, BOOL Recurse)
404 {
405    HDC hDC;
406    HWND hWnd = Wnd->head.h;
407    HRGN TempRegion = NULL;
408 
409    Wnd->state &= ~WNDS_PAINTNOTPROCESSED;
410 
411    if (Wnd->state & WNDS_SENDNCPAINT ||
412        Wnd->state & WNDS_SENDERASEBACKGROUND)
413    {
414       if (!(Wnd->style & WS_VISIBLE))
415       {
416          Wnd->state &= ~(WNDS_SENDNCPAINT|WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
417          return;
418       }
419       else
420       {
421          if (Wnd->hrgnUpdate == NULL)
422          {
423             Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
424          }
425 
426          if (Wnd->head.pti == PsGetCurrentThreadWin32Thread())
427          {
428             if (Wnd->state & WNDS_SENDNCPAINT)
429             {
430                TempRegion = IntGetNCUpdateRgn(Wnd, TRUE);
431 
432                IntSendNCPaint(Wnd, TempRegion);
433 
434                if (TempRegion > HRGN_WINDOW && GreIsHandleValid(TempRegion))
435                {
436                   /* NOTE: The region can already be deleted! */
437                   GreDeleteObject(TempRegion);
438                }
439             }
440 
441             if (Wnd->state & WNDS_SENDERASEBACKGROUND)
442             {
443                PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
444                if (Wnd->hrgnUpdate)
445                {
446                   hDC = UserGetDCEx( Wnd,
447                                      Wnd->hrgnUpdate,
448                                      DCX_CACHE|DCX_USESTYLE|DCX_INTERSECTRGN|DCX_KEEPCLIPRGN);
449 
450                   if (Wnd->head.pti->ppi != pti->ppi)
451                   {
452                      ERR("Sending DC to another Process!!!\n");
453                   }
454 
455                   Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
456                   // Kill the loop, so Clear before we send.
457                   if (!co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
458                   {
459                      Wnd->state |= (WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
460                   }
461                   UserReleaseDC(Wnd, hDC, FALSE);
462                }
463             }
464          }
465 
466       }
467    }
468 
469    /*
470     * Check that the window is still valid at this point
471     */
472    if (!IntIsWindow(hWnd))
473    {
474       return;
475    }
476 
477    /*
478     * Paint child windows.
479     */
480 
481    if (!(Flags & RDW_NOCHILDREN) &&
482        !(Wnd->style & WS_MINIMIZE) &&
483         ( Flags & RDW_ALLCHILDREN ||
484          (Flags & RDW_CLIPCHILDREN && Wnd->style & WS_CLIPCHILDREN) ) )
485    {
486       HWND *List, *phWnd;
487       PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
488 
489       if ((List = IntWinListChildren(Wnd)))
490       {
491          for (phWnd = List; *phWnd; ++phWnd)
492          {
493             if ((Wnd = UserGetWindowObject(*phWnd)) == NULL)
494                continue;
495 
496             if (Wnd->head.pti != pti && Wnd->style & WS_CHILD)
497                continue;
498 
499             if (Wnd->style & WS_VISIBLE)
500             {
501                USER_REFERENCE_ENTRY Ref;
502                UserRefObjectCo(Wnd, &Ref);
503                co_IntPaintWindows(Wnd, Flags, TRUE);
504                UserDerefObjectCo(Wnd);
505             }
506          }
507          ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
508       }
509    }
510 }
511 
512 /*
513  * IntUpdateWindows
514  *
515  * Internal function used by IntRedrawWindow, simplecall.
516  */
517 
518 VOID FASTCALL
519 co_IntUpdateWindows(PWND Wnd, ULONG Flags, BOOL Recurse)
520 {
521    HWND hWnd = Wnd->head.h;
522 
523    if ( Wnd->hrgnUpdate != NULL || Wnd->state & WNDS_INTERNALPAINT )
524    {
525       if (Wnd->hrgnUpdate)
526       {
527          if (!IntValidateParents(Wnd, Recurse))
528          {
529             return;
530          }
531       }
532 
533       if (Wnd->state & WNDS_INTERNALPAINT)
534       {
535           Wnd->state &= ~WNDS_INTERNALPAINT;
536 
537           if (Wnd->hrgnUpdate == NULL)
538              MsqDecPaintCountQueue(Wnd->head.pti);
539       }
540 
541       Wnd->state |= WNDS_PAINTNOTPROCESSED;
542       Wnd->state &= ~WNDS_UPDATEDIRTY;
543 
544       Wnd->state2 |= WNDS2_WMPAINTSENT;
545       co_IntSendMessage(hWnd, WM_PAINT, 0, 0);
546 
547       if (Wnd->state & WNDS_PAINTNOTPROCESSED)
548       {
549          USER_REFERENCE_ENTRY Ref;
550          UserRefObjectCo(Wnd, &Ref);
551          co_IntPaintWindows(Wnd, RDW_NOCHILDREN, FALSE);
552          UserDerefObjectCo(Wnd);
553       }
554    }
555 
556    // Force flags as a toggle. Fixes msg:test_paint_messages:WmChildPaintNc.
557    Flags = (Flags & RDW_NOCHILDREN) ? RDW_NOCHILDREN : RDW_ALLCHILDREN; // All children is the default.
558 
559   /*
560    * Update child windows.
561    */
562 
563    if (!(Flags & RDW_NOCHILDREN)  &&
564         (Flags & RDW_ALLCHILDREN) &&
565         !UserIsDesktopWindow(Wnd))
566    {
567       PWND Child;
568 
569       for (Child = Wnd->spwndChild; Child; Child = Child->spwndNext)
570       {
571          /* transparent window, check for non-transparent sibling to paint first, then skip it */
572          if ( Child->ExStyle & WS_EX_TRANSPARENT &&
573              ( Child->hrgnUpdate != NULL || Child->state & WNDS_INTERNALPAINT ) )
574          {
575             PWND Next = Child->spwndNext;
576             while (Next)
577             {
578                if ( Next->hrgnUpdate != NULL || Next->state & WNDS_INTERNALPAINT ) break;
579 
580                Next = Next->spwndNext;
581             }
582 
583             if (Next) continue;
584          }
585 
586          if (Child->style & WS_VISIBLE)
587          {
588              USER_REFERENCE_ENTRY Ref;
589              UserRefObjectCo(Child, &Ref);
590              co_IntUpdateWindows(Child, Flags, TRUE);
591              UserDerefObjectCo(Child);
592          }
593       }
594    }
595 }
596 
597 VOID FASTCALL
598 UserUpdateWindows(PWND pWnd, ULONG Flags)
599 {
600    // If transparent and any sibling windows below needs to be painted, leave.
601    if (pWnd->ExStyle & WS_EX_TRANSPARENT)
602    {
603       PWND Next = pWnd->spwndNext;
604 
605       while(Next)
606       {
607          if ( Next->head.pti == pWnd->head.pti &&
608             ( Next->hrgnUpdate != NULL || Next->state & WNDS_INTERNALPAINT) )
609          {
610             return;
611          }
612 
613          Next = Next->spwndNext;
614       }
615    }
616    co_IntUpdateWindows(pWnd, Flags, FALSE);
617 }
618 
619 VOID FASTCALL
620 UserSyncAndPaintWindows(PWND pWnd, ULONG Flags)
621 {
622    PWND Parent = pWnd;
623    // Find parent, if it needs to be painted, leave.
624    while(TRUE)
625    {
626       if ((Parent = Parent->spwndParent) == NULL) break;
627       if ( Parent->style & WS_CLIPCHILDREN ) break;
628       if ( Parent->hrgnUpdate != NULL || Parent->state & WNDS_INTERNALPAINT ) return;
629    }
630 
631    IntSendSyncPaint(pWnd, Flags);
632    co_IntPaintWindows(pWnd, Flags, FALSE);
633 }
634 
635 /*
636  * IntInvalidateWindows
637  *
638  * Internal function used by IntRedrawWindow, UserRedrawDesktop,
639  * co_WinPosSetWindowPos, co_UserRedrawWindow.
640  */
641 VOID FASTCALL
642 IntInvalidateWindows(PWND Wnd, PREGION Rgn, ULONG Flags)
643 {
644    INT RgnType = NULLREGION;
645    BOOL HadPaintMessage;
646 
647    TRACE("IntInvalidateWindows start Rgn %p\n",Rgn);
648 
649    if ( Rgn > PRGN_WINDOW )
650    {
651       /*
652        * If the nonclient is not to be redrawn, clip the region to the client
653        * rect
654        */
655       if ((Flags & RDW_INVALIDATE) != 0 && (Flags & RDW_FRAME) == 0)
656       {
657          PREGION RgnClient;
658 
659          RgnClient = IntSysCreateRectpRgnIndirect(&Wnd->rcClient);
660          if (RgnClient)
661          {
662              RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnClient, RGN_AND);
663              REGION_Delete(RgnClient);
664          }
665       }
666 
667       /*
668        * Clip the given region with window rectangle (or region)
669        */
670 
671       if (!Wnd->hrgnClip || (Wnd->style & WS_MINIMIZE))
672       {
673          PREGION RgnWindow = IntSysCreateRectpRgnIndirect(&Wnd->rcWindow);
674          if (RgnWindow)
675          {
676              RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnWindow, RGN_AND);
677              REGION_Delete(RgnWindow);
678          }
679       }
680       else
681       {
682           PREGION RgnClip = REGION_LockRgn(Wnd->hrgnClip);
683           if (RgnClip)
684           {
685               REGION_bOffsetRgn(Rgn,
686                                 -Wnd->rcWindow.left,
687                                 -Wnd->rcWindow.top);
688               RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnClip, RGN_AND);
689               REGION_bOffsetRgn(Rgn,
690                                 Wnd->rcWindow.left,
691                                 Wnd->rcWindow.top);
692               REGION_UnlockRgn(RgnClip);
693           }
694       }
695    }
696    else
697    {
698       RgnType = NULLREGION;
699    }
700 
701    /*
702     * Save current state of pending updates
703     */
704 
705    HadPaintMessage = IntIsWindowDirty(Wnd);
706 
707    /*
708     * Update the region and flags
709     */
710 
711    // The following flags are used to invalidate the window.
712    if (Flags & (RDW_INVALIDATE|RDW_INTERNALPAINT|RDW_ERASE|RDW_FRAME))
713    {
714       if (Flags & RDW_INTERNALPAINT)
715       {
716          Wnd->state |= WNDS_INTERNALPAINT;
717       }
718 
719       if (Flags & RDW_INVALIDATE )
720       {
721          PREGION RgnUpdate;
722 
723          Wnd->state &= ~WNDS_NONCPAINT;
724 
725          /* If not the same thread set it dirty. */
726          if (Wnd->head.pti != PsGetCurrentThreadWin32Thread())
727          {
728             Wnd->state |= WNDS_UPDATEDIRTY;
729             if (Wnd->state2 & WNDS2_WMPAINTSENT)
730                Wnd->state2 |= WNDS2_ENDPAINTINVALIDATE;
731          }
732 
733          if (Flags & RDW_FRAME)
734             Wnd->state |= WNDS_SENDNCPAINT;
735 
736          if (Flags & RDW_ERASE)
737             Wnd->state |= WNDS_SENDERASEBACKGROUND;
738 
739          if (RgnType != NULLREGION && Rgn > PRGN_WINDOW)
740          {
741             if (Wnd->hrgnUpdate == NULL)
742             {
743                Wnd->hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0);
744                IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_PUBLIC);
745             }
746 
747             if (Wnd->hrgnUpdate != HRGN_WINDOW)
748             {
749                RgnUpdate = REGION_LockRgn(Wnd->hrgnUpdate);
750                if (RgnUpdate)
751                {
752                   RgnType = IntGdiCombineRgn(RgnUpdate, RgnUpdate, Rgn, RGN_OR);
753                   REGION_UnlockRgn(RgnUpdate);
754                   if (RgnType == NULLREGION)
755                   {
756                      IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
757                      GreDeleteObject(Wnd->hrgnUpdate);
758                      Wnd->hrgnUpdate = NULL;
759                   }
760                }
761             }
762          }
763 
764          Flags |= RDW_ERASE|RDW_FRAME; // For children.
765 
766       }
767 
768       if (!HadPaintMessage && IntIsWindowDirty(Wnd))
769       {
770          MsqIncPaintCountQueue(Wnd->head.pti);
771       }
772 
773    }    // The following flags are used to validate the window.
774    else if (Flags & (RDW_VALIDATE|RDW_NOINTERNALPAINT|RDW_NOERASE|RDW_NOFRAME))
775    {
776       if (Wnd->state & WNDS_UPDATEDIRTY && !(Flags & RDW_NOUPDATEDIRTY))
777          return;
778 
779       if (Flags & RDW_NOINTERNALPAINT)
780       {
781          Wnd->state &= ~WNDS_INTERNALPAINT;
782       }
783 
784       if (Flags & RDW_VALIDATE)
785       {
786          if (Flags & RDW_NOFRAME)
787             Wnd->state &= ~WNDS_SENDNCPAINT;
788 
789          if (Flags & RDW_NOERASE)
790             Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
791 
792          if (Wnd->hrgnUpdate > HRGN_WINDOW && RgnType != NULLREGION && Rgn > PRGN_WINDOW)
793          {
794              PREGION RgnUpdate = REGION_LockRgn(Wnd->hrgnUpdate);
795 
796              if (RgnUpdate)
797              {
798                  RgnType = IntGdiCombineRgn(RgnUpdate, RgnUpdate, Rgn, RGN_DIFF);
799                  REGION_UnlockRgn(RgnUpdate);
800 
801                  if (RgnType == NULLREGION)
802                  {
803                      IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
804                      GreDeleteObject(Wnd->hrgnUpdate);
805                      Wnd->hrgnUpdate = NULL;
806                  }
807              }
808          }
809          // If update is null, do not erase.
810          if (Wnd->hrgnUpdate == NULL)
811          {
812             Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
813          }
814       }
815 
816       if (HadPaintMessage && !IntIsWindowDirty(Wnd))
817       {
818          MsqDecPaintCountQueue(Wnd->head.pti);
819       }
820    }
821 
822    /*
823     * Process children if needed
824     */
825 
826    if (!(Flags & RDW_NOCHILDREN) &&
827        !(Wnd->style & WS_MINIMIZE) &&
828          ((Flags & RDW_ALLCHILDREN) || !(Wnd->style & WS_CLIPCHILDREN)))
829    {
830       PWND Child;
831 
832       for (Child = Wnd->spwndChild; Child; Child = Child->spwndNext)
833       {
834          if (Child->style & WS_VISIBLE)
835          {
836             /*
837              * Recursive call to update children hrgnUpdate
838              */
839             PREGION RgnTemp = IntSysCreateRectpRgn(0, 0, 0, 0);
840             if (RgnTemp)
841             {
842                 if (Rgn > PRGN_WINDOW) IntGdiCombineRgn(RgnTemp, Rgn, 0, RGN_COPY);
843                 IntInvalidateWindows(Child, ((Rgn > PRGN_WINDOW)?RgnTemp:Rgn), Flags);
844                 REGION_Delete(RgnTemp);
845             }
846          }
847       }
848    }
849    TRACE("IntInvalidateWindows exit\n");
850 }
851 
852 /*
853  * IntIsWindowDrawable
854  *
855  * Remarks
856  *    Window is drawable when it is visible and all parents are not
857  *    minimized.
858  */
859 
860 BOOL FASTCALL
861 IntIsWindowDrawable(PWND Wnd)
862 {
863    PWND WndObject;
864 
865    for (WndObject = Wnd; WndObject != NULL; WndObject = WndObject->spwndParent)
866    {
867       if ( WndObject->state2 & WNDS2_INDESTROY ||
868            WndObject->state & WNDS_DESTROYED ||
869            !WndObject ||
870            !(WndObject->style & WS_VISIBLE) ||
871             ((WndObject->style & WS_MINIMIZE) && (WndObject != Wnd)))
872       {
873          return FALSE;
874       }
875    }
876 
877    return TRUE;
878 }
879 
880 /*
881  * IntRedrawWindow
882  *
883  * Internal version of NtUserRedrawWindow that takes WND as
884  * first parameter.
885  */
886 
887 BOOL FASTCALL
888 co_UserRedrawWindow(
889    PWND Window,
890    const RECTL* UpdateRect,
891    PREGION UpdateRgn,
892    ULONG Flags)
893 {
894    PREGION TmpRgn = NULL;
895    TRACE("co_UserRedrawWindow start Rgn %p\n",UpdateRgn);
896 
897    /*
898     * Step 1.
899     * Validation of passed parameters.
900     */
901 
902    if (!IntIsWindowDrawable(Window))
903    {
904       return TRUE; // Just do nothing!!!
905    }
906 
907    if (Window == NULL)
908    {
909       Window = UserGetDesktopWindow();
910    }
911 
912    /*
913     * Step 2.
914     * Transform the parameters UpdateRgn and UpdateRect into
915     * a region hRgn specified in screen coordinates.
916     */
917 
918    if (Flags & (RDW_INVALIDATE | RDW_VALIDATE)) // Both are OKAY!
919    {
920       /* We can't hold lock on GDI objects while doing roundtrips to user mode,
921        * so use a copy instead */
922       if (UpdateRgn)
923       {
924           TmpRgn = IntSysCreateRectpRgn(0, 0, 0, 0);
925 
926           if (UpdateRgn > PRGN_WINDOW)
927           {
928              IntGdiCombineRgn(TmpRgn, UpdateRgn, NULL, RGN_COPY);
929           }
930 
931           if (Window != UserGetDesktopWindow())
932           {
933              REGION_bOffsetRgn(TmpRgn, Window->rcClient.left, Window->rcClient.top);
934           }
935       }
936       else
937       {
938          if (UpdateRect != NULL)
939          {
940             if (Window == UserGetDesktopWindow())
941             {
942                TmpRgn = IntSysCreateRectpRgnIndirect(UpdateRect);
943             }
944             else
945             {
946                TmpRgn = IntSysCreateRectpRgn(Window->rcClient.left + UpdateRect->left,
947                                              Window->rcClient.top  + UpdateRect->top,
948                                              Window->rcClient.left + UpdateRect->right,
949                                              Window->rcClient.top  + UpdateRect->bottom);
950             }
951          }
952          else
953          {
954             if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
955                 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
956             {
957                if (!RECTL_bIsEmptyRect(&Window->rcWindow))
958                    TmpRgn = IntSysCreateRectpRgnIndirect(&Window->rcWindow);
959             }
960             else
961             {
962                if (!RECTL_bIsEmptyRect(&Window->rcClient))
963                    TmpRgn = IntSysCreateRectpRgnIndirect(&Window->rcClient);
964             }
965          }
966       }
967    }
968 
969    /* Fixes test RDW_INTERNALPAINT behavior */
970    if (TmpRgn == NULL)
971    {
972       TmpRgn = PRGN_WINDOW; // Need a region so the bits can be set!!!
973    }
974 
975    /*
976     * Step 3.
977     * Adjust the window update region depending on hRgn and flags.
978     */
979 
980    if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT) &&
981        TmpRgn != NULL)
982    {
983       IntInvalidateWindows(Window, TmpRgn, Flags);
984    }
985 
986    /*
987     * Step 4.
988     * Repaint and erase windows if needed.
989     */
990 
991    if (Flags & RDW_UPDATENOW)
992    {
993       UserUpdateWindows(Window, Flags);
994    }
995    else if (Flags & RDW_ERASENOW)
996    {
997       if ((Flags & (RDW_NOCHILDREN|RDW_ALLCHILDREN)) == 0)
998          Flags |= RDW_CLIPCHILDREN;
999 
1000       UserSyncAndPaintWindows(Window, Flags);
1001    }
1002 
1003    /*
1004     * Step 5.
1005     * Cleanup ;-)
1006     */
1007 
1008    if (TmpRgn > PRGN_WINDOW)
1009    {
1010       REGION_Delete(TmpRgn);
1011    }
1012    TRACE("co_UserRedrawWindow exit\n");
1013 
1014    return TRUE;
1015 }
1016 
1017 VOID FASTCALL
1018 PaintSuspendedWindow(PWND pwnd, HRGN hrgnOrig)
1019 {
1020    if (pwnd->hrgnUpdate)
1021    {
1022       HDC hDC;
1023       INT Flags = DC_NC|DC_NOSENDMSG;
1024       HRGN hrgnTemp;
1025       RECT Rect;
1026       INT type;
1027       PREGION prgn;
1028 
1029       if (pwnd->hrgnUpdate > HRGN_WINDOW)
1030       {
1031          hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
1032          type = NtGdiCombineRgn( hrgnTemp, pwnd->hrgnUpdate, 0, RGN_COPY);
1033          if (type == ERROR)
1034          {
1035             GreDeleteObject(hrgnTemp);
1036             hrgnTemp = HRGN_WINDOW;
1037          }
1038       }
1039       else
1040       {
1041          hrgnTemp = GreCreateRectRgnIndirect(&pwnd->rcWindow);
1042       }
1043 
1044       if ( hrgnOrig &&
1045            hrgnTemp > HRGN_WINDOW &&
1046            NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnOrig, RGN_AND) == NULLREGION)
1047       {
1048          GreDeleteObject(hrgnTemp);
1049          return;
1050       }
1051 
1052       hDC = UserGetDCEx(pwnd, hrgnTemp, DCX_WINDOW|DCX_INTERSECTRGN|DCX_USESTYLE|DCX_KEEPCLIPRGN);
1053 
1054       Rect = pwnd->rcWindow;
1055       RECTL_vOffsetRect(&Rect, -pwnd->rcWindow.left, -pwnd->rcWindow.top);
1056 
1057       // Clear out client area!
1058       FillRect(hDC, &Rect, IntGetSysColorBrush(COLOR_WINDOW));
1059 
1060       NC_DoNCPaint(pwnd, hDC, Flags); // Redraw without MENUs.
1061 
1062       UserReleaseDC(pwnd, hDC, FALSE);
1063 
1064       prgn = REGION_LockRgn(hrgnTemp);
1065       IntInvalidateWindows(pwnd, prgn, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN);
1066       REGION_UnlockRgn(prgn);
1067 
1068       // Set updates for this window.
1069       pwnd->state |= WNDS_SENDNCPAINT|WNDS_SENDERASEBACKGROUND|WNDS_UPDATEDIRTY;
1070 
1071       // DCX_KEEPCLIPRGN is set. Check it anyway.
1072       if (hrgnTemp > HRGN_WINDOW && GreIsHandleValid(hrgnTemp)) GreDeleteObject(hrgnTemp);
1073    }
1074 }
1075 
1076 VOID FASTCALL
1077 UpdateTheadChildren(PWND pWnd, HRGN hRgn)
1078 {
1079    PaintSuspendedWindow( pWnd, hRgn );
1080 
1081    if (!(pWnd->style & WS_CLIPCHILDREN))
1082       return;
1083 
1084    pWnd = pWnd->spwndChild; // invalidate children if any.
1085    while (pWnd)
1086    {
1087       UpdateTheadChildren( pWnd, hRgn );
1088       pWnd = pWnd->spwndNext;
1089    }
1090 }
1091 
1092 VOID FASTCALL
1093 UpdateThreadWindows(PWND pWnd, PTHREADINFO pti, HRGN hRgn)
1094 {
1095    PWND pwndTemp;
1096 
1097    for ( pwndTemp = pWnd;
1098          pwndTemp;
1099          pwndTemp = pwndTemp->spwndNext )
1100    {
1101       if (pwndTemp->head.pti == pti)
1102       {
1103           UserUpdateWindows(pwndTemp, RDW_ALLCHILDREN);
1104       }
1105       else
1106       {
1107           if (IsThreadSuspended(pwndTemp->head.pti) || MsqIsHung(pwndTemp->head.pti, MSQ_HUNG))
1108           {
1109              UpdateTheadChildren(pwndTemp, hRgn);
1110           }
1111           else
1112              UserUpdateWindows(pwndTemp, RDW_ALLCHILDREN);
1113       }
1114    }
1115 }
1116 
1117 BOOL FASTCALL
1118 IntIsWindowDirty(PWND Wnd)
1119 {
1120    return ( Wnd->style & WS_VISIBLE &&
1121            ( Wnd->hrgnUpdate != NULL ||
1122              Wnd->state & WNDS_INTERNALPAINT ) );
1123 }
1124 
1125 /*
1126    Conditions to paint any window:
1127 
1128    1. Update region is not null.
1129    2. Internal paint flag is set.
1130    3. Paint count is not zero.
1131 
1132  */
1133 PWND FASTCALL
1134 IntFindWindowToRepaint(PWND Window, PTHREADINFO Thread)
1135 {
1136    PWND hChild;
1137    PWND TempWindow;
1138 
1139    for (; Window != NULL; Window = Window->spwndNext)
1140    {
1141       if (IntWndBelongsToThread(Window, Thread))
1142       {
1143          if (IntIsWindowDirty(Window))
1144          {
1145             /* Make sure all non-transparent siblings are already drawn. */
1146             if (Window->ExStyle & WS_EX_TRANSPARENT)
1147             {
1148                for (TempWindow = Window->spwndNext; TempWindow != NULL;
1149                     TempWindow = TempWindow->spwndNext)
1150                {
1151                   if (!(TempWindow->ExStyle & WS_EX_TRANSPARENT) &&
1152                        IntWndBelongsToThread(TempWindow, Thread) &&
1153                        IntIsWindowDirty(TempWindow))
1154                   {
1155                      return TempWindow;
1156                   }
1157                }
1158             }
1159             return Window;
1160          }
1161       }
1162       /* find a child of the specified window that needs repainting */
1163       if (Window->spwndChild)
1164       {
1165          hChild = IntFindWindowToRepaint(Window->spwndChild, Thread);
1166          if (hChild != NULL)
1167             return hChild;
1168       }
1169    }
1170    return Window;
1171 }
1172 
1173 //
1174 // Internal painting of windows.
1175 //
1176 VOID FASTCALL
1177 IntPaintWindow( PWND Window )
1178 {
1179    // Handle normal painting.
1180    co_IntPaintWindows( Window, RDW_NOCHILDREN, FALSE );
1181 }
1182 
1183 BOOL FASTCALL
1184 IntGetPaintMessage(
1185    PWND Window,
1186    UINT MsgFilterMin,
1187    UINT MsgFilterMax,
1188    PTHREADINFO Thread,
1189    MSG *Message,
1190    BOOL Remove)
1191 {
1192    PWND PaintWnd, StartWnd;
1193 
1194    if ((MsgFilterMin != 0 || MsgFilterMax != 0) &&
1195          (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT))
1196       return FALSE;
1197 
1198    if (Thread->TIF_flags & TIF_SYSTEMTHREAD )
1199    {
1200       ERR("WM_PAINT is in a System Thread!\n");
1201    }
1202 
1203    StartWnd = UserGetDesktopWindow();
1204    PaintWnd = IntFindWindowToRepaint(StartWnd, Thread);
1205 
1206    Message->hwnd = PaintWnd ? UserHMGetHandle(PaintWnd) : NULL;
1207 
1208    if (Message->hwnd == NULL && Thread->cPaintsReady)
1209    {
1210       // Find note in window.c:"PAINTING BUG".
1211       ERR("WARNING SOMETHING HAS GONE WRONG: Thread marked as containing dirty windows, but no dirty windows found! Counts %u\n",Thread->cPaintsReady);
1212       /* Hack to stop spamming the debug log ! */
1213       Thread->cPaintsReady = 0;
1214       return FALSE;
1215    }
1216 
1217    if (Message->hwnd == NULL)
1218       return FALSE;
1219 
1220    if (!(Window == NULL ||
1221          PaintWnd == Window ||
1222          IntIsChildWindow(Window, PaintWnd))) /* check that it is a child of the specified parent */
1223       return FALSE;
1224 
1225    if (PaintWnd->state & WNDS_INTERNALPAINT)
1226    {
1227       PaintWnd->state &= ~WNDS_INTERNALPAINT;
1228       if (!PaintWnd->hrgnUpdate)
1229          MsqDecPaintCountQueue(Thread);
1230    }
1231    PaintWnd->state2 &= ~WNDS2_STARTPAINT;
1232    PaintWnd->state &= ~WNDS_UPDATEDIRTY;
1233 
1234    Window = PaintWnd;
1235    while (Window && !UserIsDesktopWindow(Window))
1236    {
1237       // Role back and check for clip children, do not set if any.
1238       if (Window->spwndParent && !(Window->spwndParent->style & WS_CLIPCHILDREN))
1239       {
1240          PaintWnd->state2 |= WNDS2_WMPAINTSENT;
1241       }
1242       Window = Window->spwndParent;
1243    }
1244 
1245    Message->wParam = Message->lParam = 0;
1246    Message->message = WM_PAINT;
1247    return TRUE;
1248 }
1249 
1250 BOOL
1251 FASTCALL
1252 IntPrintWindow(
1253     PWND pwnd,
1254     HDC hdcBlt,
1255     UINT nFlags)
1256 {
1257     HDC hdcSrc;
1258     INT cx, cy, xSrc, ySrc;
1259 
1260     if ( nFlags & PW_CLIENTONLY)
1261     {
1262        cx = pwnd->rcClient.right - pwnd->rcClient.left;
1263        cy = pwnd->rcClient.bottom - pwnd->rcClient.top;
1264        xSrc = pwnd->rcClient.left - pwnd->rcWindow.left;
1265        ySrc = pwnd->rcClient.top - pwnd->rcWindow.top;
1266     }
1267     else
1268     {
1269        cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
1270        cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
1271        xSrc = 0;
1272        ySrc = 0;
1273     }
1274 
1275     // TODO: Setup Redirection for Print.
1276     return FALSE;
1277 
1278     /* Update the window just incase. */
1279     co_IntUpdateWindows( pwnd, RDW_ALLCHILDREN, FALSE);
1280 
1281     hdcSrc = UserGetDCEx( pwnd, NULL, DCX_CACHE|DCX_WINDOW);
1282     /* Print window to printer context. */
1283     NtGdiBitBlt( hdcBlt,
1284                  0,
1285                  0,
1286                  cx,
1287                  cy,
1288                  hdcSrc,
1289                  xSrc,
1290                  ySrc,
1291                  SRCCOPY,
1292                  0,
1293                  0);
1294 
1295     UserReleaseDC( pwnd, hdcSrc, FALSE);
1296 
1297     // TODO: Release Redirection from Print.
1298 
1299     return TRUE;
1300 }
1301 
1302 BOOL
1303 FASTCALL
1304 IntFlashWindowEx(PWND pWnd, PFLASHWINFO pfwi)
1305 {
1306    DWORD_PTR FlashState;
1307    UINT uCount = pfwi->uCount;
1308    BOOL Activate = FALSE, Ret = FALSE;
1309 
1310    ASSERT(pfwi);
1311 
1312    FlashState = (DWORD_PTR)UserGetProp(pWnd, AtomFlashWndState, TRUE);
1313 
1314    if (FlashState == FLASHW_FINISHED)
1315    {
1316       // Cycle has finished, kill timer and set this to Stop.
1317       FlashState |= FLASHW_KILLSYSTIMER;
1318       pfwi->dwFlags = FLASHW_STOP;
1319    }
1320    else
1321    {
1322       if (FlashState)
1323       {
1324          if (pfwi->dwFlags == FLASHW_SYSTIMER)
1325          {
1326              // Called from system timer, restore flags, counts and state.
1327              pfwi->dwFlags = LOWORD(FlashState);
1328              uCount = HIWORD(FlashState);
1329              FlashState = MAKELONG(LOWORD(FlashState),0);
1330          }
1331          else
1332          {
1333              // Clean out the trash! Fix SeaMonkey crash after restart.
1334              FlashState = 0;
1335          }
1336       }
1337 
1338       if (FlashState == 0)
1339       {  // First time in cycle, setup flash state.
1340          if ( pWnd->state & WNDS_ACTIVEFRAME ||
1341              (pfwi->dwFlags & FLASHW_CAPTION && pWnd->style & (WS_BORDER|WS_DLGFRAME)))
1342          {
1343              FlashState = FLASHW_STARTED|FLASHW_ACTIVE;
1344          }
1345       }
1346 
1347       // Set previous window state.
1348       Ret = !!(FlashState & FLASHW_ACTIVE);
1349 
1350       if ( (pfwi->dwFlags & FLASHW_TIMERNOFG) == FLASHW_TIMERNOFG &&
1351            gpqForeground == pWnd->head.pti->MessageQueue )
1352       {
1353           // Flashing until foreground, set this to Stop.
1354           pfwi->dwFlags = FLASHW_STOP;
1355       }
1356    }
1357 
1358    // Toggle activate flag.
1359    if ( pfwi->dwFlags == FLASHW_STOP )
1360    {
1361       if (gpqForeground && gpqForeground->spwndActive == pWnd)
1362          Activate = TRUE;
1363       else
1364          Activate = FALSE;
1365    }
1366    else
1367    {
1368       Activate = (FlashState & FLASHW_ACTIVE) == 0;
1369    }
1370 
1371    if ( pfwi->dwFlags == FLASHW_STOP || pfwi->dwFlags & FLASHW_CAPTION )
1372    {
1373       co_IntSendMessage(UserHMGetHandle(pWnd), WM_NCACTIVATE, Activate, 0);
1374    }
1375 
1376    // FIXME: Check for a Stop Sign here.
1377    if ( pfwi->dwFlags & FLASHW_TRAY )
1378    {
1379       // Need some shell work here too.
1380       TRACE("FIXME: Flash window no Tray support!\n");
1381    }
1382 
1383    if ( pfwi->dwFlags == FLASHW_STOP )
1384    {
1385       if (FlashState & FLASHW_KILLSYSTIMER)
1386       {
1387          IntKillTimer(pWnd, ID_EVENT_SYSTIMER_FLASHWIN, TRUE);
1388       }
1389 
1390       UserRemoveProp(pWnd, AtomFlashWndState, TRUE);
1391    }
1392    else
1393    {  // Have a count and started, set timer.
1394       if ( uCount )
1395       {
1396          FlashState |= FLASHW_COUNT;
1397 
1398          if (!(Activate ^ !!(FlashState & FLASHW_STARTED)))
1399              uCount--;
1400 
1401          if (!(FlashState & FLASHW_KILLSYSTIMER))
1402              pfwi->dwFlags |= FLASHW_TIMER;
1403       }
1404 
1405       if (pfwi->dwFlags & FLASHW_TIMER)
1406       {
1407          FlashState |= FLASHW_KILLSYSTIMER;
1408 
1409          IntSetTimer( pWnd,
1410                       ID_EVENT_SYSTIMER_FLASHWIN,
1411                       pfwi->dwTimeout ? pfwi->dwTimeout : gpsi->dtCaretBlink,
1412                       SystemTimerProc,
1413                       TMRF_SYSTEM );
1414       }
1415 
1416       if (FlashState & FLASHW_COUNT && uCount == 0)
1417       {
1418          // Keep spinning? Nothing else to do.
1419          FlashState = FLASHW_FINISHED;
1420       }
1421       else
1422       {
1423          // Save state and flags so this can be restored next time through.
1424          FlashState ^= (FlashState ^ -!!(Activate)) & FLASHW_ACTIVE;
1425          FlashState ^= (FlashState ^ pfwi->dwFlags) & (FLASHW_MASK & ~FLASHW_TIMER);
1426       }
1427       FlashState = MAKELONG(LOWORD(FlashState),uCount);
1428       UserSetProp(pWnd, AtomFlashWndState, (HANDLE)FlashState, TRUE);
1429    }
1430    return Ret;
1431 }
1432 
1433 // Win: xxxBeginPaint
1434 HDC FASTCALL
1435 IntBeginPaint(PWND Window, PPAINTSTRUCT Ps)
1436 {
1437    RECT Rect;
1438    INT type;
1439    BOOL Erase = FALSE;
1440 
1441    co_UserHideCaret(Window);
1442 
1443    Window->state2 |= WNDS2_STARTPAINT;
1444    Window->state &= ~WNDS_PAINTNOTPROCESSED;
1445 
1446    if (Window->state & WNDS_SENDNCPAINT)
1447    {
1448       HRGN hRgn;
1449       // Application can keep update dirty.
1450       do
1451       {
1452          Window->state &= ~WNDS_UPDATEDIRTY;
1453          hRgn = IntGetNCUpdateRgn(Window, FALSE);
1454          IntSendNCPaint(Window, hRgn);
1455          if (hRgn > HRGN_WINDOW && GreIsHandleValid(hRgn))
1456          {
1457             /* NOTE: The region can already be deleted! */
1458             GreDeleteObject(hRgn);
1459          }
1460       }
1461       while(Window->state & WNDS_UPDATEDIRTY);
1462    }
1463    else
1464    {
1465       Window->state &= ~WNDS_UPDATEDIRTY;
1466    }
1467 
1468    RtlZeroMemory(Ps, sizeof(PAINTSTRUCT));
1469 
1470    if (Window->state2 & WNDS2_ENDPAINTINVALIDATE)
1471    {
1472       ERR("BP: Another thread invalidated this window\n");
1473    }
1474 
1475    Ps->hdc = UserGetDCEx( Window,
1476                           Window->hrgnUpdate,
1477                           DCX_INTERSECTRGN | DCX_USESTYLE);
1478    if (!Ps->hdc)
1479    {
1480       return NULL;
1481    }
1482 
1483    // If set, always clear flags out due to the conditions later on for sending the message.
1484    if (Window->state & WNDS_SENDERASEBACKGROUND)
1485    {
1486       Window->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
1487       Erase = TRUE;
1488    }
1489 
1490    if (Window->hrgnUpdate != NULL)
1491    {
1492       MsqDecPaintCountQueue(Window->head.pti);
1493       IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
1494       /* The region is part of the dc now and belongs to the process! */
1495       Window->hrgnUpdate = NULL;
1496    }
1497    else
1498    {
1499       if (Window->state & WNDS_INTERNALPAINT)
1500          MsqDecPaintCountQueue(Window->head.pti);
1501    }
1502 
1503    type = GdiGetClipBox(Ps->hdc, &Ps->rcPaint);
1504 
1505    IntGetClientRect(Window, &Rect);
1506 
1507    Window->state &= ~WNDS_INTERNALPAINT;
1508 
1509    if ( Erase &&               // Set to erase,
1510         type != NULLREGION &&  // don't erase if the clip box is empty,
1511         (!(Window->pcls->style & CS_PARENTDC) || // not parent dc or
1512          RECTL_bIntersectRect( &Rect, &Rect, &Ps->rcPaint) ) ) // intersecting.
1513    {
1514       Ps->fErase = !co_IntSendMessage(UserHMGetHandle(Window), WM_ERASEBKGND, (WPARAM)Ps->hdc, 0);
1515       if ( Ps->fErase )
1516       {
1517          Window->state |= (WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
1518       }
1519    }
1520    else
1521    {
1522       Ps->fErase = FALSE;
1523    }
1524 
1525    IntSendChildNCPaint(Window);
1526 
1527    return Ps->hdc;
1528 }
1529 
1530 // Win: xxxEndPaint
1531 BOOL FASTCALL
1532 IntEndPaint(PWND Wnd, PPAINTSTRUCT Ps)
1533 {
1534    HDC hdc = NULL;
1535 
1536    hdc = Ps->hdc;
1537 
1538    UserReleaseDC(Wnd, hdc, TRUE);
1539 
1540    if (Wnd->state2 & WNDS2_ENDPAINTINVALIDATE)
1541    {
1542       ERR("EP: Another thread invalidated this window\n");
1543       Wnd->state2 &= ~WNDS2_ENDPAINTINVALIDATE;
1544    }
1545 
1546    Wnd->state2 &= ~(WNDS2_WMPAINTSENT|WNDS2_STARTPAINT);
1547 
1548    co_UserShowCaret(Wnd);
1549 
1550    return TRUE;
1551 }
1552 
1553 // Win: xxxFillWindow
1554 BOOL FASTCALL
1555 IntFillWindow(PWND pWndParent,
1556               PWND pWnd,
1557               HDC  hDC,
1558               HBRUSH hBrush)
1559 {
1560    RECT Rect, Rect1;
1561    INT type;
1562 
1563    if (!pWndParent)
1564       pWndParent = pWnd;
1565 
1566    type = GdiGetClipBox(hDC, &Rect);
1567 
1568    IntGetClientRect(pWnd, &Rect1);
1569 
1570    if ( type != NULLREGION && // Clip box is not empty,
1571        (!(pWnd->pcls->style & CS_PARENTDC) || // not parent dc or
1572          RECTL_bIntersectRect( &Rect, &Rect, &Rect1) ) ) // intersecting.
1573    {
1574       POINT ppt;
1575       INT x = 0, y = 0;
1576 
1577       if (!UserIsDesktopWindow(pWndParent))
1578       {
1579           x = pWndParent->rcClient.left - pWnd->rcClient.left;
1580           y = pWndParent->rcClient.top  - pWnd->rcClient.top;
1581       }
1582 
1583       GreSetBrushOrg(hDC, x, y, &ppt);
1584 
1585       if ( hBrush < (HBRUSH)CTLCOLOR_MAX )
1586           hBrush = GetControlColor( pWndParent, pWnd, hDC, HandleToUlong(hBrush) + WM_CTLCOLORMSGBOX);
1587 
1588       FillRect(hDC, &Rect, hBrush);
1589 
1590       GreSetBrushOrg(hDC, ppt.x, ppt.y, NULL);
1591 
1592       return TRUE;
1593    }
1594    else
1595       return FALSE;
1596 }
1597 
1598 /* PUBLIC FUNCTIONS ***********************************************************/
1599 
1600 /*
1601  * NtUserBeginPaint
1602  *
1603  * Status
1604  *    @implemented
1605  */
1606 
1607 HDC APIENTRY
1608 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
1609 {
1610    PWND Window = NULL;
1611    PAINTSTRUCT Ps;
1612    NTSTATUS Status;
1613    HDC hDC;
1614    USER_REFERENCE_ENTRY Ref;
1615    DECLARE_RETURN(HDC);
1616 
1617    TRACE("Enter NtUserBeginPaint\n");
1618    UserEnterExclusive();
1619 
1620    if (!(Window = UserGetWindowObject(hWnd)))
1621    {
1622       RETURN( NULL);
1623    }
1624 
1625    UserRefObjectCo(Window, &Ref);
1626 
1627    hDC = IntBeginPaint(Window, &Ps);
1628 
1629    Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
1630    if (! NT_SUCCESS(Status))
1631    {
1632       SetLastNtError(Status);
1633       RETURN(NULL);
1634    }
1635 
1636    RETURN(hDC);
1637 
1638 CLEANUP:
1639    if (Window) UserDerefObjectCo(Window);
1640 
1641    TRACE("Leave NtUserBeginPaint, ret=%p\n",_ret_);
1642    UserLeave();
1643    END_CLEANUP;
1644 
1645 }
1646 
1647 /*
1648  * NtUserEndPaint
1649  *
1650  * Status
1651  *    @implemented
1652  */
1653 
1654 BOOL APIENTRY
1655 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* pUnsafePs)
1656 {
1657    NTSTATUS Status = STATUS_SUCCESS;
1658    PWND Window = NULL;
1659    PAINTSTRUCT Ps;
1660    USER_REFERENCE_ENTRY Ref;
1661    DECLARE_RETURN(BOOL);
1662 
1663    TRACE("Enter NtUserEndPaint\n");
1664    UserEnterExclusive();
1665 
1666    if (!(Window = UserGetWindowObject(hWnd)))
1667    {
1668       RETURN(FALSE);
1669    }
1670 
1671    UserRefObjectCo(Window, &Ref); // Here for the exception.
1672 
1673    _SEH2_TRY
1674    {
1675       ProbeForRead(pUnsafePs, sizeof(*pUnsafePs), 1);
1676       RtlCopyMemory(&Ps, pUnsafePs, sizeof(PAINTSTRUCT));
1677    }
1678    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1679    {
1680       Status = _SEH2_GetExceptionCode();
1681    }
1682    _SEH2_END
1683    if (!NT_SUCCESS(Status))
1684    {
1685       RETURN(FALSE);
1686    }
1687 
1688    RETURN(IntEndPaint(Window, &Ps));
1689 
1690 CLEANUP:
1691    if (Window) UserDerefObjectCo(Window);
1692 
1693    TRACE("Leave NtUserEndPaint, ret=%i\n",_ret_);
1694    UserLeave();
1695    END_CLEANUP;
1696 }
1697 
1698 /*
1699  * FillWindow: Called from User; Dialog, Edit and ListBox procs during a WM_ERASEBKGND.
1700  */
1701 /*
1702  * @implemented
1703  */
1704 BOOL APIENTRY
1705 NtUserFillWindow(HWND hWndParent,
1706                  HWND hWnd,
1707                  HDC  hDC,
1708                  HBRUSH hBrush)
1709 {
1710    BOOL ret = FALSE;
1711    PWND pWnd, pWndParent = NULL;
1712    USER_REFERENCE_ENTRY Ref;
1713 
1714    TRACE("Enter NtUserFillWindow\n");
1715    UserEnterExclusive();
1716 
1717    if (!hDC)
1718    {
1719       goto Exit;
1720    }
1721 
1722    if (!(pWnd = UserGetWindowObject(hWnd)))
1723    {
1724       goto Exit;
1725    }
1726 
1727    if (hWndParent && !(pWndParent = UserGetWindowObject(hWndParent)))
1728    {
1729       goto Exit;
1730    }
1731 
1732    UserRefObjectCo(pWnd, &Ref);
1733    ret = IntFillWindow( pWndParent, pWnd, hDC, hBrush );
1734    UserDerefObjectCo(pWnd);
1735 
1736 Exit:
1737    TRACE("Leave NtUserFillWindow, ret=%i\n",ret);
1738    UserLeave();
1739    return ret;
1740 }
1741 
1742 /*
1743  * @implemented
1744  */
1745 BOOL APIENTRY
1746 NtUserFlashWindowEx(IN PFLASHWINFO pfwi)
1747 {
1748    PWND pWnd;
1749    FLASHWINFO finfo = {0};
1750    BOOL Ret = FALSE;
1751 
1752    UserEnterExclusive();
1753 
1754    _SEH2_TRY
1755    {
1756       ProbeForRead(pfwi, sizeof(FLASHWINFO), 1);
1757       RtlCopyMemory(&finfo, pfwi, sizeof(FLASHWINFO));
1758    }
1759    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1760    {
1761       SetLastNtError(_SEH2_GetExceptionCode());
1762       _SEH2_YIELD(goto Exit);
1763    }
1764    _SEH2_END
1765 
1766    if (!( pWnd = ValidateHwndNoErr(finfo.hwnd)) ||
1767         finfo.cbSize != sizeof(FLASHWINFO) ||
1768         finfo.dwFlags & ~(FLASHW_ALL|FLASHW_TIMER|FLASHW_TIMERNOFG) )
1769    {
1770       EngSetLastError(ERROR_INVALID_PARAMETER);
1771       goto Exit;
1772    }
1773 
1774    Ret = IntFlashWindowEx(pWnd, &finfo);
1775 
1776 Exit:
1777    UserLeave();
1778    return Ret;
1779 }
1780 
1781 /*
1782     GetUpdateRgn, this fails the same as the old one.
1783  */
1784 INT FASTCALL
1785 co_UserGetUpdateRgn(PWND Window, HRGN hRgn, BOOL bErase)
1786 {
1787    int RegionType;
1788    BOOL Type;
1789    RECTL Rect;
1790 
1791    ASSERT_REFS_CO(Window);
1792 
1793    if (bErase)
1794    {
1795       USER_REFERENCE_ENTRY Ref;
1796       UserRefObjectCo(Window, &Ref);
1797       co_IntPaintWindows(Window, RDW_NOCHILDREN, FALSE);
1798       UserDerefObjectCo(Window);
1799    }
1800 
1801    Window->state &= ~WNDS_UPDATEDIRTY;
1802 
1803    if (Window->hrgnUpdate == NULL)
1804    {
1805        NtGdiSetRectRgn(hRgn, 0, 0, 0, 0);
1806        return NULLREGION;
1807    }
1808 
1809    Rect = Window->rcClient;
1810    Type = IntIntersectWithParents(Window, &Rect);
1811 
1812    if (Window->hrgnUpdate == HRGN_WINDOW)
1813    {
1814       // Trap it out.
1815       ERR("GURn: Caller is passing Window Region 1\n");
1816       if (!Type)
1817       {
1818          NtGdiSetRectRgn(hRgn, 0, 0, 0, 0);
1819          return NULLREGION;
1820       }
1821 
1822       RegionType = SIMPLEREGION;
1823 
1824       if (!UserIsDesktopWindow(Window))
1825       {
1826          RECTL_vOffsetRect(&Rect,
1827                           -Window->rcClient.left,
1828                           -Window->rcClient.top);
1829       }
1830       GreSetRectRgnIndirect(hRgn, &Rect);
1831    }
1832    else
1833    {
1834       HRGN hrgnTemp = GreCreateRectRgnIndirect(&Rect);
1835 
1836       RegionType = NtGdiCombineRgn(hRgn, hrgnTemp, Window->hrgnUpdate, RGN_AND);
1837 
1838       if (RegionType == ERROR || RegionType == NULLREGION)
1839       {
1840          if (hrgnTemp) GreDeleteObject(hrgnTemp);
1841          NtGdiSetRectRgn(hRgn, 0, 0, 0, 0);
1842          return RegionType;
1843       }
1844 
1845       if (!UserIsDesktopWindow(Window))
1846       {
1847          NtGdiOffsetRgn(hRgn,
1848                        -Window->rcClient.left,
1849                        -Window->rcClient.top);
1850       }
1851       if (hrgnTemp) GreDeleteObject(hrgnTemp);
1852    }
1853    return RegionType;
1854 }
1855 
1856 BOOL FASTCALL
1857 co_UserGetUpdateRect(PWND Window, PRECT pRect, BOOL bErase)
1858 {
1859    INT RegionType;
1860    BOOL Ret = TRUE;
1861 
1862    if (bErase)
1863    {
1864       USER_REFERENCE_ENTRY Ref;
1865       UserRefObjectCo(Window, &Ref);
1866       co_IntPaintWindows(Window, RDW_NOCHILDREN, FALSE);
1867       UserDerefObjectCo(Window);
1868    }
1869 
1870    Window->state &= ~WNDS_UPDATEDIRTY;
1871 
1872    if (Window->hrgnUpdate == NULL)
1873    {
1874       pRect->left = pRect->top = pRect->right = pRect->bottom = 0;
1875       Ret = FALSE;
1876    }
1877    else
1878    {
1879       /* Get the update region bounding box. */
1880       if (Window->hrgnUpdate == HRGN_WINDOW)
1881       {
1882          *pRect = Window->rcClient;
1883          ERR("GURt: Caller is retrieving Window Region 1\n");
1884       }
1885       else
1886       {
1887          RegionType = IntGdiGetRgnBox(Window->hrgnUpdate, pRect);
1888 
1889          if (RegionType != ERROR && RegionType != NULLREGION)
1890             RECTL_bIntersectRect(pRect, pRect, &Window->rcClient);
1891       }
1892 
1893       if (IntIntersectWithParents(Window, pRect))
1894       {
1895          if (!UserIsDesktopWindow(Window))
1896          {
1897             RECTL_vOffsetRect(pRect,
1898                               -Window->rcClient.left,
1899                               -Window->rcClient.top);
1900          }
1901          if (Window->pcls->style & CS_OWNDC)
1902          {
1903             HDC hdc;
1904             //DWORD layout;
1905             hdc = UserGetDCEx(Window, NULL, DCX_USESTYLE);
1906             //layout = NtGdiSetLayout(hdc, -1, 0);
1907             //IntMapWindowPoints( 0, Window, (LPPOINT)pRect, 2 );
1908             GreDPtoLP( hdc, (LPPOINT)pRect, 2 );
1909             //NtGdiSetLayout(hdc, -1, layout);
1910             UserReleaseDC(Window, hdc, FALSE);
1911          }
1912       }
1913       else
1914       {
1915          pRect->left = pRect->top = pRect->right = pRect->bottom = 0;
1916       }
1917    }
1918    return Ret;
1919 }
1920 
1921 /*
1922  * NtUserGetUpdateRgn
1923  *
1924  * Status
1925  *    @implemented
1926  */
1927 
1928 INT APIENTRY
1929 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
1930 {
1931    DECLARE_RETURN(INT);
1932    PWND Window;
1933    INT ret;
1934 
1935    TRACE("Enter NtUserGetUpdateRgn\n");
1936    UserEnterExclusive();
1937 
1938    if (!(Window = UserGetWindowObject(hWnd)))
1939    {
1940       RETURN(ERROR);
1941    }
1942 
1943    ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
1944 
1945    RETURN(ret);
1946 
1947 CLEANUP:
1948    TRACE("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
1949    UserLeave();
1950    END_CLEANUP;
1951 }
1952 
1953 /*
1954  * NtUserGetUpdateRect
1955  *
1956  * Status
1957  *    @implemented
1958  */
1959 
1960 BOOL APIENTRY
1961 NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
1962 {
1963    PWND Window;
1964    RECTL Rect;
1965    NTSTATUS Status;
1966    BOOL Ret;
1967    DECLARE_RETURN(BOOL);
1968 
1969    TRACE("Enter NtUserGetUpdateRect\n");
1970    UserEnterExclusive();
1971 
1972    if (!(Window = UserGetWindowObject(hWnd)))
1973    {
1974       RETURN(FALSE);
1975    }
1976 
1977    Ret = co_UserGetUpdateRect(Window, &Rect, bErase);
1978 
1979    if (UnsafeRect != NULL)
1980    {
1981       Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECTL));
1982       if (!NT_SUCCESS(Status))
1983       {
1984          EngSetLastError(ERROR_INVALID_PARAMETER);
1985          RETURN(FALSE);
1986       }
1987    }
1988 
1989    RETURN(Ret);
1990 
1991 CLEANUP:
1992    TRACE("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
1993    UserLeave();
1994    END_CLEANUP;
1995 }
1996 
1997 /*
1998  * NtUserRedrawWindow
1999  *
2000  * Status
2001  *    @implemented
2002  */
2003 
2004 BOOL APIENTRY
2005 NtUserRedrawWindow(
2006    HWND hWnd,
2007    CONST RECT *lprcUpdate,
2008    HRGN hrgnUpdate,
2009    UINT flags)
2010 {
2011    RECTL SafeUpdateRect;
2012    PWND Wnd;
2013    BOOL Ret;
2014    USER_REFERENCE_ENTRY Ref;
2015    NTSTATUS Status = STATUS_SUCCESS;
2016    PREGION RgnUpdate = NULL;
2017    DECLARE_RETURN(BOOL);
2018 
2019    TRACE("Enter NtUserRedrawWindow\n");
2020    UserEnterExclusive();
2021 
2022    if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
2023    {
2024       RETURN( FALSE);
2025    }
2026 
2027    if (lprcUpdate)
2028    {
2029       _SEH2_TRY
2030       {
2031           ProbeForRead(lprcUpdate, sizeof(RECTL), 1);
2032           RtlCopyMemory(&SafeUpdateRect, lprcUpdate, sizeof(RECTL));
2033       }
2034       _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2035       {
2036          Status = _SEH2_GetExceptionCode();
2037       }
2038       _SEH2_END
2039       if (!NT_SUCCESS(Status))
2040       {
2041          EngSetLastError(RtlNtStatusToDosError(Status));
2042          RETURN( FALSE);
2043       }
2044    }
2045 
2046    if ( flags & ~(RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE|
2047                   RDW_NOERASE|RDW_NOFRAME|RDW_NOINTERNALPAINT|RDW_VALIDATE|
2048                   RDW_ERASENOW|RDW_UPDATENOW|RDW_ALLCHILDREN|RDW_NOCHILDREN) )
2049    {
2050       /* RedrawWindow fails only in case that flags are invalid */
2051       EngSetLastError(ERROR_INVALID_FLAGS);
2052       RETURN( FALSE);
2053    }
2054 
2055    /* We can't hold lock on GDI objects while doing roundtrips to user mode,
2056     * so it will be copied.
2057     */
2058    if (hrgnUpdate > HRGN_WINDOW)
2059    {
2060        RgnUpdate = REGION_LockRgn(hrgnUpdate);
2061        if (!RgnUpdate)
2062        {
2063            EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
2064            RETURN(FALSE);
2065        }
2066        REGION_UnlockRgn(RgnUpdate);
2067    }
2068    else if (hrgnUpdate == HRGN_WINDOW) // Trap it out.
2069    {
2070        ERR("NTRW: Caller is passing Window Region 1\n");
2071    }
2072 
2073    UserRefObjectCo(Wnd, &Ref);
2074 
2075    Ret = co_UserRedrawWindow( Wnd,
2076                               lprcUpdate ? &SafeUpdateRect : NULL,
2077                               RgnUpdate,
2078                               flags);
2079 
2080    UserDerefObjectCo(Wnd);
2081 
2082    RETURN( Ret);
2083 
2084 CLEANUP:
2085    TRACE("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
2086    UserLeave();
2087    END_CLEANUP;
2088 }
2089 
2090 BOOL
2091 UserDrawCaptionText(
2092    PWND pWnd,
2093    HDC hDc,
2094    const PUNICODE_STRING Text,
2095    const RECTL *lpRc,
2096    UINT uFlags,
2097    HFONT hFont)
2098 {
2099    HFONT hOldFont = NULL;
2100    COLORREF OldTextColor;
2101    NONCLIENTMETRICSW nclm;
2102    NTSTATUS Status;
2103    BOOLEAN bDeleteFont = FALSE;
2104    SIZE Size;
2105    BOOL Ret = TRUE;
2106    ULONG fit = 0, Length;
2107    RECTL r = *lpRc;
2108 
2109    TRACE("UserDrawCaptionText: %wZ\n", Text);
2110 
2111    nclm.cbSize = sizeof(nclm);
2112    if (!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS, nclm.cbSize, &nclm, 0))
2113    {
2114       ERR("UserSystemParametersInfo() failed!\n");
2115       return FALSE;
2116    }
2117 
2118    if (!hFont)
2119    {
2120       if(uFlags & DC_SMALLCAP)
2121          Status = TextIntCreateFontIndirect(&nclm.lfSmCaptionFont, &hFont);
2122       else
2123          Status = TextIntCreateFontIndirect(&nclm.lfCaptionFont, &hFont);
2124 
2125       if(!NT_SUCCESS(Status))
2126       {
2127          ERR("TextIntCreateFontIndirect() failed! Status: 0x%x\n", Status);
2128          return FALSE;
2129       }
2130 
2131       bDeleteFont = TRUE;
2132    }
2133 
2134    IntGdiSetBkMode(hDc, TRANSPARENT);
2135 
2136    hOldFont = NtGdiSelectFont(hDc, hFont);
2137 
2138    if(uFlags & DC_INBUTTON)
2139       OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT));
2140    else
2141       OldTextColor = IntGdiSetTextColor(hDc,
2142                                         IntGetSysColor(uFlags & DC_ACTIVE ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
2143 
2144    // Adjust for system menu.
2145    if (pWnd && pWnd->style & WS_SYSMENU)
2146    {
2147       r.right -= UserGetSystemMetrics(SM_CYCAPTION) - 1;
2148       if ((pWnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX)) && !(pWnd->ExStyle & WS_EX_TOOLWINDOW))
2149       {
2150          r.right -= UserGetSystemMetrics(SM_CXSIZE) + 1;
2151          r.right -= UserGetSystemMetrics(SM_CXSIZE) + 1;
2152       }
2153    }
2154 
2155    GreGetTextExtentExW(hDc, Text->Buffer, Text->Length/sizeof(WCHAR), r.right - r.left, &fit, 0, &Size, 0);
2156 
2157    Length = (Text->Length/sizeof(WCHAR) == fit ? fit : fit+1);
2158 
2159    if (Text->Length/sizeof(WCHAR) > Length)
2160    {
2161       Ret = FALSE;
2162    }
2163 
2164    if (Ret)
2165    {  // Faster while in setup.
2166       UserExtTextOutW( hDc,
2167                       lpRc->left,
2168                       lpRc->top + (lpRc->bottom - lpRc->top - Size.cy) / 2, // DT_SINGLELINE && DT_VCENTER
2169                       ETO_CLIPPED,
2170                      (RECTL *)lpRc,
2171                       Text->Buffer,
2172                       Length);
2173    }
2174    else
2175    {
2176       DrawTextW( hDc,
2177                  Text->Buffer,
2178                  Text->Length/sizeof(WCHAR),
2179                 (RECTL *)&r,
2180                  DT_END_ELLIPSIS|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_LEFT);
2181    }
2182 
2183    IntGdiSetTextColor(hDc, OldTextColor);
2184 
2185    if (hOldFont)
2186       NtGdiSelectFont(hDc, hOldFont);
2187 
2188    if (bDeleteFont)
2189       GreDeleteObject(hFont);
2190 
2191    return Ret;
2192 }
2193 
2194 //
2195 // This draws Buttons, Icons and Text...
2196 //
2197 BOOL UserDrawCaption(
2198    PWND pWnd,
2199    HDC hDc,
2200    RECTL *lpRc,
2201    HFONT hFont,
2202    HICON hIcon,
2203    const PUNICODE_STRING Str,
2204    UINT uFlags)
2205 {
2206    BOOL Ret = FALSE;
2207    HBRUSH hBgBrush, hOldBrush = NULL;
2208    RECTL Rect = *lpRc;
2209    BOOL HasIcon;
2210 
2211    RECTL_vMakeWellOrdered(lpRc);
2212 
2213    /* Determine whether the icon needs to be displayed */
2214    if (!hIcon && pWnd != NULL)
2215    {
2216      HasIcon = (uFlags & DC_ICON) && !(uFlags & DC_SMALLCAP) &&
2217                (pWnd->style & WS_SYSMENU) && !(pWnd->ExStyle & WS_EX_TOOLWINDOW);
2218    }
2219    else
2220      HasIcon = (hIcon != NULL);
2221 
2222    // Draw the caption background
2223    if((uFlags & DC_GRADIENT) && !(uFlags & DC_INBUTTON))
2224    {
2225       static GRADIENT_RECT gcap = {0, 1};
2226       TRIVERTEX Vertices[2];
2227       COLORREF Colors[2];
2228 
2229       Colors[0] = IntGetSysColor((uFlags & DC_ACTIVE) ?
2230             COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION);
2231 
2232       Colors[1] = IntGetSysColor((uFlags & DC_ACTIVE) ?
2233             COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION);
2234 
2235       Vertices[0].x = Rect.left;
2236       Vertices[0].y = Rect.top;
2237       Vertices[0].Red = (WORD)Colors[0]<<8;
2238       Vertices[0].Green = (WORD)Colors[0] & 0xFF00;
2239       Vertices[0].Blue = (WORD)(Colors[0]>>8) & 0xFF00;
2240       Vertices[0].Alpha = 0;
2241 
2242       Vertices[1].x = Rect.right;
2243       Vertices[1].y = Rect.bottom;
2244       Vertices[1].Red = (WORD)Colors[1]<<8;
2245       Vertices[1].Green = (WORD)Colors[1] & 0xFF00;
2246       Vertices[1].Blue = (WORD)(Colors[1]>>8) & 0xFF00;
2247       Vertices[1].Alpha = 0;
2248 
2249       if(!GreGradientFill(hDc, Vertices, 2, &gcap, 1, GRADIENT_FILL_RECT_H))
2250       {
2251          ERR("GreGradientFill() failed!\n");
2252          goto cleanup;
2253       }
2254    }
2255    else
2256    {
2257       if(uFlags & DC_INBUTTON)
2258          hBgBrush = IntGetSysColorBrush(COLOR_3DFACE);
2259       else if(uFlags & DC_ACTIVE)
2260          hBgBrush = IntGetSysColorBrush(COLOR_ACTIVECAPTION);
2261       else
2262          hBgBrush = IntGetSysColorBrush(COLOR_INACTIVECAPTION);
2263 
2264       hOldBrush = NtGdiSelectBrush(hDc, hBgBrush);
2265 
2266       if(!hOldBrush)
2267       {
2268          ERR("NtGdiSelectBrush() failed!\n");
2269          goto cleanup;
2270       }
2271 
2272       if(!NtGdiPatBlt(hDc, Rect.left, Rect.top,
2273          Rect.right - Rect.left,
2274          Rect.bottom - Rect.top,
2275          PATCOPY))
2276       {
2277          ERR("NtGdiPatBlt() failed!\n");
2278          goto cleanup;
2279       }
2280    }
2281 
2282    /* Draw icon */
2283    if (HasIcon)
2284    {
2285       PCURICON_OBJECT pIcon = NULL;
2286 
2287       if (hIcon)
2288       {
2289           pIcon = UserGetCurIconObject(hIcon);
2290       }
2291       else if (pWnd)
2292       {
2293           pIcon = NC_IconForWindow(pWnd);
2294           // FIXME: NC_IconForWindow should reference it for us */
2295           if (pIcon)
2296               UserReferenceObject(pIcon);
2297       }
2298 
2299       if (pIcon)
2300       {
2301          LONG cx = UserGetSystemMetrics(SM_CXSMICON);
2302          LONG cy = UserGetSystemMetrics(SM_CYSMICON);
2303          LONG x = Rect.left - cx/2 + 1 + (Rect.bottom - Rect.top)/2; // this is really what Window does
2304          LONG y = (Rect.top + Rect.bottom - cy)/2; // center
2305          UserDrawIconEx(hDc, x, y, pIcon, cx, cy, 0, NULL, DI_NORMAL);
2306          UserDereferenceObject(pIcon);
2307       }
2308       else
2309       {
2310           HasIcon = FALSE;
2311       }
2312    }
2313 
2314    if (HasIcon)
2315       Rect.left += Rect.bottom - Rect.top;
2316 
2317    if((uFlags & DC_TEXT))
2318    {
2319       BOOL Set = FALSE;
2320       Rect.left += 2;
2321 
2322       if (Str)
2323          Set = UserDrawCaptionText(pWnd, hDc, Str, &Rect, uFlags, hFont);
2324       else if (pWnd != NULL) // FIXME: Windows does not do that
2325       {
2326          UNICODE_STRING ustr;
2327          ustr.Buffer = pWnd->strName.Buffer; // FIXME: LARGE_STRING truncated!
2328          ustr.Length = (USHORT)min(pWnd->strName.Length, MAXUSHORT);
2329          ustr.MaximumLength = (USHORT)min(pWnd->strName.MaximumLength, MAXUSHORT);
2330          Set = UserDrawCaptionText(pWnd, hDc, &ustr, &Rect, uFlags, hFont);
2331       }
2332       if (pWnd)
2333       {
2334          if (Set)
2335             pWnd->state2 &= ~WNDS2_CAPTIONTEXTTRUNCATED;
2336          else
2337             pWnd->state2 |= WNDS2_CAPTIONTEXTTRUNCATED;
2338       }
2339    }
2340 
2341    Ret = TRUE;
2342 
2343 cleanup:
2344    if (hOldBrush) NtGdiSelectBrush(hDc, hOldBrush);
2345 
2346    return Ret;
2347 }
2348 
2349 INT
2350 FASTCALL
2351 UserRealizePalette(HDC hdc)
2352 {
2353   HWND hWnd, hWndDesktop;
2354   DWORD Ret;
2355 
2356   Ret = IntGdiRealizePalette(hdc);
2357   if (Ret) // There was a change.
2358   {
2359       hWnd = IntWindowFromDC(hdc);
2360       if (hWnd) // Send broadcast if dc is associated with a window.
2361       {  // FYI: Thread locked in CallOneParam.
2362          hWndDesktop = IntGetDesktopWindow();
2363          if ( hWndDesktop != hWnd )
2364          {
2365             PWND pWnd = UserGetWindowObject(hWndDesktop);
2366             ERR("RealizePalette Desktop.\n");
2367             hdc = UserGetWindowDC(pWnd);
2368             IntPaintDesktop(hdc);
2369             UserReleaseDC(pWnd,hdc,FALSE);
2370          }
2371          UserSendNotifyMessage((HWND)HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0);
2372       }
2373   }
2374   return Ret;
2375 }
2376 
2377 BOOL
2378 APIENTRY
2379 NtUserDrawCaptionTemp(
2380    HWND hWnd,
2381    HDC hDC,
2382    LPCRECT lpRc,
2383    HFONT hFont,
2384    HICON hIcon,
2385    const PUNICODE_STRING str,
2386    UINT uFlags)
2387 {
2388    PWND pWnd = NULL;
2389    UNICODE_STRING SafeStr = {0};
2390    NTSTATUS Status = STATUS_SUCCESS;
2391    RECTL SafeRect;
2392    BOOL Ret;
2393 
2394    UserEnterExclusive();
2395 
2396    if (hWnd != NULL)
2397    {
2398      if(!(pWnd = UserGetWindowObject(hWnd)))
2399      {
2400         UserLeave();
2401         return FALSE;
2402      }
2403    }
2404 
2405    _SEH2_TRY
2406    {
2407       ProbeForRead(lpRc, sizeof(RECTL), sizeof(ULONG));
2408       RtlCopyMemory(&SafeRect, lpRc, sizeof(RECTL));
2409       if (str != NULL)
2410       {
2411          SafeStr = ProbeForReadUnicodeString(str);
2412          if (SafeStr.Length != 0)
2413          {
2414              ProbeForRead( SafeStr.Buffer,
2415                            SafeStr.Length,
2416                             sizeof(WCHAR));
2417          }
2418       }
2419    }
2420    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2421    {
2422       Status = _SEH2_GetExceptionCode();
2423    }
2424    _SEH2_END;
2425 
2426    if (Status != STATUS_SUCCESS)
2427    {
2428       SetLastNtError(Status);
2429       UserLeave();
2430       return FALSE;
2431    }
2432 
2433    if (str != NULL)
2434       Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags);
2435    else
2436    {
2437       if ( RECTL_bIsEmptyRect(&SafeRect) && hFont == 0 && hIcon == 0 )
2438       {
2439          Ret = TRUE;
2440          if (uFlags & DC_DRAWCAPTIONMD)
2441          {
2442             ERR("NC Caption Mode\n");
2443             UserDrawCaptionBar(pWnd, hDC, uFlags);
2444             goto Exit;
2445          }
2446          else if (uFlags & DC_DRAWFRAMEMD)
2447          {
2448             ERR("NC Paint Mode\n");
2449             NC_DoNCPaint(pWnd, hDC, uFlags); // Update Menus too!
2450             goto Exit;
2451          }
2452       }
2453       Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags);
2454    }
2455 Exit:
2456    UserLeave();
2457    return Ret;
2458 }
2459 
2460 BOOL
2461 APIENTRY
2462 NtUserDrawCaption(HWND hWnd,
2463    HDC hDC,
2464    LPCRECT lpRc,
2465    UINT uFlags)
2466 {
2467    return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
2468 }
2469 
2470 INT FASTCALL
2471 co_UserExcludeUpdateRgn(HDC hDC, PWND Window)
2472 {
2473     POINT pt;
2474     RECT rc;
2475 
2476     if (Window->hrgnUpdate)
2477     {
2478         if (Window->hrgnUpdate == HRGN_WINDOW)
2479         {
2480             return NtGdiIntersectClipRect(hDC, 0, 0, 0, 0);
2481         }
2482         else
2483         {
2484             INT ret = ERROR;
2485             HRGN hrgn = NtGdiCreateRectRgn(0,0,0,0);
2486 
2487             if ( hrgn && GreGetDCPoint( hDC, GdiGetDCOrg, &pt) )
2488             {
2489                 if ( NtGdiGetRandomRgn( hDC, hrgn, CLIPRGN) == NULLREGION )
2490                 {
2491                     NtGdiOffsetRgn(hrgn, pt.x, pt.y);
2492                 }
2493                 else
2494                 {
2495                     HRGN hrgnScreen;
2496                     PMONITOR pm = UserGetPrimaryMonitor();
2497                     hrgnScreen = NtGdiCreateRectRgn(0,0,0,0);
2498                     NtGdiCombineRgn(hrgnScreen, hrgnScreen, pm->hrgnMonitor, RGN_OR);
2499 
2500                     NtGdiCombineRgn(hrgn, hrgnScreen, NULL, RGN_COPY);
2501 
2502                     GreDeleteObject(hrgnScreen);
2503                 }
2504 
2505                 NtGdiCombineRgn(hrgn, hrgn, Window->hrgnUpdate, RGN_DIFF);
2506 
2507                 NtGdiOffsetRgn(hrgn, -pt.x, -pt.y);
2508 
2509                 ret = NtGdiExtSelectClipRgn(hDC, hrgn, RGN_COPY);
2510 
2511                 GreDeleteObject(hrgn);
2512             }
2513             return ret;
2514         }
2515     }
2516     else
2517     {
2518         return GdiGetClipBox( hDC, &rc);
2519     }
2520 }
2521 
2522 INT
2523 APIENTRY
2524 NtUserExcludeUpdateRgn(
2525     HDC hDC,
2526     HWND hWnd)
2527 {
2528     INT ret = ERROR;
2529     PWND pWnd;
2530 
2531     TRACE("Enter NtUserExcludeUpdateRgn\n");
2532     UserEnterExclusive();
2533 
2534     pWnd = UserGetWindowObject(hWnd);
2535 
2536     if (hDC && pWnd)
2537         ret = co_UserExcludeUpdateRgn(hDC, pWnd);
2538 
2539     TRACE("Leave NtUserExcludeUpdateRgn, ret=%i\n", ret);
2540 
2541     UserLeave();
2542     return ret;
2543 }
2544 
2545 BOOL
2546 APIENTRY
2547 NtUserInvalidateRect(
2548     HWND hWnd,
2549     CONST RECT *lpUnsafeRect,
2550     BOOL bErase)
2551 {
2552     UINT flags = RDW_INVALIDATE | (bErase ? RDW_ERASE : 0);
2553     if (!hWnd)
2554     {
2555        flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
2556        lpUnsafeRect = NULL;
2557     }
2558     return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, flags);
2559 }
2560 
2561 BOOL
2562 APIENTRY
2563 NtUserInvalidateRgn(
2564     HWND hWnd,
2565     HRGN hRgn,
2566     BOOL bErase)
2567 {
2568     if (!hWnd)
2569     {
2570        EngSetLastError( ERROR_INVALID_WINDOW_HANDLE );
2571        return FALSE;
2572     }
2573     return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2574 }
2575 
2576 BOOL
2577 APIENTRY
2578 NtUserPrintWindow(
2579     HWND hwnd,
2580     HDC  hdcBlt,
2581     UINT nFlags)
2582 {
2583     PWND Window;
2584     BOOL Ret = FALSE;
2585 
2586     UserEnterExclusive();
2587 
2588     if (hwnd)
2589     {
2590        if (!(Window = UserGetWindowObject(hwnd)) ||
2591             UserIsDesktopWindow(Window) || UserIsMessageWindow(Window))
2592        {
2593           goto Exit;
2594        }
2595 
2596        if ( Window )
2597        {
2598           /* Validate flags and check it as a mask for 0 or 1. */
2599           if ( (nFlags & PW_CLIENTONLY) == nFlags)
2600              Ret = IntPrintWindow( Window, hdcBlt, nFlags);
2601           else
2602              EngSetLastError(ERROR_INVALID_PARAMETER);
2603        }
2604     }
2605 Exit:
2606     UserLeave();
2607     return Ret;
2608 }
2609 
2610 /* ValidateRect gets redirected to NtUserValidateRect:
2611    http://blog.csdn.net/ntdll/archive/2005/10/19/509299.aspx */
2612 BOOL
2613 APIENTRY
2614 NtUserValidateRect(
2615     HWND hWnd,
2616     const RECT *lpRect)
2617 {
2618     UINT flags = RDW_VALIDATE;
2619     if (!hWnd)
2620     {
2621        flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
2622        lpRect = NULL;
2623     }
2624     return NtUserRedrawWindow(hWnd, lpRect, NULL, flags);
2625 }
2626 
2627 /* EOF */
2628