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