- Made FillRect behave like it does on Windows
[wine] / windows / painting.c
1 /*
2  * Window painting functions
3  *
4  * Copyright 1993, 1994, 1995 Alexandre Julliard
5  *                       1999 Alex Korobka
6  */
7
8 #include "region.h"
9 #include "win.h"
10 #include "queue.h"
11 #include "dce.h"
12 #include "heap.h"
13 #include "debugtools.h"
14 #include "wine/winuser16.h"
15 #include "cache.h"
16
17 DEFAULT_DEBUG_CHANNEL(win)
18 DECLARE_DEBUG_CHANNEL(nonclient)
19
20 /* client rect in window coordinates */
21
22 #define GETCLIENTRECTW( wnd, r )        (r).left = (wnd)->rectClient.left - (wnd)->rectWindow.left; \
23                                         (r).top = (wnd)->rectClient.top - (wnd)->rectWindow.top; \
24                                         (r).right = (wnd)->rectClient.right - (wnd)->rectWindow.left; \
25                                         (r).bottom = (wnd)->rectClient.bottom - (wnd)->rectWindow.top
26
27   /* Last COLOR id */
28 #define COLOR_MAX   COLOR_GRADIENTINACTIVECAPTION
29
30   /* Last CTLCOLOR id */
31 #define CTLCOLOR_MAX   CTLCOLOR_STATIC
32
33
34 /***********************************************************************
35  *           WIN_HaveToDelayNCPAINT
36  *
37  * Currently, in the Wine painting mechanism, the WM_NCPAINT message
38  * is generated as soon as a region intersecting the non-client area 
39  * of a window is invalidated.
40  *
41  * This technique will work fine for all windows whose parents
42  * have the WS_CLIPCHILDREN style. When the parents have that style,
43  * they are not going to override the contents of their children.
44  * However, when the parent doesn't have that style, Windows relies
45  * on a "painter's algorithm" to display the contents of the windows.
46  * That is, windows are painted from back to front. This includes the
47  * non-client area.
48  *
49  * This method looks at the current state of a window to determine
50  * if the sending of the WM_NCPAINT message should be delayed until 
51  * the BeginPaint call.
52  *
53  * PARAMS:
54  *   wndPtr   - A Locked window pointer to the window we're
55  *              examining.
56  *   uncFlags - This is the flag passed to the WIN_UpdateNCRgn
57  *              function. This is a shortcut for the cases when
58  *              we already know when to avoid scanning all the
59  *              parents of a window. If you already know that this
60  *              window's NCPAINT should be delayed, set the 
61  *              UNC_DELAY_NCPAINT flag for this parameter. 
62  *
63  *              This shortcut behavior is implemented in the
64  *              RDW_Paint() method.
65  * 
66  */
67 static BOOL WIN_HaveToDelayNCPAINT(
68   WND* wndPtr, 
69   UINT uncFlags)
70 {
71   WND* parentWnd = NULL;
72
73   /*
74    * Test the shortcut first. (duh)
75    */
76   if (uncFlags & UNC_DELAY_NCPAINT)
77     return TRUE;
78
79   /*
80    * The UNC_IN_BEGINPAINT flag is set in the BeginPaint
81    * method only. This is another shortcut to avoid going
82    * up the parent's chain of the window to finally
83    * figure-out that none of them has an invalid region.
84    */
85   if (uncFlags & UNC_IN_BEGINPAINT)
86     return FALSE;
87
88   /*
89    * Scan all the parents of this window to find a window
90    * that doesn't have the WS_CLIPCHILDREN style and that
91    * has an invalid region. 
92    */
93   parentWnd = WIN_LockWndPtr(wndPtr->parent);
94
95   while (parentWnd!=NULL)
96   {
97     if ( ((parentWnd->dwStyle & WS_CLIPCHILDREN) == 0) &&
98          (parentWnd->hrgnUpdate != 0) )
99     {
100       WIN_ReleaseWndPtr(parentWnd);
101       return TRUE;
102     }
103
104     WIN_UpdateWndPtr(&parentWnd, parentWnd->parent);    
105   }
106
107   WIN_ReleaseWndPtr(parentWnd);
108
109   return FALSE;
110 }
111
112 /***********************************************************************
113  *           WIN_UpdateNCRgn
114  *
115  *  Things to do:
116  *      Send WM_NCPAINT if required (when nonclient is invalid or UNC_ENTIRE flag is set)
117  *      Crop hrgnUpdate to a client rect, especially if it 1.
118  *      If UNC_REGION is set return update region for the client rect.
119  *
120  *  NOTE: UNC_REGION is mainly for the RDW_Paint() chunk that sends WM_ERASEBKGND message.
121  *        The trick is that when the returned region handle may be different from hRgn.
122  *        In this case the old hRgn must be considered gone. BUT, if the returned value
123  *        is 1 then the hRgn is preserved and RDW_Paint() will have to get 
124  *        a DC without extra clipping region.
125  */
126 HRGN WIN_UpdateNCRgn(WND* wnd, HRGN hRgn, UINT uncFlags )
127 {
128     RECT  r;
129     HRGN  hClip = 0;
130     HRGN  hrgnRet = 0;
131
132     TRACE_(nonclient)("hwnd %04x [%04x] hrgn %04x, unc %04x, ncf %i\n", 
133                       wnd->hwndSelf, wnd->hrgnUpdate, hRgn, uncFlags, wnd->flags & WIN_NEEDS_NCPAINT);
134
135     /* desktop window doesn't have a nonclient area */
136     if(wnd == WIN_GetDesktop()) 
137     {
138         wnd->flags &= ~WIN_NEEDS_NCPAINT;
139         if( wnd->hrgnUpdate > 1 )
140             hrgnRet = REGION_CropRgn( hRgn, wnd->hrgnUpdate, NULL, NULL );
141         else 
142         {
143             hrgnRet = wnd->hrgnUpdate;
144         }
145         WIN_ReleaseDesktop();
146         return hrgnRet;
147     }
148     WIN_ReleaseDesktop();
149
150     if ((wnd->hwndSelf == GetForegroundWindow()) &&
151         !(wnd->flags & WIN_NCACTIVATED) )
152     {
153         wnd->flags |= WIN_NCACTIVATED;
154         uncFlags |= UNC_ENTIRE; 
155     }
156
157     /*
158      * If the window's non-client area needs to be painted, 
159      */
160     if ( ( wnd->flags & WIN_NEEDS_NCPAINT ) &&
161          !WIN_HaveToDelayNCPAINT(wnd, uncFlags) )
162     {
163             RECT r2, r3;
164
165             wnd->flags &= ~WIN_NEEDS_NCPAINT;  
166             GETCLIENTRECTW( wnd, r );
167
168             TRACE_(nonclient)( "\tclient box (%i,%i-%i,%i), hrgnUpdate %04x\n", 
169                                 r.left, r.top, r.right, r.bottom, wnd->hrgnUpdate );
170             if( wnd->hrgnUpdate > 1 )
171             {
172                 /* Check if update rgn overlaps with nonclient area */
173
174                 GetRgnBox( wnd->hrgnUpdate, &r2 );
175                 UnionRect( &r3, &r2, &r );
176                 if( r3.left != r.left || r3.top != r.top || 
177                     r3.right != r.right || r3.bottom != r.bottom ) /* it does */
178                 {
179                     /* crop hrgnUpdate, save old one in hClip - the only
180                      * case that places a valid region handle in hClip */
181
182                     hClip = wnd->hrgnUpdate;
183                     wnd->hrgnUpdate = REGION_CropRgn( hRgn, hClip, &r, NULL );
184                     if( uncFlags & UNC_REGION ) hrgnRet = hClip;
185                 }
186
187                 if( uncFlags & UNC_CHECK )
188                 {
189                     GetRgnBox( wnd->hrgnUpdate, &r3 );
190                     if( IsRectEmpty( &r3 ) )
191                     {
192                         /* delete the update region since all invalid 
193                          * parts were in the nonclient area */
194
195                         DeleteObject( wnd->hrgnUpdate );
196                         wnd->hrgnUpdate = 0;
197                         if(!(wnd->flags & WIN_INTERNAL_PAINT))
198                             QUEUE_DecPaintCount( wnd->hmemTaskQ );
199
200                         wnd->flags &= ~WIN_NEEDS_ERASEBKGND;
201                     }
202                 }
203
204                 if(!hClip && wnd->hrgnUpdate ) goto copyrgn;
205             }
206             else 
207             if( wnd->hrgnUpdate == 1 )/* entire window */
208             {
209                 if( uncFlags & UNC_UPDATE ) wnd->hrgnUpdate = CreateRectRgnIndirect( &r );
210                 if( uncFlags & UNC_REGION ) hrgnRet = 1;
211                 uncFlags |= UNC_ENTIRE;
212             }
213     }
214     else /* no WM_NCPAINT unless forced */
215     {
216         if( wnd->hrgnUpdate >  1 )
217         {
218 copyrgn:
219             if( uncFlags & UNC_REGION )
220                 hrgnRet = REGION_CropRgn( hRgn, wnd->hrgnUpdate, NULL, NULL );
221         }
222         else
223         if( wnd->hrgnUpdate == 1 && (uncFlags & UNC_UPDATE) )
224         {
225             GETCLIENTRECTW( wnd, r ); 
226             wnd->hrgnUpdate = CreateRectRgnIndirect( &r );
227             if( uncFlags & UNC_REGION ) hrgnRet = 1;
228         }
229     }
230
231     if(!hClip && (uncFlags & UNC_ENTIRE) )
232     {
233         /* still don't do anything if there is no nonclient area */
234         hClip = (memcmp( &wnd->rectWindow, &wnd->rectClient, sizeof(RECT) ) != 0);
235     }
236
237     if( hClip ) /* NOTE: WM_NCPAINT allows wParam to be 1 */
238     {
239         if ( hClip == hrgnRet && hrgnRet > 1 ) {
240             hClip = CreateRectRgn( 0, 0, 0, 0 );
241             CombineRgn( hClip, hrgnRet, 0, RGN_COPY );
242         }
243
244         SendMessageA( wnd->hwndSelf, WM_NCPAINT, hClip, 0L );
245         if( (hClip > 1) && (hClip != hRgn) && (hClip != hrgnRet) )
246             DeleteObject( hClip );
247         /*
248          * Since all Window locks are suspended while processing the WM_NCPAINT
249          * we want to make sure the window still exists before continuing.
250          */
251         if (!IsWindow(wnd->hwndSelf))
252         {
253           DeleteObject(hrgnRet);
254           hrgnRet=0;
255         }
256     }
257
258     TRACE_(nonclient)("returning %04x (hClip = %04x, hrgnUpdate = %04x)\n", hrgnRet, hClip, wnd->hrgnUpdate );
259
260     return hrgnRet;
261 }
262
263
264 /***********************************************************************
265  *           BeginPaint16    (USER.39)
266  */
267 HDC16 WINAPI BeginPaint16( HWND16 hwnd, LPPAINTSTRUCT16 lps ) 
268 {
269     BOOL bIcon;
270     HRGN hrgnUpdate;
271     RECT16 clipRect, clientRect;
272     WND *wndPtr = WIN_FindWndPtr( hwnd );
273     if (!wndPtr) return 0;
274
275     bIcon = (wndPtr->dwStyle & WS_MINIMIZE && GetClassWord(wndPtr->hwndSelf, GCW_HICON));
276
277     wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
278
279     /* send WM_NCPAINT and make sure hrgnUpdate is a valid rgn handle */
280     WIN_UpdateNCRgn( wndPtr, 0, UNC_UPDATE | UNC_IN_BEGINPAINT);
281
282     /*
283      * Make sure the window is still a window. All window locks are suspended
284      * when the WM_NCPAINT is sent.
285      */
286     if (!IsWindow(wndPtr->hwndSelf))
287     {
288         WIN_ReleaseWndPtr(wndPtr);
289         return 0;
290     }
291
292     if( ((hrgnUpdate = wndPtr->hrgnUpdate) != 0) || (wndPtr->flags & WIN_INTERNAL_PAINT))
293         QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
294
295     wndPtr->hrgnUpdate = 0;
296     wndPtr->flags &= ~WIN_INTERNAL_PAINT;
297
298     HideCaret( hwnd );
299
300     TRACE("hrgnUpdate = %04x, \n", hrgnUpdate);
301
302     if (GetClassWord16(wndPtr->hwndSelf, GCW_STYLE) & CS_PARENTDC)
303     {
304         /* Don't clip the output to the update region for CS_PARENTDC window */
305         if( hrgnUpdate ) 
306             DeleteObject(hrgnUpdate);
307         lps->hdc = GetDCEx16( hwnd, 0, DCX_WINDOWPAINT | DCX_USESTYLE |
308                               (bIcon ? DCX_WINDOW : 0) );
309     }
310     else
311     {
312         if( hrgnUpdate ) /* convert to client coordinates */
313             OffsetRgn( hrgnUpdate, wndPtr->rectWindow.left - wndPtr->rectClient.left,
314                                    wndPtr->rectWindow.top - wndPtr->rectClient.top );
315         lps->hdc = GetDCEx16(hwnd, hrgnUpdate, DCX_INTERSECTRGN |
316                              DCX_WINDOWPAINT | DCX_USESTYLE | (bIcon ? DCX_WINDOW : 0) );
317         /* ReleaseDC() in EndPaint() will delete the region */
318     }
319
320     TRACE("hdc = %04x\n", lps->hdc);
321
322     if (!lps->hdc)
323     {
324         WARN("GetDCEx() failed in BeginPaint(), hwnd=%04x\n", hwnd);
325         WIN_ReleaseWndPtr(wndPtr);
326         return 0;
327     }
328
329     /* It is possible that the clip box is bigger than the window itself,
330        if CS_PARENTDC flag is set. Windows never return a paint rect bigger
331        than the window itself, so we need to intersect the cliprect with
332        the window  */
333     
334     GetClipBox16( lps->hdc, &clipRect );
335     GetClientRect16( hwnd, &clientRect );
336
337     /* The rect obtained by GetClipBox is in logical, so make the client in logical to*/
338     DPtoLP16(lps->hdc, (LPPOINT16) &clientRect, 2);    
339
340     IntersectRect16(&lps->rcPaint, &clientRect, &clipRect);
341
342     TRACE("box = (%i,%i - %i,%i)\n", lps->rcPaint.left, lps->rcPaint.top,
343                     lps->rcPaint.right, lps->rcPaint.bottom );
344
345     if (wndPtr->flags & WIN_NEEDS_ERASEBKGND)
346     {
347         wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
348         lps->fErase = !SendMessage16(hwnd, (bIcon) ? WM_ICONERASEBKGND
349                                                    : WM_ERASEBKGND,
350                                      (WPARAM16)lps->hdc, 0 );
351     }
352     else lps->fErase = TRUE;
353
354     WIN_ReleaseWndPtr(wndPtr);
355     return lps->hdc;
356 }
357
358
359 /***********************************************************************
360  *           BeginPaint32    (USER32.10)
361  */
362 HDC WINAPI BeginPaint( HWND hwnd, PAINTSTRUCT *lps )
363 {
364     PAINTSTRUCT16 ps;
365
366     BeginPaint16( hwnd, &ps );
367     lps->hdc            = (HDC)ps.hdc;
368     lps->fErase         = ps.fErase;
369     lps->rcPaint.top    = ps.rcPaint.top;
370     lps->rcPaint.left   = ps.rcPaint.left;
371     lps->rcPaint.right  = ps.rcPaint.right;
372     lps->rcPaint.bottom = ps.rcPaint.bottom;
373     lps->fRestore       = ps.fRestore;
374     lps->fIncUpdate     = ps.fIncUpdate;
375     return lps->hdc;
376 }
377
378
379 /***********************************************************************
380  *           EndPaint16    (USER.40)
381  */
382 BOOL16 WINAPI EndPaint16( HWND16 hwnd, const PAINTSTRUCT16* lps )
383 {
384     ReleaseDC16( hwnd, lps->hdc );
385     ShowCaret( hwnd );
386     return TRUE;
387 }
388
389
390 /***********************************************************************
391  *           EndPaint32    (USER32.176)
392  */
393 BOOL WINAPI EndPaint( HWND hwnd, const PAINTSTRUCT *lps )
394 {
395     ReleaseDC( hwnd, lps->hdc );
396     ShowCaret( hwnd );
397     return TRUE;
398 }
399
400
401 /***********************************************************************
402  *           FillWindow    (USER.324)
403  */
404 void WINAPI FillWindow16( HWND16 hwndParent, HWND16 hwnd, HDC16 hdc, HBRUSH16 hbrush )
405 {
406     RECT16 rect;
407     GetClientRect16( hwnd, &rect );
408     DPtoLP16( hdc, (LPPOINT16)&rect, 2 );
409     PaintRect16( hwndParent, hwnd, hdc, hbrush, &rect );
410 }
411
412
413 /***********************************************************************
414  *           PAINT_GetControlBrush
415  */
416 static HBRUSH16 PAINT_GetControlBrush( HWND hParent, HWND hWnd, HDC16 hDC, UINT16 ctlType )
417 {
418     HBRUSH16 bkgBrush = (HBRUSH16)SendMessageA( hParent, WM_CTLCOLORMSGBOX + ctlType, 
419                                                              (WPARAM)hDC, (LPARAM)hWnd );
420     if( !IsGDIObject16(bkgBrush) )
421         bkgBrush = DEFWND_ControlColor( hDC, ctlType );
422     return bkgBrush;
423 }
424
425
426 /***********************************************************************
427  *           PaintRect    (USER.325)
428  */
429 void WINAPI PaintRect16( HWND16 hwndParent, HWND16 hwnd, HDC16 hdc,
430                        HBRUSH16 hbrush, const RECT16 *rect)
431 {
432     if( hbrush <= CTLCOLOR_MAX ) 
433     {
434         if( hwndParent )
435             hbrush = PAINT_GetControlBrush( hwndParent, hwnd, hdc, (UINT16)hbrush );
436         else 
437             return;
438     }
439     if( hbrush ) 
440         FillRect16( hdc, rect, hbrush );
441 }
442
443
444 /***********************************************************************
445  *           GetControlBrush    (USER.326)
446  */
447 HBRUSH16 WINAPI GetControlBrush16( HWND16 hwnd, HDC16 hdc, UINT16 ctlType )
448 {
449     WND* wndPtr = WIN_FindWndPtr( hwnd );
450     HBRUSH16 retvalue;
451
452     if((ctlType <= CTLCOLOR_MAX) && wndPtr )
453     {
454         WND* parent;
455         if( wndPtr->dwStyle & WS_POPUP ) parent = WIN_LockWndPtr(wndPtr->owner);
456         else parent = WIN_LockWndPtr(wndPtr->parent);
457         if( !parent ) parent = wndPtr;
458         retvalue = (HBRUSH16)PAINT_GetControlBrush( parent->hwndSelf, hwnd, hdc, ctlType );
459         WIN_ReleaseWndPtr(parent);
460         goto END;
461     }
462     retvalue = (HBRUSH16)0;
463 END:
464     WIN_ReleaseWndPtr(wndPtr);
465     return retvalue;
466 }
467
468
469 /***********************************************************************
470  *              RDW_ValidateParent [RDW_UpdateRgns() helper] 
471  *
472  *  Validate the portions of parent that are covered by a validated child
473  *  wndPtr = child
474  */
475 void  RDW_ValidateParent(WND *wndChild)  
476 {
477     WND *wndParent = WIN_LockWndPtr(wndChild->parent);
478     WND *wndDesktop = WIN_GetDesktop();
479
480     if ((wndParent) && (wndParent != wndDesktop) && !(wndParent->dwStyle & WS_CLIPCHILDREN))
481     {
482         HRGN hrg;
483         if (wndChild->hrgnUpdate == 1 )
484         {
485             RECT r;
486
487             r.left = 0;
488             r.top = 0;
489             r.right = wndChild->rectWindow.right - wndChild->rectWindow.left;
490             r.bottom = wndChild->rectWindow.bottom - wndChild->rectWindow.top;
491
492             hrg = CreateRectRgnIndirect( &r );
493         }
494         else
495            hrg = wndChild->hrgnUpdate;
496         if (wndParent->hrgnUpdate != 0)
497         {
498             POINT ptOffset;
499             RECT rect, rectParent;
500             if( wndParent->hrgnUpdate == 1 )
501             {
502                RECT r;
503
504                r.left = 0;
505                r.top = 0;
506                r.right = wndParent->rectWindow.right - wndParent->rectWindow.left;
507                r.bottom = wndParent->rectWindow.bottom - wndParent->rectWindow.top;
508
509                wndParent->hrgnUpdate = CreateRectRgnIndirect( &r );
510             }
511             /* we must offset the child region by the offset of the child rect in the parent */
512             GetWindowRect(wndParent->hwndSelf, &rectParent);
513             GetWindowRect(wndChild->hwndSelf, &rect);
514             ptOffset.x = rect.left - rectParent.left;
515             ptOffset.y = rect.top - rectParent.top;
516             OffsetRgn( hrg, ptOffset.x, ptOffset.y );
517             CombineRgn( wndParent->hrgnUpdate, wndParent->hrgnUpdate, hrg, RGN_DIFF );
518             OffsetRgn( hrg, -ptOffset.x, -ptOffset.y );
519         }
520         if (hrg != wndChild->hrgnUpdate) DeleteObject( hrg );
521     }
522     WIN_ReleaseWndPtr(wndParent);
523     WIN_ReleaseDesktop();
524 }
525
526 /***********************************************************************
527  *              RDW_UpdateRgns [RedrawWindow() helper] 
528  *
529  *  Walks the window tree and adds/removes parts of the hRgn to/from update
530  *  regions of windows that overlap it. Also, manages internal paint flags.
531  *
532  *  NOTE: Walks the window tree so the caller must lock it.
533  *        MUST preserve hRgn (can modify but then has to restore).
534  */
535 static void RDW_UpdateRgns( WND* wndPtr, HRGN hRgn, UINT flags, BOOL firstRecursLevel )
536 {
537     /* 
538      * Called only when one of the following is set:
539      * (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT)
540      */
541
542     BOOL bHadOne =  wndPtr->hrgnUpdate && hRgn;
543     BOOL bChildren =  ( wndPtr->child && !(flags & RDW_NOCHILDREN) && !(wndPtr->dwStyle & WS_MINIMIZE) 
544                         && ((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) );
545     RECT r;
546
547     r.left = 0;
548     r.top = 0;
549     r.right = wndPtr->rectWindow.right - wndPtr->rectWindow.left;
550     r.bottom = wndPtr->rectWindow.bottom - wndPtr->rectWindow.top;
551
552     TRACE("\thwnd %04x [%04x] -> hrgn [%04x], flags [%04x]\n", wndPtr->hwndSelf, wndPtr->hrgnUpdate, hRgn, flags );
553
554     if( flags & RDW_INVALIDATE )
555     {
556         if( hRgn > 1 )
557         {
558             switch( wndPtr->hrgnUpdate )
559             {
560                 default:
561                         CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate, hRgn, RGN_OR );
562                         /* fall through */
563                 case 0:
564                         wndPtr->hrgnUpdate = REGION_CropRgn( wndPtr->hrgnUpdate, 
565                                                              wndPtr->hrgnUpdate ? wndPtr->hrgnUpdate : hRgn, 
566                                                              &r, NULL );
567                         if( !bHadOne )
568                         {
569                             GetRgnBox( wndPtr->hrgnUpdate, &r );
570                             if( IsRectEmpty( &r ) )
571                             {
572                                 DeleteObject( wndPtr->hrgnUpdate );
573                                 wndPtr->hrgnUpdate = 0;
574                                 goto end;
575                             }
576                         }
577                         break;
578                 case 1: /* already an entire window */
579                         break;
580             }
581         }
582         else if( hRgn == 1 )
583         {
584             if( wndPtr->hrgnUpdate > 1 )
585                 DeleteObject( wndPtr->hrgnUpdate );
586             wndPtr->hrgnUpdate = 1;
587         }
588         else
589             hRgn = wndPtr->hrgnUpdate;  /* this is a trick that depends on code in PAINT_RedrawWindow() */
590
591         if( !bHadOne && !(wndPtr->flags & WIN_INTERNAL_PAINT) )
592             QUEUE_IncPaintCount( wndPtr->hmemTaskQ );
593
594         if (flags & RDW_FRAME) wndPtr->flags |= WIN_NEEDS_NCPAINT;
595         if (flags & RDW_ERASE) wndPtr->flags |= WIN_NEEDS_ERASEBKGND;
596         flags    |= RDW_FRAME;
597     }
598     else if( flags & RDW_VALIDATE )
599     {
600         if( wndPtr->hrgnUpdate )
601         {
602             if( hRgn > 1 )
603             {
604                 if( wndPtr->hrgnUpdate == 1 )
605                     wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r );
606
607                 if( CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate, hRgn, RGN_DIFF )
608                     == NULLREGION )
609                     goto EMPTY;
610             }
611             else /* validate everything */
612             {
613                 if( wndPtr->hrgnUpdate > 1 )
614                 {
615 EMPTY:
616                     DeleteObject( wndPtr->hrgnUpdate );
617                 }
618                 wndPtr->hrgnUpdate = 0;
619             }
620
621             if( !wndPtr->hrgnUpdate )
622             {
623                 wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
624                 if( !(wndPtr->flags & WIN_INTERNAL_PAINT) )
625                     QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
626             }
627         }
628
629         if (flags & RDW_NOFRAME) wndPtr->flags &= ~WIN_NEEDS_NCPAINT;
630         if (flags & RDW_NOERASE) wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
631
632     }
633
634     if ((firstRecursLevel) && (wndPtr->hrgnUpdate != 0) && (flags & RDW_UPDATENOW))
635         RDW_ValidateParent(wndPtr); /* validate parent covered by region */
636
637     /* in/validate child windows that intersect with the region if it
638      * is a valid handle. */
639
640     if( flags & (RDW_INVALIDATE | RDW_VALIDATE) )
641     {
642         if( hRgn > 1 && bChildren )
643         {
644             WND* wnd = wndPtr->child;
645             POINT ptTotal, prevOrigin = {0,0};
646             POINT ptClient;
647
648             ptClient.x = wndPtr->rectClient.left - wndPtr->rectWindow.left;
649             ptClient.y = wndPtr->rectClient.top - wndPtr->rectWindow.top;
650
651             for( ptTotal.x = ptTotal.y = 0; wnd; wnd = wnd->next )
652             {
653                 if( wnd->dwStyle & WS_VISIBLE )
654                 {
655                     POINT ptOffset;
656
657                     r.left = wnd->rectWindow.left + ptClient.x;
658                     r.right = wnd->rectWindow.right + ptClient.x;
659                     r.top = wnd->rectWindow.top + ptClient.y;
660                     r.bottom = wnd->rectWindow.bottom + ptClient.y;
661
662                     ptOffset.x = r.left - prevOrigin.x; 
663                     ptOffset.y = r.top - prevOrigin.y;
664                     OffsetRect( &r, -ptTotal.x, -ptTotal.y );
665
666                     if( RectInRegion( hRgn, &r ) )
667                     {
668                         OffsetRgn( hRgn, -ptOffset.x, -ptOffset.y );
669                         RDW_UpdateRgns( wnd, hRgn, flags, FALSE );
670                         prevOrigin.x = r.left + ptTotal.x;
671                         prevOrigin.y = r.top + ptTotal.y;
672                         ptTotal.x += ptOffset.x;
673                         ptTotal.y += ptOffset.y;
674                     }
675                 }
676             }
677             OffsetRgn( hRgn, ptTotal.x, ptTotal.y );
678             bChildren = 0;
679         }
680     }
681
682     /* handle hRgn == 1 (alias for entire window) and/or internal paint recursion */
683
684     if( bChildren )
685     {
686         WND* wnd;
687         for( wnd = wndPtr->child; wnd; wnd = wnd->next )
688              if( wnd->dwStyle & WS_VISIBLE )
689                  RDW_UpdateRgns( wnd, hRgn, flags, FALSE );
690     }
691
692 end:
693
694     /* Set/clear internal paint flag */
695
696     if (flags & RDW_INTERNALPAINT)
697     {
698         if ( !wndPtr->hrgnUpdate && !(wndPtr->flags & WIN_INTERNAL_PAINT))
699             QUEUE_IncPaintCount( wndPtr->hmemTaskQ );
700         wndPtr->flags |= WIN_INTERNAL_PAINT;
701     }
702     else if (flags & RDW_NOINTERNALPAINT)
703     {
704         if ( !wndPtr->hrgnUpdate && (wndPtr->flags & WIN_INTERNAL_PAINT))
705             QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
706         wndPtr->flags &= ~WIN_INTERNAL_PAINT;
707     }
708 }
709
710 /***********************************************************************
711  *           RDW_Paint [RedrawWindow() helper]
712  *
713  * Walks the window tree and paints/erases windows that have
714  * nonzero update regions according to redraw flags. hrgn is a scratch
715  * region passed down during recursion. Must not be 1.
716  *
717  */
718 static HRGN RDW_Paint( WND* wndPtr, HRGN hrgn, UINT flags, UINT ex )
719 {
720 /* NOTE: wndPtr is locked by caller.
721  * 
722  * FIXME: Windows uses WM_SYNCPAINT to cut down the number of intertask
723  * SendMessage() calls. This is a comment inside DefWindowProc() source
724  * from 16-bit SDK:
725  *
726  *   This message avoids lots of inter-app message traffic
727  *   by switching to the other task and continuing the
728  *   recursion there.
729  *
730  * wParam         = flags
731  * LOWORD(lParam) = hrgnClip
732  * HIWORD(lParam) = hwndSkip  (not used; always NULL)
733  *
734  */
735     HDC  hDC;
736     HWND hWnd = wndPtr->hwndSelf;
737     BOOL bIcon = ((wndPtr->dwStyle & WS_MINIMIZE) && GetClassWord(wndPtr->hwndSelf, GCW_HICON)); 
738
739       /* Erase/update the window itself ... */
740
741     TRACE("\thwnd %04x [%04x] -> hrgn [%04x], flags [%04x]\n", hWnd, wndPtr->hrgnUpdate, hrgn, flags );
742
743     /*
744      * Check if this window should delay it's processing of WM_NCPAINT.
745      * See WIN_HaveToDelayNCPAINT for a description of the mechanism
746      */
747     if ((ex & RDW_EX_DELAY_NCPAINT) || WIN_HaveToDelayNCPAINT(wndPtr, 0) )
748         ex |= RDW_EX_DELAY_NCPAINT;
749
750     if (flags & RDW_UPDATENOW)
751     {
752         if (wndPtr->hrgnUpdate) /* wm_painticon wparam is 1 */
753             SendMessage16( hWnd, (bIcon) ? WM_PAINTICON : WM_PAINT, bIcon, 0 );
754     }
755     else if ((flags & RDW_ERASENOW) || (ex & RDW_EX_TOPFRAME))
756     {
757         UINT dcx = DCX_INTERSECTRGN | DCX_USESTYLE | DCX_KEEPCLIPRGN | DCX_WINDOWPAINT | DCX_CACHE;
758         HRGN hrgnRet;
759
760         hrgnRet = WIN_UpdateNCRgn(wndPtr, 
761                                   hrgn, 
762                                   UNC_REGION | UNC_CHECK | 
763                                   ((ex & RDW_EX_TOPFRAME) ? UNC_ENTIRE : 0) |
764                                   ((ex & RDW_EX_DELAY_NCPAINT) ? UNC_DELAY_NCPAINT : 0) ); 
765
766         if( hrgnRet )
767         {
768             if( hrgnRet > 1 ) hrgn = hrgnRet; else hrgnRet = 0; /* entire client */
769             if( wndPtr->flags & WIN_NEEDS_ERASEBKGND )
770             {
771                 if( bIcon ) dcx |= DCX_WINDOW;
772                 else 
773                 if( hrgnRet )
774                     OffsetRgn( hrgnRet, wndPtr->rectWindow.left - wndPtr->rectClient.left, 
775                                         wndPtr->rectWindow.top  - wndPtr->rectClient.top);
776                 else
777                     dcx &= ~DCX_INTERSECTRGN;
778                 if (( hDC = GetDCEx( hWnd, hrgnRet, dcx )) )
779                 {
780                     if (SendMessage16( hWnd, (bIcon) ? WM_ICONERASEBKGND
781                                        : WM_ERASEBKGND, (WPARAM16)hDC, 0 ))
782                     wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
783                     ReleaseDC( hWnd, hDC );
784                 }
785             }
786         }
787     }
788
789     if( !IsWindow(hWnd) ) return hrgn;
790     ex &= ~RDW_EX_TOPFRAME;
791
792       /* ... and its child windows */
793
794     if( wndPtr->child && !(flags & RDW_NOCHILDREN) && !(wndPtr->dwStyle & WS_MINIMIZE) 
795         && ((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) )
796     {
797         WND** list, **ppWnd;
798
799         if( (list = WIN_BuildWinArray( wndPtr, 0, NULL )) )
800         {
801             wndPtr = NULL;
802             for (ppWnd = list; *ppWnd; ppWnd++)
803             {
804                 WIN_UpdateWndPtr(&wndPtr,*ppWnd);
805                 if (!IsWindow(wndPtr->hwndSelf)) continue;
806                     if ( (wndPtr->dwStyle & WS_VISIBLE) &&
807                          (wndPtr->hrgnUpdate || (wndPtr->flags & WIN_INTERNAL_PAINT)) )
808                         hrgn = RDW_Paint( wndPtr, hrgn, flags, ex );
809             }
810             WIN_ReleaseWndPtr(wndPtr);
811             WIN_ReleaseWinArray(list);
812         }
813     }
814
815     return hrgn;
816 }
817
818 /***********************************************************************
819  *           PAINT_RedrawWindow
820  *
821  */
822 BOOL PAINT_RedrawWindow( HWND hwnd, const RECT *rectUpdate,
823                            HRGN hrgnUpdate, UINT flags, UINT ex )
824 {
825     HRGN hRgn = 0;
826     RECT r, r2;
827     POINT pt;
828     WND* wndPtr;
829
830     if (!hwnd) hwnd = GetDesktopWindow();
831     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE;
832
833     /* check if the window or its parents are visible/not minimized */
834
835     if (!WIN_IsWindowDrawable( wndPtr, !(flags & RDW_FRAME) ) )
836     {
837         WIN_ReleaseWndPtr(wndPtr);
838         return TRUE; 
839     }
840
841     if (TRACE_ON(win))
842     {
843         if( hrgnUpdate )
844         {
845             GetRgnBox( hrgnUpdate, &r );
846             TRACE( "%04x (%04x) NULL %04x box (%i,%i-%i,%i) flags=%04x, exflags=%04x\n", 
847                   hwnd, wndPtr->hrgnUpdate, hrgnUpdate, r.left, r.top, r.right, r.bottom, flags, ex);
848         }
849         else
850         {
851             if( rectUpdate )
852                 r = *rectUpdate;
853             else
854                 SetRectEmpty( &r );
855             TRACE( "%04x (%04x) %s %d,%d-%d,%d %04x flags=%04x, exflags=%04x\n",
856                         hwnd, wndPtr->hrgnUpdate, rectUpdate ? "rect" : "NULL", r.left, 
857                         r.top, r.right, r.bottom, hrgnUpdate, flags, ex );
858         }
859     }
860
861     /* prepare an update region in window coordinates */
862
863     if( flags & RDW_FRAME )
864         r = wndPtr->rectWindow;
865     else
866         r = wndPtr->rectClient;
867
868     if( ex & RDW_EX_XYWINDOW )
869     {
870         pt.x = pt.y = 0;
871         OffsetRect( &r, -wndPtr->rectWindow.left, -wndPtr->rectWindow.top );
872     }
873     else
874     {
875         pt.x = wndPtr->rectClient.left - wndPtr->rectWindow.left;
876         pt.y = wndPtr->rectClient.top - wndPtr->rectWindow.top;
877         OffsetRect( &r, -wndPtr->rectClient.left, -wndPtr->rectClient.top );
878     }
879
880     if (flags & RDW_INVALIDATE)  /* ------------------------- Invalidate */
881     {
882         /* If the window doesn't have hrgnUpdate we leave hRgn zero
883          * and put a new region straight into wndPtr->hrgnUpdate
884          * so that RDW_UpdateRgns() won't have to do any extra work.
885          */
886
887         if( hrgnUpdate )
888         {
889             if( wndPtr->hrgnUpdate )
890                 hRgn = REGION_CropRgn( 0, hrgnUpdate, NULL, &pt );
891             else 
892                 wndPtr->hrgnUpdate = REGION_CropRgn( 0, hrgnUpdate, &r, &pt ); 
893         }
894         else if( rectUpdate )
895         {
896             if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
897             OffsetRect( &r2, pt.x, pt.y );
898
899 rect2i:
900             if( wndPtr->hrgnUpdate == 0 )
901                 wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r2 );
902             else
903                 hRgn = CreateRectRgnIndirect( &r2 );
904         }
905         else /* entire window or client depending on RDW_FRAME */
906         {
907             if( flags & RDW_FRAME )
908             {
909                 if( wndPtr->hrgnUpdate )
910                     DeleteObject( wndPtr->hrgnUpdate );
911                 wndPtr->hrgnUpdate = 1;
912             }
913             else
914             {
915                 GETCLIENTRECTW( wndPtr, r2 );
916                 goto rect2i;
917             }
918         }
919     }
920     else if (flags & RDW_VALIDATE)  /* ------------------------- Validate */
921     {
922         /* In this we cannot leave with zero hRgn */
923         if( hrgnUpdate )
924         {
925             hRgn = REGION_CropRgn( hRgn, hrgnUpdate,  &r, &pt );
926             GetRgnBox( hRgn, &r2 );
927             if( IsRectEmpty( &r2 ) ) goto END;
928         }
929         else if( rectUpdate )
930         {
931             if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
932                 OffsetRect( &r2, pt.x, pt.y );
933 rect2v:
934             hRgn = CreateRectRgnIndirect( &r2 );
935         }
936         else /* entire window or client depending on RDW_FRAME */
937         {
938             if( flags & RDW_FRAME ) 
939                 hRgn = 1;
940             else
941             {
942                 GETCLIENTRECTW( wndPtr, r2 );
943                 goto rect2v;
944             }
945         }
946     }
947
948     /* At this point hRgn is either an update region in window coordinates or 1 or 0 */
949
950     RDW_UpdateRgns( wndPtr, hRgn, flags, TRUE );
951
952     /* Erase/update windows, from now on hRgn is a scratch region */
953
954     hRgn = RDW_Paint( wndPtr, (hRgn == 1) ? 0 : hRgn, flags, ex );
955
956 END:
957     if( hRgn > 1 && (hRgn != hrgnUpdate) )
958         DeleteObject(hRgn );
959     WIN_ReleaseWndPtr(wndPtr);
960     return TRUE;
961 }
962
963
964 /***********************************************************************
965  *           RedrawWindow32    (USER32.426)
966  */
967 BOOL WINAPI RedrawWindow( HWND hwnd, const RECT *rectUpdate,
968                               HRGN hrgnUpdate, UINT flags )
969 {
970     return PAINT_RedrawWindow( hwnd, rectUpdate, hrgnUpdate, flags, 0 );
971 }
972
973
974 /***********************************************************************
975  *           RedrawWindow16    (USER.290)
976  */
977 BOOL16 WINAPI RedrawWindow16( HWND16 hwnd, const RECT16 *rectUpdate,
978                               HRGN16 hrgnUpdate, UINT16 flags )
979 {
980     if (rectUpdate)
981     {
982         RECT r;
983         CONV_RECT16TO32( rectUpdate, &r );
984         return (BOOL16)RedrawWindow( (HWND)hwnd, &r, hrgnUpdate, flags );
985     }
986     return (BOOL16)PAINT_RedrawWindow( (HWND)hwnd, NULL, 
987                                        (HRGN)hrgnUpdate, flags, 0 );
988 }
989
990
991 /***********************************************************************
992  *           UpdateWindow16   (USER.124)
993  */
994 void WINAPI UpdateWindow16( HWND16 hwnd )
995 {
996     PAINT_RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_NOCHILDREN, 0 );
997 }
998
999 /***********************************************************************
1000  *           UpdateWindow32   (USER32.567)
1001  */
1002 void WINAPI UpdateWindow( HWND hwnd )
1003 {
1004     PAINT_RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_NOCHILDREN, 0 );
1005 }
1006
1007 /***********************************************************************
1008  *           InvalidateRgn16   (USER.126)
1009  */
1010 void WINAPI InvalidateRgn16( HWND16 hwnd, HRGN16 hrgn, BOOL16 erase )
1011 {
1012     PAINT_RedrawWindow((HWND)hwnd, NULL, (HRGN)hrgn, 
1013                        RDW_INVALIDATE | (erase ? RDW_ERASE : 0), 0 );
1014 }
1015
1016
1017 /***********************************************************************
1018  *           InvalidateRgn32   (USER32.329)
1019  */
1020 BOOL WINAPI InvalidateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
1021 {
1022     return PAINT_RedrawWindow(hwnd, NULL, hrgn, RDW_INVALIDATE | (erase ? RDW_ERASE : 0), 0 );
1023 }
1024
1025
1026 /***********************************************************************
1027  *           InvalidateRect16   (USER.125)
1028  */
1029 void WINAPI InvalidateRect16( HWND16 hwnd, const RECT16 *rect, BOOL16 erase )
1030 {
1031     RedrawWindow16( hwnd, rect, 0, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
1032 }
1033
1034
1035 /***********************************************************************
1036  *           InvalidateRect32   (USER32.328)
1037  */
1038 BOOL WINAPI InvalidateRect( HWND hwnd, const RECT *rect, BOOL erase )
1039 {
1040     return PAINT_RedrawWindow( hwnd, rect, 0, 
1041                                RDW_INVALIDATE | (erase ? RDW_ERASE : 0), 0 );
1042 }
1043
1044
1045 /***********************************************************************
1046  *           ValidateRgn16   (USER.128)
1047  */
1048 void WINAPI ValidateRgn16( HWND16 hwnd, HRGN16 hrgn )
1049 {
1050     PAINT_RedrawWindow( (HWND)hwnd, NULL, (HRGN)hrgn, 
1051                         RDW_VALIDATE | RDW_NOCHILDREN, 0 );
1052 }
1053
1054
1055 /***********************************************************************
1056  *           ValidateRgn32   (USER32.572)
1057  */
1058 void WINAPI ValidateRgn( HWND hwnd, HRGN hrgn )
1059 {
1060     PAINT_RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOCHILDREN, 0 );
1061 }
1062
1063
1064 /***********************************************************************
1065  *           ValidateRect16   (USER.127)
1066  */
1067 void WINAPI ValidateRect16( HWND16 hwnd, const RECT16 *rect )
1068 {
1069     RedrawWindow16( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN );
1070 }
1071
1072
1073 /***********************************************************************
1074  *           ValidateRect32   (USER32.571)
1075  */
1076 void WINAPI ValidateRect( HWND hwnd, const RECT *rect )
1077 {
1078     PAINT_RedrawWindow( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN, 0 );
1079 }
1080
1081
1082 /***********************************************************************
1083  *           GetUpdateRect16   (USER.190)
1084  */
1085 BOOL16 WINAPI GetUpdateRect16( HWND16 hwnd, LPRECT16 rect, BOOL16 erase )
1086 {
1087     RECT r;
1088     BOOL16 ret;
1089
1090     if (!rect) return GetUpdateRect( hwnd, NULL, erase );
1091     ret = GetUpdateRect( hwnd, &r, erase );
1092     CONV_RECT32TO16( &r, rect );
1093     return ret;
1094 }
1095
1096
1097 /***********************************************************************
1098  *           GetUpdateRect32   (USER32.297)
1099  */
1100 BOOL WINAPI GetUpdateRect( HWND hwnd, LPRECT rect, BOOL erase )
1101 {
1102     BOOL retvalue;
1103     WND * wndPtr = WIN_FindWndPtr( hwnd );
1104     if (!wndPtr) return FALSE;
1105
1106     if (rect)
1107     {
1108         if (wndPtr->hrgnUpdate > 1)
1109         {
1110             HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
1111             if (GetUpdateRgn( hwnd, hrgn, erase ) == ERROR)
1112             {
1113                 retvalue = FALSE;
1114                 goto END;
1115             }
1116             GetRgnBox( hrgn, rect );
1117             DeleteObject( hrgn );
1118             if (GetClassLongA(wndPtr->hwndSelf, GCL_STYLE) & CS_OWNDC)
1119             {
1120                 if (GetMapMode(wndPtr->dce->hDC) != MM_TEXT)
1121                 {
1122                     DPtoLP (wndPtr->dce->hDC, (LPPOINT)rect,  2);
1123                 }
1124             }
1125         }
1126         else
1127         if( wndPtr->hrgnUpdate == 1 )
1128         {
1129             GetClientRect( hwnd, rect );
1130             if (erase) RedrawWindow( hwnd, NULL, 0, RDW_FRAME | RDW_ERASENOW | RDW_NOCHILDREN );
1131         }
1132         else 
1133             SetRectEmpty( rect );
1134     }
1135     retvalue = (wndPtr->hrgnUpdate >= 1);
1136 END:
1137     WIN_ReleaseWndPtr(wndPtr);
1138     return retvalue;
1139 }
1140
1141
1142 /***********************************************************************
1143  *           GetUpdateRgn16   (USER.237)
1144  */
1145 INT16 WINAPI GetUpdateRgn16( HWND16 hwnd, HRGN16 hrgn, BOOL16 erase )
1146 {
1147     return GetUpdateRgn( hwnd, hrgn, erase );
1148 }
1149
1150
1151 /***********************************************************************
1152  *           GetUpdateRgn    (USER32.298)
1153  */
1154 INT WINAPI GetUpdateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
1155 {
1156     INT retval;
1157     WND * wndPtr = WIN_FindWndPtr( hwnd );
1158     if (!wndPtr) return ERROR;
1159
1160     if (wndPtr->hrgnUpdate == 0)
1161     {
1162         SetRectRgn( hrgn, 0, 0, 0, 0 );
1163         retval = NULLREGION;
1164         goto END;
1165     }
1166     else
1167     if (wndPtr->hrgnUpdate == 1)
1168     {
1169         SetRectRgn( hrgn, 0, 0, wndPtr->rectClient.right - wndPtr->rectClient.left,
1170                                 wndPtr->rectClient.bottom - wndPtr->rectClient.top );
1171         retval = SIMPLEREGION;
1172     }
1173     else
1174     {
1175         retval = CombineRgn( hrgn, wndPtr->hrgnUpdate, 0, RGN_COPY );
1176         OffsetRgn( hrgn, wndPtr->rectWindow.left - wndPtr->rectClient.left,
1177                          wndPtr->rectWindow.top - wndPtr->rectClient.top );
1178     }
1179     if (erase) RedrawWindow( hwnd, NULL, 0, RDW_ERASENOW | RDW_NOCHILDREN );
1180 END:
1181     WIN_ReleaseWndPtr(wndPtr);
1182     return retval;
1183 }
1184
1185
1186 /***********************************************************************
1187  *           ExcludeUpdateRgn16   (USER.238)
1188  */
1189 INT16 WINAPI ExcludeUpdateRgn16( HDC16 hdc, HWND16 hwnd )
1190 {
1191     return ExcludeUpdateRgn( hdc, hwnd );
1192 }
1193
1194
1195 /***********************************************************************
1196  *           ExcludeUpdateRgn32   (USER32.195)
1197  */
1198 INT WINAPI ExcludeUpdateRgn( HDC hdc, HWND hwnd )
1199 {
1200     RECT rect;
1201     WND * wndPtr;
1202
1203     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return ERROR;
1204
1205     if (wndPtr->hrgnUpdate)
1206     {
1207         INT ret;
1208         HRGN hrgn = CreateRectRgn(wndPtr->rectWindow.left - wndPtr->rectClient.left,
1209                                       wndPtr->rectWindow.top - wndPtr->rectClient.top,
1210                                       wndPtr->rectWindow.right - wndPtr->rectClient.left,
1211                                       wndPtr->rectWindow.bottom - wndPtr->rectClient.top);
1212         if( wndPtr->hrgnUpdate > 1 )
1213         {
1214             CombineRgn(hrgn, wndPtr->hrgnUpdate, 0, RGN_COPY);
1215             OffsetRgn(hrgn, wndPtr->rectWindow.left - wndPtr->rectClient.left, 
1216                             wndPtr->rectWindow.top - wndPtr->rectClient.top );
1217         }
1218
1219         /* do ugly coordinate translations in dce.c */
1220
1221         ret = DCE_ExcludeRgn( hdc, wndPtr, hrgn );
1222         DeleteObject( hrgn );
1223         WIN_ReleaseWndPtr(wndPtr);
1224         return ret;
1225     } 
1226     WIN_ReleaseWndPtr(wndPtr);
1227     return GetClipBox( hdc, &rect );
1228 }
1229
1230
1231
1232 /***********************************************************************
1233  *           FillRect16    (USER.81)
1234  * NOTE
1235  *   The Win16 variant doesn't support special color brushes like
1236  *   the Win32 one, despite the fact that Win16, as well as Win32,
1237  *   supports special background brushes for a window class.
1238  */
1239 INT16 WINAPI FillRect16( HDC16 hdc, const RECT16 *rect, HBRUSH16 hbrush )
1240 {
1241     HBRUSH16 prevBrush;
1242
1243     /* coordinates are logical so we cannot fast-check 'rect',
1244      * it will be done later in the PatBlt().
1245      */
1246
1247     if (!(prevBrush = SelectObject16( hdc, hbrush ))) return 0;
1248     PatBlt( hdc, rect->left, rect->top,
1249               rect->right - rect->left, rect->bottom - rect->top, PATCOPY );
1250     SelectObject16( hdc, prevBrush );
1251     return 1;
1252 }
1253
1254
1255 /***********************************************************************
1256  *           FillRect    (USER32.197)
1257  */
1258 INT WINAPI FillRect( HDC hdc, const RECT *rect, HBRUSH hbrush )
1259 {
1260     HBRUSH prevBrush;
1261
1262     if (hbrush <= (HBRUSH) (COLOR_MAX + 1)) {
1263         hbrush = GetSysColorBrush( (INT) hbrush - 1 );
1264     }
1265
1266     if (!(prevBrush = SelectObject( hdc, hbrush ))) return 0;
1267     PatBlt( hdc, rect->left, rect->top,
1268               rect->right - rect->left, rect->bottom - rect->top, PATCOPY );
1269     SelectObject( hdc, prevBrush );
1270     return 1;
1271 }
1272
1273
1274 /***********************************************************************
1275  *           InvertRect16    (USER.82)
1276  */
1277 void WINAPI InvertRect16( HDC16 hdc, const RECT16 *rect )
1278 {
1279     PatBlt( hdc, rect->left, rect->top,
1280               rect->right - rect->left, rect->bottom - rect->top, DSTINVERT );
1281 }
1282
1283
1284 /***********************************************************************
1285  *           InvertRect    (USER32.330)
1286  */
1287 BOOL WINAPI InvertRect( HDC hdc, const RECT *rect )
1288 {
1289     return PatBlt( hdc, rect->left, rect->top,
1290                      rect->right - rect->left, rect->bottom - rect->top, 
1291                      DSTINVERT );
1292 }
1293
1294
1295 /***********************************************************************
1296  *           FrameRect    (USER32.203)
1297  */
1298 INT WINAPI FrameRect( HDC hdc, const RECT *rect, HBRUSH hbrush )
1299 {
1300     HBRUSH prevBrush;
1301     RECT r = *rect;
1302
1303     LPtoDP(hdc, (POINT *)&r, 2);
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