Rearranged PAINT_RedrawWindow() so that it first recursively updates
[wine] / windows / painting.c
1 /*
2  * Window painting functions
3  *
4  * Copyright 1993, 1994, 1995 Alexandre Julliard
5  *                       1999 Alex Korobka
6  */
7
8 #include "region.h"
9 #include "win.h"
10 #include "queue.h"
11 #include "dce.h"
12 #include "heap.h"
13 #include "debugtools.h"
14 #include "wine/winuser16.h"
15
16 DECLARE_DEBUG_CHANNEL(nonclient)
17 DECLARE_DEBUG_CHANNEL(win)
18
19 /* client rect in window coordinates */
20
21 #define GETCLIENTRECTW( wnd, r )        (r).left = (wnd)->rectClient.left - (wnd)->rectWindow.left; \
22                                         (r).top = (wnd)->rectClient.top - (wnd)->rectWindow.top; \
23                                         (r).right = (wnd)->rectClient.right - (wnd)->rectWindow.left; \
24                                         (r).bottom = (wnd)->rectClient.bottom - (wnd)->rectWindow.top
25
26   /* Last CTLCOLOR id */
27 #define CTLCOLOR_MAX   CTLCOLOR_STATIC
28
29
30 /***********************************************************************
31  *           WIN_UpdateNCRgn
32  *
33  *  Things to do:
34  *      Send WM_NCPAINT if required (when nonclient is invalid or UNC_ENTIRE flag is set)
35  *      Crop hrgnUpdate to a client rect, especially if it 1.
36  *      If UNC_REGION is set return update region for the client rect.
37  *
38  *  NOTE: UNC_REGION is mainly for the RDW_Paint() chunk that sends WM_ERASEBKGND message.
39  *        The trick is that when the returned region handle may be different from hRgn.
40  *        In this case the old hRgn must be considered gone. BUT, if the returned value
41  *        is 1 then the hRgn is preserved and RDW_Paint() will have to get 
42  *        a DC without extra clipping region.
43  */
44 HRGN WIN_UpdateNCRgn(WND* wnd, HRGN hRgn, UINT uncFlags )
45 {
46     RECT  r;
47     HRGN  hClip = 0;
48     HRGN  hrgnRet = 0;
49
50     TRACE_(nonclient)("hwnd %04x [%04x] hrgn %04x, unc %04x, ncf %i\n", 
51                       wnd->hwndSelf, wnd->hrgnUpdate, hRgn, uncFlags, wnd->flags & WIN_NEEDS_NCPAINT);
52
53     /* desktop window doesn't have a nonclient area */
54     if(wnd == WIN_GetDesktop()) 
55     {
56         wnd->flags &= ~WIN_NEEDS_NCPAINT;
57         if( wnd->hrgnUpdate > 1 )
58             hrgnRet = REGION_CropRgn( hRgn, wnd->hrgnUpdate, NULL, NULL );
59         else 
60         {
61             hrgnRet = wnd->hrgnUpdate;
62         }
63         WIN_ReleaseDesktop();
64         return hrgnRet;
65     }
66     WIN_ReleaseDesktop();
67
68     if ((wnd->hwndSelf == GetForegroundWindow()) &&
69         !(wnd->flags & WIN_NCACTIVATED) )
70     {
71         wnd->flags |= WIN_NCACTIVATED;
72         uncFlags |= UNC_ENTIRE; 
73     }
74
75     if( wnd->flags & WIN_NEEDS_NCPAINT )
76     {
77             RECT r2, r3;
78
79             wnd->flags &= ~WIN_NEEDS_NCPAINT;  
80             GETCLIENTRECTW( wnd, r );
81
82             TRACE_(nonclient)( "\tclient box (%i,%i-%i,%i), hrgnUpdate %04x\n", 
83                                 r.left, r.top, r.right, r.bottom, wnd->hrgnUpdate );
84             if( wnd->hrgnUpdate > 1 )
85             {
86                 /* Check if update rgn overlaps with nonclient area */
87
88                 GetRgnBox( wnd->hrgnUpdate, &r2 );
89                 UnionRect( &r3, &r2, &r );
90                 if( r3.left != r.left || r3.top != r.top || 
91                     r3.right != r.right || r3.bottom != r.bottom ) /* it does */
92                 {
93                     /* crop hrgnUpdate, save old one in hClip - the only
94                      * case that places a valid region handle in hClip */
95
96                     hClip = wnd->hrgnUpdate;
97                     wnd->hrgnUpdate = REGION_CropRgn( hRgn, hClip, &r, NULL );
98                     if( uncFlags & UNC_REGION ) hrgnRet = hClip;
99                 }
100
101                 if( uncFlags & UNC_CHECK )
102                 {
103                     GetRgnBox( wnd->hrgnUpdate, &r3 );
104                     if( IsRectEmpty( &r3 ) )
105                     {
106                         /* delete the update region since all invalid 
107                          * parts were in the nonclient area */
108
109                         DeleteObject( wnd->hrgnUpdate );
110                         wnd->hrgnUpdate = 0;
111                         if(!(wnd->flags & WIN_INTERNAL_PAINT))
112                             QUEUE_DecPaintCount( wnd->hmemTaskQ );
113
114                         wnd->flags &= ~WIN_NEEDS_ERASEBKGND;
115                     }
116                 }
117
118                 if(!hClip && wnd->hrgnUpdate ) goto copyrgn;
119             }
120             else 
121             if( wnd->hrgnUpdate == 1 )/* entire window */
122             {
123                 if( uncFlags & UNC_UPDATE ) wnd->hrgnUpdate = CreateRectRgnIndirect( &r );
124                 if( uncFlags & UNC_REGION ) hrgnRet = 1;
125                 uncFlags |= UNC_ENTIRE;
126             }
127     }
128     else /* no WM_NCPAINT unless forced */
129     {
130         if( wnd->hrgnUpdate >  1 )
131         {
132 copyrgn:
133             if( uncFlags & UNC_REGION )
134                 hrgnRet = REGION_CropRgn( hRgn, wnd->hrgnUpdate, NULL, NULL );
135         }
136         else
137         if( wnd->hrgnUpdate == 1 && (uncFlags & UNC_UPDATE) )
138         {
139             GETCLIENTRECTW( wnd, r ); 
140             wnd->hrgnUpdate = CreateRectRgnIndirect( &r );
141             if( uncFlags & UNC_REGION ) hrgnRet = 1;
142         }
143     }
144
145     if(!hClip && (uncFlags & UNC_ENTIRE) )
146     {
147         /* still don't do anything if there is no nonclient area */
148         hClip = (memcmp( &wnd->rectWindow, &wnd->rectClient, sizeof(RECT) ) != 0);
149     }
150
151     if( hClip ) /* NOTE: WM_NCPAINT allows wParam to be 1 */
152     {
153         SendMessageA( wnd->hwndSelf, WM_NCPAINT, hClip, 0L );
154         if( (hClip > 1)&& (hClip != hRgn) && (hClip != hrgnRet) ) DeleteObject( hClip );
155     }
156
157     TRACE_(nonclient)("returning %04x (hClip = %04x, hrgnUpdate = %04x)\n", hrgnRet, hClip, wnd->hrgnUpdate );
158
159     return hrgnRet;
160 }
161
162
163 /***********************************************************************
164  *           BeginPaint16    (USER.39)
165  */
166 HDC16 WINAPI BeginPaint16( HWND16 hwnd, LPPAINTSTRUCT16 lps ) 
167 {
168     BOOL bIcon;
169     HRGN hrgnUpdate;
170     WND *wndPtr = WIN_FindWndPtr( hwnd );
171     if (!wndPtr) return 0;
172
173     bIcon = (wndPtr->dwStyle & WS_MINIMIZE && wndPtr->class->hIcon);
174
175     wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
176
177     /* send WM_NCPAINT and make sure hrgnUpdate is a valid rgn handle */
178     WIN_UpdateNCRgn( wndPtr, 0, UNC_UPDATE );
179
180     if( ((hrgnUpdate = wndPtr->hrgnUpdate) != 0) || (wndPtr->flags & WIN_INTERNAL_PAINT))
181         QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
182
183     wndPtr->hrgnUpdate = 0;
184     wndPtr->flags &= ~WIN_INTERNAL_PAINT;
185
186     HideCaret( hwnd );
187
188     TRACE_(win)("hrgnUpdate = %04x, \n", hrgnUpdate);
189
190     if (wndPtr->class->style & CS_PARENTDC)
191     {
192         /* Don't clip the output to the update region for CS_PARENTDC window */
193         if( hrgnUpdate ) 
194             DeleteObject(hrgnUpdate);
195         lps->hdc = GetDCEx16( hwnd, 0, DCX_WINDOWPAINT | DCX_USESTYLE |
196                               (bIcon ? DCX_WINDOW : 0) );
197     }
198     else
199     {
200         if( hrgnUpdate ) /* convert to client coordinates */
201             OffsetRgn( hrgnUpdate, wndPtr->rectWindow.left - wndPtr->rectClient.left,
202                                    wndPtr->rectWindow.top - wndPtr->rectClient.top );
203         lps->hdc = GetDCEx16(hwnd, hrgnUpdate, DCX_INTERSECTRGN |
204                              DCX_WINDOWPAINT | DCX_USESTYLE | (bIcon ? DCX_WINDOW : 0) );
205         /* ReleaseDC() in EndPaint() will delete the region */
206     }
207
208     TRACE_(win)("hdc = %04x\n", lps->hdc);
209
210     if (!lps->hdc)
211     {
212         WARN_(win)("GetDCEx() failed in BeginPaint(), hwnd=%04x\n", hwnd);
213         WIN_ReleaseWndPtr(wndPtr);
214         return 0;
215     }
216
217     GetClipBox16( lps->hdc, &lps->rcPaint );
218
219     TRACE_(win)("box = (%i,%i - %i,%i)\n", lps->rcPaint.left, lps->rcPaint.top,
220                     lps->rcPaint.right, lps->rcPaint.bottom );
221
222     if (wndPtr->flags & WIN_NEEDS_ERASEBKGND)
223     {
224         wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
225         lps->fErase = !SendMessage16(hwnd, (bIcon) ? WM_ICONERASEBKGND
226                                                    : WM_ERASEBKGND,
227                                      (WPARAM16)lps->hdc, 0 );
228     }
229     else lps->fErase = TRUE;
230
231     WIN_ReleaseWndPtr(wndPtr);
232     return lps->hdc;
233 }
234
235
236 /***********************************************************************
237  *           BeginPaint32    (USER32.10)
238  */
239 HDC WINAPI BeginPaint( HWND hwnd, PAINTSTRUCT *lps )
240 {
241     PAINTSTRUCT16 ps;
242
243     BeginPaint16( hwnd, &ps );
244     lps->hdc            = (HDC)ps.hdc;
245     lps->fErase         = ps.fErase;
246     lps->rcPaint.top    = ps.rcPaint.top;
247     lps->rcPaint.left   = ps.rcPaint.left;
248     lps->rcPaint.right  = ps.rcPaint.right;
249     lps->rcPaint.bottom = ps.rcPaint.bottom;
250     lps->fRestore       = ps.fRestore;
251     lps->fIncUpdate     = ps.fIncUpdate;
252     return lps->hdc;
253 }
254
255
256 /***********************************************************************
257  *           EndPaint16    (USER.40)
258  */
259 BOOL16 WINAPI EndPaint16( HWND16 hwnd, const PAINTSTRUCT16* lps )
260 {
261     ReleaseDC16( hwnd, lps->hdc );
262     ShowCaret( hwnd );
263     return TRUE;
264 }
265
266
267 /***********************************************************************
268  *           EndPaint32    (USER32.176)
269  */
270 BOOL WINAPI EndPaint( HWND hwnd, const PAINTSTRUCT *lps )
271 {
272     ReleaseDC( hwnd, lps->hdc );
273     ShowCaret( hwnd );
274     return TRUE;
275 }
276
277
278 /***********************************************************************
279  *           FillWindow    (USER.324)
280  */
281 void WINAPI FillWindow16( HWND16 hwndParent, HWND16 hwnd, HDC16 hdc, HBRUSH16 hbrush )
282 {
283     RECT16 rect;
284     GetClientRect16( hwnd, &rect );
285     DPtoLP16( hdc, (LPPOINT16)&rect, 2 );
286     PaintRect16( hwndParent, hwnd, hdc, hbrush, &rect );
287 }
288
289
290 /***********************************************************************
291  *           PAINT_GetControlBrush
292  */
293 static HBRUSH16 PAINT_GetControlBrush( HWND hParent, HWND hWnd, HDC16 hDC, UINT16 ctlType )
294 {
295     HBRUSH16 bkgBrush = (HBRUSH16)SendMessageA( hParent, WM_CTLCOLORMSGBOX + ctlType, 
296                                                              (WPARAM)hDC, (LPARAM)hWnd );
297     if( !IsGDIObject16(bkgBrush) )
298         bkgBrush = DEFWND_ControlColor( hDC, ctlType );
299     return bkgBrush;
300 }
301
302
303 /***********************************************************************
304  *           PaintRect    (USER.325)
305  */
306 void WINAPI PaintRect16( HWND16 hwndParent, HWND16 hwnd, HDC16 hdc,
307                        HBRUSH16 hbrush, const RECT16 *rect)
308 {
309     if( hbrush <= CTLCOLOR_MAX ) 
310     {
311         if( hwndParent )
312             hbrush = PAINT_GetControlBrush( hwndParent, hwnd, hdc, (UINT16)hbrush );
313         else 
314             return;
315     }
316     if( hbrush ) 
317         FillRect16( hdc, rect, hbrush );
318 }
319
320
321 /***********************************************************************
322  *           GetControlBrush    (USER.326)
323  */
324 HBRUSH16 WINAPI GetControlBrush16( HWND16 hwnd, HDC16 hdc, UINT16 ctlType )
325 {
326     WND* wndPtr = WIN_FindWndPtr( hwnd );
327     HBRUSH16 retvalue;
328
329     if((ctlType <= CTLCOLOR_MAX) && wndPtr )
330     {
331         WND* parent;
332         if( wndPtr->dwStyle & WS_POPUP ) parent = WIN_LockWndPtr(wndPtr->owner);
333         else parent = WIN_LockWndPtr(wndPtr->parent);
334         if( !parent ) parent = wndPtr;
335         retvalue = (HBRUSH16)PAINT_GetControlBrush( parent->hwndSelf, hwnd, hdc, ctlType );
336         WIN_ReleaseWndPtr(parent);
337         goto END;
338     }
339     retvalue = (HBRUSH16)0;
340 END:
341     WIN_ReleaseWndPtr(wndPtr);
342     return retvalue;
343 }
344
345 int i;
346
347 /***********************************************************************
348  *              RDW_UpdateRgns [RedrawWindow() helper] 
349  *
350  *  Walks the window tree and adds/removes parts of the hRgn to/from update
351  *  regions of windows that overlap it. Also, manages internal paint flags.
352  *
353  *  NOTE: Walks the window tree so the caller must lock it.
354  *        MUST preserve hRgn (can modify but then has to restore).
355  */
356 static void RDW_UpdateRgns( WND* wndPtr, HRGN hRgn, UINT flags )
357 {
358     /* 
359      * Called only when one of the following is set:
360      * (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT)
361      */
362
363     BOOL bHadOne =  wndPtr->hrgnUpdate && hRgn;
364     RECT r = {0, 0, wndPtr->rectWindow.right - wndPtr->rectWindow.left,
365                     wndPtr->rectWindow.bottom - wndPtr->rectWindow.top };
366     BOOL bChildren =  ( wndPtr->child && !(flags & RDW_NOCHILDREN) && !(wndPtr->dwStyle & WS_MINIMIZE) 
367                         && ((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) );
368
369     TRACE_(win)("\thwnd %04x [%04x] -> hrgn [%04x], flags [%04x]\n", wndPtr->hwndSelf, wndPtr->hrgnUpdate, hRgn, flags );
370
371     if( flags & RDW_INVALIDATE )
372     {
373         if( hRgn > 1 )
374         {
375             switch( wndPtr->hrgnUpdate )
376             {
377                 default:
378                         CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate, hRgn, RGN_OR );
379                         /* fall through */
380                 case 0:
381                         wndPtr->hrgnUpdate = REGION_CropRgn( wndPtr->hrgnUpdate, 
382                                                              wndPtr->hrgnUpdate ? wndPtr->hrgnUpdate : hRgn, 
383                                                              &r, NULL );
384                         if( !bHadOne )
385                         {
386                             GetRgnBox( wndPtr->hrgnUpdate, &r );
387                             if( IsRectEmpty( &r ) )
388                             {
389                                 DeleteObject( wndPtr->hrgnUpdate );
390                                 wndPtr->hrgnUpdate = 0;
391                                 goto OUT;
392                             }
393                         }
394                         break;
395
396                 case 1: /* already an entire window */
397             }
398         }
399         else if( hRgn == 1 )
400         {
401             if( wndPtr->hrgnUpdate > 1 )
402                 DeleteObject( wndPtr->hrgnUpdate );
403             wndPtr->hrgnUpdate = 1;
404         }
405         else
406             hRgn = wndPtr->hrgnUpdate;  /* this is a trick that depends on code in PAINT_RedrawWindow() */
407
408         if( !bHadOne && !(wndPtr->flags & WIN_INTERNAL_PAINT) )
409             QUEUE_IncPaintCount( wndPtr->hmemTaskQ );
410
411         if (flags & RDW_FRAME) wndPtr->flags |= WIN_NEEDS_NCPAINT;
412         if (flags & RDW_ERASE) wndPtr->flags |= WIN_NEEDS_ERASEBKGND;
413         flags    |= RDW_FRAME;
414     }
415     else if( flags & RDW_VALIDATE )
416     {
417         if( wndPtr->hrgnUpdate )
418         {
419             if( hRgn > 1 )
420             {
421                 if( wndPtr->hrgnUpdate == 1 )
422                     wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r );
423
424                 if( CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate, hRgn, RGN_DIFF )
425                     == NULLREGION )
426                     goto EMPTY;
427             }
428             else /* validate everything */
429             {
430                 if( wndPtr->hrgnUpdate > 1 )
431                 {
432 EMPTY:
433                     DeleteObject( wndPtr->hrgnUpdate );
434                 }
435                 wndPtr->hrgnUpdate = 0;
436             }
437
438             if( !wndPtr->hrgnUpdate )
439             {
440                 wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
441                 if( !(wndPtr->flags & WIN_INTERNAL_PAINT) )
442                     QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
443             }
444         }
445
446         if (flags & RDW_NOFRAME) wndPtr->flags &= ~WIN_NEEDS_NCPAINT;
447         if (flags & RDW_NOERASE) wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
448     }
449
450     /* in/validate child windows that intersect with the region if it
451      * is a valid handle. */
452
453     if( flags & (RDW_INVALIDATE | RDW_VALIDATE) )
454     {
455         if( hRgn > 1 && bChildren )
456         {
457             WND* wnd = wndPtr->child;
458             POINT ptClient = { wndPtr->rectClient.left - wndPtr->rectWindow.left,
459                                wndPtr->rectClient.top - wndPtr->rectWindow.top };
460             POINT ptTotal, prevOrigin = {0,0};
461
462             for( ptTotal.x = ptTotal.y = 0; wnd; wnd = wnd->next )
463             {
464                 if( wnd->dwStyle & WS_VISIBLE )
465                 {
466                     POINT ptOffset;
467
468                     r.left = wnd->rectWindow.left + ptClient.x;
469                     r.right = wnd->rectWindow.right + ptClient.x;
470                     r.top = wnd->rectWindow.top + ptClient.y;
471                     r.bottom = wnd->rectWindow.bottom + ptClient.y;
472
473                     ptOffset.x = r.left - prevOrigin.x; 
474                     ptOffset.y = r.top - prevOrigin.y;
475                     OffsetRect( &r, -ptTotal.x, -ptTotal.y );
476
477                     if( RectInRegion( hRgn, &r ) )
478                     {
479                         OffsetRgn( hRgn, -ptOffset.x, -ptOffset.y );
480                         RDW_UpdateRgns( wnd, hRgn, flags );
481                         prevOrigin.x = r.left + ptTotal.x;
482                         prevOrigin.y = r.top + ptTotal.y;
483                         ptTotal.x += ptOffset.x;
484                         ptTotal.y += ptOffset.y;
485                     }
486                 }
487             }
488             OffsetRgn( hRgn, ptTotal.x, ptTotal.y );
489             bChildren = 0;
490         }
491     }
492
493     /* handle hRgn == 1 (alias for entire window) and/or internal paint recursion */
494
495     if( bChildren )
496     {
497         WND* wnd;
498         for( wnd = wndPtr->child; wnd; wnd = wnd->next )
499              if( wnd->dwStyle & WS_VISIBLE )
500                  RDW_UpdateRgns( wnd, hRgn, flags );
501     }
502
503 OUT:
504
505     /* Set/clear internal paint flag */
506
507     if (flags & RDW_INTERNALPAINT)
508     {
509         if ( !wndPtr->hrgnUpdate && !(wndPtr->flags & WIN_INTERNAL_PAINT))
510             QUEUE_IncPaintCount( wndPtr->hmemTaskQ );
511         wndPtr->flags |= WIN_INTERNAL_PAINT;
512     }
513     else if (flags & RDW_NOINTERNALPAINT)
514     {
515         if ( !wndPtr->hrgnUpdate && (wndPtr->flags & WIN_INTERNAL_PAINT))
516             QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
517         wndPtr->flags &= ~WIN_INTERNAL_PAINT;
518     }
519 }
520
521 /***********************************************************************
522  *           RDW_Paint [RedrawWindow() helper]
523  *
524  * Walks the window tree and paints/erases windows that have
525  * nonzero update regions according to redraw flags. hrgn is a scratch
526  * region passed down during recursion. Must not be 1.
527  *
528  */
529 static HRGN RDW_Paint( WND* wndPtr, HRGN hrgn, UINT flags, UINT ex )
530 {
531 /* NOTE: wndPtr is locked by caller.
532  * 
533  * FIXME: Windows uses WM_SYNCPAINT to cut down the number of intertask
534  * SendMessage() calls. This is a comment inside DefWindowProc() source
535  * from 16-bit SDK:
536  *
537  *   This message avoids lots of inter-app message traffic
538  *   by switching to the other task and continuing the
539  *   recursion there.
540  *
541  * wParam         = flags
542  * LOWORD(lParam) = hrgnClip
543  * HIWORD(lParam) = hwndSkip  (not used; always NULL)
544  *
545  */
546     HDC  hDC;
547     HWND hWnd = wndPtr->hwndSelf;
548     BOOL bIcon = ((wndPtr->dwStyle & WS_MINIMIZE) && wndPtr->class->hIcon); 
549
550       /* Erase/update the window itself ... */
551
552     TRACE_(win)("\thwnd %04x [%04x] -> hrgn [%04x], flags [%04x]\n", hWnd, wndPtr->hrgnUpdate, hrgn, flags );
553
554     if (flags & RDW_UPDATENOW)
555     {
556         if (wndPtr->hrgnUpdate) /* wm_painticon wparam is 1 */
557             SendMessage16( hWnd, (bIcon) ? WM_PAINTICON : WM_PAINT, bIcon, 0 );
558     }
559     else if ((flags & RDW_ERASENOW) || (ex & RDW_EX_TOPFRAME))
560     {
561         UINT dcx = DCX_INTERSECTRGN | DCX_USESTYLE | DCX_KEEPCLIPRGN | DCX_WINDOWPAINT;
562         HRGN hrgnRet = WIN_UpdateNCRgn( wndPtr, hrgn, UNC_REGION | UNC_CHECK | 
563                             ((ex & RDW_EX_TOPFRAME) ? UNC_ENTIRE : 0) ); 
564         if( hrgnRet )
565         {
566             if( hrgnRet > 1 ) hrgn = hrgnRet; else hrgnRet = 0; /* entire client */
567             if( wndPtr->flags & WIN_NEEDS_ERASEBKGND )
568             {
569                 if( bIcon ) dcx |= DCX_WINDOW;
570                 else 
571                 if( hrgnRet )
572                     OffsetRgn( hrgnRet, wndPtr->rectWindow.left - wndPtr->rectClient.left, 
573                                         wndPtr->rectWindow.top  - wndPtr->rectClient.top);
574                 else
575                     dcx &= ~DCX_INTERSECTRGN;
576                 if (( hDC = GetDCEx( hWnd, hrgnRet, dcx )) )
577                 {
578                     if (SendMessage16( hWnd, (bIcon) ? WM_ICONERASEBKGND
579                                        : WM_ERASEBKGND, (WPARAM16)hDC, 0 ))
580                     wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
581                     ReleaseDC( hWnd, hDC );
582                 }
583             }
584         }
585     }
586
587     if( !IsWindow(hWnd) ) return hrgn;
588     ex &= ~RDW_EX_TOPFRAME;
589
590       /* ... and its child windows */
591
592     if( wndPtr->child && !(flags & RDW_NOCHILDREN) && !(wndPtr->dwStyle & WS_MINIMIZE) 
593         && ((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) )
594     {
595         WND** list, **ppWnd;
596
597         if( (list = WIN_BuildWinArray( wndPtr, 0, NULL )) )
598         {
599             for (ppWnd = list; *ppWnd; ppWnd++)
600             {
601                 WIN_UpdateWndPtr(&wndPtr,*ppWnd);
602                 if (!IsWindow(wndPtr->hwndSelf)) continue;
603                     if ( (wndPtr->dwStyle & WS_VISIBLE) &&
604                          (wndPtr->hrgnUpdate || (wndPtr->flags & WIN_INTERNAL_PAINT)) )
605                         hrgn = RDW_Paint( wndPtr, hrgn, flags, ex );
606             }
607             WIN_ReleaseWinArray(list);
608         }
609     }
610
611     return hrgn;
612 }
613
614 /***********************************************************************
615  *           PAINT_RedrawWindow
616  *
617  */
618 BOOL PAINT_RedrawWindow( HWND hwnd, const RECT *rectUpdate,
619                            HRGN hrgnUpdate, UINT flags, UINT ex )
620 {
621     HRGN hRgn = 0;
622     RECT r, r2;
623     POINT pt;
624     WND* wndPtr;
625
626     if (!hwnd) hwnd = GetDesktopWindow();
627     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE;
628
629     /* check if the window or its parents are visible/not minimized */
630
631     if (!WIN_IsWindowDrawable( wndPtr, !(flags & RDW_FRAME) ) )
632     {
633         WIN_ReleaseWndPtr(wndPtr);
634         return TRUE; 
635     }
636
637     if (TRACE_ON(win))
638     {
639         if( hrgnUpdate )
640         {
641             GetRgnBox( hrgnUpdate, &r );
642             TRACE_(win)( "%04x (%04x) NULL %04x box (%i,%i-%i,%i) flags=%04x, exflags=%04x\n", 
643                   hwnd, wndPtr->hrgnUpdate, hrgnUpdate, r.left, r.top, r.right, r.bottom, flags, ex);
644         }
645         else
646         {
647             if( rectUpdate )
648                 r = *rectUpdate;
649             else
650                 SetRectEmpty( &r );
651             TRACE_(win)( "%04x (%04x) %s %d,%d-%d,%d %04x flags=%04x, exflags=%04x\n",
652                         hwnd, wndPtr->hrgnUpdate, rectUpdate ? "rect" : "NULL", r.left, 
653                         r.top, r.right, r.bottom, hrgnUpdate, flags, ex );
654         }
655     }
656
657     /* prepare an update region in window coordinates */
658
659     if( flags & RDW_FRAME )
660         r = wndPtr->rectWindow;
661     else
662         r = wndPtr->rectClient;
663
664     if( ex & RDW_EX_XYWINDOW )
665     {
666         pt.x = pt.y = 0;
667         OffsetRect( &r, -wndPtr->rectWindow.left, -wndPtr->rectWindow.top );
668     }
669     else
670     {
671         pt.x = wndPtr->rectClient.left - wndPtr->rectWindow.left;
672         pt.y = wndPtr->rectClient.top - wndPtr->rectWindow.top;
673         OffsetRect( &r, -wndPtr->rectClient.left, -wndPtr->rectClient.top );
674     }
675
676     if (flags & RDW_INVALIDATE)  /* ------------------------- Invalidate */
677     {
678         /* If the window doesn't have hrgnUpdate we leave hRgn zero
679          * and put a new region straight into wndPtr->hrgnUpdate
680          * so that RDW_UpdateRgns() won't have to do any extra work.
681          */
682
683         if( hrgnUpdate )
684         {
685             if( wndPtr->hrgnUpdate )
686                 hRgn = REGION_CropRgn( 0, hrgnUpdate, NULL, &pt );
687             else 
688                 wndPtr->hrgnUpdate = REGION_CropRgn( 0, hrgnUpdate, &r, &pt ); 
689         }
690         else if( rectUpdate )
691         {
692             if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
693             OffsetRect( &r2, pt.x, pt.y );
694
695 rect2i:
696             if( wndPtr->hrgnUpdate == 0 )
697                 wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r2 );
698             else
699                 hRgn = CreateRectRgnIndirect( &r2 );
700         }
701         else /* entire window or client depending on RDW_FRAME */
702         {
703             if( flags & RDW_FRAME )
704             {
705                 if( wndPtr->hrgnUpdate )
706                     DeleteObject( wndPtr->hrgnUpdate );
707                 wndPtr->hrgnUpdate = 1;
708             }
709             else
710             {
711                 GETCLIENTRECTW( wndPtr, r2 );
712                 goto rect2i;
713             }
714         }
715     }
716     else if (flags & RDW_VALIDATE)  /* ------------------------- Validate */
717     {
718         /* In this we cannot leave with zero hRgn */
719         if( hrgnUpdate )
720         {
721             hRgn = REGION_CropRgn( hRgn, hrgnUpdate,  &r, &pt );
722             GetRgnBox( hRgn, &r2 );
723             if( IsRectEmpty( &r2 ) ) goto END;
724         }
725         else if( rectUpdate )
726         {
727             if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
728                 OffsetRect( &r2, pt.x, pt.y );
729 rect2v:
730             hRgn = CreateRectRgnIndirect( &r2 );
731         }
732         else /* entire window or client depending on RDW_FRAME */
733         {
734             if( flags & RDW_FRAME ) 
735                 hRgn = 1;
736             else
737             {
738                 GETCLIENTRECTW( wndPtr, r2 );
739                 goto rect2v;
740             }
741         }
742     }
743
744     /* At this point hRgn is either an update region in window coordinates or 1 or 0 */
745
746     RDW_UpdateRgns( wndPtr, hRgn, flags );
747
748     /* Erase/update windows, from now on hRgn is a scratch region */
749
750     hRgn = RDW_Paint( wndPtr, (hRgn == 1) ? 0 : hRgn, flags, ex );
751
752 END:
753     if( hRgn > 1 && (hRgn != hrgnUpdate) )
754         DeleteObject(hRgn );
755     WIN_ReleaseWndPtr(wndPtr);
756     return TRUE;
757 }
758
759
760 /***********************************************************************
761  *           RedrawWindow32    (USER32.426)
762  */
763 BOOL WINAPI RedrawWindow( HWND hwnd, const RECT *rectUpdate,
764                               HRGN hrgnUpdate, UINT flags )
765 {
766     return PAINT_RedrawWindow( hwnd, rectUpdate, hrgnUpdate, flags, 0 );
767 }
768
769
770 /***********************************************************************
771  *           RedrawWindow16    (USER.290)
772  */
773 BOOL16 WINAPI RedrawWindow16( HWND16 hwnd, const RECT16 *rectUpdate,
774                               HRGN16 hrgnUpdate, UINT16 flags )
775 {
776     if (rectUpdate)
777     {
778         RECT r;
779         CONV_RECT16TO32( rectUpdate, &r );
780         return (BOOL16)RedrawWindow( (HWND)hwnd, &r, hrgnUpdate, flags );
781     }
782     return (BOOL16)PAINT_RedrawWindow( (HWND)hwnd, NULL, 
783                                        (HRGN)hrgnUpdate, flags, 0 );
784 }
785
786
787 /***********************************************************************
788  *           UpdateWindow16   (USER.124)
789  */
790 void WINAPI UpdateWindow16( HWND16 hwnd )
791 {
792     PAINT_RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_NOCHILDREN, 0 );
793 }
794
795 /***********************************************************************
796  *           UpdateWindow32   (USER32.567)
797  */
798 void WINAPI UpdateWindow( HWND hwnd )
799 {
800     PAINT_RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_NOCHILDREN, 0 );
801 }
802
803 /***********************************************************************
804  *           InvalidateRgn16   (USER.126)
805  */
806 void WINAPI InvalidateRgn16( HWND16 hwnd, HRGN16 hrgn, BOOL16 erase )
807 {
808     PAINT_RedrawWindow((HWND)hwnd, NULL, (HRGN)hrgn, 
809                        RDW_INVALIDATE | (erase ? RDW_ERASE : 0), 0 );
810 }
811
812
813 /***********************************************************************
814  *           InvalidateRgn32   (USER32.329)
815  */
816 BOOL WINAPI InvalidateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
817 {
818     return PAINT_RedrawWindow(hwnd, NULL, hrgn, RDW_INVALIDATE | (erase ? RDW_ERASE : 0), 0 );
819 }
820
821
822 /***********************************************************************
823  *           InvalidateRect16   (USER.125)
824  */
825 void WINAPI InvalidateRect16( HWND16 hwnd, const RECT16 *rect, BOOL16 erase )
826 {
827     RedrawWindow16( hwnd, rect, 0, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
828 }
829
830
831 /***********************************************************************
832  *           InvalidateRect32   (USER32.328)
833  */
834 BOOL WINAPI InvalidateRect( HWND hwnd, const RECT *rect, BOOL erase )
835 {
836     return PAINT_RedrawWindow( hwnd, rect, 0, 
837                                RDW_INVALIDATE | (erase ? RDW_ERASE : 0), 0 );
838 }
839
840
841 /***********************************************************************
842  *           ValidateRgn16   (USER.128)
843  */
844 void WINAPI ValidateRgn16( HWND16 hwnd, HRGN16 hrgn )
845 {
846     PAINT_RedrawWindow( (HWND)hwnd, NULL, (HRGN)hrgn, 
847                         RDW_VALIDATE | RDW_NOCHILDREN, 0 );
848 }
849
850
851 /***********************************************************************
852  *           ValidateRgn32   (USER32.572)
853  */
854 void WINAPI ValidateRgn( HWND hwnd, HRGN hrgn )
855 {
856     PAINT_RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOCHILDREN, 0 );
857 }
858
859
860 /***********************************************************************
861  *           ValidateRect16   (USER.127)
862  */
863 void WINAPI ValidateRect16( HWND16 hwnd, const RECT16 *rect )
864 {
865     RedrawWindow16( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN );
866 }
867
868
869 /***********************************************************************
870  *           ValidateRect32   (USER32.571)
871  */
872 void WINAPI ValidateRect( HWND hwnd, const RECT *rect )
873 {
874     PAINT_RedrawWindow( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN, 0 );
875 }
876
877
878 /***********************************************************************
879  *           GetUpdateRect16   (USER.190)
880  */
881 BOOL16 WINAPI GetUpdateRect16( HWND16 hwnd, LPRECT16 rect, BOOL16 erase )
882 {
883     RECT r;
884     BOOL16 ret;
885
886     if (!rect) return GetUpdateRect( hwnd, NULL, erase );
887     ret = GetUpdateRect( hwnd, &r, erase );
888     CONV_RECT32TO16( &r, rect );
889     return ret;
890 }
891
892
893 /***********************************************************************
894  *           GetUpdateRect32   (USER32.297)
895  */
896 BOOL WINAPI GetUpdateRect( HWND hwnd, LPRECT rect, BOOL erase )
897 {
898     BOOL retvalue;
899     WND * wndPtr = WIN_FindWndPtr( hwnd );
900     if (!wndPtr) return FALSE;
901
902     if (rect)
903     {
904         if (wndPtr->hrgnUpdate > 1)
905         {
906             HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
907             if (GetUpdateRgn( hwnd, hrgn, erase ) == ERROR)
908             {
909                 retvalue = FALSE;
910                 goto END;
911             }
912             GetRgnBox( hrgn, rect );
913             DeleteObject( hrgn );
914             if (wndPtr->class->style & CS_OWNDC)
915             {
916                 if (GetMapMode(wndPtr->dce->hDC) != MM_TEXT)
917                 {
918                     DPtoLP (wndPtr->dce->hDC, (LPPOINT)rect,  2);
919                 }
920             }
921         }
922         else
923         if( wndPtr->hrgnUpdate == 1 )
924         {
925             GetClientRect( hwnd, rect );
926             if (erase) RedrawWindow( hwnd, NULL, 0, RDW_FRAME | RDW_ERASENOW | RDW_NOCHILDREN );
927         }
928         else 
929             SetRectEmpty( rect );
930     }
931     retvalue = (wndPtr->hrgnUpdate >= 1);
932 END:
933     WIN_ReleaseWndPtr(wndPtr);
934     return retvalue;
935 }
936
937
938 /***********************************************************************
939  *           GetUpdateRgn16   (USER.237)
940  */
941 INT16 WINAPI GetUpdateRgn16( HWND16 hwnd, HRGN16 hrgn, BOOL16 erase )
942 {
943     return GetUpdateRgn( hwnd, hrgn, erase );
944 }
945
946
947 /***********************************************************************
948  *           GetUpdateRgn    (USER32.298)
949  */
950 INT WINAPI GetUpdateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
951 {
952     INT retval;
953     WND * wndPtr = WIN_FindWndPtr( hwnd );
954     if (!wndPtr) return ERROR;
955
956     if (wndPtr->hrgnUpdate == 0)
957     {
958         SetRectRgn( hrgn, 0, 0, 0, 0 );
959         retval = NULLREGION;
960         goto END;
961     }
962     else
963     if (wndPtr->hrgnUpdate == 1)
964     {
965         SetRectRgn( hrgn, 0, 0, wndPtr->rectClient.right - wndPtr->rectClient.left,
966                                 wndPtr->rectClient.bottom - wndPtr->rectClient.top );
967         retval = SIMPLEREGION;
968     }
969     else
970     {
971         retval = CombineRgn( hrgn, wndPtr->hrgnUpdate, 0, RGN_COPY );
972         OffsetRgn( hrgn, wndPtr->rectWindow.left - wndPtr->rectClient.left,
973                          wndPtr->rectWindow.top - wndPtr->rectClient.top );
974     }
975     if (erase) RedrawWindow( hwnd, NULL, 0, RDW_ERASENOW | RDW_NOCHILDREN );
976 END:
977     WIN_ReleaseWndPtr(wndPtr);
978     return retval;
979 }
980
981
982 /***********************************************************************
983  *           ExcludeUpdateRgn16   (USER.238)
984  */
985 INT16 WINAPI ExcludeUpdateRgn16( HDC16 hdc, HWND16 hwnd )
986 {
987     return ExcludeUpdateRgn( hdc, hwnd );
988 }
989
990
991 /***********************************************************************
992  *           ExcludeUpdateRgn32   (USER32.195)
993  */
994 INT WINAPI ExcludeUpdateRgn( HDC hdc, HWND hwnd )
995 {
996     RECT rect;
997     WND * wndPtr;
998
999     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return ERROR;
1000
1001     if (wndPtr->hrgnUpdate)
1002     {
1003         INT ret;
1004         HRGN hrgn = CreateRectRgn(wndPtr->rectWindow.left - wndPtr->rectClient.left,
1005                                       wndPtr->rectWindow.top - wndPtr->rectClient.top,
1006                                       wndPtr->rectWindow.right - wndPtr->rectClient.left,
1007                                       wndPtr->rectWindow.bottom - wndPtr->rectClient.top);
1008         if( wndPtr->hrgnUpdate > 1 )
1009         {
1010             CombineRgn(hrgn, wndPtr->hrgnUpdate, 0, RGN_COPY);
1011             OffsetRgn(hrgn, wndPtr->rectWindow.left - wndPtr->rectClient.left, 
1012                             wndPtr->rectWindow.top - wndPtr->rectClient.top );
1013         }
1014
1015         /* do ugly coordinate translations in dce.c */
1016
1017         ret = DCE_ExcludeRgn( hdc, wndPtr, hrgn );
1018         DeleteObject( hrgn );
1019         WIN_ReleaseWndPtr(wndPtr);
1020         return ret;
1021     } 
1022     WIN_ReleaseWndPtr(wndPtr);
1023     return GetClipBox( hdc, &rect );
1024 }
1025
1026