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