Added WIN_ListParents function and renamed WIN_BuildWinArray into
[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 <string.h>
9 #include "windef.h"
10 #include "wingdi.h"
11 #include "wine/winuser16.h"
12 #include "wine/unicode.h"
13 #include "wine/server.h"
14 #include "region.h"
15 #include "user.h"
16 #include "win.h"
17 #include "queue.h"
18 #include "dce.h"
19 #include "debugtools.h"
20
21 DEFAULT_DEBUG_CHANNEL(win);
22 DECLARE_DEBUG_CHANNEL(nonclient);
23
24 /* client rect in window coordinates */
25
26 #define GETCLIENTRECTW( wnd, r )        (r).left = (wnd)->rectClient.left - (wnd)->rectWindow.left; \
27                                         (r).top = (wnd)->rectClient.top - (wnd)->rectWindow.top; \
28                                         (r).right = (wnd)->rectClient.right - (wnd)->rectWindow.left; \
29                                         (r).bottom = (wnd)->rectClient.bottom - (wnd)->rectWindow.top
30
31   /* PAINT_RedrawWindow() control flags */
32 #define RDW_EX_DELAY_NCPAINT    0x0020
33
34   /* WIN_UpdateNCRgn() flags */
35 #define UNC_CHECK               0x0001
36 #define UNC_ENTIRE              0x0002
37 #define UNC_REGION              0x0004
38 #define UNC_UPDATE              0x0008
39 #define UNC_DELAY_NCPAINT       0x0010
40 #define UNC_IN_BEGINPAINT       0x0020
41
42   /* Last COLOR id */
43 #define COLOR_MAX   COLOR_GRADIENTINACTIVECAPTION
44
45   /* Last CTLCOLOR id */
46 #define CTLCOLOR_MAX   CTLCOLOR_STATIC
47
48
49 /***********************************************************************
50  *           add_paint_count
51  *
52  * Add an increment (normally 1 or -1) to the current paint count of a window.
53  */
54 static void add_paint_count( HWND hwnd, int incr )
55 {
56     SERVER_START_REQ( inc_queue_paint_count )
57     {
58         req->id   = (void *)GetWindowThreadProcessId( hwnd, NULL );
59         req->incr = incr;
60         SERVER_CALL();
61     }
62     SERVER_END_REQ;
63 }
64
65
66 /***********************************************************************
67  *           WIN_HaveToDelayNCPAINT
68  *
69  * Currently, in the Wine painting mechanism, the WM_NCPAINT message
70  * is generated as soon as a region intersecting the non-client area 
71  * of a window is invalidated.
72  *
73  * This technique will work fine for all windows whose parents
74  * have the WS_CLIPCHILDREN style. When the parents have that style,
75  * they are not going to override the contents of their children.
76  * However, when the parent doesn't have that style, Windows relies
77  * on a "painter's algorithm" to display the contents of the windows.
78  * That is, windows are painted from back to front. This includes the
79  * non-client area.
80  *
81  * This method looks at the current state of a window to determine
82  * if the sending of the WM_NCPAINT message should be delayed until 
83  * the BeginPaint call.
84  *
85  * PARAMS:
86  *   wndPtr   - A Locked window pointer to the window we're
87  *              examining.
88  *   uncFlags - This is the flag passed to the WIN_UpdateNCRgn
89  *              function. This is a shortcut for the cases when
90  *              we already know when to avoid scanning all the
91  *              parents of a window. If you already know that this
92  *              window's NCPAINT should be delayed, set the 
93  *              UNC_DELAY_NCPAINT flag for this parameter. 
94  *
95  *              This shortcut behavior is implemented in the
96  *              RDW_Paint() method.
97  * 
98  */
99 static BOOL WIN_HaveToDelayNCPAINT( HWND hwnd, UINT uncFlags)
100 {
101   /*
102    * Test the shortcut first. (duh)
103    */
104   if (uncFlags & UNC_DELAY_NCPAINT)
105     return TRUE;
106
107   /*
108    * The UNC_IN_BEGINPAINT flag is set in the BeginPaint
109    * method only. This is another shortcut to avoid going
110    * up the parent's chain of the window to finally
111    * figure-out that none of them has an invalid region.
112    */
113   if (uncFlags & UNC_IN_BEGINPAINT)
114     return FALSE;
115
116   /*
117    * Scan all the parents of this window to find a window
118    * that doesn't have the WS_CLIPCHILDREN style and that
119    * has an invalid region. 
120    */
121   while ((hwnd = GetAncestor( hwnd, GA_PARENT )))
122   {
123       WND* parentWnd = WIN_FindWndPtr( hwnd );
124       if (!(parentWnd->dwStyle & WS_CLIPCHILDREN) && parentWnd->hrgnUpdate)
125       {
126           WIN_ReleaseWndPtr( parentWnd );
127           return TRUE;
128       }
129       WIN_ReleaseWndPtr( parentWnd );
130   }
131   return FALSE;
132 }
133
134 /***********************************************************************
135  *           WIN_UpdateNCRgn
136  *
137  *  Things to do:
138  *      Send WM_NCPAINT if required (when nonclient is invalid or UNC_ENTIRE flag is set)
139  *      Crop hrgnUpdate to a client rect, especially if it 1.
140  *      If UNC_REGION is set return update region for the client rect.
141  *
142  *  NOTE: UNC_REGION is mainly for the RDW_Paint() chunk that sends WM_ERASEBKGND message.
143  *        The trick is that when the returned region handle may be different from hRgn.
144  *        In this case the old hRgn must be considered gone. BUT, if the returned value
145  *        is 1 then the hRgn is preserved and RDW_Paint() will have to get 
146  *        a DC without extra clipping region.
147  */
148 static HRGN WIN_UpdateNCRgn(WND* wnd, HRGN hRgn, UINT uncFlags )
149 {
150     RECT  r;
151     HRGN  hClip = 0;
152     HRGN  hrgnRet = 0;
153
154     TRACE_(nonclient)("hwnd %04x [%04x] hrgn %04x, unc %04x, ncf %i\n", 
155                       wnd->hwndSelf, wnd->hrgnUpdate, hRgn, uncFlags, wnd->flags & WIN_NEEDS_NCPAINT);
156
157     /* desktop window doesn't have a nonclient area */
158     if(wnd->hwndSelf == GetDesktopWindow())
159     {
160         wnd->flags &= ~WIN_NEEDS_NCPAINT;
161         if( wnd->hrgnUpdate > 1 )
162             hrgnRet = REGION_CropRgn( hRgn, wnd->hrgnUpdate, NULL, NULL );
163         else 
164         {
165             hrgnRet = wnd->hrgnUpdate;
166         }
167         return hrgnRet;
168     }
169
170     if ((wnd->hwndSelf == GetForegroundWindow()) &&
171         !(wnd->flags & WIN_NCACTIVATED) )
172     {
173         wnd->flags |= WIN_NCACTIVATED;
174         uncFlags |= UNC_ENTIRE; 
175     }
176
177     /*
178      * If the window's non-client area needs to be painted, 
179      */
180     if ( ( wnd->flags & WIN_NEEDS_NCPAINT ) &&
181          !WIN_HaveToDelayNCPAINT(wnd->hwndSelf, uncFlags) )
182     {
183             RECT r2, r3;
184
185             wnd->flags &= ~WIN_NEEDS_NCPAINT;  
186             GETCLIENTRECTW( wnd, r );
187
188             TRACE_(nonclient)( "\tclient box (%i,%i-%i,%i), hrgnUpdate %04x\n", 
189                                 r.left, r.top, r.right, r.bottom, wnd->hrgnUpdate );
190             if( wnd->hrgnUpdate > 1 )
191             {
192                 /* Check if update rgn overlaps with nonclient area */
193
194                 GetRgnBox( wnd->hrgnUpdate, &r2 );
195                 UnionRect( &r3, &r2, &r );
196                 if( r3.left != r.left || r3.top != r.top || 
197                     r3.right != r.right || r3.bottom != r.bottom ) /* it does */
198                 {
199                     /* crop hrgnUpdate, save old one in hClip - the only
200                      * case that places a valid region handle in hClip */
201
202                     hClip = wnd->hrgnUpdate;
203                     wnd->hrgnUpdate = REGION_CropRgn( hRgn, hClip, &r, NULL );
204                     if( uncFlags & UNC_REGION ) hrgnRet = hClip;
205                 }
206
207                 if( uncFlags & UNC_CHECK )
208                 {
209                     GetRgnBox( wnd->hrgnUpdate, &r3 );
210                     if( IsRectEmpty( &r3 ) )
211                     {
212                         /* delete the update region since all invalid 
213                          * parts were in the nonclient area */
214
215                         DeleteObject( wnd->hrgnUpdate );
216                         wnd->hrgnUpdate = 0;
217                         if(!(wnd->flags & WIN_INTERNAL_PAINT))
218                             add_paint_count( wnd->hwndSelf, -1 );
219
220                         wnd->flags &= ~WIN_NEEDS_ERASEBKGND;
221                     }
222                 }
223
224                 if(!hClip && wnd->hrgnUpdate ) goto copyrgn;
225             }
226             else 
227             if( wnd->hrgnUpdate == 1 )/* entire window */
228             {
229                 if( uncFlags & UNC_UPDATE ) wnd->hrgnUpdate = CreateRectRgnIndirect( &r );
230                 if( uncFlags & UNC_REGION ) hrgnRet = 1;
231                 uncFlags |= UNC_ENTIRE;
232             }
233     }
234     else /* no WM_NCPAINT unless forced */
235     {
236         if( wnd->hrgnUpdate >  1 )
237         {
238 copyrgn:
239             if( uncFlags & UNC_REGION )
240                 hrgnRet = REGION_CropRgn( hRgn, wnd->hrgnUpdate, NULL, NULL );
241         }
242         else
243         if( wnd->hrgnUpdate == 1 && (uncFlags & UNC_UPDATE) )
244         {
245             GETCLIENTRECTW( wnd, r ); 
246             wnd->hrgnUpdate = CreateRectRgnIndirect( &r );
247             if( uncFlags & UNC_REGION ) hrgnRet = 1;
248         }
249     }
250
251     if(!hClip && (uncFlags & UNC_ENTIRE) )
252     {
253         /* still don't do anything if there is no nonclient area */
254         hClip = (memcmp( &wnd->rectWindow, &wnd->rectClient, sizeof(RECT) ) != 0);
255     }
256
257     if( hClip ) /* NOTE: WM_NCPAINT allows wParam to be 1 */
258     {
259         if ( hClip == hrgnRet && hrgnRet > 1 ) {
260             hClip = CreateRectRgn( 0, 0, 0, 0 );
261             CombineRgn( hClip, hrgnRet, 0, RGN_COPY );
262         }
263
264         SendMessageA( wnd->hwndSelf, WM_NCPAINT, hClip, 0L );
265         if( (hClip > 1) && (hClip != hRgn) && (hClip != hrgnRet) )
266             DeleteObject( hClip );
267         /*
268          * Since all Window locks are suspended while processing the WM_NCPAINT
269          * we want to make sure the window still exists before continuing.
270          */
271         if (!IsWindow(wnd->hwndSelf))
272         {
273           DeleteObject(hrgnRet);
274           hrgnRet=0;
275         }
276     }
277
278     TRACE_(nonclient)("returning %04x (hClip = %04x, hrgnUpdate = %04x)\n", hrgnRet, hClip, wnd->hrgnUpdate );
279
280     return hrgnRet;
281 }
282
283
284 /***********************************************************************
285  *              BeginPaint (USER.39)
286  */
287 HDC16 WINAPI BeginPaint16( HWND16 hwnd, LPPAINTSTRUCT16 lps ) 
288 {
289     PAINTSTRUCT ps;
290
291     BeginPaint( hwnd, &ps );
292     lps->hdc            = ps.hdc;
293     lps->fErase         = ps.fErase;
294     lps->rcPaint.top    = ps.rcPaint.top;
295     lps->rcPaint.left   = ps.rcPaint.left;
296     lps->rcPaint.right  = ps.rcPaint.right;
297     lps->rcPaint.bottom = ps.rcPaint.bottom;
298     lps->fRestore       = ps.fRestore;
299     lps->fIncUpdate     = ps.fIncUpdate;
300     return lps->hdc;
301 }
302
303
304 /***********************************************************************
305  *              BeginPaint (USER32.@)
306  */
307 HDC WINAPI BeginPaint( HWND hwnd, PAINTSTRUCT *lps )
308 {
309     BOOL bIcon;
310     HRGN hrgnUpdate;
311     RECT clipRect, clientRect;
312     WND *wndPtr = WIN_FindWndPtr( hwnd );
313     if (!wndPtr) return 0;
314
315     bIcon = (wndPtr->dwStyle & WS_MINIMIZE && GetClassLongA(hwnd, GCL_HICON));
316
317     wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
318
319     /* send WM_NCPAINT and make sure hrgnUpdate is a valid rgn handle */
320     WIN_UpdateNCRgn( wndPtr, 0, UNC_UPDATE | UNC_IN_BEGINPAINT);
321
322     /*
323      * Make sure the window is still a window. All window locks are suspended
324      * when the WM_NCPAINT is sent.
325      */
326     if (!IsWindow(wndPtr->hwndSelf))
327     {
328         WIN_ReleaseWndPtr(wndPtr);
329         return 0;
330     }
331
332     if( ((hrgnUpdate = wndPtr->hrgnUpdate) != 0) || (wndPtr->flags & WIN_INTERNAL_PAINT))
333         add_paint_count( hwnd, -1 );
334
335     wndPtr->hrgnUpdate = 0;
336     wndPtr->flags &= ~WIN_INTERNAL_PAINT;
337
338     HideCaret( hwnd );
339
340     TRACE("hrgnUpdate = %04x, \n", hrgnUpdate);
341
342     if (GetClassLongA(wndPtr->hwndSelf, GCL_STYLE) & CS_PARENTDC)
343     {
344         /* Don't clip the output to the update region for CS_PARENTDC window */
345         if( hrgnUpdate ) 
346             DeleteObject(hrgnUpdate);
347         lps->hdc = GetDCEx( hwnd, 0, DCX_WINDOWPAINT | DCX_USESTYLE |
348                               (bIcon ? DCX_WINDOW : 0) );
349     }
350     else
351     {
352         if( hrgnUpdate ) /* convert to client coordinates */
353             OffsetRgn( hrgnUpdate, wndPtr->rectWindow.left - wndPtr->rectClient.left,
354                                    wndPtr->rectWindow.top - wndPtr->rectClient.top );
355         lps->hdc = GetDCEx(hwnd, hrgnUpdate, DCX_INTERSECTRGN | 
356                              DCX_WINDOWPAINT | DCX_USESTYLE | (bIcon ? DCX_WINDOW : 0) );
357         /* ReleaseDC() in EndPaint() will delete the region */
358     }
359
360     TRACE("hdc = %04x\n", lps->hdc);
361
362     if (!lps->hdc)
363     {
364         WARN("GetDCEx() failed in BeginPaint(), hwnd=%04x\n", hwnd);
365         WIN_ReleaseWndPtr(wndPtr);
366         return 0;
367     }
368
369     /* It is possible that the clip box is bigger than the window itself,
370        if CS_PARENTDC flag is set. Windows never return a paint rect bigger
371        than the window itself, so we need to intersect the cliprect with
372        the window  */
373     
374     GetClientRect( hwnd, &clientRect );
375
376     GetClipBox( lps->hdc, &clipRect );
377     LPtoDP(lps->hdc, (LPPOINT)&clipRect, 2);      /* GetClipBox returns LP */
378
379     IntersectRect(&lps->rcPaint, &clientRect, &clipRect);
380     DPtoLP(lps->hdc, (LPPOINT)&lps->rcPaint, 2);  /* we must return LP */
381
382     TRACE("box = (%i,%i - %i,%i)\n", lps->rcPaint.left, lps->rcPaint.top,
383                     lps->rcPaint.right, lps->rcPaint.bottom );
384
385     if (wndPtr->flags & WIN_NEEDS_ERASEBKGND)
386     {
387         wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
388         lps->fErase = !SendMessageA(hwnd, (bIcon) ? WM_ICONERASEBKGND : WM_ERASEBKGND,
389                                     (WPARAM16)lps->hdc, 0 );
390     }
391     else lps->fErase = TRUE;
392
393     WIN_ReleaseWndPtr(wndPtr);
394     return lps->hdc;
395 }
396
397
398 /***********************************************************************
399  *              EndPaint (USER.40)
400  */
401 BOOL16 WINAPI EndPaint16( HWND16 hwnd, const PAINTSTRUCT16* lps )
402 {
403     ReleaseDC16( hwnd, lps->hdc );
404     ShowCaret( hwnd );
405     return TRUE;
406 }
407
408
409 /***********************************************************************
410  *              EndPaint (USER32.@)
411  */
412 BOOL WINAPI EndPaint( HWND hwnd, const PAINTSTRUCT *lps )
413 {
414     ReleaseDC( hwnd, lps->hdc );
415     ShowCaret( hwnd );
416     return TRUE;
417 }
418
419
420 /***********************************************************************
421  *              FillWindow (USER.324)
422  */
423 void WINAPI FillWindow16( HWND16 hwndParent, HWND16 hwnd, HDC16 hdc, HBRUSH16 hbrush )
424 {
425     RECT rect;
426     RECT16 rc16;
427     GetClientRect( hwnd, &rect );
428     DPtoLP( hdc, (LPPOINT)&rect, 2 );
429     CONV_RECT32TO16( &rect, &rc16 );
430     PaintRect16( hwndParent, hwnd, hdc, hbrush, &rc16 );
431 }
432
433
434 /***********************************************************************
435  *           PAINT_GetControlBrush
436  */
437 static HBRUSH16 PAINT_GetControlBrush( HWND hParent, HWND hWnd, HDC16 hDC, UINT16 ctlType )
438 {
439     HBRUSH16 bkgBrush = (HBRUSH16)SendMessageA( hParent, WM_CTLCOLORMSGBOX + ctlType, 
440                                                              (WPARAM)hDC, (LPARAM)hWnd );
441     if( !IsGDIObject16(bkgBrush) )
442         bkgBrush = DEFWND_ControlColor( hDC, ctlType );
443     return bkgBrush;
444 }
445
446
447 /***********************************************************************
448  *              PaintRect (USER.325)
449  */
450 void WINAPI PaintRect16( HWND16 hwndParent, HWND16 hwnd, HDC16 hdc,
451                        HBRUSH16 hbrush, const RECT16 *rect)
452 {
453     if( hbrush <= CTLCOLOR_MAX ) 
454     {
455         if( hwndParent )
456             hbrush = PAINT_GetControlBrush( hwndParent, hwnd, hdc, (UINT16)hbrush );
457         else 
458             return;
459     }
460     if( hbrush ) 
461         FillRect16( hdc, rect, hbrush );
462 }
463
464
465 /***********************************************************************
466  *              GetControlBrush (USER.326)
467  */
468 HBRUSH16 WINAPI GetControlBrush16( HWND16 hwnd, HDC16 hdc, UINT16 ctlType )
469 {
470     if (ctlType <= CTLCOLOR_MAX)
471     {
472         HWND16 parent = GetParent16( hwnd );
473         if (!parent) parent = hwnd;
474         return PAINT_GetControlBrush( parent, hwnd, hdc, ctlType );
475     }
476     return 0;
477 }
478
479
480 /***********************************************************************
481  *              RDW_ValidateParent [RDW_UpdateRgns() helper] 
482  *
483  *  Validate the portions of parents that are covered by a validated child
484  *  wndPtr = child
485  */
486 static void RDW_ValidateParent(WND *wndChild)
487 {
488     HWND parent;
489     HRGN hrg;
490
491     if (wndChild->hrgnUpdate == 1 ) {
492         RECT r;
493         r.left = 0;
494         r.top = 0;
495         r.right = wndChild->rectWindow.right - wndChild->rectWindow.left;
496         r.bottom = wndChild->rectWindow.bottom - wndChild->rectWindow.top;
497         hrg = CreateRectRgnIndirect( &r );
498     } else
499         hrg = wndChild->hrgnUpdate;
500
501     parent = GetAncestor( wndChild->hwndSelf, GA_PARENT );
502     while (parent && parent != GetDesktopWindow())
503     {
504         WND *wndParent = WIN_FindWndPtr( parent );
505         if (!(wndParent->dwStyle & WS_CLIPCHILDREN))
506         {
507             if (wndParent->hrgnUpdate != 0)
508             {
509                 POINT ptOffset;
510                 RECT rect, rectParent;
511                 if( wndParent->hrgnUpdate == 1 )
512                 {
513                    RECT r;
514
515                    r.left = 0;
516                    r.top = 0;
517                    r.right = wndParent->rectWindow.right - wndParent->rectWindow.left;
518                    r.bottom = wndParent->rectWindow.bottom - wndParent->rectWindow.top;
519
520                    wndParent->hrgnUpdate = CreateRectRgnIndirect( &r );
521                 }
522                 /* we must offset the child region by the offset of the child rect in the parent */
523                 GetWindowRect(wndParent->hwndSelf, &rectParent);
524                 GetWindowRect(wndChild->hwndSelf, &rect);
525                 ptOffset.x = rect.left - rectParent.left;
526                 ptOffset.y = rect.top - rectParent.top;
527                 OffsetRgn( hrg, ptOffset.x, ptOffset.y );
528                 CombineRgn( wndParent->hrgnUpdate, wndParent->hrgnUpdate, hrg, RGN_DIFF );
529                 OffsetRgn( hrg, -ptOffset.x, -ptOffset.y );
530             }
531         }
532         WIN_ReleaseWndPtr( wndParent );
533         parent = GetAncestor( parent, GA_PARENT );
534     }
535     if (hrg != wndChild->hrgnUpdate) DeleteObject( hrg );
536 }
537
538 /***********************************************************************
539  *              RDW_UpdateRgns [RedrawWindow() helper] 
540  *
541  *  Walks the window tree and adds/removes parts of the hRgn to/from update
542  *  regions of windows that overlap it. Also, manages internal paint flags.
543  *
544  *  NOTE: Walks the window tree so the caller must lock it.
545  *        MUST preserve hRgn (can modify but then has to restore).
546  */
547 static void RDW_UpdateRgns( WND* wndPtr, HRGN hRgn, UINT flags, BOOL firstRecursLevel )
548 {
549     /* 
550      * Called only when one of the following is set:
551      * (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT)
552      */
553
554     BOOL bHadOne =  wndPtr->hrgnUpdate && hRgn;
555     BOOL bChildren =  (!(flags & RDW_NOCHILDREN) && !(wndPtr->dwStyle & WS_MINIMIZE) &&
556                        ((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) );
557     RECT r;
558
559     r.left = 0;
560     r.top = 0;
561     r.right = wndPtr->rectWindow.right - wndPtr->rectWindow.left;
562     r.bottom = wndPtr->rectWindow.bottom - wndPtr->rectWindow.top;
563
564     TRACE("\thwnd %04x [%04x] -> hrgn [%04x], flags [%04x]\n", wndPtr->hwndSelf, wndPtr->hrgnUpdate, hRgn, flags );
565
566     if( flags & RDW_INVALIDATE )
567     {
568         if( hRgn > 1 )
569         {
570             switch( wndPtr->hrgnUpdate )
571             {
572                 default:
573                         CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate, hRgn, RGN_OR );
574                         /* fall through */
575                 case 0:
576                         wndPtr->hrgnUpdate = REGION_CropRgn( wndPtr->hrgnUpdate, 
577                                                              wndPtr->hrgnUpdate ? wndPtr->hrgnUpdate : hRgn, 
578                                                              &r, NULL );
579                         if( !bHadOne )
580                         {
581                             GetRgnBox( wndPtr->hrgnUpdate, &r );
582                             if( IsRectEmpty( &r ) )
583                             {
584                                 DeleteObject( wndPtr->hrgnUpdate );
585                                 wndPtr->hrgnUpdate = 0;
586                                 goto end;
587                             }
588                         }
589                         break;
590                 case 1: /* already an entire window */
591                         break;
592             }
593         }
594         else if( hRgn == 1 )
595         {
596             if( wndPtr->hrgnUpdate > 1 )
597                 DeleteObject( wndPtr->hrgnUpdate );
598             wndPtr->hrgnUpdate = 1;
599         }
600         else
601             hRgn = wndPtr->hrgnUpdate;  /* this is a trick that depends on code in PAINT_RedrawWindow() */
602
603         if( !bHadOne && !(wndPtr->flags & WIN_INTERNAL_PAINT) )
604             add_paint_count( wndPtr->hwndSelf, 1 );
605
606         if (flags & RDW_FRAME) wndPtr->flags |= WIN_NEEDS_NCPAINT;
607         if (flags & RDW_ERASE) wndPtr->flags |= WIN_NEEDS_ERASEBKGND;
608         flags    |= RDW_FRAME;
609     }
610     else if( flags & RDW_VALIDATE )
611     {
612         if( wndPtr->hrgnUpdate )
613         {
614             if( hRgn > 1 )
615             {
616                 if( wndPtr->hrgnUpdate == 1 )
617                     wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r );
618
619                 if( CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate, hRgn, RGN_DIFF )
620                     == NULLREGION )
621                 {
622                     DeleteObject( wndPtr->hrgnUpdate );
623                     wndPtr->hrgnUpdate = 0;
624                 }
625             }
626             else /* validate everything */
627             {
628                 if( wndPtr->hrgnUpdate > 1 ) DeleteObject( wndPtr->hrgnUpdate );
629                 wndPtr->hrgnUpdate = 0;
630             }
631
632             if( !wndPtr->hrgnUpdate )
633             {
634                 wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
635                 if( !(wndPtr->flags & WIN_INTERNAL_PAINT) )
636                     add_paint_count( wndPtr->hwndSelf, -1 );
637             }
638         }
639
640         if (flags & RDW_NOFRAME) wndPtr->flags &= ~WIN_NEEDS_NCPAINT;
641         if (flags & RDW_NOERASE) wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
642
643     }
644
645     if ((firstRecursLevel) && (wndPtr->hrgnUpdate != 0) && (flags & RDW_UPDATENOW))
646         RDW_ValidateParent(wndPtr); /* validate parent covered by region */
647
648     /* in/validate child windows that intersect with the region if it
649      * is a valid handle. */
650
651     if( flags & (RDW_INVALIDATE | RDW_VALIDATE) )
652     {
653         HWND *list;
654         if( hRgn > 1 && bChildren && (list = WIN_ListChildren( wndPtr->hwndSelf )))
655         {
656             POINT ptTotal, prevOrigin = {0,0};
657             POINT ptClient;
658             INT i;
659
660             ptClient.x = wndPtr->rectClient.left - wndPtr->rectWindow.left;
661             ptClient.y = wndPtr->rectClient.top - wndPtr->rectWindow.top;
662
663             for(i = ptTotal.x = ptTotal.y = 0; list[i]; i++)
664             {
665                 WND *wnd = WIN_FindWndPtr( list[i] );
666                 if (!wnd) continue;
667                 if( wnd->dwStyle & WS_VISIBLE )
668                 {
669                     POINT ptOffset;
670
671                     r.left = wnd->rectWindow.left + ptClient.x;
672                     r.right = wnd->rectWindow.right + ptClient.x;
673                     r.top = wnd->rectWindow.top + ptClient.y;
674                     r.bottom = wnd->rectWindow.bottom + ptClient.y;
675
676                     ptOffset.x = r.left - prevOrigin.x;
677                     ptOffset.y = r.top - prevOrigin.y;
678                     OffsetRect( &r, -ptTotal.x, -ptTotal.y );
679
680                     if( RectInRegion( hRgn, &r ) )
681                     {
682                         OffsetRgn( hRgn, -ptOffset.x, -ptOffset.y );
683                         RDW_UpdateRgns( wnd, hRgn, flags, FALSE );
684                         prevOrigin.x = r.left + ptTotal.x;
685                         prevOrigin.y = r.top + ptTotal.y;
686                         ptTotal.x += ptOffset.x;
687                         ptTotal.y += ptOffset.y;
688                     }
689                 }
690                 WIN_ReleaseWndPtr( wnd );
691             }
692             HeapFree( GetProcessHeap(), 0, list );
693             OffsetRgn( hRgn, ptTotal.x, ptTotal.y );
694             bChildren = 0;
695         }
696     }
697
698     /* handle hRgn == 1 (alias for entire window) and/or internal paint recursion */
699
700     if( bChildren )
701     {
702         HWND *list;
703         if ((list = WIN_ListChildren( wndPtr->hwndSelf )))
704         {
705             INT i;
706             for (i = 0; list[i]; i++)
707             {
708                 WND *wnd = WIN_FindWndPtr( list[i] );
709                 if (!wnd) continue;
710                 if( wnd->dwStyle & WS_VISIBLE )
711                     RDW_UpdateRgns( wnd, hRgn, flags, FALSE );
712                 WIN_ReleaseWndPtr( wnd );
713             }
714             HeapFree( GetProcessHeap(), 0, list );
715         }
716     }
717
718 end:
719
720     /* Set/clear internal paint flag */
721
722     if (flags & RDW_INTERNALPAINT)
723     {
724         if ( !wndPtr->hrgnUpdate && !(wndPtr->flags & WIN_INTERNAL_PAINT))
725             add_paint_count( wndPtr->hwndSelf, 1 );
726         wndPtr->flags |= WIN_INTERNAL_PAINT;
727     }
728     else if (flags & RDW_NOINTERNALPAINT)
729     {
730         if ( !wndPtr->hrgnUpdate && (wndPtr->flags & WIN_INTERNAL_PAINT))
731             add_paint_count( wndPtr->hwndSelf, -1 );
732         wndPtr->flags &= ~WIN_INTERNAL_PAINT;
733     }
734 }
735
736 /***********************************************************************
737  *           RDW_Paint [RedrawWindow() helper]
738  *
739  * Walks the window tree and paints/erases windows that have
740  * nonzero update regions according to redraw flags. hrgn is a scratch
741  * region passed down during recursion. Must not be 1.
742  *
743  */
744 static HRGN RDW_Paint( WND* wndPtr, HRGN hrgn, UINT flags, UINT ex )
745 {
746 /* NOTE: wndPtr is locked by caller.
747  * 
748  * FIXME: Windows uses WM_SYNCPAINT to cut down the number of intertask
749  * SendMessage() calls. This is a comment inside DefWindowProc() source
750  * from 16-bit SDK:
751  *
752  *   This message avoids lots of inter-app message traffic
753  *   by switching to the other task and continuing the
754  *   recursion there.
755  *
756  * wParam         = flags
757  * LOWORD(lParam) = hrgnClip
758  * HIWORD(lParam) = hwndSkip  (not used; always NULL)
759  *
760  */
761     HDC  hDC;
762     HWND hWnd = wndPtr->hwndSelf;
763     BOOL bIcon = ((wndPtr->dwStyle & WS_MINIMIZE) && GetClassLongA(hWnd, GCL_HICON));
764
765       /* Erase/update the window itself ... */
766
767     TRACE("\thwnd %04x [%04x] -> hrgn [%04x], flags [%04x]\n", hWnd, wndPtr->hrgnUpdate, hrgn, flags );
768
769     /*
770      * Check if this window should delay it's processing of WM_NCPAINT.
771      * See WIN_HaveToDelayNCPAINT for a description of the mechanism
772      */
773     if ((ex & RDW_EX_DELAY_NCPAINT) || WIN_HaveToDelayNCPAINT(wndPtr->hwndSelf, 0) )
774         ex |= RDW_EX_DELAY_NCPAINT;
775
776     if (flags & RDW_UPDATENOW)
777     {
778         if (wndPtr->hrgnUpdate) /* wm_painticon wparam is 1 */
779             SendMessageW( hWnd, (bIcon) ? WM_PAINTICON : WM_PAINT, bIcon, 0 );
780     }
781     else if (flags & RDW_ERASENOW)
782     {
783         UINT dcx = DCX_INTERSECTRGN | DCX_USESTYLE | DCX_KEEPCLIPRGN | DCX_WINDOWPAINT | DCX_CACHE;
784         HRGN hrgnRet;
785
786         hrgnRet = WIN_UpdateNCRgn(wndPtr, 
787                                   hrgn, 
788                                   UNC_REGION | UNC_CHECK | 
789                                   ((ex & RDW_EX_DELAY_NCPAINT) ? UNC_DELAY_NCPAINT : 0) ); 
790
791         if( hrgnRet )
792         {
793             if( hrgnRet > 1 ) hrgn = hrgnRet; else hrgnRet = 0; /* entire client */
794             if( wndPtr->flags & WIN_NEEDS_ERASEBKGND )
795             {
796                 if( bIcon ) dcx |= DCX_WINDOW;
797                 else 
798                 if( hrgnRet )
799                     OffsetRgn( hrgnRet, wndPtr->rectWindow.left - wndPtr->rectClient.left, 
800                                         wndPtr->rectWindow.top  - wndPtr->rectClient.top);
801                 else
802                     dcx &= ~DCX_INTERSECTRGN;
803                 if (( hDC = GetDCEx( hWnd, hrgnRet, dcx )) )
804                 {
805                     if (SendMessageW( hWnd, (bIcon) ? WM_ICONERASEBKGND : WM_ERASEBKGND,
806                                       (WPARAM)hDC, 0 ))
807                     wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
808                     ReleaseDC( hWnd, hDC );
809                 }
810             }
811         }
812     }
813
814     if( !IsWindow(hWnd) ) return hrgn;
815
816       /* ... and its child windows */
817
818     if(!(flags & RDW_NOCHILDREN) && !(wndPtr->dwStyle & WS_MINIMIZE) &&
819        ((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) )
820     {
821         HWND *list, *phwnd;
822
823         if( (list = WIN_ListChildren( wndPtr->hwndSelf )) )
824         {
825             for (phwnd = list; *phwnd; phwnd++)
826             {
827                 if (!(wndPtr = WIN_FindWndPtr( *phwnd ))) continue;
828                 if ( (wndPtr->dwStyle & WS_VISIBLE) &&
829                      (wndPtr->hrgnUpdate || (wndPtr->flags & WIN_INTERNAL_PAINT)) )
830                     hrgn = RDW_Paint( wndPtr, hrgn, flags, ex );
831                 WIN_ReleaseWndPtr(wndPtr);
832             }
833             HeapFree( GetProcessHeap(), 0, list );
834         }
835     }
836
837     return hrgn;
838 }
839
840
841 /***********************************************************************
842  *              RedrawWindow (USER32.@)
843  */
844 BOOL WINAPI RedrawWindow( HWND hwnd, const RECT *rectUpdate,
845                               HRGN hrgnUpdate, UINT flags )
846 {
847     HRGN hRgn = 0;
848     RECT r, r2;
849     POINT pt;
850     WND* wndPtr;
851
852     if (!hwnd) hwnd = GetDesktopWindow();
853     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE;
854
855     /* check if the window or its parents are visible/not minimized */
856
857     if (!WIN_IsWindowDrawable( wndPtr, !(flags & RDW_FRAME) ) )
858     {
859         WIN_ReleaseWndPtr(wndPtr);
860         return TRUE; 
861     }
862
863     if (TRACE_ON(win))
864     {
865         if( hrgnUpdate )
866         {
867             GetRgnBox( hrgnUpdate, &r );
868             TRACE( "%04x (%04x) NULL %04x box (%i,%i-%i,%i) flags=%04x\n",
869                   hwnd, wndPtr->hrgnUpdate, hrgnUpdate, r.left, r.top, r.right, r.bottom, flags );
870         }
871         else
872         {
873             if( rectUpdate )
874                 r = *rectUpdate;
875             else
876                 SetRectEmpty( &r );
877             TRACE( "%04x (%04x) %s %d,%d-%d,%d %04x flags=%04x\n",
878                         hwnd, wndPtr->hrgnUpdate, rectUpdate ? "rect" : "NULL", r.left, 
879                         r.top, r.right, r.bottom, hrgnUpdate, flags );
880         }
881     }
882
883     /* prepare an update region in window coordinates */
884
885     if( flags & RDW_FRAME )
886         r = wndPtr->rectWindow;
887     else
888         r = wndPtr->rectClient;
889
890     pt.x = wndPtr->rectClient.left - wndPtr->rectWindow.left;
891     pt.y = wndPtr->rectClient.top - wndPtr->rectWindow.top;
892     OffsetRect( &r, -wndPtr->rectClient.left, -wndPtr->rectClient.top );
893
894     if (flags & RDW_INVALIDATE)  /* ------------------------- Invalidate */
895     {
896         /* If the window doesn't have hrgnUpdate we leave hRgn zero
897          * and put a new region straight into wndPtr->hrgnUpdate
898          * so that RDW_UpdateRgns() won't have to do any extra work.
899          */
900
901         if( hrgnUpdate )
902         {
903             if( wndPtr->hrgnUpdate )
904                 hRgn = REGION_CropRgn( 0, hrgnUpdate, NULL, &pt );
905             else 
906                 wndPtr->hrgnUpdate = REGION_CropRgn( 0, hrgnUpdate, &r, &pt ); 
907         }
908         else if( rectUpdate )
909         {
910             if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
911             OffsetRect( &r2, pt.x, pt.y );
912
913 rect2i:
914             if( wndPtr->hrgnUpdate == 0 )
915                 wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r2 );
916             else
917                 hRgn = CreateRectRgnIndirect( &r2 );
918         }
919         else /* entire window or client depending on RDW_FRAME */
920         {
921             if( flags & RDW_FRAME )
922             {
923                 if( wndPtr->hrgnUpdate )
924                     DeleteObject( wndPtr->hrgnUpdate );
925                 wndPtr->hrgnUpdate = 1;
926             }
927             else
928             {
929                 GETCLIENTRECTW( wndPtr, r2 );
930                 goto rect2i;
931             }
932         }
933     }
934     else if (flags & RDW_VALIDATE)  /* ------------------------- Validate */
935     {
936         /* In this we cannot leave with zero hRgn */
937         if( hrgnUpdate )
938         {
939             hRgn = REGION_CropRgn( hRgn, hrgnUpdate,  &r, &pt );
940             GetRgnBox( hRgn, &r2 );
941             if( IsRectEmpty( &r2 ) ) goto END;
942         }
943         else if( rectUpdate )
944         {
945             if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
946                 OffsetRect( &r2, pt.x, pt.y );
947             hRgn = CreateRectRgnIndirect( &r2 );
948         }
949         else /* entire window or client depending on RDW_FRAME */
950         {
951             if( flags & RDW_FRAME ) 
952                 hRgn = 1;
953             else
954             {
955                 GETCLIENTRECTW( wndPtr, r2 );
956                 hRgn = CreateRectRgnIndirect( &r2 );
957             }
958         }
959     }
960
961     /* At this point hRgn is either an update region in window coordinates or 1 or 0 */
962
963     RDW_UpdateRgns( wndPtr, hRgn, flags, TRUE );
964
965     /* Erase/update windows, from now on hRgn is a scratch region */
966
967     hRgn = RDW_Paint( wndPtr, (hRgn == 1) ? 0 : hRgn, flags, 0 );
968
969 END:
970     if( hRgn > 1 && (hRgn != hrgnUpdate) )
971         DeleteObject(hRgn );
972     WIN_ReleaseWndPtr(wndPtr);
973     return TRUE;
974 }
975
976
977 /***********************************************************************
978  *              RedrawWindow (USER.290)
979  */
980 BOOL16 WINAPI RedrawWindow16( HWND16 hwnd, const RECT16 *rectUpdate,
981                               HRGN16 hrgnUpdate, UINT16 flags )
982 {
983     if (rectUpdate)
984     {
985         RECT r;
986         CONV_RECT16TO32( rectUpdate, &r );
987         return (BOOL16)RedrawWindow( (HWND)hwnd, &r, hrgnUpdate, flags );
988     }
989     return RedrawWindow( hwnd, NULL, hrgnUpdate, flags );
990 }
991
992
993 /***********************************************************************
994  *              UpdateWindow (USER.124)
995  */
996 void WINAPI UpdateWindow16( HWND16 hwnd )
997 {
998     RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN );
999 }
1000
1001 /***********************************************************************
1002  *              UpdateWindow (USER32.@)
1003  */
1004 void WINAPI UpdateWindow( HWND hwnd )
1005 {
1006     RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN );
1007 }
1008
1009 /***********************************************************************
1010  *              InvalidateRgn (USER.126)
1011  */
1012 void WINAPI InvalidateRgn16( HWND16 hwnd, HRGN16 hrgn, BOOL16 erase )
1013 {
1014     RedrawWindow((HWND)hwnd, NULL, (HRGN)hrgn, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
1015 }
1016
1017
1018 /***********************************************************************
1019  *              InvalidateRgn (USER32.@)
1020  */
1021 BOOL WINAPI InvalidateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
1022 {
1023     return RedrawWindow(hwnd, NULL, hrgn, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
1024 }
1025
1026
1027 /***********************************************************************
1028  *              InvalidateRect (USER.125)
1029  */
1030 void WINAPI InvalidateRect16( HWND16 hwnd, const RECT16 *rect, BOOL16 erase )
1031 {
1032     RedrawWindow16( hwnd, rect, 0, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
1033 }
1034
1035
1036 /***********************************************************************
1037  *              InvalidateRect (USER32.@)
1038  */
1039 BOOL WINAPI InvalidateRect( HWND hwnd, const RECT *rect, BOOL erase )
1040 {
1041     return RedrawWindow( hwnd, rect, 0, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
1042 }
1043
1044
1045 /***********************************************************************
1046  *              ValidateRgn (USER.128)
1047  */
1048 void WINAPI ValidateRgn16( HWND16 hwnd, HRGN16 hrgn )
1049 {
1050     RedrawWindow( (HWND)hwnd, NULL, (HRGN)hrgn, RDW_VALIDATE | RDW_NOCHILDREN );
1051 }
1052
1053
1054 /***********************************************************************
1055  *              ValidateRgn (USER32.@)
1056  */
1057 void WINAPI ValidateRgn( HWND hwnd, HRGN hrgn )
1058 {
1059     RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOCHILDREN );
1060 }
1061
1062
1063 /***********************************************************************
1064  *              ValidateRect (USER.127)
1065  */
1066 void WINAPI ValidateRect16( HWND16 hwnd, const RECT16 *rect )
1067 {
1068     RedrawWindow16( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN );
1069 }
1070
1071
1072 /***********************************************************************
1073  *              ValidateRect (USER32.@)
1074  */
1075 void WINAPI ValidateRect( HWND hwnd, const RECT *rect )
1076 {
1077     RedrawWindow( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN );
1078 }
1079
1080
1081 /***********************************************************************
1082  *              GetUpdateRect (USER.190)
1083  */
1084 BOOL16 WINAPI GetUpdateRect16( HWND16 hwnd, LPRECT16 rect, BOOL16 erase )
1085 {
1086     RECT r;
1087     BOOL16 ret;
1088
1089     if (!rect) return GetUpdateRect( hwnd, NULL, erase );
1090     ret = GetUpdateRect( hwnd, &r, erase );
1091     CONV_RECT32TO16( &r, rect );
1092     return ret;
1093 }
1094
1095
1096 /***********************************************************************
1097  *              GetUpdateRect (USER32.@)
1098  */
1099 BOOL WINAPI GetUpdateRect( HWND hwnd, LPRECT rect, BOOL erase )
1100 {
1101     BOOL retvalue;
1102     WND * wndPtr = WIN_FindWndPtr( hwnd );
1103     if (!wndPtr) return FALSE;
1104
1105     if (rect)
1106     {
1107         if (wndPtr->hrgnUpdate > 1)
1108         {
1109             HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
1110             if (GetUpdateRgn( hwnd, hrgn, erase ) == ERROR)
1111             {
1112                 retvalue = FALSE;
1113                 goto END;
1114             }
1115             GetRgnBox( hrgn, rect );
1116             DeleteObject( hrgn );
1117             if (GetClassLongA(wndPtr->hwndSelf, GCL_STYLE) & CS_OWNDC)
1118             {
1119                 if (GetMapMode(wndPtr->dce->hDC) != MM_TEXT)
1120                 {
1121                     DPtoLP (wndPtr->dce->hDC, (LPPOINT)rect,  2);
1122                 }
1123             }
1124         }
1125         else
1126         if( wndPtr->hrgnUpdate == 1 )
1127         {
1128             GetClientRect( hwnd, rect );
1129             if (erase) RedrawWindow( hwnd, NULL, 0, RDW_FRAME | RDW_ERASENOW | RDW_NOCHILDREN );
1130         }
1131         else 
1132             SetRectEmpty( rect );
1133     }
1134     retvalue = (wndPtr->hrgnUpdate >= 1);
1135 END:
1136     WIN_ReleaseWndPtr(wndPtr);
1137     return retvalue;
1138 }
1139
1140
1141 /***********************************************************************
1142  *              GetUpdateRgn (USER.237)
1143  */
1144 INT16 WINAPI GetUpdateRgn16( HWND16 hwnd, HRGN16 hrgn, BOOL16 erase )
1145 {
1146     return GetUpdateRgn( hwnd, hrgn, erase );
1147 }
1148
1149
1150 /***********************************************************************
1151  *              GetUpdateRgn (USER32.@)
1152  */
1153 INT WINAPI GetUpdateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
1154 {
1155     INT retval;
1156     WND * wndPtr = WIN_FindWndPtr( hwnd );
1157     if (!wndPtr) return ERROR;
1158
1159     if (wndPtr->hrgnUpdate == 0)
1160     {
1161         SetRectRgn( hrgn, 0, 0, 0, 0 );
1162         retval = NULLREGION;
1163         goto END;
1164     }
1165     else
1166     if (wndPtr->hrgnUpdate == 1)
1167     {
1168         SetRectRgn( hrgn, 0, 0, wndPtr->rectClient.right - wndPtr->rectClient.left,
1169                                 wndPtr->rectClient.bottom - wndPtr->rectClient.top );
1170         retval = SIMPLEREGION;
1171     }
1172     else
1173     {
1174         retval = CombineRgn( hrgn, wndPtr->hrgnUpdate, 0, RGN_COPY );
1175         OffsetRgn( hrgn, wndPtr->rectWindow.left - wndPtr->rectClient.left,
1176                          wndPtr->rectWindow.top - wndPtr->rectClient.top );
1177     }
1178     if (erase) RedrawWindow( hwnd, NULL, 0, RDW_ERASENOW | RDW_NOCHILDREN );
1179 END:
1180     WIN_ReleaseWndPtr(wndPtr);
1181     return retval;
1182 }
1183
1184
1185 /***********************************************************************
1186  *              ExcludeUpdateRgn (USER.238)
1187  */
1188 INT16 WINAPI ExcludeUpdateRgn16( HDC16 hdc, HWND16 hwnd )
1189 {
1190     return ExcludeUpdateRgn( hdc, hwnd );
1191 }
1192
1193
1194 /***********************************************************************
1195  *              ExcludeUpdateRgn (USER32.@)
1196  */
1197 INT WINAPI ExcludeUpdateRgn( HDC hdc, HWND hwnd )
1198 {
1199     RECT rect;
1200     WND * wndPtr;
1201
1202     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return ERROR;
1203
1204     if (wndPtr->hrgnUpdate)
1205     {
1206         INT ret;
1207         HRGN hrgn = CreateRectRgn(wndPtr->rectWindow.left - wndPtr->rectClient.left,
1208                                       wndPtr->rectWindow.top - wndPtr->rectClient.top,
1209                                       wndPtr->rectWindow.right - wndPtr->rectClient.left,
1210                                       wndPtr->rectWindow.bottom - wndPtr->rectClient.top);
1211         if( wndPtr->hrgnUpdate > 1 )
1212         {
1213             CombineRgn(hrgn, wndPtr->hrgnUpdate, 0, RGN_COPY);
1214             OffsetRgn(hrgn, wndPtr->rectWindow.left - wndPtr->rectClient.left, 
1215                             wndPtr->rectWindow.top - wndPtr->rectClient.top );
1216         }
1217
1218         /* do ugly coordinate translations in dce.c */
1219
1220         ret = DCE_ExcludeRgn( hdc, hwnd, hrgn );
1221         DeleteObject( hrgn );
1222         WIN_ReleaseWndPtr(wndPtr);
1223         return ret;
1224     } 
1225     WIN_ReleaseWndPtr(wndPtr);
1226     return GetClipBox( hdc, &rect );
1227 }
1228
1229
1230
1231 /***********************************************************************
1232  *              FillRect (USER.81)
1233  * NOTE
1234  *   The Win16 variant doesn't support special color brushes like
1235  *   the Win32 one, despite the fact that Win16, as well as Win32,
1236  *   supports special background brushes for a window class.
1237  */
1238 INT16 WINAPI FillRect16( HDC16 hdc, const RECT16 *rect, HBRUSH16 hbrush )
1239 {
1240     HBRUSH prevBrush;
1241
1242     /* coordinates are logical so we cannot fast-check 'rect',
1243      * it will be done later in the PatBlt().
1244      */
1245
1246     if (!(prevBrush = SelectObject( hdc, hbrush ))) return 0;
1247     PatBlt( hdc, rect->left, rect->top,
1248               rect->right - rect->left, rect->bottom - rect->top, PATCOPY );
1249     SelectObject( hdc, prevBrush );
1250     return 1;
1251 }
1252
1253
1254 /***********************************************************************
1255  *              FillRect (USER32.@)
1256  */
1257 INT WINAPI FillRect( HDC hdc, const RECT *rect, HBRUSH hbrush )
1258 {
1259     HBRUSH prevBrush;
1260
1261     if (hbrush <= (HBRUSH) (COLOR_MAX + 1)) {
1262         hbrush = GetSysColorBrush( (INT) hbrush - 1 );
1263     }
1264
1265     if (!(prevBrush = SelectObject( hdc, hbrush ))) return 0;
1266     PatBlt( hdc, rect->left, rect->top,
1267               rect->right - rect->left, rect->bottom - rect->top, PATCOPY );
1268     SelectObject( hdc, prevBrush );
1269     return 1;
1270 }
1271
1272
1273 /***********************************************************************
1274  *              InvertRect (USER.82)
1275  */
1276 void WINAPI InvertRect16( HDC16 hdc, const RECT16 *rect )
1277 {
1278     PatBlt( hdc, rect->left, rect->top,
1279               rect->right - rect->left, rect->bottom - rect->top, DSTINVERT );
1280 }
1281
1282
1283 /***********************************************************************
1284  *              InvertRect (USER32.@)
1285  */
1286 BOOL WINAPI InvertRect( HDC hdc, const RECT *rect )
1287 {
1288     return PatBlt( hdc, rect->left, rect->top,
1289                      rect->right - rect->left, rect->bottom - rect->top, 
1290                      DSTINVERT );
1291 }
1292
1293
1294 /***********************************************************************
1295  *              FrameRect (USER32.@)
1296  */
1297 INT WINAPI FrameRect( HDC hdc, const RECT *rect, HBRUSH hbrush )
1298 {
1299     HBRUSH prevBrush;
1300     RECT r = *rect;
1301
1302     if ( (r.right <= r.left) || (r.bottom <= r.top) ) return 0;
1303     if (!(prevBrush = SelectObject( hdc, hbrush ))) return 0;
1304     
1305     PatBlt( hdc, r.left, r.top, 1,
1306               r.bottom - r.top, PATCOPY );
1307     PatBlt( hdc, r.right - 1, r.top, 1,
1308               r.bottom - r.top, PATCOPY );
1309     PatBlt( hdc, r.left, r.top,
1310               r.right - r.left, 1, PATCOPY );
1311     PatBlt( hdc, r.left, r.bottom - 1,
1312               r.right - r.left, 1, PATCOPY );
1313
1314     SelectObject( hdc, prevBrush );
1315     return TRUE;
1316 }
1317
1318
1319 /***********************************************************************
1320  *              FrameRect (USER.83)
1321  */
1322 INT16 WINAPI FrameRect16( HDC16 hdc, const RECT16 *rect16, HBRUSH16 hbrush )
1323 {
1324     RECT rect;
1325     CONV_RECT16TO32( rect16, &rect );
1326     return FrameRect( hdc, &rect, hbrush );
1327 }
1328
1329
1330 /***********************************************************************
1331  *              DrawFocusRect (USER.466)
1332  */
1333 void WINAPI DrawFocusRect16( HDC16 hdc, const RECT16* rc )
1334 {
1335     RECT rect32;
1336     CONV_RECT16TO32( rc, &rect32 );
1337     DrawFocusRect( hdc, &rect32 );
1338 }
1339
1340
1341 /***********************************************************************
1342  *              DrawFocusRect (USER32.@)
1343  *
1344  * FIXME: PatBlt(PATINVERT) with background brush.
1345  */
1346 BOOL WINAPI DrawFocusRect( HDC hdc, const RECT* rc )
1347 {
1348     HBRUSH hOldBrush;
1349     HPEN hOldPen, hNewPen;
1350     INT oldDrawMode, oldBkMode;
1351
1352     hOldBrush = SelectObject(hdc, GetStockObject(NULL_BRUSH));
1353     hNewPen = CreatePen(PS_ALTERNATE, 1, GetSysColor(COLOR_WINDOWTEXT));
1354     hOldPen = SelectObject(hdc, hNewPen);
1355     oldDrawMode = SetROP2(hdc, R2_XORPEN);
1356     oldBkMode = SetBkMode(hdc, TRANSPARENT);
1357
1358     Rectangle(hdc, rc->left, rc->top, rc->right, rc->bottom);
1359
1360     SetBkMode(hdc, oldBkMode);
1361     SetROP2(hdc, oldDrawMode);
1362     SelectObject(hdc, hOldPen);
1363     DeleteObject(hNewPen);
1364     SelectObject(hdc, hOldBrush);
1365
1366     return TRUE;
1367 }
1368
1369 /**********************************************************************
1370  *              DrawAnimatedRects (USER.448)
1371  */
1372 BOOL16 WINAPI DrawAnimatedRects16( HWND16 hwnd, INT16 idAni,
1373                                    const RECT16* lprcFrom,
1374                                    const RECT16* lprcTo )
1375 {
1376     RECT rcFrom32, rcTo32;
1377
1378     rcFrom32.left       = (INT)lprcFrom->left;
1379     rcFrom32.top        = (INT)lprcFrom->top;
1380     rcFrom32.right      = (INT)lprcFrom->right;
1381     rcFrom32.bottom     = (INT)lprcFrom->bottom;
1382
1383     rcTo32.left         = (INT)lprcTo->left;
1384     rcTo32.top          = (INT)lprcTo->top;
1385     rcTo32.right        = (INT)lprcTo->right;
1386     rcTo32.bottom       = (INT)lprcTo->bottom;
1387
1388     return DrawAnimatedRects((HWND)hwnd, (INT)idAni, &rcFrom32, &rcTo32);
1389 }
1390
1391
1392 /**********************************************************************
1393  *              DrawAnimatedRects (USER32.@)
1394  */
1395 BOOL WINAPI DrawAnimatedRects( HWND hwnd, INT idAni,
1396                                    const RECT* lprcFrom,
1397                                    const RECT* lprcTo )
1398 {
1399     FIXME_(win)("(0x%x,%d,%p,%p): stub\n",hwnd,idAni,lprcFrom,lprcTo);
1400     return TRUE;
1401 }
1402
1403
1404 /**********************************************************************
1405  *          PAINTING_DrawStateJam
1406  *
1407  * Jams in the requested type in the dc
1408  */
1409 static BOOL PAINTING_DrawStateJam(HDC hdc, UINT opcode,
1410                                   DRAWSTATEPROC func, LPARAM lp, WPARAM wp,
1411                                   LPRECT rc, UINT dtflags, BOOL unicode )
1412 {
1413     HDC memdc;
1414     HBITMAP hbmsave;
1415     BOOL retval;
1416     INT cx = rc->right - rc->left;
1417     INT cy = rc->bottom - rc->top;
1418     
1419     switch(opcode)
1420     {
1421     case DST_TEXT:
1422     case DST_PREFIXTEXT:
1423         if(unicode)
1424             return DrawTextW(hdc, (LPWSTR)lp, (INT)wp, rc, dtflags);
1425         else
1426             return DrawTextA(hdc, (LPSTR)lp, (INT)wp, rc, dtflags);
1427
1428     case DST_ICON:
1429         return DrawIcon(hdc, rc->left, rc->top, (HICON)lp);
1430
1431     case DST_BITMAP:
1432         memdc = CreateCompatibleDC(hdc);
1433         if(!memdc) return FALSE;
1434         hbmsave = (HBITMAP)SelectObject(memdc, (HBITMAP)lp);
1435         if(!hbmsave) 
1436         {
1437             DeleteDC(memdc);
1438             return FALSE;
1439         }
1440         retval = BitBlt(hdc, rc->left, rc->top, cx, cy, memdc, 0, 0, SRCCOPY);
1441         SelectObject(memdc, hbmsave);
1442         DeleteDC(memdc);
1443         return retval;
1444             
1445     case DST_COMPLEX:
1446         if(func) {
1447             BOOL bRet;
1448             /* DRAWSTATEPROC assumes that it draws at the center of coordinates  */
1449
1450             OffsetViewportOrgEx(hdc, rc->left, rc->top, NULL);
1451             bRet = func(hdc, lp, wp, cx, cy);
1452             /* Restore origin */
1453             OffsetViewportOrgEx(hdc, -rc->left, -rc->top, NULL);
1454             return bRet;
1455         } else
1456             return FALSE;
1457     }
1458     return FALSE;
1459 }
1460
1461 /**********************************************************************
1462  *      PAINTING_DrawState()
1463  */
1464 static BOOL PAINTING_DrawState(HDC hdc, HBRUSH hbr, DRAWSTATEPROC func, LPARAM lp, WPARAM wp,
1465                                INT x, INT y, INT cx, INT cy, UINT flags, BOOL unicode )
1466 {
1467     HBITMAP hbm, hbmsave;
1468     HFONT hfsave;
1469     HBRUSH hbsave, hbrtmp = 0;
1470     HDC memdc;
1471     RECT rc;
1472     UINT dtflags = DT_NOCLIP;
1473     COLORREF fg, bg;
1474     UINT opcode = flags & 0xf;
1475     INT len = wp;
1476     BOOL retval, tmp;
1477
1478     if((opcode == DST_TEXT || opcode == DST_PREFIXTEXT) && !len)    /* The string is '\0' terminated */
1479     {
1480         if(unicode)
1481             len = strlenW((LPWSTR)lp);
1482         else
1483             len = strlen((LPSTR)lp);
1484     }
1485
1486     /* Find out what size the image has if not given by caller */
1487     if(!cx || !cy)
1488     {
1489         SIZE s;
1490         CURSORICONINFO *ici;
1491         BITMAP bm;
1492
1493         switch(opcode)
1494         {
1495         case DST_TEXT:
1496         case DST_PREFIXTEXT:
1497             if(unicode)
1498                 retval = GetTextExtentPoint32W(hdc, (LPWSTR)lp, len, &s);
1499             else
1500                 retval = GetTextExtentPoint32A(hdc, (LPSTR)lp, len, &s);
1501             if(!retval) return FALSE;
1502             break;
1503             
1504         case DST_ICON:
1505             ici = (CURSORICONINFO *)GlobalLock16((HGLOBAL16)lp);
1506             if(!ici) return FALSE;
1507             s.cx = ici->nWidth;
1508             s.cy = ici->nHeight;
1509             GlobalUnlock16((HGLOBAL16)lp);
1510             break;            
1511
1512         case DST_BITMAP:
1513             if(!GetObjectA((HBITMAP)lp, sizeof(bm), &bm))
1514                 return FALSE;
1515             s.cx = bm.bmWidth;
1516             s.cy = bm.bmHeight;
1517             break;
1518             
1519         case DST_COMPLEX: /* cx and cy must be set in this mode */
1520             return FALSE;
1521         }
1522                     
1523         if(!cx) cx = s.cx;
1524         if(!cy) cy = s.cy;
1525     }
1526
1527     rc.left   = x;
1528     rc.top    = y;
1529     rc.right  = x + cx;
1530     rc.bottom = y + cy;
1531
1532     if(flags & DSS_RIGHT)    /* This one is not documented in the win32.hlp file */
1533         dtflags |= DT_RIGHT;
1534     if(opcode == DST_TEXT)
1535         dtflags |= DT_NOPREFIX;
1536
1537     /* For DSS_NORMAL we just jam in the image and return */
1538     if((flags & 0x7ff0) == DSS_NORMAL)
1539     {
1540         return PAINTING_DrawStateJam(hdc, opcode, func, lp, len, &rc, dtflags, unicode);
1541     }
1542
1543     /* For all other states we need to convert the image to B/W in a local bitmap */
1544     /* before it is displayed */
1545     fg = SetTextColor(hdc, RGB(0, 0, 0));
1546     bg = SetBkColor(hdc, RGB(255, 255, 255));
1547     hbm = (HBITMAP)NULL; hbmsave = (HBITMAP)NULL;
1548     memdc = (HDC)NULL; hbsave = (HBRUSH)NULL;
1549     retval = FALSE; /* assume failure */
1550     
1551     /* From here on we must use "goto cleanup" when something goes wrong */
1552     hbm     = CreateBitmap(cx, cy, 1, 1, NULL);
1553     if(!hbm) goto cleanup;
1554     memdc   = CreateCompatibleDC(hdc);
1555     if(!memdc) goto cleanup;
1556     hbmsave = (HBITMAP)SelectObject(memdc, hbm);
1557     if(!hbmsave) goto cleanup;
1558     rc.left = rc.top = 0;
1559     rc.right = cx;
1560     rc.bottom = cy;
1561     if(!FillRect(memdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH))) goto cleanup;
1562     SetBkColor(memdc, RGB(255, 255, 255));
1563     SetTextColor(memdc, RGB(0, 0, 0));
1564     hfsave  = (HFONT)SelectObject(memdc, GetCurrentObject(hdc, OBJ_FONT));
1565
1566     /* DST_COMPLEX may draw text as well,
1567      * so we must be sure that correct font is selected
1568      */
1569     if(!hfsave && (opcode <= DST_PREFIXTEXT)) goto cleanup;
1570     tmp = PAINTING_DrawStateJam(memdc, opcode, func, lp, len, &rc, dtflags, unicode);
1571     if(hfsave) SelectObject(memdc, hfsave);
1572     if(!tmp) goto cleanup;
1573     
1574     /* This state cause the image to be dithered */
1575     if(flags & DSS_UNION)
1576     {
1577         hbsave = (HBRUSH)SelectObject(memdc, CACHE_GetPattern55AABrush());
1578         if(!hbsave) goto cleanup;
1579         tmp = PatBlt(memdc, 0, 0, cx, cy, 0x00FA0089);
1580         SelectObject(memdc, hbsave);
1581         if(!tmp) goto cleanup;
1582     }
1583
1584     if (flags & DSS_DISABLED)
1585        hbrtmp = CreateSolidBrush(GetSysColor(COLOR_3DHILIGHT));
1586     else if (flags & DSS_DEFAULT)
1587        hbrtmp = CreateSolidBrush(GetSysColor(COLOR_3DSHADOW));
1588
1589     /* Draw light or dark shadow */
1590     if (flags & (DSS_DISABLED|DSS_DEFAULT))
1591     {
1592        if(!hbrtmp) goto cleanup;
1593        hbsave = (HBRUSH)SelectObject(hdc, hbrtmp);
1594        if(!hbsave) goto cleanup;
1595        if(!BitBlt(hdc, x+1, y+1, cx, cy, memdc, 0, 0, 0x00B8074A)) goto cleanup;
1596        SelectObject(hdc, hbsave);
1597        DeleteObject(hbrtmp);
1598        hbrtmp = 0;
1599     }
1600
1601     if (flags & DSS_DISABLED)
1602     {
1603        hbr = hbrtmp = CreateSolidBrush(GetSysColor(COLOR_3DSHADOW));
1604        if(!hbrtmp) goto cleanup;
1605     }
1606     else if (!hbr)
1607     {
1608        hbr = (HBRUSH)GetStockObject(BLACK_BRUSH);
1609     }
1610
1611     hbsave = (HBRUSH)SelectObject(hdc, hbr);
1612     
1613     if(!BitBlt(hdc, x, y, cx, cy, memdc, 0, 0, 0x00B8074A)) goto cleanup;
1614     
1615     retval = TRUE; /* We succeeded */
1616     
1617 cleanup:    
1618     SetTextColor(hdc, fg);
1619     SetBkColor(hdc, bg);
1620
1621     if(hbsave)  SelectObject(hdc, hbsave);
1622     if(hbmsave) SelectObject(memdc, hbmsave);
1623     if(hbrtmp)  DeleteObject(hbrtmp);
1624     if(hbm)     DeleteObject(hbm);
1625     if(memdc)   DeleteDC(memdc);
1626
1627     return retval;
1628 }
1629
1630 /**********************************************************************
1631  *              DrawStateA (USER32.@)
1632  */
1633 BOOL WINAPI DrawStateA(HDC hdc, HBRUSH hbr,
1634                    DRAWSTATEPROC func, LPARAM ldata, WPARAM wdata,
1635                    INT x, INT y, INT cx, INT cy, UINT flags)
1636 {
1637     return PAINTING_DrawState(hdc, hbr, func, ldata, wdata, x, y, cx, cy, flags, FALSE);
1638 }
1639
1640 /**********************************************************************
1641  *              DrawStateW (USER32.@)
1642  */
1643 BOOL WINAPI DrawStateW(HDC hdc, HBRUSH hbr,
1644                    DRAWSTATEPROC func, LPARAM ldata, WPARAM wdata,
1645                    INT x, INT y, INT cx, INT cy, UINT flags)
1646 {
1647     return PAINTING_DrawState(hdc, hbr, func, ldata, wdata, x, y, cx, cy, flags, TRUE);
1648 }
1649
1650 /***********************************************************************
1651  *              SelectPalette (USER.282)
1652  */
1653 HPALETTE16 WINAPI SelectPalette16( HDC16 hDC, HPALETTE16 hPal,
1654                                    BOOL16 bForceBackground )
1655 {
1656     WORD wBkgPalette = 1;
1657
1658     if (!bForceBackground && (hPal != GetStockObject(DEFAULT_PALETTE)))
1659     {
1660         HWND hwnd = WindowFromDC( hDC );
1661         if (hwnd)
1662         {
1663             HWND hForeground = GetForegroundWindow();
1664             /* set primary palette if it's related to current active */
1665             if (hForeground == hwnd || IsChild(hForeground,hwnd)) wBkgPalette = 0;
1666         }
1667     }
1668     return GDISelectPalette16( hDC, hPal, wBkgPalette);
1669 }
1670
1671
1672 /***********************************************************************
1673  *              RealizePalette (USER.283)
1674  */
1675 UINT16 WINAPI RealizePalette16( HDC16 hDC )
1676 {
1677     UINT16 realized = GDIRealizePalette16( hDC );
1678
1679     /* do not send anything if no colors were changed */
1680     if (realized && IsDCCurrentPalette16( hDC ))
1681     {
1682         /* send palette change notification */
1683         HWND hWnd = WindowFromDC( hDC );
1684         if (hWnd) SendMessageA( HWND_BROADCAST, WM_PALETTECHANGED, hWnd, 0L);
1685     }
1686     return realized;
1687 }
1688
1689
1690 /***********************************************************************
1691  *              UserRealizePalette (USER32.@)
1692  */
1693 UINT WINAPI UserRealizePalette( HDC hDC )
1694 {
1695     return RealizePalette16( hDC );
1696 }