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