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