Release 960818
[wine] / windows / painting.c
1 /*
2  * Window painting functions
3  *
4  * Copyright 1993, 1994, 1995 Alexandre Julliard
5  */
6
7 #include <stdio.h>
8 #include <X11/Xlib.h>
9
10 #include "win.h"
11 #include "queue.h"
12 #include "gdi.h"
13 #include "dce.h"
14 #include "stddebug.h"
15 /* #define DEBUG_WIN */
16 #include "debug.h"
17
18   /* Last CTLCOLOR id */
19 #define CTLCOLOR_MAX   CTLCOLOR_STATIC
20
21 /***********************************************************************
22  *           WIN_UpdateNCArea
23  *
24  */
25 void WIN_UpdateNCArea(WND* wnd, BOOL bUpdate)
26 {
27     POINT16 pt = {0, 0}; 
28     HRGN hClip = 1;
29
30     dprintf_nonclient(stddeb,"NCUpdate: hwnd %04x, hrgnUpdate %04x\n", 
31                       wnd->hwndSelf, wnd->hrgnUpdate );
32
33     /* desktop window doesn't have nonclient area */
34     if(wnd == WIN_GetDesktop()) 
35     {
36         wnd->flags &= ~WIN_NEEDS_NCPAINT;
37         return;
38     }
39
40     if( wnd->hrgnUpdate > 1 )
41     {
42         ClientToScreen16(wnd->hwndSelf, &pt);
43
44         hClip = CreateRectRgn( 0, 0, 0, 0 );
45         if (!CombineRgn(hClip, wnd->hrgnUpdate, 0, RGN_COPY) )
46         {
47             DeleteObject(hClip);
48             hClip = 1;
49         }
50         else
51             OffsetRgn(hClip, pt.x, pt.y);
52
53         if (bUpdate)
54         {
55             /* exclude non-client area from update region */
56             HRGN hrgn = CreateRectRgn(0, 0, wnd->rectClient.right - wnd->rectClient.left,
57                                             wnd->rectClient.bottom - wnd->rectClient.top);
58
59             if (hrgn && (CombineRgn(wnd->hrgnUpdate, wnd->hrgnUpdate,
60                                     hrgn, RGN_AND) == NULLREGION))
61             {
62                 DeleteObject(wnd->hrgnUpdate);
63                 wnd->hrgnUpdate = 1;
64             }
65
66             DeleteObject( hrgn );
67         }
68     }
69
70     wnd->flags &= ~WIN_NEEDS_NCPAINT;
71
72     if ((wnd->hwndSelf == GetActiveWindow()) &&
73         !(wnd->flags & WIN_NCACTIVATED))
74     {
75         wnd->flags |= WIN_NCACTIVATED;
76         if( hClip > 1) DeleteObject(hClip);
77         hClip = 1;
78     }
79
80     if (hClip) SendMessage16( wnd->hwndSelf, WM_NCPAINT, hClip, 0L );
81
82     if (hClip > 1) DeleteObject( hClip );
83 }
84
85
86 /***********************************************************************
87  *           BeginPaint16    (USER.39)
88  */
89 HDC16 BeginPaint16( HWND16 hwnd, LPPAINTSTRUCT16 lps ) 
90 {
91     BOOL32 bIcon;
92     HRGN hrgnUpdate;
93     WND *wndPtr = WIN_FindWndPtr( hwnd );
94     if (!wndPtr) return 0;
95
96     bIcon = (wndPtr->dwStyle & WS_MINIMIZE && wndPtr->class->hIcon);
97
98     wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
99
100     if (wndPtr->flags & WIN_NEEDS_NCPAINT) WIN_UpdateNCArea( wndPtr, TRUE );
101
102     if (((hrgnUpdate = wndPtr->hrgnUpdate) != 0) ||
103         (wndPtr->flags & WIN_INTERNAL_PAINT))
104         QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
105
106     wndPtr->hrgnUpdate = 0;
107     wndPtr->flags &= ~WIN_INTERNAL_PAINT;
108
109     HideCaret( hwnd );
110
111     dprintf_win(stddeb,"hrgnUpdate = %04x, ", hrgnUpdate);
112
113     /* When bIcon is TRUE hrgnUpdate is automatically in window coordinates
114      * (because rectClient == rectWindow for WS_MINIMIZE windows).
115      */
116
117     lps->hdc = GetDCEx( hwnd, hrgnUpdate, DCX_INTERSECTRGN | DCX_WINDOWPAINT |
118                         DCX_USESTYLE | (bIcon ? DCX_WINDOW : 0) );
119
120     dprintf_win(stddeb,"hdc = %04x\n", lps->hdc);
121
122     if (!lps->hdc)
123     {
124         fprintf(stderr, "GetDCEx() failed in BeginPaint(), hwnd=%04x\n", hwnd);
125         return 0;
126     }
127
128     GetRgnBox16( InquireVisRgn(lps->hdc), &lps->rcPaint );
129     DPtoLP16( lps->hdc, (LPPOINT16)&lps->rcPaint, 2 );
130
131     if (wndPtr->flags & WIN_NEEDS_ERASEBKGND)
132     {
133         wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
134         lps->fErase = !SendMessage16(hwnd, (bIcon) ? WM_ICONERASEBKGND
135                                                    : WM_ERASEBKGND,
136                                      (WPARAM)lps->hdc, 0 );
137     }
138     else lps->fErase = TRUE;
139
140     return lps->hdc;
141 }
142
143
144 /***********************************************************************
145  *           BeginPaint32    (USER32.9)
146  */
147 HDC32 BeginPaint32( HWND32 hwnd, PAINTSTRUCT32 *lps )
148 {
149     PAINTSTRUCT16 ps;
150
151     BeginPaint16( hwnd, &ps );
152     lps->hdc            = (HDC32)ps.hdc;
153     lps->fErase         = ps.fErase;
154     lps->rcPaint.top    = ps.rcPaint.top;
155     lps->rcPaint.left   = ps.rcPaint.left;
156     lps->rcPaint.right  = ps.rcPaint.right;
157     lps->rcPaint.bottom = ps.rcPaint.bottom;
158     lps->fRestore       = ps.fRestore;
159     lps->fIncUpdate     = ps.fIncUpdate;
160     return lps->hdc;
161 }
162
163
164 /***********************************************************************
165  *           EndPaint16    (USER.40)
166  */
167 BOOL16 EndPaint16( HWND16 hwnd, const PAINTSTRUCT16* lps )
168 {
169     ReleaseDC( hwnd, lps->hdc );
170     ShowCaret( hwnd );
171     return TRUE;
172 }
173
174
175 /***********************************************************************
176  *           EndPaint32    (USER32.175)
177  */
178 BOOL32 EndPaint32( HWND32 hwnd, const PAINTSTRUCT32 *lps )
179 {
180     ReleaseDC( hwnd, (HDC16)lps->hdc );
181     ShowCaret( hwnd );
182     return TRUE;
183 }
184
185
186 /***********************************************************************
187  *           FillWindow    (USER.324)
188  */
189 void FillWindow( HWND16 hwndParent, HWND16 hwnd, HDC16 hdc, HBRUSH16 hbrush )
190 {
191     RECT16 rect;
192     GetClientRect16( hwnd, &rect );
193     DPtoLP16( hdc, (LPPOINT16)&rect, 2 );
194     PaintRect( hwndParent, hwnd, hdc, hbrush, &rect );
195 }
196
197
198 /***********************************************************************
199  *           PaintRect    (USER.325)
200  */
201 void PaintRect( HWND16 hwndParent, HWND16 hwnd, HDC16 hdc,
202                 HBRUSH16 hbrush, const RECT16 *rect)
203 {
204       /* Send WM_CTLCOLOR message if needed */
205
206     if ((UINT32)hbrush <= CTLCOLOR_MAX)
207     {
208         if (!hwndParent) return;
209         hbrush = (HBRUSH)SendMessage32A( hwndParent, 
210                                          WM_CTLCOLORMSGBOX + (UINT32)hbrush,
211                                          (WPARAM)hdc, (LPARAM)hwnd );
212     }
213     if (hbrush) FillRect16( hdc, rect, hbrush );
214 }
215
216
217 /***********************************************************************
218  *           GetControlBrush    (USER.326)
219  */
220 HBRUSH GetControlBrush( HWND hwnd, HDC hdc, WORD control )
221 {
222     return (HBRUSH)SendMessage32A( GetParent(hwnd), WM_CTLCOLOR+control,
223                                    (WPARAM)hdc, (LPARAM)hwnd );
224 }
225
226
227 /***********************************************************************
228  *           PAINT_RedrawWindow
229  *
230  * Note: Windows uses WM_SYNCPAINT to cut down the number of intertask
231  * SendMessage() calls. From SDK:
232  *   This message avoids lots of inter-app message traffic
233  *   by switching to the other task and continuing the
234  *   recursion there.
235  * 
236  * wParam         = flags
237  * LOWORD(lParam) = hrgnClip
238  * HIWORD(lParam) = hwndSkip  (not used; always NULL)
239  */
240 BOOL32 PAINT_RedrawWindow( HWND32 hwnd, const RECT32 *rectUpdate,
241                          HRGN32 hrgnUpdate, UINT32 flags, UINT32 control )
242 {
243     BOOL32 bIcon;
244     HRGN hrgn;
245     RECT32 rectClient;
246     WND* wndPtr;
247
248     if (!hwnd) hwnd = GetDesktopWindow();
249     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE;
250     if (!IsWindowVisible(hwnd) || (wndPtr->flags & WIN_NO_REDRAW))
251         return TRUE;  /* No redraw needed */
252
253     bIcon = (wndPtr->dwStyle & WS_MINIMIZE && wndPtr->class->hIcon);
254     if (rectUpdate)
255     {
256         dprintf_win(stddeb, "RedrawWindow: %04x %d,%d-%d,%d %04x flags=%04x\n",
257                     hwnd, rectUpdate->left, rectUpdate->top,
258                     rectUpdate->right, rectUpdate->bottom, hrgnUpdate, flags );
259     }
260     else
261     {
262         dprintf_win(stddeb, "RedrawWindow: %04x NULL %04x flags=%04x\n",
263                      hwnd, hrgnUpdate, flags);
264     }
265     GetClientRect32( hwnd, &rectClient );
266
267     if (flags & RDW_INVALIDATE)  /* Invalidate */
268     {
269         int rgnNotEmpty = COMPLEXREGION;
270
271         if (wndPtr->hrgnUpdate > 1)  /* Is there already an update region? */
272         {
273             if ((hrgn = hrgnUpdate) == 0)
274                 hrgn = CreateRectRgnIndirect32( rectUpdate ? rectUpdate :
275                                                 &rectClient );
276             rgnNotEmpty = CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate, hrgn, RGN_OR );
277             if (!hrgnUpdate) DeleteObject( hrgn );
278         }
279         else  /* No update region yet */
280         {
281             if (!(wndPtr->flags & WIN_INTERNAL_PAINT))
282                 QUEUE_IncPaintCount( wndPtr->hmemTaskQ );
283             if (hrgnUpdate)
284             {
285                 wndPtr->hrgnUpdate = CreateRectRgn( 0, 0, 0, 0 );
286                 rgnNotEmpty = CombineRgn( wndPtr->hrgnUpdate, hrgnUpdate, 0, RGN_COPY );
287             }
288             else wndPtr->hrgnUpdate = CreateRectRgnIndirect32( rectUpdate ?
289                                                     rectUpdate : &rectClient );
290         }
291         
292         if (flags & RDW_FRAME) wndPtr->flags |= WIN_NEEDS_NCPAINT;
293
294         /* check for bogus update region */ 
295         if ( rgnNotEmpty == NULLREGION )
296            {
297              wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
298              DeleteObject(wndPtr->hrgnUpdate);
299              wndPtr->hrgnUpdate=0;
300              if (!(wndPtr->flags & WIN_INTERNAL_PAINT))
301                    QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
302            }
303         else
304              if (flags & RDW_ERASE) wndPtr->flags |= WIN_NEEDS_ERASEBKGND;
305         flags |= RDW_FRAME;  /* Force children frame invalidation */
306     }
307     else if (flags & RDW_VALIDATE)  /* Validate */
308     {
309           /* We need an update region in order to validate anything */
310         if (wndPtr->hrgnUpdate > 1)
311         {
312             if (!hrgnUpdate && !rectUpdate)
313             {
314                   /* Special case: validate everything */
315                 DeleteObject( wndPtr->hrgnUpdate );
316                 wndPtr->hrgnUpdate = 0;
317             }
318             else
319             {
320                 if ((hrgn = hrgnUpdate) == 0)
321                     hrgn = CreateRectRgnIndirect32( rectUpdate );
322                 if (CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate,
323                                 hrgn, RGN_DIFF ) == NULLREGION)
324                 {
325                     DeleteObject( wndPtr->hrgnUpdate );
326                     wndPtr->hrgnUpdate = 0;
327                 }
328                 if (!hrgnUpdate) DeleteObject( hrgn );
329             }
330             if (!wndPtr->hrgnUpdate)  /* No more update region */
331                 if (!(wndPtr->flags & WIN_INTERNAL_PAINT))
332                     QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
333         }
334         if (flags & RDW_NOFRAME) wndPtr->flags &= ~WIN_NEEDS_NCPAINT;
335         if (flags & RDW_NOERASE) wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
336     }
337
338       /* Set/clear internal paint flag */
339
340     if (flags & RDW_INTERNALPAINT)
341     {
342         if ( wndPtr->hrgnUpdate <= 1 && !(wndPtr->flags & WIN_INTERNAL_PAINT))
343             QUEUE_IncPaintCount( wndPtr->hmemTaskQ );
344         wndPtr->flags |= WIN_INTERNAL_PAINT;        
345     }
346     else if (flags & RDW_NOINTERNALPAINT)
347     {
348         if ( wndPtr->hrgnUpdate <= 1 && (wndPtr->flags & WIN_INTERNAL_PAINT))
349             QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
350         wndPtr->flags &= ~WIN_INTERNAL_PAINT;
351     }
352
353       /* Erase/update window */
354
355     if (flags & RDW_UPDATENOW)
356     {
357         if (wndPtr->hrgnUpdate) /* wm_painticon wparam is 1 */
358             SendMessage16( hwnd, (bIcon) ? WM_PAINTICON : WM_PAINT, bIcon, 0 );
359     }
360     else if (flags & RDW_ERASENOW)
361     {
362         if (wndPtr->flags & WIN_NEEDS_NCPAINT)
363             WIN_UpdateNCArea( wndPtr, FALSE);
364
365         if (wndPtr->flags & WIN_NEEDS_ERASEBKGND)
366         {
367             HDC hdc = GetDCEx( hwnd, wndPtr->hrgnUpdate,
368                                DCX_INTERSECTRGN | DCX_USESTYLE |
369                                DCX_KEEPCLIPRGN | DCX_WINDOWPAINT |
370                                (bIcon ? DCX_WINDOW : 0) );
371             if (hdc)
372             {
373                if (SendMessage16( hwnd, (bIcon) ? WM_ICONERASEBKGND
374                                                 : WM_ERASEBKGND,
375                                   (WPARAM)hdc, 0 ))
376                   wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
377                ReleaseDC( hwnd, hdc );
378             }
379         }
380     }
381
382       /* Recursively process children */
383
384     if (!(flags & RDW_NOCHILDREN) &&
385         ((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) &&
386         !(wndPtr->dwStyle & WS_MINIMIZE) )
387     {
388         if ( hrgnUpdate || rectUpdate )
389         {
390            if( !(hrgn = CreateRectRgn( 0, 0, 0, 0 )) ) return TRUE;
391            if( !hrgnUpdate )
392              {
393                 control |= (RDW_C_DELETEHRGN | RDW_C_USEHRGN);
394                 if( !(hrgnUpdate = CreateRectRgnIndirect32( rectUpdate )) )
395                 {
396                     DeleteObject( hrgn );
397                     return TRUE;
398                 }
399              }
400            for (wndPtr = wndPtr->child; wndPtr; wndPtr = wndPtr->next)
401              if( wndPtr->dwStyle & WS_VISIBLE )
402                {
403                  SetRectRgn( hrgn, wndPtr->rectWindow.left, wndPtr->rectWindow.top,
404                                    wndPtr->rectWindow.right, wndPtr->rectWindow.bottom);
405                  if( CombineRgn( hrgn, hrgn, hrgnUpdate, RGN_AND ) != NULLREGION )
406                  {
407                    if( control & RDW_C_USEHRGN &&
408                        wndPtr->dwStyle & WS_CLIPSIBLINGS ) 
409                        CombineRgn( hrgnUpdate, hrgnUpdate, hrgn, RGN_DIFF );
410
411                    OffsetRgn( hrgn, -wndPtr->rectClient.left,
412                                  -wndPtr->rectClient.top );
413                    PAINT_RedrawWindow( wndPtr->hwndSelf, NULL, hrgn, flags, RDW_C_USEHRGN );
414                  }
415                }
416            DeleteObject( hrgn );
417            if( control & RDW_C_DELETEHRGN ) DeleteObject( hrgnUpdate );
418         }
419         else for (wndPtr = wndPtr->child; wndPtr; wndPtr = wndPtr->next)
420                   PAINT_RedrawWindow( wndPtr->hwndSelf, NULL, 0, flags, 0 );
421
422     }
423     return TRUE;
424 }
425
426
427 /***********************************************************************
428  *           RedrawWindow32    (USER32.425)
429  */
430 BOOL32 RedrawWindow32( HWND32 hwnd, const RECT32 *rectUpdate,
431                        HRGN32 hrgnUpdate, UINT32 flags )
432 {
433     WND* wnd = WIN_FindWndPtr( hwnd );
434
435     /* check if there is something to redraw */
436
437     return ( wnd && WIN_IsWindowDrawable( wnd, !(flags & RDW_FRAME) ) )
438            ? PAINT_RedrawWindow( hwnd, rectUpdate, hrgnUpdate, flags, 0 )
439            : 1;
440 }
441
442
443 /***********************************************************************
444  *           RedrawWindow16    (USER.290)
445  */
446 BOOL16 RedrawWindow16( HWND16 hwnd, const RECT16 *rectUpdate,
447                        HRGN16 hrgnUpdate, UINT16 flags )
448 {
449     if (rectUpdate)
450     {
451         RECT32 r;
452         CONV_RECT16TO32( rectUpdate, &r );
453         return (BOOL16)RedrawWindow32( (HWND32)hwnd, &r, hrgnUpdate, flags );
454     }
455     return (BOOL16)RedrawWindow32( (HWND32)hwnd, NULL, hrgnUpdate, flags );
456 }
457
458
459 /***********************************************************************
460  *           UpdateWindow   (USER.124) (USER32.566)
461  */
462 void UpdateWindow( HWND32 hwnd )
463 {
464     RedrawWindow32( hwnd, NULL, 0, RDW_UPDATENOW | RDW_NOCHILDREN );
465 }
466
467
468 /***********************************************************************
469  *           InvalidateRgn   (USER.126) (USER32.328)
470  */
471 void InvalidateRgn( HWND32 hwnd, HRGN32 hrgn, BOOL32 erase )
472 {
473     RedrawWindow32(hwnd, NULL, hrgn, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
474 }
475
476
477 /***********************************************************************
478  *           InvalidateRect16   (USER.125)
479  */
480 void InvalidateRect16( HWND16 hwnd, const RECT16 *rect, BOOL16 erase )
481 {
482     RedrawWindow16( hwnd, rect, 0, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
483 }
484
485
486 /***********************************************************************
487  *           InvalidateRect32   (USER32.327)
488  */
489 void InvalidateRect32( HWND32 hwnd, const RECT32 *rect, BOOL32 erase )
490 {
491     RedrawWindow32( hwnd, rect, 0, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
492 }
493
494
495 /***********************************************************************
496  *           ValidateRgn   (USER.128) (USER32.571)
497  */
498 void ValidateRgn( HWND32 hwnd, HRGN32 hrgn )
499 {
500     RedrawWindow32( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOCHILDREN );
501 }
502
503
504 /***********************************************************************
505  *           ValidateRect16   (USER.127)
506  */
507 void ValidateRect16( HWND16 hwnd, const RECT16 *rect )
508 {
509     RedrawWindow16( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN );
510 }
511
512
513 /***********************************************************************
514  *           ValidateRect32   (USER32.570)
515  */
516 void ValidateRect32( HWND32 hwnd, const RECT32 *rect )
517 {
518     RedrawWindow32( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN );
519 }
520
521
522 /***********************************************************************
523  *           GetUpdateRect16   (USER.190)
524  */
525 BOOL16 GetUpdateRect16( HWND16 hwnd, LPRECT16 rect, BOOL16 erase )
526 {
527     RECT32 r;
528     BOOL16 ret;
529
530     if (!rect) return GetUpdateRect32( hwnd, NULL, erase );
531     ret = GetUpdateRect32( hwnd, &r, erase );
532     CONV_RECT32TO16( &r, rect );
533     return ret;
534 }
535
536
537 /***********************************************************************
538  *           GetUpdateRect32   (USER32.296)
539  */
540 BOOL32 GetUpdateRect32( HWND32 hwnd, LPRECT32 rect, BOOL32 erase )
541 {
542     WND * wndPtr = WIN_FindWndPtr( hwnd );
543     if (!wndPtr) return FALSE;
544
545     if (rect)
546     {
547         if (wndPtr->hrgnUpdate > 1)
548         {
549             HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
550             if (GetUpdateRgn( hwnd, hrgn, erase ) == ERROR) return FALSE;
551             GetRgnBox32( hrgn, rect );
552             DeleteObject( hrgn );
553         }
554         else SetRectEmpty32( rect );
555     }
556     return (wndPtr->hrgnUpdate > 1);
557 }
558
559
560 /***********************************************************************
561  *           GetUpdateRgn   (USER.237) (USER32.297)
562  */
563 INT16 GetUpdateRgn( HWND32 hwnd, HRGN32 hrgn, BOOL32 erase )
564 {
565     INT16 retval;
566     WND * wndPtr = WIN_FindWndPtr( hwnd );
567     if (!wndPtr) return ERROR;
568
569     if (wndPtr->hrgnUpdate <= 1)
570     {
571         SetRectRgn( hrgn, 0, 0, 0, 0 );
572         return NULLREGION;
573     }
574     retval = CombineRgn( hrgn, wndPtr->hrgnUpdate, 0, RGN_COPY );
575     if (erase) RedrawWindow32( hwnd, NULL, 0, RDW_ERASENOW | RDW_NOCHILDREN );
576     return retval;
577 }
578
579
580 /***********************************************************************
581  *           ExcludeUpdateRgn   (USER.238) (USER32.194)
582  */
583 INT16 ExcludeUpdateRgn( HDC32 hdc, HWND32 hwnd )
584 {
585     INT16 retval = ERROR;
586     HRGN hrgn;
587     WND * wndPtr;
588
589     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return ERROR;
590     if ((hrgn = CreateRectRgn( 0, 0, 0, 0 )) != 0)
591     {
592         retval = CombineRgn( hrgn, InquireVisRgn(hdc),
593                              (wndPtr->hrgnUpdate>1)?wndPtr->hrgnUpdate:0,
594                              (wndPtr->hrgnUpdate>1)?RGN_DIFF:RGN_COPY);
595         if (retval) SelectVisRgn( hdc, hrgn );
596         DeleteObject( hrgn );
597     }
598     return retval;
599 }
600
601