Fixed invalid window refcount handling in RDW_Paint().
[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             wndPtr = NULL;
600             for (ppWnd = list; *ppWnd; ppWnd++)
601             {
602                 WIN_UpdateWndPtr(&wndPtr,*ppWnd);
603                 if (!IsWindow(wndPtr->hwndSelf)) continue;
604                     if ( (wndPtr->dwStyle & WS_VISIBLE) &&
605                          (wndPtr->hrgnUpdate || (wndPtr->flags & WIN_INTERNAL_PAINT)) )
606                         hrgn = RDW_Paint( wndPtr, hrgn, flags, ex );
607             }
608             WIN_ReleaseWndPtr(wndPtr);
609             WIN_ReleaseWinArray(list);
610         }
611     }
612
613     return hrgn;
614 }
615
616 /***********************************************************************
617  *           PAINT_RedrawWindow
618  *
619  */
620 BOOL PAINT_RedrawWindow( HWND hwnd, const RECT *rectUpdate,
621                            HRGN hrgnUpdate, UINT flags, UINT ex )
622 {
623     HRGN hRgn = 0;
624     RECT r, r2;
625     POINT pt;
626     WND* wndPtr;
627
628     if (!hwnd) hwnd = GetDesktopWindow();
629     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE;
630
631     /* check if the window or its parents are visible/not minimized */
632
633     if (!WIN_IsWindowDrawable( wndPtr, !(flags & RDW_FRAME) ) )
634     {
635         WIN_ReleaseWndPtr(wndPtr);
636         return TRUE; 
637     }
638
639     if (TRACE_ON(win))
640     {
641         if( hrgnUpdate )
642         {
643             GetRgnBox( hrgnUpdate, &r );
644             TRACE_(win)( "%04x (%04x) NULL %04x box (%i,%i-%i,%i) flags=%04x, exflags=%04x\n", 
645                   hwnd, wndPtr->hrgnUpdate, hrgnUpdate, r.left, r.top, r.right, r.bottom, flags, ex);
646         }
647         else
648         {
649             if( rectUpdate )
650                 r = *rectUpdate;
651             else
652                 SetRectEmpty( &r );
653             TRACE_(win)( "%04x (%04x) %s %d,%d-%d,%d %04x flags=%04x, exflags=%04x\n",
654                         hwnd, wndPtr->hrgnUpdate, rectUpdate ? "rect" : "NULL", r.left, 
655                         r.top, r.right, r.bottom, hrgnUpdate, flags, ex );
656         }
657     }
658
659     /* prepare an update region in window coordinates */
660
661     if( flags & RDW_FRAME )
662         r = wndPtr->rectWindow;
663     else
664         r = wndPtr->rectClient;
665
666     if( ex & RDW_EX_XYWINDOW )
667     {
668         pt.x = pt.y = 0;
669         OffsetRect( &r, -wndPtr->rectWindow.left, -wndPtr->rectWindow.top );
670     }
671     else
672     {
673         pt.x = wndPtr->rectClient.left - wndPtr->rectWindow.left;
674         pt.y = wndPtr->rectClient.top - wndPtr->rectWindow.top;
675         OffsetRect( &r, -wndPtr->rectClient.left, -wndPtr->rectClient.top );
676     }
677
678     if (flags & RDW_INVALIDATE)  /* ------------------------- Invalidate */
679     {
680         /* If the window doesn't have hrgnUpdate we leave hRgn zero
681          * and put a new region straight into wndPtr->hrgnUpdate
682          * so that RDW_UpdateRgns() won't have to do any extra work.
683          */
684
685         if( hrgnUpdate )
686         {
687             if( wndPtr->hrgnUpdate )
688                 hRgn = REGION_CropRgn( 0, hrgnUpdate, NULL, &pt );
689             else 
690                 wndPtr->hrgnUpdate = REGION_CropRgn( 0, hrgnUpdate, &r, &pt ); 
691         }
692         else if( rectUpdate )
693         {
694             if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
695             OffsetRect( &r2, pt.x, pt.y );
696
697 rect2i:
698             if( wndPtr->hrgnUpdate == 0 )
699                 wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r2 );
700             else
701                 hRgn = CreateRectRgnIndirect( &r2 );
702         }
703         else /* entire window or client depending on RDW_FRAME */
704         {
705             if( flags & RDW_FRAME )
706             {
707                 if( wndPtr->hrgnUpdate )
708                     DeleteObject( wndPtr->hrgnUpdate );
709                 wndPtr->hrgnUpdate = 1;
710             }
711             else
712             {
713                 GETCLIENTRECTW( wndPtr, r2 );
714                 goto rect2i;
715             }
716         }
717     }
718     else if (flags & RDW_VALIDATE)  /* ------------------------- Validate */
719     {
720         /* In this we cannot leave with zero hRgn */
721         if( hrgnUpdate )
722         {
723             hRgn = REGION_CropRgn( hRgn, hrgnUpdate,  &r, &pt );
724             GetRgnBox( hRgn, &r2 );
725             if( IsRectEmpty( &r2 ) ) goto END;
726         }
727         else if( rectUpdate )
728         {
729             if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
730                 OffsetRect( &r2, pt.x, pt.y );
731 rect2v:
732             hRgn = CreateRectRgnIndirect( &r2 );
733         }
734         else /* entire window or client depending on RDW_FRAME */
735         {
736             if( flags & RDW_FRAME ) 
737                 hRgn = 1;
738             else
739             {
740                 GETCLIENTRECTW( wndPtr, r2 );
741                 goto rect2v;
742             }
743         }
744     }
745
746     /* At this point hRgn is either an update region in window coordinates or 1 or 0 */
747
748     RDW_UpdateRgns( wndPtr, hRgn, flags );
749
750     /* Erase/update windows, from now on hRgn is a scratch region */
751
752     hRgn = RDW_Paint( wndPtr, (hRgn == 1) ? 0 : hRgn, flags, ex );
753
754 END:
755     if( hRgn > 1 && (hRgn != hrgnUpdate) )
756         DeleteObject(hRgn );
757     WIN_ReleaseWndPtr(wndPtr);
758     return TRUE;
759 }
760
761
762 /***********************************************************************
763  *           RedrawWindow32    (USER32.426)
764  */
765 BOOL WINAPI RedrawWindow( HWND hwnd, const RECT *rectUpdate,
766                               HRGN hrgnUpdate, UINT flags )
767 {
768     return PAINT_RedrawWindow( hwnd, rectUpdate, hrgnUpdate, flags, 0 );
769 }
770
771
772 /***********************************************************************
773  *           RedrawWindow16    (USER.290)
774  */
775 BOOL16 WINAPI RedrawWindow16( HWND16 hwnd, const RECT16 *rectUpdate,
776                               HRGN16 hrgnUpdate, UINT16 flags )
777 {
778     if (rectUpdate)
779     {
780         RECT r;
781         CONV_RECT16TO32( rectUpdate, &r );
782         return (BOOL16)RedrawWindow( (HWND)hwnd, &r, hrgnUpdate, flags );
783     }
784     return (BOOL16)PAINT_RedrawWindow( (HWND)hwnd, NULL, 
785                                        (HRGN)hrgnUpdate, flags, 0 );
786 }
787
788
789 /***********************************************************************
790  *           UpdateWindow16   (USER.124)
791  */
792 void WINAPI UpdateWindow16( HWND16 hwnd )
793 {
794     PAINT_RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_NOCHILDREN, 0 );
795 }
796
797 /***********************************************************************
798  *           UpdateWindow32   (USER32.567)
799  */
800 void WINAPI UpdateWindow( HWND hwnd )
801 {
802     PAINT_RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_NOCHILDREN, 0 );
803 }
804
805 /***********************************************************************
806  *           InvalidateRgn16   (USER.126)
807  */
808 void WINAPI InvalidateRgn16( HWND16 hwnd, HRGN16 hrgn, BOOL16 erase )
809 {
810     PAINT_RedrawWindow((HWND)hwnd, NULL, (HRGN)hrgn, 
811                        RDW_INVALIDATE | (erase ? RDW_ERASE : 0), 0 );
812 }
813
814
815 /***********************************************************************
816  *           InvalidateRgn32   (USER32.329)
817  */
818 BOOL WINAPI InvalidateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
819 {
820     return PAINT_RedrawWindow(hwnd, NULL, hrgn, RDW_INVALIDATE | (erase ? RDW_ERASE : 0), 0 );
821 }
822
823
824 /***********************************************************************
825  *           InvalidateRect16   (USER.125)
826  */
827 void WINAPI InvalidateRect16( HWND16 hwnd, const RECT16 *rect, BOOL16 erase )
828 {
829     RedrawWindow16( hwnd, rect, 0, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
830 }
831
832
833 /***********************************************************************
834  *           InvalidateRect32   (USER32.328)
835  */
836 BOOL WINAPI InvalidateRect( HWND hwnd, const RECT *rect, BOOL erase )
837 {
838     return PAINT_RedrawWindow( hwnd, rect, 0, 
839                                RDW_INVALIDATE | (erase ? RDW_ERASE : 0), 0 );
840 }
841
842
843 /***********************************************************************
844  *           ValidateRgn16   (USER.128)
845  */
846 void WINAPI ValidateRgn16( HWND16 hwnd, HRGN16 hrgn )
847 {
848     PAINT_RedrawWindow( (HWND)hwnd, NULL, (HRGN)hrgn, 
849                         RDW_VALIDATE | RDW_NOCHILDREN, 0 );
850 }
851
852
853 /***********************************************************************
854  *           ValidateRgn32   (USER32.572)
855  */
856 void WINAPI ValidateRgn( HWND hwnd, HRGN hrgn )
857 {
858     PAINT_RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOCHILDREN, 0 );
859 }
860
861
862 /***********************************************************************
863  *           ValidateRect16   (USER.127)
864  */
865 void WINAPI ValidateRect16( HWND16 hwnd, const RECT16 *rect )
866 {
867     RedrawWindow16( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN );
868 }
869
870
871 /***********************************************************************
872  *           ValidateRect32   (USER32.571)
873  */
874 void WINAPI ValidateRect( HWND hwnd, const RECT *rect )
875 {
876     PAINT_RedrawWindow( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN, 0 );
877 }
878
879
880 /***********************************************************************
881  *           GetUpdateRect16   (USER.190)
882  */
883 BOOL16 WINAPI GetUpdateRect16( HWND16 hwnd, LPRECT16 rect, BOOL16 erase )
884 {
885     RECT r;
886     BOOL16 ret;
887
888     if (!rect) return GetUpdateRect( hwnd, NULL, erase );
889     ret = GetUpdateRect( hwnd, &r, erase );
890     CONV_RECT32TO16( &r, rect );
891     return ret;
892 }
893
894
895 /***********************************************************************
896  *           GetUpdateRect32   (USER32.297)
897  */
898 BOOL WINAPI GetUpdateRect( HWND hwnd, LPRECT rect, BOOL erase )
899 {
900     BOOL retvalue;
901     WND * wndPtr = WIN_FindWndPtr( hwnd );
902     if (!wndPtr) return FALSE;
903
904     if (rect)
905     {
906         if (wndPtr->hrgnUpdate > 1)
907         {
908             HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
909             if (GetUpdateRgn( hwnd, hrgn, erase ) == ERROR)
910             {
911                 retvalue = FALSE;
912                 goto END;
913             }
914             GetRgnBox( hrgn, rect );
915             DeleteObject( hrgn );
916             if (wndPtr->class->style & CS_OWNDC)
917             {
918                 if (GetMapMode(wndPtr->dce->hDC) != MM_TEXT)
919                 {
920                     DPtoLP (wndPtr->dce->hDC, (LPPOINT)rect,  2);
921                 }
922             }
923         }
924         else
925         if( wndPtr->hrgnUpdate == 1 )
926         {
927             GetClientRect( hwnd, rect );
928             if (erase) RedrawWindow( hwnd, NULL, 0, RDW_FRAME | RDW_ERASENOW | RDW_NOCHILDREN );
929         }
930         else 
931             SetRectEmpty( rect );
932     }
933     retvalue = (wndPtr->hrgnUpdate >= 1);
934 END:
935     WIN_ReleaseWndPtr(wndPtr);
936     return retvalue;
937 }
938
939
940 /***********************************************************************
941  *           GetUpdateRgn16   (USER.237)
942  */
943 INT16 WINAPI GetUpdateRgn16( HWND16 hwnd, HRGN16 hrgn, BOOL16 erase )
944 {
945     return GetUpdateRgn( hwnd, hrgn, erase );
946 }
947
948
949 /***********************************************************************
950  *           GetUpdateRgn    (USER32.298)
951  */
952 INT WINAPI GetUpdateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
953 {
954     INT retval;
955     WND * wndPtr = WIN_FindWndPtr( hwnd );
956     if (!wndPtr) return ERROR;
957
958     if (wndPtr->hrgnUpdate == 0)
959     {
960         SetRectRgn( hrgn, 0, 0, 0, 0 );
961         retval = NULLREGION;
962         goto END;
963     }
964     else
965     if (wndPtr->hrgnUpdate == 1)
966     {
967         SetRectRgn( hrgn, 0, 0, wndPtr->rectClient.right - wndPtr->rectClient.left,
968                                 wndPtr->rectClient.bottom - wndPtr->rectClient.top );
969         retval = SIMPLEREGION;
970     }
971     else
972     {
973         retval = CombineRgn( hrgn, wndPtr->hrgnUpdate, 0, RGN_COPY );
974         OffsetRgn( hrgn, wndPtr->rectWindow.left - wndPtr->rectClient.left,
975                          wndPtr->rectWindow.top - wndPtr->rectClient.top );
976     }
977     if (erase) RedrawWindow( hwnd, NULL, 0, RDW_ERASENOW | RDW_NOCHILDREN );
978 END:
979     WIN_ReleaseWndPtr(wndPtr);
980     return retval;
981 }
982
983
984 /***********************************************************************
985  *           ExcludeUpdateRgn16   (USER.238)
986  */
987 INT16 WINAPI ExcludeUpdateRgn16( HDC16 hdc, HWND16 hwnd )
988 {
989     return ExcludeUpdateRgn( hdc, hwnd );
990 }
991
992
993 /***********************************************************************
994  *           ExcludeUpdateRgn32   (USER32.195)
995  */
996 INT WINAPI ExcludeUpdateRgn( HDC hdc, HWND hwnd )
997 {
998     RECT rect;
999     WND * wndPtr;
1000
1001     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return ERROR;
1002
1003     if (wndPtr->hrgnUpdate)
1004     {
1005         INT ret;
1006         HRGN hrgn = CreateRectRgn(wndPtr->rectWindow.left - wndPtr->rectClient.left,
1007                                       wndPtr->rectWindow.top - wndPtr->rectClient.top,
1008                                       wndPtr->rectWindow.right - wndPtr->rectClient.left,
1009                                       wndPtr->rectWindow.bottom - wndPtr->rectClient.top);
1010         if( wndPtr->hrgnUpdate > 1 )
1011         {
1012             CombineRgn(hrgn, wndPtr->hrgnUpdate, 0, RGN_COPY);
1013             OffsetRgn(hrgn, wndPtr->rectWindow.left - wndPtr->rectClient.left, 
1014                             wndPtr->rectWindow.top - wndPtr->rectClient.top );
1015         }
1016
1017         /* do ugly coordinate translations in dce.c */
1018
1019         ret = DCE_ExcludeRgn( hdc, wndPtr, hrgn );
1020         DeleteObject( hrgn );
1021         WIN_ReleaseWndPtr(wndPtr);
1022         return ret;
1023     } 
1024     WIN_ReleaseWndPtr(wndPtr);
1025     return GetClipBox( hdc, &rect );
1026 }
1027
1028