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