update for HEAD-2003091401
[reactos.git] / subsys / win32k / ntuser / painting.c
1 /*
2  *  ReactOS W32 Subsystem
3  *  Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /* $Id$
20  *
21  * COPYRIGHT:        See COPYING in the top level directory
22  * PROJECT:          ReactOS kernel
23  * PURPOSE:          Painting
24  * FILE:             subsys/win32k/ntuser/painting.c
25  * PROGRAMER:        Casper S. Hornstrup (chorns@users.sourceforge.net)
26  * REVISION HISTORY:
27  *       06-06-2001  CSH  Created
28  */
29 /* INCLUDES ******************************************************************/
30
31 #include <ddk/ntddk.h>
32 #include <win32k/win32k.h>
33 #include <include/object.h>
34 #include <include/guicheck.h>
35 #include <include/window.h>
36 #include <include/class.h>
37 #include <include/error.h>
38 #include <include/winsta.h>
39 #include <windows.h>
40 #include <include/painting.h>
41 #include <user32/wininternal.h>
42 #include <include/rect.h>
43 #include <win32k/coord.h>
44 #include <win32k/region.h>
45 #include <include/vis.h>
46
47 #define NDEBUG
48 #include <debug.h>
49
50
51 /* GLOBALS *******************************************************************/
52
53 /* client rect in window coordinates */
54 #define GETCLIENTRECTW(wnd, r)  (r).left = (wnd)->ClientRect.left - (wnd)->WindowRect.left; \
55                                 (r).top = (wnd)->ClientRect.top - (wnd)->WindowRect.top; \
56                                 (r).right = (wnd)->ClientRect.right - (wnd)->WindowRect.left; \
57                                 (r).bottom = (wnd)->ClientRect.bottom - (wnd)->WindowRect.top
58
59 /* FUNCTIONS *****************************************************************/
60
61 HRGN STATIC STDCALL
62 PaintDoPaint(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags, ULONG ExFlags)
63 {
64   HDC hDC;
65   HWND hWnd = Window->Self;
66   BOOL bIcon = (0 != (Window->Style & WS_MINIMIZE)) &&
67                (0 != IntGetClassLong(Window, GCL_HICON, FALSE));
68
69   if (0 != (ExFlags & RDW_EX_DELAY_NCPAINT) ||
70       PaintHaveToDelayNCPaint(Window, 0))
71     {
72       ExFlags |= RDW_EX_DELAY_NCPAINT;
73     }
74
75   if (Flags & RDW_UPDATENOW)
76     {
77       if (NULL != Window->UpdateRegion)
78         {
79           if (IntIsDesktopWindow(Window))
80             {
81               VIS_RepaintDesktop(Window->Self, Window->UpdateRegion);
82             }
83           else
84             {
85               NtUserSendMessage(hWnd, bIcon ? WM_PAINTICON : WM_PAINT, bIcon, 0);
86             }
87         }
88     }
89   else if (Flags & RDW_ERASENOW || ExFlags & RDW_EX_TOPFRAME)
90     {
91       UINT Dcx = DCX_INTERSECTRGN | DCX_USESTYLE | DCX_KEEPCLIPRGN |
92         DCX_WINDOWPAINT | DCX_CACHE;
93       HRGN hRgnRet;
94
95       hRgnRet =
96         PaintUpdateNCRegion(Window,
97                          hRgn,
98                          UNC_REGION | UNC_CHECK |
99                          ((ExFlags & RDW_EX_TOPFRAME) ? UNC_ENTIRE : 0) |
100                          ((ExFlags & RDW_EX_DELAY_NCPAINT) ?
101                           UNC_DELAY_NCPAINT : 0));
102       if (NULL != hRgnRet)
103         {
104           if ((HRGN) 1 < hRgnRet)
105             {
106               hRgn = hRgnRet;
107             }
108           else
109             {
110               hRgnRet = NULL;
111             }
112           if (0 != (Window->Flags & WINDOWOBJECT_NEED_ERASEBACKGRD))
113             {
114               if (bIcon)
115                 {
116                   Dcx |= DCX_WINDOW;
117                 }
118               if (NULL != hRgnRet)
119                 {
120                   NtGdiOffsetRgn(hRgnRet,
121                                 Window->WindowRect.left -
122                                 Window->ClientRect.left,
123                                 Window->WindowRect.top -
124                                 Window->ClientRect.top);
125                 }
126               else
127                 {
128                   Dcx &= ~DCX_INTERSECTRGN;
129                 }
130               if (NULL != (hDC = NtUserGetDCEx(hWnd, hRgnRet, Dcx)))
131                 {
132                   if (IntIsDesktopWindow(Window))
133                     {
134                       VIS_RepaintDesktop(Window->Self, Window->UpdateRegion);
135                       NtGdiDeleteObject(Window->UpdateRegion);
136                       Window->UpdateRegion = 0;
137                     }
138                   else
139                     {
140                       if (0 != NtUserSendMessage(hWnd, bIcon ? WM_ICONERASEBKGND :
141                                                  WM_ERASEBKGND, (WPARAM)hDC, 0))
142                         {
143                           Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBACKGRD;
144                         }                     
145                     }
146                   NtUserReleaseDC(hWnd, hDC);
147                 }
148             }
149         }
150     }
151
152   /* FIXME: Check that the window is still valid at this point. */
153
154   ExFlags &= ~RDW_EX_TOPFRAME;
155
156   /* FIXME: Paint child windows. */
157
158   return(hRgn);
159 }
160
161 VOID STATIC FASTCALL
162 PaintUpdateInternalPaint(PWINDOW_OBJECT Window, ULONG Flags)
163 {
164   if (Flags & RDW_INTERNALPAINT)
165     {
166       if (Window->UpdateRegion == NULL &&
167           !(Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
168         {
169           MsqIncPaintCountQueue(Window->MessageQueue);
170         }
171       Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
172     }
173   else if (Flags & RDW_NOINTERNALPAINT)
174     {
175       if (Window->UpdateRegion == NULL &&
176           (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
177         {
178           MsqDecPaintCountQueue(Window->MessageQueue);
179         }
180       Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
181     }
182 }
183
184 VOID STATIC FASTCALL
185 PaintValidateParent(PWINDOW_OBJECT Child)
186 {
187   HWND DesktopHandle = IntGetDesktopWindow();
188   PWINDOW_OBJECT Parent = Child->Parent;
189   PWINDOW_OBJECT Desktop = IntGetWindowObject(DesktopHandle);
190   HRGN hRgn;
191
192   if ((HRGN) 1 == Child->UpdateRegion)
193     {
194       RECT Rect;
195
196       Rect.left = Rect.top = 0;
197       Rect.right = Child->WindowRect.right - Child->WindowRect.left;
198       Rect.bottom = Child->WindowRect.bottom - Child->WindowRect.top;
199
200       hRgn = UnsafeIntCreateRectRgnIndirect(&Rect);
201     }
202   else
203     {
204       hRgn = Child->UpdateRegion;
205     }
206
207   while (NULL != Parent && Parent != Desktop)
208     {
209       if (0 == (Parent->Style & WS_CLIPCHILDREN))
210         {
211           if (NULL != Parent->UpdateRegion)
212             {
213               POINT Offset;
214
215               if ((HRGN) 1 == Parent->UpdateRegion)
216                 {
217                   RECT Rect1;
218
219                   Rect1.left = Rect1.top = 0;
220                   Rect1.right = Parent->WindowRect.right - 
221                     Parent->WindowRect.left;
222                   Rect1.bottom = Parent->WindowRect.bottom -
223                     Parent->WindowRect.top;
224
225                   Parent->UpdateRegion = 
226                     UnsafeIntCreateRectRgnIndirect(&Rect1);
227                 }
228               Offset.x = Child->WindowRect.left - Parent->WindowRect.left;
229               Offset.y = Child->WindowRect.top - Parent->WindowRect.top;
230               NtGdiOffsetRgn(hRgn, Offset.x, Offset.y);
231               NtGdiCombineRgn(Parent->UpdateRegion, Parent->UpdateRegion, hRgn,
232                              RGN_DIFF);
233               NtGdiOffsetRgn(hRgn, -Offset.x, -Offset.y);
234             }
235         }
236       Parent = Parent->Parent;
237     }
238   if (hRgn != Child->UpdateRegion)
239     {
240       NtGdiDeleteObject(Child->UpdateRegion);
241     }
242   IntReleaseWindowObject(Desktop);
243 }
244
245 VOID STATIC STDCALL
246 PaintUpdateRgns(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags,
247                 BOOL First)
248 {
249   /*
250    * Called only when one of the following is set:
251    * (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT)
252    */
253
254   BOOL HadOne = NULL != Window->UpdateRegion && NULL != hRgn;
255   BOOL HasChildren = Window->FirstChild &&
256     !(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
257     ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN));
258   RECT Rect;
259
260   Rect.left = Rect.top = 0;
261   Rect.right = Window->WindowRect.right - Window->WindowRect.left;
262   Rect.bottom = Window->WindowRect.bottom - Window->WindowRect.top;
263
264   if (Flags & RDW_INVALIDATE)
265     {
266       if ((HRGN) 1 < hRgn)
267         {
268           if ((HRGN) 1 != Window->UpdateRegion)
269             {
270               if ((HRGN) 1 < Window->UpdateRegion)
271                 {
272                   NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
273                                  hRgn, RGN_OR);
274                 }
275               Window->UpdateRegion =
276                 REGION_CropRgn(Window->UpdateRegion,
277                                Window->UpdateRegion ? Window->UpdateRegion : hRgn,
278                                &Rect, NULL);
279               if (! HadOne)
280                 {
281                   UnsafeIntGetRgnBox(Window->UpdateRegion, &Rect);
282                   if (NtGdiIsEmptyRect(&Rect))
283                     {
284                       NtGdiDeleteObject(Window->UpdateRegion);
285                       Window->UpdateRegion = NULL;
286                       PaintUpdateInternalPaint(Window, Flags);
287                       return;
288                     }
289                 }
290             }
291         }
292       else if ((HRGN) 1 == hRgn)
293         {
294           if ((HRGN) 1 < Window->UpdateRegion)
295             {
296               NtGdiDeleteObject(Window->UpdateRegion);
297             }
298           Window->UpdateRegion = (HRGN) 1;
299         }
300       else
301         {
302           hRgn = Window->UpdateRegion; /* this is a trick that depends on code in PaintRedrawWindow() */
303         }
304
305       if (! HadOne && 0 == (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT) &&
306           !IntIsDesktopWindow(Window))
307         {
308           MsqIncPaintCountQueue(Window->MessageQueue);
309         }
310
311       if (Flags & RDW_FRAME)
312         {
313           Window->Flags |= WINDOWOBJECT_NEED_NCPAINT;
314         }
315       if (Flags & RDW_ERASE)
316         {
317           Window->Flags |= WINDOWOBJECT_NEED_ERASEBACKGRD;
318         }
319       Flags |= RDW_FRAME;
320     }
321   else if (Flags & RDW_VALIDATE)
322     {
323       if (NULL != Window->UpdateRegion)
324         {
325           if ((HRGN) 1 < hRgn)
326             {
327               if ((HRGN) 1 == Window->UpdateRegion)
328                 {
329                   /* If no NCPAINT needed or if we're going to turn it off
330                      the special value 1 means the whole client rect */
331                   if (0 == (Window->Flags & WINDOWOBJECT_NEED_NCPAINT) ||
332                       0 != (Flags & RDW_NOFRAME))
333                     {
334                       Rect.left = Window->ClientRect.left - Window->WindowRect.left;
335                       Rect.top = Window->ClientRect.top - Window->WindowRect.top;
336                       Rect.right = Window->ClientRect.right - Window->WindowRect.left;
337                       Rect.bottom = Window->ClientRect.bottom - Window->WindowRect.top;
338                     }
339                   Window->UpdateRegion = 
340                     UnsafeIntCreateRectRgnIndirect(&Rect);
341                 }
342               if (NtGdiCombineRgn(Window->UpdateRegion, 
343                                  Window->UpdateRegion, hRgn, 
344                                  RGN_DIFF) == NULLREGION)
345                 {
346                   NtGdiDeleteObject(Window->UpdateRegion);
347                   Window->UpdateRegion = NULL;
348                 }
349             }
350           else /* validate everything */
351             {
352               if ((HRGN) 1 < Window->UpdateRegion)
353                 {
354                   NtGdiDeleteObject(Window->UpdateRegion);
355                 }
356               Window->UpdateRegion = NULL;
357             }
358
359           if (NULL != Window->UpdateRegion)
360             {
361               Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBACKGRD;
362               if (0 != (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
363                 {
364                   MsqDecPaintCountQueue(Window->MessageQueue);
365                 }
366             }
367         }
368
369       if (Flags & RDW_NOFRAME)
370         {
371           Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
372         }
373       if (Flags & RDW_NOERASE)
374         {
375           Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBACKGRD;
376         }
377     }
378
379   if (First && NULL != Window->UpdateRegion && 0 != (Flags & RDW_UPDATENOW))
380     {
381       PaintValidateParent(Window); /* validate parent covered by region */
382     }
383
384   /* in/validate child windows that intersect with the region if it
385    * is a valid handle. */
386
387   if (0 != (Flags & (RDW_INVALIDATE | RDW_VALIDATE)))
388     {
389       if ((HRGN) 1 < hRgn && HasChildren)
390         {
391           POINT Total = {0, 0};
392           POINT PrevOrign = {0, 0};
393           PWINDOW_OBJECT Child;
394
395           ExAcquireFastMutexUnsafe(&Window->ChildrenListLock);
396           Child = Window->FirstChild;
397           while (Child)
398             {
399               if (0 != (Child->Style & WS_VISIBLE))
400                 {
401                   POINT Offset;
402
403                   Rect.left = Child->WindowRect.left - Window->WindowRect.left;
404                   Rect.top = Child->WindowRect.top - Window->WindowRect.top;
405                   Rect.right = Child->WindowRect.right - Window->WindowRect.left;
406                   Rect.bottom = Child->WindowRect.bottom - Window->WindowRect.top;
407
408                   Offset.x = Rect.left - PrevOrign.x;
409                   Offset.y = Rect.top - PrevOrign.y;
410                   NtGdiOffsetRect(&Rect, -Total.x, -Total.y);
411
412                   if (UnsafeIntRectInRegion(hRgn, &Rect))
413                     {
414                       NtGdiOffsetRgn(hRgn, -Offset.x, -Offset.y);
415                       PaintUpdateRgns(Child, hRgn, Flags, FALSE);
416                       PrevOrign.x = Rect.left + Total.x;
417                       PrevOrign.y = Rect.top + Total.y;
418                       Total.x += Offset.x;
419                       Total.y += Offset.y;
420                     }
421                 }
422               Child = Child->NextSibling;
423             }
424           ExReleaseFastMutexUnsafe(&Window->ChildrenListLock);
425
426           NtGdiOffsetRgn(hRgn, Total.x, Total.y);
427           HasChildren = FALSE;
428         }
429     }
430
431   if (HasChildren)
432   {
433     PWINDOW_OBJECT Child;
434
435     ExAcquireFastMutexUnsafe(&Window->ChildrenListLock);
436     Child = Window->FirstChild;
437     while (Child)
438     {
439       if (Child->Style & WS_VISIBLE)
440       {
441         PaintUpdateRgns(Child, hRgn, Flags, FALSE);
442       }
443       Child = Child->NextSibling;
444     }
445     ExReleaseFastMutexUnsafe(&Window->ChildrenListLock);
446   }
447
448   PaintUpdateInternalPaint(Window, Flags);
449 }
450
451 BOOL STDCALL
452 PaintRedrawWindow( PWINDOW_OBJECT Window, 
453                    const RECT* UpdateRect, 
454                    HRGN UpdateRgn,
455                    ULONG Flags, 
456                    ULONG ExFlags)
457 {
458   RECT Rect, Rect2;
459   POINT Pt;
460   HRGN hRgn = NULL;
461
462   DPRINT("[win32k.sys:painting] In PaintRedrawWindow()\n");
463
464   if ((RDW_INVALIDATE | RDW_FRAME) == (Flags & (RDW_INVALIDATE | RDW_FRAME)) ||
465       (RDW_VALIDATE | RDW_NOFRAME) == (Flags & (RDW_VALIDATE | RDW_NOFRAME)))
466     {
467       Rect = Window->WindowRect;
468     }
469   else
470     {
471       Rect = Window->ClientRect;
472     }
473
474   if (ExFlags & RDW_EX_XYWINDOW)
475     {
476       Pt.x = Pt.y = 0;
477       NtGdiOffsetRect(&Rect, -Window->WindowRect.left, -Window->WindowRect.top);
478     }
479   else
480     {
481       Pt.x = Window->ClientRect.left - Window->WindowRect.left;
482       Pt.y = Window->ClientRect.top - Window->WindowRect.top;
483       NtGdiOffsetRect(&Rect, -Window->ClientRect.left, -Window->ClientRect.top);
484     }
485
486   if (0 != (Flags & RDW_INVALIDATE))  /* ------------------------- Invalidate */
487     {
488       if (NULL != UpdateRgn)
489         {
490           if (NULL != Window->UpdateRegion)
491             {
492               hRgn = REGION_CropRgn(NULL, UpdateRgn, NULL, &Pt);
493             }
494           else
495             {
496               Window->UpdateRegion = REGION_CropRgn(NULL, UpdateRgn, &Rect, &Pt);
497             }
498         }
499       else if (NULL != UpdateRect)
500         {
501           if (! NtGdiIntersectRect(&Rect2, &Rect, UpdateRect))
502             {
503               
504               if ((HRGN) 1 < hRgn && hRgn != UpdateRgn)
505                 {
506                   NtGdiDeleteObject(hRgn);
507                 }
508               return TRUE;
509             }
510           NtGdiOffsetRect(&Rect2, Pt.x, Pt.y);
511           if (NULL == Window->UpdateRegion)
512             {
513               Window->UpdateRegion =
514                 UnsafeIntCreateRectRgnIndirect(&Rect2);
515             }
516           else
517             {
518               hRgn = UnsafeIntCreateRectRgnIndirect(&Rect2);
519             }
520         }
521       else /* entire window or client depending on RDW_FRAME */
522         {
523           if (Flags & RDW_FRAME)
524             {
525               if (NULL != Window->UpdateRegion)
526                 {
527                   hRgn = (HRGN) 1;
528                 }
529               else
530                 {
531                 Window->UpdateRegion = (HRGN) 1;
532                 }
533             }
534           else
535             {
536               GETCLIENTRECTW(Window, Rect2);
537               if (NULL == Window->UpdateRegion)
538                 {
539                   Window->UpdateRegion = UnsafeIntCreateRectRgnIndirect(&Rect2);
540                 }
541               else
542                 {
543                   hRgn = UnsafeIntCreateRectRgnIndirect(&Rect2);
544                 }
545             }
546         }
547     }
548   else if (Flags & RDW_VALIDATE)
549     {
550       /* In this we cannot leave with zero hRgn */
551       if (NULL != UpdateRgn)
552         {
553           hRgn = REGION_CropRgn(hRgn, UpdateRgn,  &Rect, &Pt);
554           UnsafeIntGetRgnBox(hRgn, &Rect2);
555           if (NtGdiIsEmptyRect(&Rect2))
556             {
557               
558               if ((HRGN) 1 < hRgn && hRgn != UpdateRgn)
559                 {
560                   NtGdiDeleteObject(hRgn);
561                 }
562               return TRUE;
563             }
564         }
565       else if (NULL != UpdateRect)
566         {
567           if (! NtGdiIntersectRect(&Rect2, &Rect, UpdateRect))
568             {
569               
570               if ((HRGN) 1 < hRgn && hRgn != UpdateRgn)
571                 {
572                   NtGdiDeleteObject(hRgn);
573                 }
574               return TRUE;
575             }
576           NtGdiOffsetRect(&Rect2, Pt.x, Pt.y);
577           hRgn = UnsafeIntCreateRectRgnIndirect(&Rect2);
578         }
579       else /* entire window or client depending on RDW_NOFRAME */
580         {
581           if (0 != (Flags & RDW_NOFRAME))
582             {
583               hRgn = (HRGN) 1;
584             }
585           else
586             {
587               GETCLIENTRECTW(Window, Rect2);
588               hRgn = UnsafeIntCreateRectRgnIndirect(&Rect2);
589             }
590         }
591     }
592
593   /* At this point hRgn is either an update region in window coordinates or 1 or 0 */
594
595   PaintUpdateRgns(Window, hRgn, Flags, TRUE);
596
597   /* Erase/update windows, from now on hRgn is a scratch region */
598
599   hRgn = PaintDoPaint(Window, (HRGN) 1 == hRgn ? NULL : hRgn, Flags, ExFlags);
600
601   if ((HRGN) 1 < hRgn && hRgn != UpdateRgn)
602     {
603       NtGdiDeleteObject(hRgn);
604     }
605
606   return TRUE;
607 }
608
609 BOOL STDCALL
610 PaintHaveToDelayNCPaint(PWINDOW_OBJECT Window, ULONG Flags)
611 {
612   if (Flags & UNC_DELAY_NCPAINT)
613     {
614       return(TRUE);
615     }
616
617   if (Flags & UNC_IN_BEGINPAINT)
618     {
619       return(FALSE);
620     }
621
622   Window = Window->Parent;
623   while (Window != NULL)
624     {
625       if (Window->Style & WS_CLIPCHILDREN && Window->UpdateRegion != NULL)
626         {
627           return TRUE;
628         }
629       Window = Window->Parent;
630     }
631
632   return FALSE;
633 }
634
635 HWND STDCALL
636 PaintingFindWinToRepaint(HWND hWnd, PW32THREAD Thread)
637 {
638   PWINDOW_OBJECT Window;
639   PWINDOW_OBJECT BaseWindow;
640   PLIST_ENTRY current_entry;
641   HWND hFoundWnd = NULL;
642
643   if (hWnd == NULL)
644     {
645       ExAcquireFastMutex(&Thread->WindowListLock);
646       current_entry = Thread->WindowListHead.Flink;
647       while (current_entry != &Thread->WindowListHead)
648         {
649           Window = CONTAINING_RECORD(current_entry, WINDOW_OBJECT,
650                                      ThreadListEntry);
651           if (Window->Style & WS_VISIBLE)
652             {
653               hFoundWnd = 
654                 PaintingFindWinToRepaint(Window->Self, Thread);
655               if (hFoundWnd != NULL)
656                 {
657                   ExReleaseFastMutex(&Thread->WindowListLock);
658                   return(hFoundWnd);
659                 }
660             }
661           current_entry = current_entry->Flink;
662         }
663       ExReleaseFastMutex(&Thread->WindowListLock);
664       return(NULL);
665     }
666
667   BaseWindow = IntGetWindowObject(hWnd);
668   if (BaseWindow == NULL)
669     {
670       return(NULL);
671     }
672   if (BaseWindow->UpdateRegion != NULL ||
673       BaseWindow->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
674     {
675       IntReleaseWindowObject(BaseWindow);
676       return(hWnd);
677     }
678
679   ExAcquireFastMutex(&BaseWindow->ChildrenListLock);
680   Window = BaseWindow->FirstChild;
681   while (Window)
682   {
683     if (Window->Style & WS_VISIBLE)
684     {
685       hFoundWnd = PaintingFindWinToRepaint(Window->Self, Thread);
686       if (hFoundWnd != NULL)
687       {
688         break;
689       }
690     }
691     Window = Window->NextSibling;
692   }
693   ExReleaseFastMutex(&BaseWindow->ChildrenListLock);
694
695   IntReleaseWindowObject(BaseWindow);
696   return(hFoundWnd);
697 }
698
699 HRGN STDCALL
700 PaintUpdateNCRegion(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags)
701 {
702   HRGN hRgnRet;
703   RECT ClientRect;
704   HRGN hClip = NULL;
705
706   /* Desktop has no parent. */
707   if (Window->Parent == NULL)
708     {
709       Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
710       if ((HRGN) 1 < Window->UpdateRegion)
711         {
712           hRgnRet = REGION_CropRgn(hRgn, Window->UpdateRegion, NULL, NULL);
713         }
714       else
715         {
716           hRgnRet = Window->UpdateRegion;
717         }
718       return(hRgnRet);
719     }
720
721 #if 0 /* NtUserGetFOregroundWindow() not implemented yet */
722   if ((Window->Self == NtUserGetForegroundWindow()) &&
723       0 == (Window->Flags & WIN_NCACTIVATED) )
724     {
725       Window->Flags |= WIN_NCACTIVATED;
726       Flags |= UNC_ENTIRE;
727     }
728 #endif
729
730     /*
731      * If the window's non-client area needs to be painted,
732      */
733   if (0 != (Window->Flags & WINDOWOBJECT_NEED_NCPAINT) &&
734       ! PaintHaveToDelayNCPaint(Window, Flags))
735     {
736       RECT UpdateRegionBox;
737       RECT Rect;
738
739       Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
740       GETCLIENTRECTW(Window, ClientRect);
741
742       if ((HRGN) 1 < Window->UpdateRegion)
743         {
744           UnsafeIntGetRgnBox(Window->UpdateRegion, &UpdateRegionBox);
745           NtGdiUnionRect(&Rect, &ClientRect, &UpdateRegionBox);
746           if (Rect.left != ClientRect.left || Rect.top != ClientRect.top ||
747               Rect.right != ClientRect.right || Rect.bottom != ClientRect.bottom)
748             {
749               hClip = Window->UpdateRegion;
750               Window->UpdateRegion = REGION_CropRgn(hRgn, hClip,
751                                                     &ClientRect, NULL);
752               if (Flags & UNC_REGION)
753                 {
754                   hRgnRet = hClip;
755                 }
756             }
757
758           if (Flags & UNC_CHECK)
759             {
760               UnsafeIntGetRgnBox(Window->UpdateRegion, &UpdateRegionBox);
761               if (NtGdiIsEmptyRect(&UpdateRegionBox))
762                 {
763                   NtGdiDeleteObject(Window->UpdateRegion);
764                   Window->UpdateRegion = NULL;
765                   if (0 == (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
766                     {
767                       MsqDecPaintCountQueue(Window->MessageQueue);
768                     }
769                   Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBACKGRD;
770                 }
771             }
772
773           if (0 == hClip && 0 != Window->UpdateRegion)
774             {
775             goto copyrgn;
776             }
777         }
778       else if ((HRGN) 1 == Window->UpdateRegion)
779         {
780           if (0 != (Flags & UNC_UPDATE))
781             {
782               Window->UpdateRegion =
783                 UnsafeIntCreateRectRgnIndirect(&ClientRect);
784             }
785           if (Flags & UNC_REGION)
786             {
787               hRgnRet = (HRGN) 1;
788             }
789           Flags |= UNC_ENTIRE;
790         }
791     }
792   else /* no WM_NCPAINT unless forced */
793     {
794       if ((HRGN) 1 < Window->UpdateRegion)
795         {
796 copyrgn:
797           if (0 != (Flags & UNC_REGION))
798             {
799               hRgnRet = REGION_CropRgn(hRgn, Window->UpdateRegion, NULL, NULL);
800             }
801         }
802       else if ((HRGN) 1 == Window->UpdateRegion && 0 != (Flags & UNC_UPDATE))
803         {
804           GETCLIENTRECTW(Window, ClientRect);
805           Window->UpdateRegion =
806             UnsafeIntCreateRectRgnIndirect(&ClientRect);
807           if (Flags & UNC_REGION)
808             {
809               hRgnRet = (HRGN) 1;
810             }
811         }
812     }
813
814   if (NULL == hClip && 0 != (Flags & UNC_ENTIRE))
815     {
816       if (RtlCompareMemory(&Window->WindowRect, &Window->ClientRect,
817                            sizeof(RECT)) != sizeof(RECT))
818         {
819           hClip = (HRGN) 1;
820         }
821       else
822         {
823           hClip = NULL;
824         }
825     }
826
827   if (NULL != hClip) /* NOTE: WM_NCPAINT allows wParam to be 1 */
828     {
829       if (hClip == hRgnRet && (HRGN) 1 < hRgnRet)
830         {
831           hClip = NtGdiCreateRectRgn(0, 0, 0, 0);
832           NtGdiCombineRgn(hClip, hRgnRet, 0, RGN_COPY);
833         }
834
835       NtUserSendMessage(Window->Self, WM_NCPAINT, (WPARAM) hClip, 0);
836
837       if ((HRGN) 1 < hClip && hClip != hRgn && hClip != hRgnRet)
838         {
839           NtGdiDeleteObject(hClip);
840         }
841
842       /* FIXME: Need to check the window is still valid. */
843     }
844   return(hRgnRet);
845 }
846
847 BOOL STDCALL
848 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* lPs)
849 {
850   NtUserReleaseDC(hWnd, lPs->hdc);
851   /* FIXME: Show claret. */
852   return(TRUE);
853 }
854
855 static
856 HRGN FASTCALL
857 GetClientUpdateRegion(PWINDOW_OBJECT Window)
858 {
859   POINT Offset;
860   RECT Rect;
861
862   if ((DWORD) Window->UpdateRegion <= 1)
863     {
864       return Window->UpdateRegion;
865     }
866
867   Offset.x = Window->WindowRect.left - Window->ClientRect.left;
868   Offset.y = Window->WindowRect.top - Window->ClientRect.top;
869   Rect.left = - Offset.x;
870   Rect.top = - Offset.y;
871   Rect.right = Rect.left + (Window->ClientRect.right - Window->ClientRect.left);
872   Rect.bottom = Rect.top + (Window->ClientRect.bottom - Window->ClientRect.top);
873
874   return REGION_CropRgn(NULL, Window->UpdateRegion, &Rect, &Offset);
875 }
876
877 HDC STDCALL
878 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* lPs)
879 {
880   BOOL IsIcon;
881   PWINDOW_OBJECT Window;
882   HRGN UpdateRegion;
883   RECT ClientRect;
884   RECT ClipRect;
885   //NTSTATUS Status;
886   INT DcxFlags;
887
888   if (!(Window = IntGetWindowObject(hWnd)))
889   {
890     SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
891     return NULL;
892   }
893   
894   /* Send WM_NCPAINT */
895   PaintUpdateNCRegion(Window, 0, UNC_UPDATE | UNC_IN_BEGINPAINT);
896
897   /* Check ifthe window is still valid. */
898   if (!IntGetWindowObject(hWnd))
899   {
900     return 0;
901   }
902
903   /* retrieve update region */
904   UpdateRegion = GetClientUpdateRegion(Window);
905   if (1 < (DWORD) Window->UpdateRegion)
906     {
907       NtGdiDeleteObject(Window->UpdateRegion);
908     }
909   Window->UpdateRegion = 0;
910   if (UpdateRegion != NULL || (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
911     {
912       MsqDecPaintCountQueue(Window->MessageQueue);
913     }
914   Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
915
916   /* FIXME: Hide caret. */
917
918   IsIcon = (Window->Style & WS_MINIMIZE) && IntGetClassLong(Window, GCL_HICON, FALSE);
919
920   DcxFlags = DCX_INTERSECTRGN | DCX_WINDOWPAINT | DCX_USESTYLE;
921   if (IsIcon)
922     {
923     DcxFlags |= DCX_WINDOW;
924     }
925   if (IntGetClassLong(Window, GCL_STYLE, FALSE) & CS_PARENTDC)
926     {
927       /* Don't clip the output to the update region for CS_PARENTDC window */
928       if ((HRGN) 1 < UpdateRegion)
929         {
930           NtGdiDeleteObject(UpdateRegion);
931         }
932       UpdateRegion = NULL;
933       DcxFlags &= ~DCX_INTERSECTRGN;
934     }
935   else
936     {
937       if (NULL == UpdateRegion)  /* empty region, clip everything */
938         {
939           UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
940         }
941       else if ((HRGN) 1 == UpdateRegion)  /* whole client area, don't clip */
942         {
943           UpdateRegion = NULL;
944           DcxFlags &= ~DCX_INTERSECTRGN;
945         }
946     }
947   lPs->hdc = NtUserGetDCEx(hWnd, UpdateRegion, DcxFlags);
948
949   /* FIXME: Check for DC creation failure. */
950
951   IntGetClientRect(Window, &ClientRect);
952   NtGdiGetClipBox(lPs->hdc, &ClipRect);
953   NtGdiLPtoDP(lPs->hdc, (LPPOINT)&ClipRect, 2);
954   NtGdiIntersectRect(&lPs->rcPaint, &ClientRect, &ClipRect);
955   NtGdiDPtoLP(lPs->hdc, (LPPOINT)&lPs->rcPaint, 2);
956
957   if (Window->Flags & WINDOWOBJECT_NEED_ERASEBACKGRD)
958     {
959       BOOLEAN Result;
960       Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBACKGRD;
961       Result = NtUserSendMessage(hWnd,
962                                  IsIcon ? WM_ICONERASEBKGND : WM_ERASEBKGND,
963                                  (WPARAM)lPs->hdc,
964                                  0);
965       lPs->fErase = !Result;
966     }
967   else
968     {
969       lPs->fErase = FALSE;
970     }
971
972   ObmDereferenceObject(Window);
973   return(lPs->hdc);
974 }
975
976 DWORD
977 STDCALL
978 NtUserInvalidateRect(
979   HWND hWnd,
980   CONST RECT *Rect,
981   WINBOOL Erase)
982 {
983   return NtUserRedrawWindow(hWnd, Rect, 0, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
984 }
985
986 DWORD
987 STDCALL
988 NtUserInvalidateRgn(
989   HWND hWnd,
990   HRGN Rgn,
991   WINBOOL Erase)
992 {
993   return NtUserRedrawWindow(hWnd, NULL, Rgn, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
994 }
995
996 BOOL
997 STDCALL
998 NtUserValidateRgn(
999   HWND hWnd,
1000   HRGN hRgn)
1001 {
1002   return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_VALIDATE | RDW_NOCHILDREN);
1003 }
1004
1005 int
1006 STDCALL
1007 NtUserGetUpdateRgn(
1008   HWND hWnd,
1009   HRGN hRgn,
1010   WINBOOL bErase)
1011 {
1012   PWINDOW_OBJECT Window;
1013   int RegionType;
1014
1015   if (!(Window = IntGetWindowObject(hWnd)))
1016   {
1017     SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
1018     return ERROR;
1019   }
1020     
1021   if (NULL == Window->UpdateRegion)
1022     {
1023       RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
1024     }
1025   else if ((HRGN) 1 == Window->UpdateRegion)
1026     {
1027       RegionType = (NtGdiSetRectRgn(hRgn,
1028                                    0, 0,
1029                                    Window->ClientRect.right - Window->ClientRect.left,
1030                                    Window->ClientRect.bottom - Window->ClientRect.top) ?
1031                     SIMPLEREGION : ERROR);
1032     }
1033   else
1034     {
1035       RegionType = NtGdiCombineRgn(hRgn, Window->UpdateRegion, hRgn, RGN_COPY);
1036       NtGdiOffsetRgn(hRgn, Window->WindowRect.left - Window->ClientRect.left,
1037                           Window->WindowRect.top - Window->ClientRect.top );
1038     }
1039
1040   if (bErase &&
1041       (SIMPLEREGION == RegionType || COMPLEXREGION == RegionType))
1042     {
1043       PaintRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN, 0);
1044     }
1045   
1046   IntReleaseWindowObject(Window);
1047
1048   return RegionType;
1049 }
1050
1051 /* EOF */