offsets array is the size of the wine data format so there is no need
[wine.git] / windows / painting.c
blob70b6fb1768922ab79b4dd5b09c6953cf259f5c61
1 /*
2 * Window painting functions
4 * Copyright 1993, 1994, 1995 Alexandre Julliard
5 * 1999 Alex Korobka
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "config.h"
23 #include "wine/port.h"
25 #include <stdarg.h>
26 #include <string.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "wine/winuser16.h"
31 #include "wownt32.h"
32 #include "wine/unicode.h"
33 #include "wine/server.h"
34 #include "user.h"
35 #include "win.h"
36 #include "message.h"
37 #include "dce.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(win);
41 WINE_DECLARE_DEBUG_CHANNEL(nonclient);
43 /* client rect in window coordinates */
45 #define GETCLIENTRECTW( wnd, r ) (r).left = (wnd)->rectClient.left - (wnd)->rectWindow.left; \
46 (r).top = (wnd)->rectClient.top - (wnd)->rectWindow.top; \
47 (r).right = (wnd)->rectClient.right - (wnd)->rectWindow.left; \
48 (r).bottom = (wnd)->rectClient.bottom - (wnd)->rectWindow.top
50 /* PAINT_RedrawWindow() control flags */
51 #define RDW_EX_DELAY_NCPAINT 0x0020
53 /* WIN_UpdateNCRgn() flags */
54 #define UNC_CHECK 0x0001
55 #define UNC_ENTIRE 0x0002
56 #define UNC_REGION 0x0004
57 #define UNC_UPDATE 0x0008
58 #define UNC_DELAY_NCPAINT 0x0010
59 #define UNC_IN_BEGINPAINT 0x0020
61 HPALETTE (WINAPI *pfnGDISelectPalette)(HDC hdc, HPALETTE hpal, WORD bkgnd ) = NULL;
62 UINT (WINAPI *pfnGDIRealizePalette)(HDC hdc) = NULL;
65 /***********************************************************************
66 * add_paint_count
68 * Add an increment (normally 1 or -1) to the current paint count of a window.
70 static void add_paint_count( HWND hwnd, int incr )
72 SERVER_START_REQ( inc_window_paint_count )
74 req->handle = hwnd;
75 req->incr = incr;
76 wine_server_call( req );
78 SERVER_END_REQ;
82 /***********************************************************************
83 * crop_rgn
85 * hSrc: Region to crop.
86 * lpRect: Clipping rectangle.
88 * hDst: Region to hold the result (a new region is created if it's 0).
89 * Allowed to be the same region as hSrc in which case everything
90 * will be done in place, with no memory reallocations.
92 * Returns: hDst if success, 0 otherwise.
94 static HRGN crop_rgn( HRGN hDst, HRGN hSrc, const RECT *rect )
96 HRGN h = CreateRectRgnIndirect( rect );
97 if (hDst == 0) hDst = h;
98 CombineRgn( hDst, hSrc, h, RGN_AND );
99 if (hDst != h) DeleteObject( h );
100 return hDst;
104 /***********************************************************************
105 * WIN_HaveToDelayNCPAINT
107 * Currently, in the Wine painting mechanism, the WM_NCPAINT message
108 * is generated as soon as a region intersecting the non-client area
109 * of a window is invalidated.
111 * This technique will work fine for all windows whose parents
112 * have the WS_CLIPCHILDREN style. When the parents have that style,
113 * they are not going to override the contents of their children.
114 * However, when the parent doesn't have that style, Windows relies
115 * on a "painter's algorithm" to display the contents of the windows.
116 * That is, windows are painted from back to front. This includes the
117 * non-client area.
119 * This method looks at the current state of a window to determine
120 * if the sending of the WM_NCPAINT message should be delayed until
121 * the BeginPaint call.
123 * PARAMS:
124 * wndPtr - A Locked window pointer to the window we're
125 * examining.
126 * uncFlags - This is the flag passed to the WIN_UpdateNCRgn
127 * function. This is a shortcut for the cases when
128 * we already know when to avoid scanning all the
129 * parents of a window. If you already know that this
130 * window's NCPAINT should be delayed, set the
131 * UNC_DELAY_NCPAINT flag for this parameter.
133 * This shortcut behavior is implemented in the
134 * RDW_Paint() method.
137 static BOOL WIN_HaveToDelayNCPAINT( HWND hwnd, UINT uncFlags)
140 * Test the shortcut first. (duh)
142 if (uncFlags & UNC_DELAY_NCPAINT)
143 return TRUE;
146 * The UNC_IN_BEGINPAINT flag is set in the BeginPaint
147 * method only. This is another shortcut to avoid going
148 * up the parent's chain of the window to finally
149 * figure-out that none of them has an invalid region.
151 if (uncFlags & UNC_IN_BEGINPAINT)
152 return FALSE;
155 * Scan all the parents of this window to find a window
156 * that doesn't have the WS_CLIPCHILDREN style and that
157 * has an invalid region.
159 while ((hwnd = GetAncestor( hwnd, GA_PARENT )))
161 WND* parentWnd = WIN_FindWndPtr( hwnd );
162 if (parentWnd && !(parentWnd->dwStyle & WS_CLIPCHILDREN) && parentWnd->hrgnUpdate)
164 WIN_ReleaseWndPtr( parentWnd );
165 return TRUE;
167 WIN_ReleaseWndPtr( parentWnd );
169 return FALSE;
172 /***********************************************************************
173 * WIN_UpdateNCRgn
175 * Things to do:
176 * Send WM_NCPAINT if required (when nonclient is invalid or UNC_ENTIRE flag is set)
177 * Crop hrgnUpdate to a client rect, especially if it 1.
178 * If UNC_REGION is set return update region for the client rect.
180 * NOTE: UNC_REGION is mainly for the RDW_Paint() chunk that sends WM_ERASEBKGND message.
181 * The trick is that when the returned region handle may be different from hRgn.
182 * In this case the old hRgn must be considered gone. BUT, if the returned value
183 * is 1 then the hRgn is preserved and RDW_Paint() will have to get
184 * a DC without extra clipping region.
186 static HRGN WIN_UpdateNCRgn(WND* wnd, HRGN hRgn, UINT uncFlags )
188 RECT r;
189 HRGN hClip = 0;
190 HRGN hrgnRet = 0;
192 TRACE_(nonclient)("hwnd %p [%p] hrgn %p, unc %04x, ncf %i\n",
193 wnd->hwndSelf, wnd->hrgnUpdate, hRgn, uncFlags, wnd->flags & WIN_NEEDS_NCPAINT);
195 /* desktop window doesn't have a nonclient area */
196 if(wnd->hwndSelf == GetDesktopWindow())
198 wnd->flags &= ~WIN_NEEDS_NCPAINT;
199 if( wnd->hrgnUpdate > (HRGN)1 )
201 if (!hRgn) hRgn = CreateRectRgn( 0, 0, 0, 0 );
202 CombineRgn( hRgn, wnd->hrgnUpdate, 0, RGN_COPY );
203 hrgnRet = hRgn;
205 else
207 hrgnRet = wnd->hrgnUpdate;
209 return hrgnRet;
212 if ((wnd->hwndSelf == GetForegroundWindow()) &&
213 !(wnd->flags & WIN_NCACTIVATED) )
215 wnd->flags |= WIN_NCACTIVATED;
216 uncFlags |= UNC_ENTIRE;
220 * If the window's non-client area needs to be painted,
222 if ( ( wnd->flags & WIN_NEEDS_NCPAINT ) &&
223 !WIN_HaveToDelayNCPAINT(wnd->hwndSelf, uncFlags) )
225 RECT r2, r3;
227 wnd->flags &= ~WIN_NEEDS_NCPAINT;
228 GETCLIENTRECTW( wnd, r );
230 TRACE_(nonclient)( "\tclient box (%ld,%ld-%ld,%ld), hrgnUpdate %p\n",
231 r.left, r.top, r.right, r.bottom, wnd->hrgnUpdate );
232 if( wnd->hrgnUpdate > (HRGN)1 )
234 /* Check if update rgn overlaps with nonclient area */
236 GetRgnBox( wnd->hrgnUpdate, &r2 );
237 UnionRect( &r3, &r2, &r );
238 if( r3.left != r.left || r3.top != r.top ||
239 r3.right != r.right || r3.bottom != r.bottom ) /* it does */
241 /* crop hrgnUpdate, save old one in hClip - the only
242 * case that places a valid region handle in hClip */
244 hClip = wnd->hrgnUpdate;
245 wnd->hrgnUpdate = crop_rgn( hRgn, hClip, &r );
246 if( uncFlags & UNC_REGION ) hrgnRet = hClip;
249 if( uncFlags & UNC_CHECK )
251 GetRgnBox( wnd->hrgnUpdate, &r3 );
252 if( IsRectEmpty( &r3 ) )
254 /* delete the update region since all invalid
255 * parts were in the nonclient area */
257 DeleteObject( wnd->hrgnUpdate );
258 wnd->hrgnUpdate = 0;
259 if(!(wnd->flags & WIN_INTERNAL_PAINT))
260 add_paint_count( wnd->hwndSelf, -1 );
262 wnd->flags &= ~WIN_NEEDS_ERASEBKGND;
266 if(!hClip && wnd->hrgnUpdate ) goto copyrgn;
268 else
269 if( wnd->hrgnUpdate == (HRGN)1 )/* entire window */
271 if( uncFlags & UNC_UPDATE ) wnd->hrgnUpdate = CreateRectRgnIndirect( &r );
272 if( uncFlags & UNC_REGION ) hrgnRet = (HRGN)1;
273 uncFlags |= UNC_ENTIRE;
276 else /* no WM_NCPAINT unless forced */
278 if( wnd->hrgnUpdate > (HRGN)1 )
280 copyrgn:
281 if( uncFlags & UNC_REGION )
283 if (!hRgn) hRgn = CreateRectRgn( 0, 0, 0, 0 );
284 CombineRgn( hRgn, wnd->hrgnUpdate, 0, RGN_COPY );
285 hrgnRet = hRgn;
288 else
289 if( wnd->hrgnUpdate == (HRGN)1 && (uncFlags & UNC_UPDATE) )
291 GETCLIENTRECTW( wnd, r );
292 wnd->hrgnUpdate = CreateRectRgnIndirect( &r );
293 if( uncFlags & UNC_REGION ) hrgnRet = (HRGN)1;
297 if(!hClip && (uncFlags & UNC_ENTIRE) )
299 /* still don't do anything if there is no nonclient area */
300 hClip = (HRGN)(memcmp( &wnd->rectWindow, &wnd->rectClient, sizeof(RECT) ) != 0);
303 if( hClip ) /* NOTE: WM_NCPAINT allows wParam to be 1 */
305 if ( hClip == hrgnRet && hrgnRet > (HRGN)1 ) {
306 hClip = CreateRectRgn( 0, 0, 0, 0 );
307 CombineRgn( hClip, hrgnRet, 0, RGN_COPY );
310 SendMessageA( wnd->hwndSelf, WM_NCPAINT, (WPARAM)hClip, 0L );
311 if( (hClip > (HRGN)1) && (hClip != hRgn) && (hClip != hrgnRet) )
312 DeleteObject( hClip );
314 * Since all Window locks are suspended while processing the WM_NCPAINT
315 * we want to make sure the window still exists before continuing.
317 if (!IsWindow(wnd->hwndSelf))
319 DeleteObject(hrgnRet);
320 hrgnRet=0;
324 TRACE_(nonclient)("returning %p (hClip = %p, hrgnUpdate = %p)\n", hrgnRet, hClip, wnd->hrgnUpdate );
326 return hrgnRet;
330 /***********************************************************************
331 * RDW_ValidateParent [RDW_UpdateRgns() helper]
333 * Validate the portions of parents that are covered by a validated child
334 * wndPtr = child
336 static void RDW_ValidateParent(WND *wndChild)
338 HWND parent;
339 HRGN hrg;
341 if (wndChild->hrgnUpdate == (HRGN)1 ) {
342 RECT r;
343 r.left = 0;
344 r.top = 0;
345 r.right = wndChild->rectWindow.right - wndChild->rectWindow.left;
346 r.bottom = wndChild->rectWindow.bottom - wndChild->rectWindow.top;
347 hrg = CreateRectRgnIndirect( &r );
348 } else
349 hrg = wndChild->hrgnUpdate;
351 parent = GetAncestor( wndChild->hwndSelf, GA_PARENT );
352 while (parent && parent != GetDesktopWindow())
354 WND *wndParent = WIN_FindWndPtr( parent );
355 if (wndParent && !(wndParent->dwStyle & WS_CLIPCHILDREN))
357 if (wndParent->hrgnUpdate != 0)
359 POINT ptOffset;
360 RECT rect, rectParent;
361 if( wndParent->hrgnUpdate == (HRGN)1 )
363 RECT r;
365 r.left = 0;
366 r.top = 0;
367 r.right = wndParent->rectWindow.right - wndParent->rectWindow.left;
368 r.bottom = wndParent->rectWindow.bottom - wndParent->rectWindow.top;
370 wndParent->hrgnUpdate = CreateRectRgnIndirect( &r );
372 /* we must offset the child region by the offset of the child rect in the parent */
373 GetWindowRect(wndParent->hwndSelf, &rectParent);
374 GetWindowRect(wndChild->hwndSelf, &rect);
375 ptOffset.x = rect.left - rectParent.left;
376 ptOffset.y = rect.top - rectParent.top;
377 OffsetRgn( hrg, ptOffset.x, ptOffset.y );
378 if (CombineRgn( wndParent->hrgnUpdate, wndParent->hrgnUpdate, hrg, RGN_DIFF ) == NULLREGION)
380 /* the update region has become empty */
381 DeleteObject( wndParent->hrgnUpdate );
382 wndParent->hrgnUpdate = 0;
383 wndParent->flags &= ~WIN_NEEDS_ERASEBKGND;
384 if( !(wndParent->flags & WIN_INTERNAL_PAINT) )
385 add_paint_count( wndParent->hwndSelf, -1 );
387 OffsetRgn( hrg, -ptOffset.x, -ptOffset.y );
390 WIN_ReleaseWndPtr( wndParent );
391 parent = GetAncestor( parent, GA_PARENT );
393 if (hrg != wndChild->hrgnUpdate) DeleteObject( hrg );
396 /***********************************************************************
397 * RDW_UpdateRgns [RedrawWindow() helper]
399 * Walks the window tree and adds/removes parts of the hRgn to/from update
400 * regions of windows that overlap it. Also, manages internal paint flags.
402 * NOTE: Walks the window tree so the caller must lock it.
403 * MUST preserve hRgn (can modify but then has to restore).
405 static void RDW_UpdateRgns( WND* wndPtr, HRGN hRgn, UINT flags, BOOL firstRecursLevel )
408 * Called only when one of the following is set:
409 * (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT)
412 BOOL bHadOne = wndPtr->hrgnUpdate && hRgn;
413 BOOL bChildren = (!(flags & RDW_NOCHILDREN) && !(wndPtr->dwStyle & WS_MINIMIZE) &&
414 ((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) );
415 RECT r;
417 r.left = 0;
418 r.top = 0;
419 r.right = wndPtr->rectWindow.right - wndPtr->rectWindow.left;
420 r.bottom = wndPtr->rectWindow.bottom - wndPtr->rectWindow.top;
422 TRACE("\thwnd %p [%p] -> hrgn [%p], flags [%04x]\n", wndPtr->hwndSelf, wndPtr->hrgnUpdate, hRgn, flags );
424 if( flags & RDW_INVALIDATE )
426 if( hRgn > (HRGN)1 )
428 switch ((UINT)wndPtr->hrgnUpdate)
430 default:
431 CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate, hRgn, RGN_OR );
432 /* fall through */
433 case 0:
434 wndPtr->hrgnUpdate = crop_rgn( wndPtr->hrgnUpdate,
435 wndPtr->hrgnUpdate ? wndPtr->hrgnUpdate : hRgn,
436 &r );
437 if( !bHadOne )
439 GetRgnBox( wndPtr->hrgnUpdate, &r );
440 if( IsRectEmpty( &r ) )
442 DeleteObject( wndPtr->hrgnUpdate );
443 wndPtr->hrgnUpdate = 0;
444 goto end;
447 break;
448 case 1: /* already an entire window */
449 break;
452 else if( hRgn == (HRGN)1 )
454 if( wndPtr->hrgnUpdate > (HRGN)1 )
455 DeleteObject( wndPtr->hrgnUpdate );
456 wndPtr->hrgnUpdate = (HRGN)1;
458 else
460 /* hRgn is zero */
461 if( wndPtr->hrgnUpdate > (HRGN)1)
463 GetRgnBox( wndPtr->hrgnUpdate, &r );
464 if( IsRectEmpty( &r ) )
466 DeleteObject( wndPtr->hrgnUpdate );
467 wndPtr->hrgnUpdate = 0;
468 goto end;
471 hRgn = wndPtr->hrgnUpdate; /* this is a trick that depends
472 * on code in RDW_Paint() */
475 if( !bHadOne && !(wndPtr->flags & WIN_INTERNAL_PAINT) )
476 add_paint_count( wndPtr->hwndSelf, 1 );
478 if (flags & RDW_FRAME) wndPtr->flags |= WIN_NEEDS_NCPAINT;
479 if (flags & RDW_ERASE) wndPtr->flags |= WIN_NEEDS_ERASEBKGND;
480 flags |= RDW_FRAME;
482 else if( flags & RDW_VALIDATE )
484 if( wndPtr->hrgnUpdate )
486 if( hRgn > (HRGN)1 )
488 if( wndPtr->hrgnUpdate == (HRGN)1 )
489 wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r );
491 if( CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate, hRgn, RGN_DIFF )
492 == NULLREGION )
494 DeleteObject( wndPtr->hrgnUpdate );
495 wndPtr->hrgnUpdate = 0;
498 else /* validate everything */
500 if( wndPtr->hrgnUpdate > (HRGN)1 ) DeleteObject( wndPtr->hrgnUpdate );
501 wndPtr->hrgnUpdate = 0;
504 if( !wndPtr->hrgnUpdate )
506 wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
507 if( !(wndPtr->flags & WIN_INTERNAL_PAINT) )
508 add_paint_count( wndPtr->hwndSelf, -1 );
512 if (flags & RDW_NOFRAME) wndPtr->flags &= ~WIN_NEEDS_NCPAINT;
513 if (flags & RDW_NOERASE) wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
517 if ((firstRecursLevel) && (wndPtr->hrgnUpdate != 0) && (flags & RDW_UPDATENOW))
518 RDW_ValidateParent(wndPtr); /* validate parent covered by region */
520 /* in/validate child windows that intersect with the region if it
521 * is a valid handle. */
523 if( flags & (RDW_INVALIDATE | RDW_VALIDATE) )
525 HWND *list;
526 if( hRgn > (HRGN)1 && bChildren && (list = WIN_ListChildren( wndPtr->hwndSelf )))
528 POINT ptTotal, prevOrigin = {0,0};
529 POINT ptClient;
530 INT i;
532 ptClient.x = wndPtr->rectClient.left - wndPtr->rectWindow.left;
533 ptClient.y = wndPtr->rectClient.top - wndPtr->rectWindow.top;
535 for(i = ptTotal.x = ptTotal.y = 0; list[i]; i++)
537 WND *wnd = WIN_FindWndPtr( list[i] );
538 if (!wnd) continue;
539 if( wnd->dwStyle & WS_VISIBLE )
541 POINT ptOffset;
543 r.left = wnd->rectWindow.left + ptClient.x;
544 r.right = wnd->rectWindow.right + ptClient.x;
545 r.top = wnd->rectWindow.top + ptClient.y;
546 r.bottom = wnd->rectWindow.bottom + ptClient.y;
548 ptOffset.x = r.left - prevOrigin.x;
549 ptOffset.y = r.top - prevOrigin.y;
550 OffsetRect( &r, -ptTotal.x, -ptTotal.y );
552 if( RectInRegion( hRgn, &r ) )
554 OffsetRgn( hRgn, -ptOffset.x, -ptOffset.y );
555 RDW_UpdateRgns( wnd, hRgn, flags, FALSE );
556 prevOrigin.x = r.left + ptTotal.x;
557 prevOrigin.y = r.top + ptTotal.y;
558 ptTotal.x += ptOffset.x;
559 ptTotal.y += ptOffset.y;
562 WIN_ReleaseWndPtr( wnd );
564 HeapFree( GetProcessHeap(), 0, list );
565 OffsetRgn( hRgn, ptTotal.x, ptTotal.y );
566 bChildren = 0;
570 /* handle hRgn == 1 (alias for entire window) and/or internal paint recursion */
572 if( bChildren )
574 HWND *list;
575 if ((list = WIN_ListChildren( wndPtr->hwndSelf )))
577 INT i;
578 for (i = 0; list[i]; i++)
580 WND *wnd = WIN_FindWndPtr( list[i] );
581 if (!wnd) continue;
582 if( wnd->dwStyle & WS_VISIBLE )
583 RDW_UpdateRgns( wnd, hRgn, flags, FALSE );
584 WIN_ReleaseWndPtr( wnd );
586 HeapFree( GetProcessHeap(), 0, list );
590 end:
592 /* Set/clear internal paint flag */
594 if (flags & RDW_INTERNALPAINT)
596 if ( !wndPtr->hrgnUpdate && !(wndPtr->flags & WIN_INTERNAL_PAINT))
597 add_paint_count( wndPtr->hwndSelf, 1 );
598 wndPtr->flags |= WIN_INTERNAL_PAINT;
600 else if (flags & RDW_NOINTERNALPAINT)
602 if ( !wndPtr->hrgnUpdate && (wndPtr->flags & WIN_INTERNAL_PAINT))
603 add_paint_count( wndPtr->hwndSelf, -1 );
604 wndPtr->flags &= ~WIN_INTERNAL_PAINT;
608 /***********************************************************************
609 * RDW_Paint [RedrawWindow() helper]
611 * Walks the window tree and paints/erases windows that have
612 * nonzero update regions according to redraw flags. hrgn is a scratch
613 * region passed down during recursion. Must not be 1.
616 static HRGN RDW_Paint( WND* wndPtr, HRGN hrgn, UINT flags, UINT ex )
618 /* NOTE: wndPtr is locked by caller.
620 * FIXME: Windows uses WM_SYNCPAINT to cut down the number of intertask
621 * SendMessage() calls. This is a comment inside DefWindowProc() source
622 * from 16-bit SDK:
624 * This message avoids lots of inter-app message traffic
625 * by switching to the other task and continuing the
626 * recursion there.
628 * wParam = flags
629 * LOWORD(lParam) = hrgnClip
630 * HIWORD(lParam) = hwndSkip (not used; always NULL)
633 HDC hDC;
634 HWND hWnd = wndPtr->hwndSelf;
635 BOOL bIcon = ((wndPtr->dwStyle & WS_MINIMIZE) && GetClassLongA(hWnd, GCL_HICON));
637 /* Erase/update the window itself ... */
639 TRACE("\thwnd %p [%p] -> hrgn [%p], flags [%04x]\n", hWnd, wndPtr->hrgnUpdate, hrgn, flags );
642 * Check if this window should delay it's processing of WM_NCPAINT.
643 * See WIN_HaveToDelayNCPAINT for a description of the mechanism
645 if ((ex & RDW_EX_DELAY_NCPAINT) || WIN_HaveToDelayNCPAINT(wndPtr->hwndSelf, 0) )
646 ex |= RDW_EX_DELAY_NCPAINT;
648 if (flags & RDW_UPDATENOW)
650 if (wndPtr->hrgnUpdate) /* wm_painticon wparam is 1 */
651 SendMessageW( hWnd, (bIcon) ? WM_PAINTICON : WM_PAINT, bIcon, 0 );
653 else if (flags & RDW_ERASENOW)
655 UINT dcx = DCX_INTERSECTRGN | DCX_USESTYLE | DCX_KEEPCLIPRGN | DCX_WINDOWPAINT | DCX_CACHE;
656 HRGN hrgnRet;
658 hrgnRet = WIN_UpdateNCRgn(wndPtr,
659 hrgn,
660 UNC_REGION | UNC_CHECK |
661 ((ex & RDW_EX_DELAY_NCPAINT) ? UNC_DELAY_NCPAINT : 0) );
663 if( hrgnRet )
665 if( hrgnRet > (HRGN)1 ) hrgn = hrgnRet; else hrgnRet = 0; /* entire client */
666 if( wndPtr->flags & WIN_NEEDS_ERASEBKGND )
668 if( bIcon ) dcx |= DCX_WINDOW;
669 else
670 if( hrgnRet )
671 OffsetRgn( hrgnRet, wndPtr->rectWindow.left - wndPtr->rectClient.left,
672 wndPtr->rectWindow.top - wndPtr->rectClient.top);
673 else
674 dcx &= ~DCX_INTERSECTRGN;
675 if (( hDC = GetDCEx( hWnd, hrgnRet, dcx )) )
677 if (SendMessageW( hWnd, (bIcon) ? WM_ICONERASEBKGND : WM_ERASEBKGND,
678 (WPARAM)hDC, 0 ))
679 wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
680 ReleaseDC( hWnd, hDC );
686 if( !IsWindow(hWnd) ) return hrgn;
688 /* ... and its child windows */
690 if(!(flags & RDW_NOCHILDREN) && !(wndPtr->dwStyle & WS_MINIMIZE) &&
691 ((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) )
693 HWND *list, *phwnd;
695 if( (list = WIN_ListChildren( wndPtr->hwndSelf )) )
697 for (phwnd = list; *phwnd; phwnd++)
699 if (!(wndPtr = WIN_FindWndPtr( *phwnd ))) continue;
700 if ( (wndPtr->dwStyle & WS_VISIBLE) &&
701 (wndPtr->hrgnUpdate || (wndPtr->flags & WIN_INTERNAL_PAINT)) )
702 hrgn = RDW_Paint( wndPtr, hrgn, flags, ex );
703 WIN_ReleaseWndPtr(wndPtr);
705 HeapFree( GetProcessHeap(), 0, list );
709 return hrgn;
713 /***********************************************************************
714 * dump_rdw_flags
716 static void dump_rdw_flags(UINT flags)
718 TRACE("flags:");
719 if (flags & RDW_INVALIDATE) TRACE(" RDW_INVALIDATE");
720 if (flags & RDW_INTERNALPAINT) TRACE(" RDW_INTERNALPAINT");
721 if (flags & RDW_ERASE) TRACE(" RDW_ERASE");
722 if (flags & RDW_VALIDATE) TRACE(" RDW_VALIDATE");
723 if (flags & RDW_NOINTERNALPAINT) TRACE(" RDW_NOINTERNALPAINT");
724 if (flags & RDW_NOERASE) TRACE(" RDW_NOERASE");
725 if (flags & RDW_NOCHILDREN) TRACE(" RDW_NOCHILDREN");
726 if (flags & RDW_ALLCHILDREN) TRACE(" RDW_ALLCHILDREN");
727 if (flags & RDW_UPDATENOW) TRACE(" RDW_UPDATENOW");
728 if (flags & RDW_ERASENOW) TRACE(" RDW_ERASENOW");
729 if (flags & RDW_FRAME) TRACE(" RDW_FRAME");
730 if (flags & RDW_NOFRAME) TRACE(" RDW_NOFRAME");
732 #define RDW_FLAGS \
733 (RDW_INVALIDATE | \
734 RDW_INTERNALPAINT | \
735 RDW_ERASE | \
736 RDW_VALIDATE | \
737 RDW_NOINTERNALPAINT | \
738 RDW_NOERASE | \
739 RDW_NOCHILDREN | \
740 RDW_ALLCHILDREN | \
741 RDW_UPDATENOW | \
742 RDW_ERASENOW | \
743 RDW_FRAME | \
744 RDW_NOFRAME)
746 if (flags & ~RDW_FLAGS) TRACE(" %04x", flags & ~RDW_FLAGS);
747 TRACE("\n");
748 #undef RDW_FLAGS
752 /***********************************************************************
753 * RedrawWindow (USER32.@)
755 BOOL WINAPI RedrawWindow( HWND hwnd, const RECT *rectUpdate,
756 HRGN hrgnUpdate, UINT flags )
758 HRGN hRgn = 0;
759 RECT r, r2;
760 POINT pt;
761 WND* wndPtr;
763 if (!hwnd) hwnd = GetDesktopWindow();
765 /* check if the window or its parents are visible/not minimized */
767 if (!WIN_IsWindowDrawable( hwnd, !(flags & RDW_FRAME) )) return TRUE;
769 /* process pending events and messages before painting */
770 if (flags & RDW_UPDATENOW)
771 MsgWaitForMultipleObjects( 0, NULL, FALSE, 0, QS_ALLINPUT );
773 if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE;
774 if (TRACE_ON(win))
776 if( hrgnUpdate )
778 GetRgnBox( hrgnUpdate, &r );
779 TRACE( "%p (%p) NULL %p box (%ld,%ld-%ld,%ld) flags=%04x\n",
780 hwnd, wndPtr->hrgnUpdate, hrgnUpdate, r.left, r.top, r.right, r.bottom, flags );
782 else
784 if( rectUpdate )
785 r = *rectUpdate;
786 else
787 SetRectEmpty( &r );
788 TRACE( "%p (%p) %s %ld,%ld-%ld,%ld %p flags=%04x\n",
789 hwnd, wndPtr->hrgnUpdate, rectUpdate ? "rect" : "NULL", r.left,
790 r.top, r.right, r.bottom, hrgnUpdate, flags );
792 dump_rdw_flags(flags);
795 /* prepare an update region in window coordinates */
797 if (((flags & (RDW_INVALIDATE|RDW_FRAME)) == (RDW_INVALIDATE|RDW_FRAME)) ||
798 ((flags & (RDW_VALIDATE|RDW_NOFRAME)) == (RDW_VALIDATE|RDW_NOFRAME)))
799 r = wndPtr->rectWindow;
800 else
801 r = wndPtr->rectClient;
803 pt.x = wndPtr->rectClient.left - wndPtr->rectWindow.left;
804 pt.y = wndPtr->rectClient.top - wndPtr->rectWindow.top;
805 OffsetRect( &r, -wndPtr->rectClient.left, -wndPtr->rectClient.top );
807 if (flags & RDW_INVALIDATE) /* ------------------------- Invalidate */
809 /* If the window doesn't have hrgnUpdate we leave hRgn zero
810 * and put a new region straight into wndPtr->hrgnUpdate
811 * so that RDW_UpdateRgns() won't have to do any extra work.
814 if( hrgnUpdate )
816 if( wndPtr->hrgnUpdate )
818 hRgn = CreateRectRgn( 0, 0, 0, 0 );
819 CombineRgn( hRgn, hrgnUpdate, 0, RGN_COPY );
820 OffsetRgn( hRgn, pt.x, pt.y );
822 else
824 wndPtr->hrgnUpdate = crop_rgn( 0, hrgnUpdate, &r );
825 OffsetRgn( wndPtr->hrgnUpdate, pt.x, pt.y );
828 else if( rectUpdate )
830 if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
831 OffsetRect( &r2, pt.x, pt.y );
832 if( wndPtr->hrgnUpdate == 0 )
833 wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r2 );
834 else
835 hRgn = CreateRectRgnIndirect( &r2 );
837 else /* entire window or client depending on RDW_FRAME */
839 if( flags & RDW_FRAME )
841 if (wndPtr->hrgnUpdate) hRgn = (HRGN)1;
842 else wndPtr->hrgnUpdate = (HRGN)1;
844 else
846 GETCLIENTRECTW( wndPtr, r2 );
847 if( wndPtr->hrgnUpdate == 0 )
848 wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r2 );
849 else
850 hRgn = CreateRectRgnIndirect( &r2 );
854 else if (flags & RDW_VALIDATE) /* ------------------------- Validate */
856 /* In this we cannot leave with zero hRgn */
857 if( hrgnUpdate )
859 hRgn = crop_rgn( hRgn, hrgnUpdate, &r );
860 OffsetRgn( hRgn, pt.x, pt.y );
861 GetRgnBox( hRgn, &r2 );
862 if( IsRectEmpty( &r2 ) ) goto END;
864 else if( rectUpdate )
866 if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
867 OffsetRect( &r2, pt.x, pt.y );
868 hRgn = CreateRectRgnIndirect( &r2 );
870 else /* entire window or client depending on RDW_NOFRAME */
872 if( flags & RDW_NOFRAME )
873 hRgn = (HRGN)1;
874 else
876 GETCLIENTRECTW( wndPtr, r2 );
877 hRgn = CreateRectRgnIndirect( &r2 );
882 /* At this point hRgn is either an update region in window coordinates or 1 or 0 */
884 RDW_UpdateRgns( wndPtr, hRgn, flags, TRUE );
886 /* Erase/update windows, from now on hRgn is a scratch region */
888 hRgn = RDW_Paint( wndPtr, (hRgn == (HRGN)1) ? 0 : hRgn, flags, 0 );
890 END:
891 if( hRgn > (HRGN)1 && (hRgn != hrgnUpdate) )
892 DeleteObject(hRgn );
893 WIN_ReleaseWndPtr(wndPtr);
894 return TRUE;
898 /***********************************************************************
899 * UpdateWindow (USER32.@)
901 BOOL WINAPI UpdateWindow( HWND hwnd )
903 return RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN );
907 /***********************************************************************
908 * InvalidateRgn (USER32.@)
910 BOOL WINAPI InvalidateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
912 return RedrawWindow(hwnd, NULL, hrgn, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
916 /***********************************************************************
917 * InvalidateRect (USER32.@)
919 BOOL WINAPI InvalidateRect( HWND hwnd, const RECT *rect, BOOL erase )
921 return RedrawWindow( hwnd, rect, 0, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
925 /***********************************************************************
926 * ValidateRgn (USER32.@)
928 BOOL WINAPI ValidateRgn( HWND hwnd, HRGN hrgn )
930 return RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOCHILDREN );
934 /***********************************************************************
935 * ValidateRect (USER32.@)
937 BOOL WINAPI ValidateRect( HWND hwnd, const RECT *rect )
939 return RedrawWindow( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN );
943 /***********************************************************************
944 * SelectPalette (Not a Windows API)
946 HPALETTE WINAPI SelectPalette( HDC hDC, HPALETTE hPal, BOOL bForceBackground )
948 WORD wBkgPalette = 1;
950 if (!bForceBackground && (hPal != GetStockObject(DEFAULT_PALETTE)))
952 HWND hwnd = WindowFromDC( hDC );
953 if (hwnd)
955 HWND hForeground = GetForegroundWindow();
956 /* set primary palette if it's related to current active */
957 if (hForeground == hwnd || IsChild(hForeground,hwnd)) wBkgPalette = 0;
960 return pfnGDISelectPalette( hDC, hPal, wBkgPalette);
964 /***********************************************************************
965 * UserRealizePalette (USER32.@)
967 UINT WINAPI UserRealizePalette( HDC hDC )
969 UINT realized = pfnGDIRealizePalette( hDC );
971 /* do not send anything if no colors were changed */
972 if (realized && IsDCCurrentPalette16( HDC_16(hDC) ))
974 /* send palette change notification */
975 HWND hWnd = WindowFromDC( hDC );
976 if (hWnd) SendMessageTimeoutW( HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0,
977 SMTO_ABORTIFHUNG, 2000, NULL );
979 return realized;