
invalid regions and only then starts sending out messages. This should help with reentrancy during message processing and now it needs just one (or none) scratch region for all there is to do. Also, small fixes for SetWindowPos().
1027 lines
29 KiB
C
1027 lines
29 KiB
C
/*
|
|
* Window painting functions
|
|
*
|
|
* Copyright 1993, 1994, 1995 Alexandre Julliard
|
|
* 1999 Alex Korobka
|
|
*/
|
|
|
|
#include "region.h"
|
|
#include "win.h"
|
|
#include "queue.h"
|
|
#include "dce.h"
|
|
#include "heap.h"
|
|
#include "debugtools.h"
|
|
#include "wine/winuser16.h"
|
|
|
|
DECLARE_DEBUG_CHANNEL(nonclient)
|
|
DECLARE_DEBUG_CHANNEL(win)
|
|
|
|
/* client rect in window coordinates */
|
|
|
|
#define GETCLIENTRECTW( wnd, r ) (r).left = (wnd)->rectClient.left - (wnd)->rectWindow.left; \
|
|
(r).top = (wnd)->rectClient.top - (wnd)->rectWindow.top; \
|
|
(r).right = (wnd)->rectClient.right - (wnd)->rectWindow.left; \
|
|
(r).bottom = (wnd)->rectClient.bottom - (wnd)->rectWindow.top
|
|
|
|
/* Last CTLCOLOR id */
|
|
#define CTLCOLOR_MAX CTLCOLOR_STATIC
|
|
|
|
|
|
/***********************************************************************
|
|
* WIN_UpdateNCRgn
|
|
*
|
|
* Things to do:
|
|
* Send WM_NCPAINT if required (when nonclient is invalid or UNC_ENTIRE flag is set)
|
|
* Crop hrgnUpdate to a client rect, especially if it 1.
|
|
* If UNC_REGION is set return update region for the client rect.
|
|
*
|
|
* NOTE: UNC_REGION is mainly for the RDW_Paint() chunk that sends WM_ERASEBKGND message.
|
|
* The trick is that when the returned region handle may be different from hRgn.
|
|
* In this case the old hRgn must be considered gone. BUT, if the returned value
|
|
* is 1 then the hRgn is preserved and RDW_Paint() will have to get
|
|
* a DC without extra clipping region.
|
|
*/
|
|
HRGN WIN_UpdateNCRgn(WND* wnd, HRGN hRgn, UINT uncFlags )
|
|
{
|
|
RECT r;
|
|
HRGN hClip = 0;
|
|
HRGN hrgnRet = 0;
|
|
|
|
TRACE_(nonclient)("hwnd %04x [%04x] hrgn %04x, unc %04x, ncf %i\n",
|
|
wnd->hwndSelf, wnd->hrgnUpdate, hRgn, uncFlags, wnd->flags & WIN_NEEDS_NCPAINT);
|
|
|
|
/* desktop window doesn't have a nonclient area */
|
|
if(wnd == WIN_GetDesktop())
|
|
{
|
|
wnd->flags &= ~WIN_NEEDS_NCPAINT;
|
|
if( wnd->hrgnUpdate > 1 )
|
|
hrgnRet = REGION_CropRgn( hRgn, wnd->hrgnUpdate, NULL, NULL );
|
|
else
|
|
{
|
|
hrgnRet = wnd->hrgnUpdate;
|
|
}
|
|
WIN_ReleaseDesktop();
|
|
return hrgnRet;
|
|
}
|
|
WIN_ReleaseDesktop();
|
|
|
|
if ((wnd->hwndSelf == GetForegroundWindow()) &&
|
|
!(wnd->flags & WIN_NCACTIVATED) )
|
|
{
|
|
wnd->flags |= WIN_NCACTIVATED;
|
|
uncFlags |= UNC_ENTIRE;
|
|
}
|
|
|
|
if( wnd->flags & WIN_NEEDS_NCPAINT )
|
|
{
|
|
RECT r2, r3;
|
|
|
|
wnd->flags &= ~WIN_NEEDS_NCPAINT;
|
|
GETCLIENTRECTW( wnd, r );
|
|
|
|
TRACE_(nonclient)( "\tclient box (%i,%i-%i,%i), hrgnUpdate %04x\n",
|
|
r.left, r.top, r.right, r.bottom, wnd->hrgnUpdate );
|
|
if( wnd->hrgnUpdate > 1 )
|
|
{
|
|
/* Check if update rgn overlaps with nonclient area */
|
|
|
|
GetRgnBox( wnd->hrgnUpdate, &r2 );
|
|
UnionRect( &r3, &r2, &r );
|
|
if( r3.left != r.left || r3.top != r.top ||
|
|
r3.right != r.right || r3.bottom != r.bottom ) /* it does */
|
|
{
|
|
/* crop hrgnUpdate, save old one in hClip - the only
|
|
* case that places a valid region handle in hClip */
|
|
|
|
hClip = wnd->hrgnUpdate;
|
|
wnd->hrgnUpdate = REGION_CropRgn( hRgn, hClip, &r, NULL );
|
|
if( uncFlags & UNC_REGION ) hrgnRet = hClip;
|
|
}
|
|
|
|
if( uncFlags & UNC_CHECK )
|
|
{
|
|
GetRgnBox( wnd->hrgnUpdate, &r3 );
|
|
if( IsRectEmpty( &r3 ) )
|
|
{
|
|
/* delete the update region since all invalid
|
|
* parts were in the nonclient area */
|
|
|
|
DeleteObject( wnd->hrgnUpdate );
|
|
wnd->hrgnUpdate = 0;
|
|
if(!(wnd->flags & WIN_INTERNAL_PAINT))
|
|
QUEUE_DecPaintCount( wnd->hmemTaskQ );
|
|
|
|
wnd->flags &= ~WIN_NEEDS_ERASEBKGND;
|
|
}
|
|
}
|
|
|
|
if(!hClip && wnd->hrgnUpdate ) goto copyrgn;
|
|
}
|
|
else
|
|
if( wnd->hrgnUpdate == 1 )/* entire window */
|
|
{
|
|
if( uncFlags & UNC_UPDATE ) wnd->hrgnUpdate = CreateRectRgnIndirect( &r );
|
|
if( uncFlags & UNC_REGION ) hrgnRet = 1;
|
|
uncFlags |= UNC_ENTIRE;
|
|
}
|
|
}
|
|
else /* no WM_NCPAINT unless forced */
|
|
{
|
|
if( wnd->hrgnUpdate > 1 )
|
|
{
|
|
copyrgn:
|
|
if( uncFlags & UNC_REGION )
|
|
hrgnRet = REGION_CropRgn( hRgn, wnd->hrgnUpdate, NULL, NULL );
|
|
}
|
|
else
|
|
if( wnd->hrgnUpdate == 1 && (uncFlags & UNC_UPDATE) )
|
|
{
|
|
GETCLIENTRECTW( wnd, r );
|
|
wnd->hrgnUpdate = CreateRectRgnIndirect( &r );
|
|
if( uncFlags & UNC_REGION ) hrgnRet = 1;
|
|
}
|
|
}
|
|
|
|
if(!hClip && (uncFlags & UNC_ENTIRE) )
|
|
{
|
|
/* still don't do anything if there is no nonclient area */
|
|
hClip = (memcmp( &wnd->rectWindow, &wnd->rectClient, sizeof(RECT) ) != 0);
|
|
}
|
|
|
|
if( hClip ) /* NOTE: WM_NCPAINT allows wParam to be 1 */
|
|
{
|
|
SendMessageA( wnd->hwndSelf, WM_NCPAINT, hClip, 0L );
|
|
if( (hClip > 1)&& (hClip != hRgn) && (hClip != hrgnRet) ) DeleteObject( hClip );
|
|
}
|
|
|
|
TRACE_(nonclient)("returning %04x (hClip = %04x, hrgnUpdate = %04x)\n", hrgnRet, hClip, wnd->hrgnUpdate );
|
|
|
|
return hrgnRet;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* BeginPaint16 (USER.39)
|
|
*/
|
|
HDC16 WINAPI BeginPaint16( HWND16 hwnd, LPPAINTSTRUCT16 lps )
|
|
{
|
|
BOOL bIcon;
|
|
HRGN hrgnUpdate;
|
|
WND *wndPtr = WIN_FindWndPtr( hwnd );
|
|
if (!wndPtr) return 0;
|
|
|
|
bIcon = (wndPtr->dwStyle & WS_MINIMIZE && wndPtr->class->hIcon);
|
|
|
|
wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
|
|
|
|
/* send WM_NCPAINT and make sure hrgnUpdate is a valid rgn handle */
|
|
WIN_UpdateNCRgn( wndPtr, 0, UNC_UPDATE );
|
|
|
|
if( ((hrgnUpdate = wndPtr->hrgnUpdate) != 0) || (wndPtr->flags & WIN_INTERNAL_PAINT))
|
|
QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
|
|
|
|
wndPtr->hrgnUpdate = 0;
|
|
wndPtr->flags &= ~WIN_INTERNAL_PAINT;
|
|
|
|
HideCaret( hwnd );
|
|
|
|
TRACE_(win)("hrgnUpdate = %04x, \n", hrgnUpdate);
|
|
|
|
if (wndPtr->class->style & CS_PARENTDC)
|
|
{
|
|
/* Don't clip the output to the update region for CS_PARENTDC window */
|
|
if( hrgnUpdate )
|
|
DeleteObject(hrgnUpdate);
|
|
lps->hdc = GetDCEx16( hwnd, 0, DCX_WINDOWPAINT | DCX_USESTYLE |
|
|
(bIcon ? DCX_WINDOW : 0) );
|
|
}
|
|
else
|
|
{
|
|
if( hrgnUpdate ) /* convert to client coordinates */
|
|
OffsetRgn( hrgnUpdate, wndPtr->rectWindow.left - wndPtr->rectClient.left,
|
|
wndPtr->rectWindow.top - wndPtr->rectClient.top );
|
|
lps->hdc = GetDCEx16(hwnd, hrgnUpdate, DCX_INTERSECTRGN |
|
|
DCX_WINDOWPAINT | DCX_USESTYLE | (bIcon ? DCX_WINDOW : 0) );
|
|
/* ReleaseDC() in EndPaint() will delete the region */
|
|
}
|
|
|
|
TRACE_(win)("hdc = %04x\n", lps->hdc);
|
|
|
|
if (!lps->hdc)
|
|
{
|
|
WARN_(win)("GetDCEx() failed in BeginPaint(), hwnd=%04x\n", hwnd);
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return 0;
|
|
}
|
|
|
|
GetClipBox16( lps->hdc, &lps->rcPaint );
|
|
|
|
TRACE_(win)("box = (%i,%i - %i,%i)\n", lps->rcPaint.left, lps->rcPaint.top,
|
|
lps->rcPaint.right, lps->rcPaint.bottom );
|
|
|
|
if (wndPtr->flags & WIN_NEEDS_ERASEBKGND)
|
|
{
|
|
wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
|
|
lps->fErase = !SendMessage16(hwnd, (bIcon) ? WM_ICONERASEBKGND
|
|
: WM_ERASEBKGND,
|
|
(WPARAM16)lps->hdc, 0 );
|
|
}
|
|
else lps->fErase = TRUE;
|
|
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return lps->hdc;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* BeginPaint32 (USER32.10)
|
|
*/
|
|
HDC WINAPI BeginPaint( HWND hwnd, PAINTSTRUCT *lps )
|
|
{
|
|
PAINTSTRUCT16 ps;
|
|
|
|
BeginPaint16( hwnd, &ps );
|
|
lps->hdc = (HDC)ps.hdc;
|
|
lps->fErase = ps.fErase;
|
|
lps->rcPaint.top = ps.rcPaint.top;
|
|
lps->rcPaint.left = ps.rcPaint.left;
|
|
lps->rcPaint.right = ps.rcPaint.right;
|
|
lps->rcPaint.bottom = ps.rcPaint.bottom;
|
|
lps->fRestore = ps.fRestore;
|
|
lps->fIncUpdate = ps.fIncUpdate;
|
|
return lps->hdc;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* EndPaint16 (USER.40)
|
|
*/
|
|
BOOL16 WINAPI EndPaint16( HWND16 hwnd, const PAINTSTRUCT16* lps )
|
|
{
|
|
ReleaseDC16( hwnd, lps->hdc );
|
|
ShowCaret( hwnd );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* EndPaint32 (USER32.176)
|
|
*/
|
|
BOOL WINAPI EndPaint( HWND hwnd, const PAINTSTRUCT *lps )
|
|
{
|
|
ReleaseDC( hwnd, lps->hdc );
|
|
ShowCaret( hwnd );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* FillWindow (USER.324)
|
|
*/
|
|
void WINAPI FillWindow16( HWND16 hwndParent, HWND16 hwnd, HDC16 hdc, HBRUSH16 hbrush )
|
|
{
|
|
RECT16 rect;
|
|
GetClientRect16( hwnd, &rect );
|
|
DPtoLP16( hdc, (LPPOINT16)&rect, 2 );
|
|
PaintRect16( hwndParent, hwnd, hdc, hbrush, &rect );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* PAINT_GetControlBrush
|
|
*/
|
|
static HBRUSH16 PAINT_GetControlBrush( HWND hParent, HWND hWnd, HDC16 hDC, UINT16 ctlType )
|
|
{
|
|
HBRUSH16 bkgBrush = (HBRUSH16)SendMessageA( hParent, WM_CTLCOLORMSGBOX + ctlType,
|
|
(WPARAM)hDC, (LPARAM)hWnd );
|
|
if( !IsGDIObject16(bkgBrush) )
|
|
bkgBrush = DEFWND_ControlColor( hDC, ctlType );
|
|
return bkgBrush;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* PaintRect (USER.325)
|
|
*/
|
|
void WINAPI PaintRect16( HWND16 hwndParent, HWND16 hwnd, HDC16 hdc,
|
|
HBRUSH16 hbrush, const RECT16 *rect)
|
|
{
|
|
if( hbrush <= CTLCOLOR_MAX )
|
|
{
|
|
if( hwndParent )
|
|
hbrush = PAINT_GetControlBrush( hwndParent, hwnd, hdc, (UINT16)hbrush );
|
|
else
|
|
return;
|
|
}
|
|
if( hbrush )
|
|
FillRect16( hdc, rect, hbrush );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetControlBrush (USER.326)
|
|
*/
|
|
HBRUSH16 WINAPI GetControlBrush16( HWND16 hwnd, HDC16 hdc, UINT16 ctlType )
|
|
{
|
|
WND* wndPtr = WIN_FindWndPtr( hwnd );
|
|
HBRUSH16 retvalue;
|
|
|
|
if((ctlType <= CTLCOLOR_MAX) && wndPtr )
|
|
{
|
|
WND* parent;
|
|
if( wndPtr->dwStyle & WS_POPUP ) parent = WIN_LockWndPtr(wndPtr->owner);
|
|
else parent = WIN_LockWndPtr(wndPtr->parent);
|
|
if( !parent ) parent = wndPtr;
|
|
retvalue = (HBRUSH16)PAINT_GetControlBrush( parent->hwndSelf, hwnd, hdc, ctlType );
|
|
WIN_ReleaseWndPtr(parent);
|
|
goto END;
|
|
}
|
|
retvalue = (HBRUSH16)0;
|
|
END:
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return retvalue;
|
|
}
|
|
|
|
int i;
|
|
|
|
/***********************************************************************
|
|
* RDW_UpdateRgns [RedrawWindow() helper]
|
|
*
|
|
* Walks the window tree and adds/removes parts of the hRgn to/from update
|
|
* regions of windows that overlap it. Also, manages internal paint flags.
|
|
*
|
|
* NOTE: Walks the window tree so the caller must lock it.
|
|
* MUST preserve hRgn (can modify but then has to restore).
|
|
*/
|
|
static void RDW_UpdateRgns( WND* wndPtr, HRGN hRgn, UINT flags )
|
|
{
|
|
/*
|
|
* Called only when one of the following is set:
|
|
* (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT)
|
|
*/
|
|
|
|
BOOL bHadOne = wndPtr->hrgnUpdate && hRgn;
|
|
RECT r = {0, 0, wndPtr->rectWindow.right - wndPtr->rectWindow.left,
|
|
wndPtr->rectWindow.bottom - wndPtr->rectWindow.top };
|
|
BOOL bChildren = ( wndPtr->child && !(flags & RDW_NOCHILDREN) && !(wndPtr->dwStyle & WS_MINIMIZE)
|
|
&& ((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) );
|
|
|
|
TRACE_(win)("\thwnd %04x [%04x] -> hrgn [%04x], flags [%04x]\n", wndPtr->hwndSelf, wndPtr->hrgnUpdate, hRgn, flags );
|
|
|
|
if( flags & RDW_INVALIDATE )
|
|
{
|
|
if( hRgn > 1 )
|
|
{
|
|
switch( wndPtr->hrgnUpdate )
|
|
{
|
|
default:
|
|
CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate, hRgn, RGN_OR );
|
|
/* fall through */
|
|
case 0:
|
|
wndPtr->hrgnUpdate = REGION_CropRgn( wndPtr->hrgnUpdate,
|
|
wndPtr->hrgnUpdate ? wndPtr->hrgnUpdate : hRgn,
|
|
&r, NULL );
|
|
if( !bHadOne )
|
|
{
|
|
GetRgnBox( wndPtr->hrgnUpdate, &r );
|
|
if( IsRectEmpty( &r ) )
|
|
{
|
|
DeleteObject( wndPtr->hrgnUpdate );
|
|
wndPtr->hrgnUpdate = 0;
|
|
goto OUT;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 1: /* already an entire window */
|
|
}
|
|
}
|
|
else if( hRgn == 1 )
|
|
{
|
|
if( wndPtr->hrgnUpdate > 1 )
|
|
DeleteObject( wndPtr->hrgnUpdate );
|
|
wndPtr->hrgnUpdate = 1;
|
|
}
|
|
else
|
|
hRgn = wndPtr->hrgnUpdate; /* this is a trick that depends on code in PAINT_RedrawWindow() */
|
|
|
|
if( !bHadOne && !(wndPtr->flags & WIN_INTERNAL_PAINT) )
|
|
QUEUE_IncPaintCount( wndPtr->hmemTaskQ );
|
|
|
|
if (flags & RDW_FRAME) wndPtr->flags |= WIN_NEEDS_NCPAINT;
|
|
if (flags & RDW_ERASE) wndPtr->flags |= WIN_NEEDS_ERASEBKGND;
|
|
flags |= RDW_FRAME;
|
|
}
|
|
else if( flags & RDW_VALIDATE )
|
|
{
|
|
if( wndPtr->hrgnUpdate )
|
|
{
|
|
if( hRgn > 1 )
|
|
{
|
|
if( wndPtr->hrgnUpdate == 1 )
|
|
wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r );
|
|
|
|
if( CombineRgn( wndPtr->hrgnUpdate, wndPtr->hrgnUpdate, hRgn, RGN_DIFF )
|
|
== NULLREGION )
|
|
goto EMPTY;
|
|
}
|
|
else /* validate everything */
|
|
{
|
|
if( wndPtr->hrgnUpdate > 1 )
|
|
{
|
|
EMPTY:
|
|
DeleteObject( wndPtr->hrgnUpdate );
|
|
}
|
|
wndPtr->hrgnUpdate = 0;
|
|
}
|
|
|
|
if( !wndPtr->hrgnUpdate )
|
|
{
|
|
wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
|
|
if( !(wndPtr->flags & WIN_INTERNAL_PAINT) )
|
|
QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
|
|
}
|
|
}
|
|
|
|
if (flags & RDW_NOFRAME) wndPtr->flags &= ~WIN_NEEDS_NCPAINT;
|
|
if (flags & RDW_NOERASE) wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
|
|
}
|
|
|
|
/* in/validate child windows that intersect with the region if it
|
|
* is a valid handle. */
|
|
|
|
if( flags & (RDW_INVALIDATE | RDW_VALIDATE) )
|
|
{
|
|
if( hRgn > 1 && bChildren )
|
|
{
|
|
WND* wnd = wndPtr->child;
|
|
POINT ptClient = { wndPtr->rectClient.left - wndPtr->rectWindow.left,
|
|
wndPtr->rectClient.top - wndPtr->rectWindow.top };
|
|
POINT ptTotal, prevOrigin = {0,0};
|
|
|
|
for( ptTotal.x = ptTotal.y = 0; wnd; wnd = wnd->next )
|
|
{
|
|
if( wnd->dwStyle & WS_VISIBLE )
|
|
{
|
|
POINT ptOffset;
|
|
|
|
r.left = wnd->rectWindow.left + ptClient.x;
|
|
r.right = wnd->rectWindow.right + ptClient.x;
|
|
r.top = wnd->rectWindow.top + ptClient.y;
|
|
r.bottom = wnd->rectWindow.bottom + ptClient.y;
|
|
|
|
ptOffset.x = r.left - prevOrigin.x;
|
|
ptOffset.y = r.top - prevOrigin.y;
|
|
OffsetRect( &r, -ptTotal.x, -ptTotal.y );
|
|
|
|
if( RectInRegion( hRgn, &r ) )
|
|
{
|
|
OffsetRgn( hRgn, -ptOffset.x, -ptOffset.y );
|
|
RDW_UpdateRgns( wnd, hRgn, flags );
|
|
prevOrigin.x = r.left + ptTotal.x;
|
|
prevOrigin.y = r.top + ptTotal.y;
|
|
ptTotal.x += ptOffset.x;
|
|
ptTotal.y += ptOffset.y;
|
|
}
|
|
}
|
|
}
|
|
OffsetRgn( hRgn, ptTotal.x, ptTotal.y );
|
|
bChildren = 0;
|
|
}
|
|
}
|
|
|
|
/* handle hRgn == 1 (alias for entire window) and/or internal paint recursion */
|
|
|
|
if( bChildren )
|
|
{
|
|
WND* wnd;
|
|
for( wnd = wndPtr->child; wnd; wnd = wnd->next )
|
|
if( wnd->dwStyle & WS_VISIBLE )
|
|
RDW_UpdateRgns( wnd, hRgn, flags );
|
|
}
|
|
|
|
OUT:
|
|
|
|
/* Set/clear internal paint flag */
|
|
|
|
if (flags & RDW_INTERNALPAINT)
|
|
{
|
|
if ( !wndPtr->hrgnUpdate && !(wndPtr->flags & WIN_INTERNAL_PAINT))
|
|
QUEUE_IncPaintCount( wndPtr->hmemTaskQ );
|
|
wndPtr->flags |= WIN_INTERNAL_PAINT;
|
|
}
|
|
else if (flags & RDW_NOINTERNALPAINT)
|
|
{
|
|
if ( !wndPtr->hrgnUpdate && (wndPtr->flags & WIN_INTERNAL_PAINT))
|
|
QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
|
|
wndPtr->flags &= ~WIN_INTERNAL_PAINT;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RDW_Paint [RedrawWindow() helper]
|
|
*
|
|
* Walks the window tree and paints/erases windows that have
|
|
* nonzero update regions according to redraw flags. hrgn is a scratch
|
|
* region passed down during recursion. Must not be 1.
|
|
*
|
|
*/
|
|
static HRGN RDW_Paint( WND* wndPtr, HRGN hrgn, UINT flags, UINT ex )
|
|
{
|
|
/* NOTE: wndPtr is locked by caller.
|
|
*
|
|
* FIXME: Windows uses WM_SYNCPAINT to cut down the number of intertask
|
|
* SendMessage() calls. This is a comment inside DefWindowProc() source
|
|
* from 16-bit SDK:
|
|
*
|
|
* This message avoids lots of inter-app message traffic
|
|
* by switching to the other task and continuing the
|
|
* recursion there.
|
|
*
|
|
* wParam = flags
|
|
* LOWORD(lParam) = hrgnClip
|
|
* HIWORD(lParam) = hwndSkip (not used; always NULL)
|
|
*
|
|
*/
|
|
HDC hDC;
|
|
HWND hWnd = wndPtr->hwndSelf;
|
|
BOOL bIcon = ((wndPtr->dwStyle & WS_MINIMIZE) && wndPtr->class->hIcon);
|
|
|
|
/* Erase/update the window itself ... */
|
|
|
|
TRACE_(win)("\thwnd %04x [%04x] -> hrgn [%04x], flags [%04x]\n", hWnd, wndPtr->hrgnUpdate, hrgn, flags );
|
|
|
|
if (flags & RDW_UPDATENOW)
|
|
{
|
|
if (wndPtr->hrgnUpdate) /* wm_painticon wparam is 1 */
|
|
SendMessage16( hWnd, (bIcon) ? WM_PAINTICON : WM_PAINT, bIcon, 0 );
|
|
}
|
|
else if ((flags & RDW_ERASENOW) || (ex & RDW_EX_TOPFRAME))
|
|
{
|
|
UINT dcx = DCX_INTERSECTRGN | DCX_USESTYLE | DCX_KEEPCLIPRGN | DCX_WINDOWPAINT;
|
|
HRGN hrgnRet = WIN_UpdateNCRgn( wndPtr, hrgn, UNC_REGION | UNC_CHECK |
|
|
((ex & RDW_EX_TOPFRAME) ? UNC_ENTIRE : 0) );
|
|
if( hrgnRet )
|
|
{
|
|
if( hrgnRet > 1 ) hrgn = hrgnRet; else hrgnRet = 0; /* entire client */
|
|
if( wndPtr->flags & WIN_NEEDS_ERASEBKGND )
|
|
{
|
|
if( bIcon ) dcx |= DCX_WINDOW;
|
|
else
|
|
if( hrgnRet )
|
|
OffsetRgn( hrgnRet, wndPtr->rectWindow.left - wndPtr->rectClient.left,
|
|
wndPtr->rectWindow.top - wndPtr->rectClient.top);
|
|
else
|
|
dcx &= ~DCX_INTERSECTRGN;
|
|
if (( hDC = GetDCEx( hWnd, hrgnRet, dcx )) )
|
|
{
|
|
if (SendMessage16( hWnd, (bIcon) ? WM_ICONERASEBKGND
|
|
: WM_ERASEBKGND, (WPARAM16)hDC, 0 ))
|
|
wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
|
|
ReleaseDC( hWnd, hDC );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !IsWindow(hWnd) ) return hrgn;
|
|
ex &= ~RDW_EX_TOPFRAME;
|
|
|
|
/* ... and its child windows */
|
|
|
|
if( wndPtr->child && !(flags & RDW_NOCHILDREN) && !(wndPtr->dwStyle & WS_MINIMIZE)
|
|
&& ((flags & RDW_ALLCHILDREN) || !(wndPtr->dwStyle & WS_CLIPCHILDREN)) )
|
|
{
|
|
WND** list, **ppWnd;
|
|
|
|
if( (list = WIN_BuildWinArray( wndPtr, 0, NULL )) )
|
|
{
|
|
for (ppWnd = list; *ppWnd; ppWnd++)
|
|
{
|
|
WIN_UpdateWndPtr(&wndPtr,*ppWnd);
|
|
if (!IsWindow(wndPtr->hwndSelf)) continue;
|
|
if ( (wndPtr->dwStyle & WS_VISIBLE) &&
|
|
(wndPtr->hrgnUpdate || (wndPtr->flags & WIN_INTERNAL_PAINT)) )
|
|
hrgn = RDW_Paint( wndPtr, hrgn, flags, ex );
|
|
}
|
|
WIN_ReleaseWinArray(list);
|
|
}
|
|
}
|
|
|
|
return hrgn;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* PAINT_RedrawWindow
|
|
*
|
|
*/
|
|
BOOL PAINT_RedrawWindow( HWND hwnd, const RECT *rectUpdate,
|
|
HRGN hrgnUpdate, UINT flags, UINT ex )
|
|
{
|
|
HRGN hRgn = 0;
|
|
RECT r, r2;
|
|
POINT pt;
|
|
WND* wndPtr;
|
|
|
|
if (!hwnd) hwnd = GetDesktopWindow();
|
|
if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE;
|
|
|
|
/* check if the window or its parents are visible/not minimized */
|
|
|
|
if (!WIN_IsWindowDrawable( wndPtr, !(flags & RDW_FRAME) ) )
|
|
{
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return TRUE;
|
|
}
|
|
|
|
if (TRACE_ON(win))
|
|
{
|
|
if( hrgnUpdate )
|
|
{
|
|
GetRgnBox( hrgnUpdate, &r );
|
|
TRACE_(win)( "%04x (%04x) NULL %04x box (%i,%i-%i,%i) flags=%04x, exflags=%04x\n",
|
|
hwnd, wndPtr->hrgnUpdate, hrgnUpdate, r.left, r.top, r.right, r.bottom, flags, ex);
|
|
}
|
|
else
|
|
{
|
|
if( rectUpdate )
|
|
r = *rectUpdate;
|
|
else
|
|
SetRectEmpty( &r );
|
|
TRACE_(win)( "%04x (%04x) %s %d,%d-%d,%d %04x flags=%04x, exflags=%04x\n",
|
|
hwnd, wndPtr->hrgnUpdate, rectUpdate ? "rect" : "NULL", r.left,
|
|
r.top, r.right, r.bottom, hrgnUpdate, flags, ex );
|
|
}
|
|
}
|
|
|
|
/* prepare an update region in window coordinates */
|
|
|
|
if( flags & RDW_FRAME )
|
|
r = wndPtr->rectWindow;
|
|
else
|
|
r = wndPtr->rectClient;
|
|
|
|
if( ex & RDW_EX_XYWINDOW )
|
|
{
|
|
pt.x = pt.y = 0;
|
|
OffsetRect( &r, -wndPtr->rectWindow.left, -wndPtr->rectWindow.top );
|
|
}
|
|
else
|
|
{
|
|
pt.x = wndPtr->rectClient.left - wndPtr->rectWindow.left;
|
|
pt.y = wndPtr->rectClient.top - wndPtr->rectWindow.top;
|
|
OffsetRect( &r, -wndPtr->rectClient.left, -wndPtr->rectClient.top );
|
|
}
|
|
|
|
if (flags & RDW_INVALIDATE) /* ------------------------- Invalidate */
|
|
{
|
|
/* If the window doesn't have hrgnUpdate we leave hRgn zero
|
|
* and put a new region straight into wndPtr->hrgnUpdate
|
|
* so that RDW_UpdateRgns() won't have to do any extra work.
|
|
*/
|
|
|
|
if( hrgnUpdate )
|
|
{
|
|
if( wndPtr->hrgnUpdate )
|
|
hRgn = REGION_CropRgn( 0, hrgnUpdate, NULL, &pt );
|
|
else
|
|
wndPtr->hrgnUpdate = REGION_CropRgn( 0, hrgnUpdate, &r, &pt );
|
|
}
|
|
else if( rectUpdate )
|
|
{
|
|
if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
|
|
OffsetRect( &r2, pt.x, pt.y );
|
|
|
|
rect2i:
|
|
if( wndPtr->hrgnUpdate == 0 )
|
|
wndPtr->hrgnUpdate = CreateRectRgnIndirect( &r2 );
|
|
else
|
|
hRgn = CreateRectRgnIndirect( &r2 );
|
|
}
|
|
else /* entire window or client depending on RDW_FRAME */
|
|
{
|
|
if( flags & RDW_FRAME )
|
|
{
|
|
if( wndPtr->hrgnUpdate )
|
|
DeleteObject( wndPtr->hrgnUpdate );
|
|
wndPtr->hrgnUpdate = 1;
|
|
}
|
|
else
|
|
{
|
|
GETCLIENTRECTW( wndPtr, r2 );
|
|
goto rect2i;
|
|
}
|
|
}
|
|
}
|
|
else if (flags & RDW_VALIDATE) /* ------------------------- Validate */
|
|
{
|
|
/* In this we cannot leave with zero hRgn */
|
|
if( hrgnUpdate )
|
|
{
|
|
hRgn = REGION_CropRgn( hRgn, hrgnUpdate, &r, &pt );
|
|
GetRgnBox( hRgn, &r2 );
|
|
if( IsRectEmpty( &r2 ) ) goto END;
|
|
}
|
|
else if( rectUpdate )
|
|
{
|
|
if( !IntersectRect( &r2, &r, rectUpdate ) ) goto END;
|
|
OffsetRect( &r2, pt.x, pt.y );
|
|
rect2v:
|
|
hRgn = CreateRectRgnIndirect( &r2 );
|
|
}
|
|
else /* entire window or client depending on RDW_FRAME */
|
|
{
|
|
if( flags & RDW_FRAME )
|
|
hRgn = 1;
|
|
else
|
|
{
|
|
GETCLIENTRECTW( wndPtr, r2 );
|
|
goto rect2v;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* At this point hRgn is either an update region in window coordinates or 1 or 0 */
|
|
|
|
RDW_UpdateRgns( wndPtr, hRgn, flags );
|
|
|
|
/* Erase/update windows, from now on hRgn is a scratch region */
|
|
|
|
hRgn = RDW_Paint( wndPtr, (hRgn == 1) ? 0 : hRgn, flags, ex );
|
|
|
|
END:
|
|
if( hRgn > 1 && (hRgn != hrgnUpdate) )
|
|
DeleteObject(hRgn );
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* RedrawWindow32 (USER32.426)
|
|
*/
|
|
BOOL WINAPI RedrawWindow( HWND hwnd, const RECT *rectUpdate,
|
|
HRGN hrgnUpdate, UINT flags )
|
|
{
|
|
return PAINT_RedrawWindow( hwnd, rectUpdate, hrgnUpdate, flags, 0 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* RedrawWindow16 (USER.290)
|
|
*/
|
|
BOOL16 WINAPI RedrawWindow16( HWND16 hwnd, const RECT16 *rectUpdate,
|
|
HRGN16 hrgnUpdate, UINT16 flags )
|
|
{
|
|
if (rectUpdate)
|
|
{
|
|
RECT r;
|
|
CONV_RECT16TO32( rectUpdate, &r );
|
|
return (BOOL16)RedrawWindow( (HWND)hwnd, &r, hrgnUpdate, flags );
|
|
}
|
|
return (BOOL16)PAINT_RedrawWindow( (HWND)hwnd, NULL,
|
|
(HRGN)hrgnUpdate, flags, 0 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* UpdateWindow16 (USER.124)
|
|
*/
|
|
void WINAPI UpdateWindow16( HWND16 hwnd )
|
|
{
|
|
PAINT_RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_NOCHILDREN, 0 );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* UpdateWindow32 (USER32.567)
|
|
*/
|
|
void WINAPI UpdateWindow( HWND hwnd )
|
|
{
|
|
PAINT_RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_NOCHILDREN, 0 );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* InvalidateRgn16 (USER.126)
|
|
*/
|
|
void WINAPI InvalidateRgn16( HWND16 hwnd, HRGN16 hrgn, BOOL16 erase )
|
|
{
|
|
PAINT_RedrawWindow((HWND)hwnd, NULL, (HRGN)hrgn,
|
|
RDW_INVALIDATE | (erase ? RDW_ERASE : 0), 0 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* InvalidateRgn32 (USER32.329)
|
|
*/
|
|
BOOL WINAPI InvalidateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
|
|
{
|
|
return PAINT_RedrawWindow(hwnd, NULL, hrgn, RDW_INVALIDATE | (erase ? RDW_ERASE : 0), 0 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* InvalidateRect16 (USER.125)
|
|
*/
|
|
void WINAPI InvalidateRect16( HWND16 hwnd, const RECT16 *rect, BOOL16 erase )
|
|
{
|
|
RedrawWindow16( hwnd, rect, 0, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* InvalidateRect32 (USER32.328)
|
|
*/
|
|
BOOL WINAPI InvalidateRect( HWND hwnd, const RECT *rect, BOOL erase )
|
|
{
|
|
return PAINT_RedrawWindow( hwnd, rect, 0,
|
|
RDW_INVALIDATE | (erase ? RDW_ERASE : 0), 0 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* ValidateRgn16 (USER.128)
|
|
*/
|
|
void WINAPI ValidateRgn16( HWND16 hwnd, HRGN16 hrgn )
|
|
{
|
|
PAINT_RedrawWindow( (HWND)hwnd, NULL, (HRGN)hrgn,
|
|
RDW_VALIDATE | RDW_NOCHILDREN, 0 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* ValidateRgn32 (USER32.572)
|
|
*/
|
|
void WINAPI ValidateRgn( HWND hwnd, HRGN hrgn )
|
|
{
|
|
PAINT_RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOCHILDREN, 0 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* ValidateRect16 (USER.127)
|
|
*/
|
|
void WINAPI ValidateRect16( HWND16 hwnd, const RECT16 *rect )
|
|
{
|
|
RedrawWindow16( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* ValidateRect32 (USER32.571)
|
|
*/
|
|
void WINAPI ValidateRect( HWND hwnd, const RECT *rect )
|
|
{
|
|
PAINT_RedrawWindow( hwnd, rect, 0, RDW_VALIDATE | RDW_NOCHILDREN, 0 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetUpdateRect16 (USER.190)
|
|
*/
|
|
BOOL16 WINAPI GetUpdateRect16( HWND16 hwnd, LPRECT16 rect, BOOL16 erase )
|
|
{
|
|
RECT r;
|
|
BOOL16 ret;
|
|
|
|
if (!rect) return GetUpdateRect( hwnd, NULL, erase );
|
|
ret = GetUpdateRect( hwnd, &r, erase );
|
|
CONV_RECT32TO16( &r, rect );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetUpdateRect32 (USER32.297)
|
|
*/
|
|
BOOL WINAPI GetUpdateRect( HWND hwnd, LPRECT rect, BOOL erase )
|
|
{
|
|
BOOL retvalue;
|
|
WND * wndPtr = WIN_FindWndPtr( hwnd );
|
|
if (!wndPtr) return FALSE;
|
|
|
|
if (rect)
|
|
{
|
|
if (wndPtr->hrgnUpdate > 1)
|
|
{
|
|
HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
|
|
if (GetUpdateRgn( hwnd, hrgn, erase ) == ERROR)
|
|
{
|
|
retvalue = FALSE;
|
|
goto END;
|
|
}
|
|
GetRgnBox( hrgn, rect );
|
|
DeleteObject( hrgn );
|
|
if (wndPtr->class->style & CS_OWNDC)
|
|
{
|
|
if (GetMapMode(wndPtr->dce->hDC) != MM_TEXT)
|
|
{
|
|
DPtoLP (wndPtr->dce->hDC, (LPPOINT)rect, 2);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if( wndPtr->hrgnUpdate == 1 )
|
|
{
|
|
GetClientRect( hwnd, rect );
|
|
if (erase) RedrawWindow( hwnd, NULL, 0, RDW_FRAME | RDW_ERASENOW | RDW_NOCHILDREN );
|
|
}
|
|
else
|
|
SetRectEmpty( rect );
|
|
}
|
|
retvalue = (wndPtr->hrgnUpdate >= 1);
|
|
END:
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return retvalue;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetUpdateRgn16 (USER.237)
|
|
*/
|
|
INT16 WINAPI GetUpdateRgn16( HWND16 hwnd, HRGN16 hrgn, BOOL16 erase )
|
|
{
|
|
return GetUpdateRgn( hwnd, hrgn, erase );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetUpdateRgn (USER32.298)
|
|
*/
|
|
INT WINAPI GetUpdateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
|
|
{
|
|
INT retval;
|
|
WND * wndPtr = WIN_FindWndPtr( hwnd );
|
|
if (!wndPtr) return ERROR;
|
|
|
|
if (wndPtr->hrgnUpdate == 0)
|
|
{
|
|
SetRectRgn( hrgn, 0, 0, 0, 0 );
|
|
retval = NULLREGION;
|
|
goto END;
|
|
}
|
|
else
|
|
if (wndPtr->hrgnUpdate == 1)
|
|
{
|
|
SetRectRgn( hrgn, 0, 0, wndPtr->rectClient.right - wndPtr->rectClient.left,
|
|
wndPtr->rectClient.bottom - wndPtr->rectClient.top );
|
|
retval = SIMPLEREGION;
|
|
}
|
|
else
|
|
{
|
|
retval = CombineRgn( hrgn, wndPtr->hrgnUpdate, 0, RGN_COPY );
|
|
OffsetRgn( hrgn, wndPtr->rectWindow.left - wndPtr->rectClient.left,
|
|
wndPtr->rectWindow.top - wndPtr->rectClient.top );
|
|
}
|
|
if (erase) RedrawWindow( hwnd, NULL, 0, RDW_ERASENOW | RDW_NOCHILDREN );
|
|
END:
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return retval;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* ExcludeUpdateRgn16 (USER.238)
|
|
*/
|
|
INT16 WINAPI ExcludeUpdateRgn16( HDC16 hdc, HWND16 hwnd )
|
|
{
|
|
return ExcludeUpdateRgn( hdc, hwnd );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* ExcludeUpdateRgn32 (USER32.195)
|
|
*/
|
|
INT WINAPI ExcludeUpdateRgn( HDC hdc, HWND hwnd )
|
|
{
|
|
RECT rect;
|
|
WND * wndPtr;
|
|
|
|
if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return ERROR;
|
|
|
|
if (wndPtr->hrgnUpdate)
|
|
{
|
|
INT ret;
|
|
HRGN hrgn = CreateRectRgn(wndPtr->rectWindow.left - wndPtr->rectClient.left,
|
|
wndPtr->rectWindow.top - wndPtr->rectClient.top,
|
|
wndPtr->rectWindow.right - wndPtr->rectClient.left,
|
|
wndPtr->rectWindow.bottom - wndPtr->rectClient.top);
|
|
if( wndPtr->hrgnUpdate > 1 )
|
|
{
|
|
CombineRgn(hrgn, wndPtr->hrgnUpdate, 0, RGN_COPY);
|
|
OffsetRgn(hrgn, wndPtr->rectWindow.left - wndPtr->rectClient.left,
|
|
wndPtr->rectWindow.top - wndPtr->rectClient.top );
|
|
}
|
|
|
|
/* do ugly coordinate translations in dce.c */
|
|
|
|
ret = DCE_ExcludeRgn( hdc, wndPtr, hrgn );
|
|
DeleteObject( hrgn );
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return ret;
|
|
}
|
|
WIN_ReleaseWndPtr(wndPtr);
|
|
return GetClipBox( hdc, &rect );
|
|
}
|
|
|
|
|