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