reactos/win32ss/user/ntuser/painting.c
Doug Lyons 29235147e2
[NTUSER] Remove some extra repaints/redraws (#6827)
Reposition logic and add Flags testing for RgnType == NULLREGION.
This was suggested by @HBelusca and approved by @I_Kill_Bugs
2024-05-04 07:35:53 -05:00

2622 lines
64 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Win32k subsystem
* PURPOSE: Window painting function
* FILE: win32ss/user/ntuser/painting.c
* PROGRAMER: Filip Navara (xnavara@volny.cz)
*/
#include <win32k.h>
DBG_DEFAULT_CHANNEL(UserPainting);
BOOL UserExtTextOutW(HDC hdc, INT x, INT y, UINT flags, PRECTL lprc,
LPCWSTR lpString, UINT count);
/* PRIVATE FUNCTIONS **********************************************************/
/**
* @name IntIntersectWithParents
*
* Intersect window rectangle with all parent client rectangles.
*
* @param Child
* Pointer to child window to start intersecting from.
* @param WindowRect
* Pointer to rectangle that we want to intersect in screen
* coordinates on input and intersected rectangle on output (if TRUE
* is returned).
*
* @return
* If any parent is minimized or invisible or the resulting rectangle
* is empty then FALSE is returned. Otherwise TRUE is returned.
*/
BOOL FASTCALL
IntIntersectWithParents(PWND Child, RECTL *WindowRect)
{
PWND ParentWnd;
if (Child->ExStyle & WS_EX_REDIRECTED)
return TRUE;
ParentWnd = Child->spwndParent;
while (ParentWnd != NULL)
{
if (!(ParentWnd->style & WS_VISIBLE) ||
(ParentWnd->style & WS_MINIMIZE) ||
!RECTL_bIntersectRect(WindowRect, WindowRect, &ParentWnd->rcClient) )
{
return FALSE;
}
if (ParentWnd->ExStyle & WS_EX_REDIRECTED)
return TRUE;
ParentWnd = ParentWnd->spwndParent;
}
return TRUE;
}
BOOL FASTCALL
IntValidateParents(PWND Child, BOOL Recurse)
{
RECTL ParentRect, Rect;
BOOL Start, Ret = TRUE;
PWND ParentWnd = Child;
PREGION Rgn = NULL;
if (ParentWnd->style & WS_CHILD)
{
do
ParentWnd = ParentWnd->spwndParent;
while (ParentWnd->style & WS_CHILD);
}
// No pending nonclient paints.
if (!(ParentWnd->state & WNDS_SYNCPAINTPENDING)) Recurse = FALSE;
Start = TRUE;
ParentWnd = Child->spwndParent;
while (ParentWnd)
{
if (ParentWnd->style & WS_CLIPCHILDREN)
break;
if (ParentWnd->hrgnUpdate != 0)
{
if (Recurse)
{
Ret = FALSE;
break;
}
// Start with child clipping.
if (Start)
{
Start = FALSE;
Rect = Child->rcWindow;
if (!IntIntersectWithParents(Child, &Rect)) break;
Rgn = IntSysCreateRectpRgnIndirect(&Rect);
if (Child->hrgnClip)
{
PREGION RgnClip = REGION_LockRgn(Child->hrgnClip);
IntGdiCombineRgn(Rgn, Rgn, RgnClip, RGN_AND);
REGION_UnlockRgn(RgnClip);
}
}
ParentRect = ParentWnd->rcWindow;
if (!IntIntersectWithParents(ParentWnd, &ParentRect)) break;
IntInvalidateWindows( ParentWnd,
Rgn,
RDW_VALIDATE | RDW_NOCHILDREN | RDW_NOUPDATEDIRTY);
}
ParentWnd = ParentWnd->spwndParent;
}
if (Rgn) REGION_Delete(Rgn);
return Ret;
}
/*
Synchronize painting to the top-level windows of other threads.
*/
VOID FASTCALL
IntSendSyncPaint(PWND Wnd, ULONG Flags)
{
PTHREADINFO ptiCur, ptiWnd;
PUSER_SENT_MESSAGE Message;
PLIST_ENTRY Entry;
BOOL bSend = TRUE;
ptiWnd = Wnd->head.pti;
ptiCur = PsGetCurrentThreadWin32Thread();
/*
Not the current thread, Wnd is in send Nonclient paint also in send erase background and it is visiable.
*/
if ( Wnd->head.pti != ptiCur &&
Wnd->state & WNDS_SENDNCPAINT &&
Wnd->state & WNDS_SENDERASEBACKGROUND &&
Wnd->style & WS_VISIBLE)
{
// For testing, if you see this, break out the Champagne and have a party!
TRACE("SendSyncPaint Wnd in State!\n");
if (!IsListEmpty(&ptiWnd->SentMessagesListHead))
{
// Scan sent queue messages to see if we received sync paint messages.
Entry = ptiWnd->SentMessagesListHead.Flink;
Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
do
{
ERR("LOOP it\n");
if (Message->Msg.message == WM_SYNCPAINT &&
Message->Msg.hwnd == UserHMGetHandle(Wnd))
{ // Already received so exit out.
ERR("SendSyncPaint Found one in the Sent Msg Queue!\n");
bSend = FALSE;
break;
}
Entry = Message->ListEntry.Flink;
Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
}
while (Entry != &ptiWnd->SentMessagesListHead);
}
if (bSend)
{
TRACE("Sending WM_SYNCPAINT\n");
// This message has no parameters. But it does! Pass Flags along.
co_IntSendMessageNoWait(UserHMGetHandle(Wnd), WM_SYNCPAINT, Flags, 0);
Wnd->state |= WNDS_SYNCPAINTPENDING;
}
}
// Send to all the children if this is the desktop window.
if (UserIsDesktopWindow(Wnd))
{
if ( Flags & RDW_ALLCHILDREN ||
( !(Flags & RDW_NOCHILDREN) && Wnd->style & WS_CLIPCHILDREN))
{
PWND spwndChild = Wnd->spwndChild;
while(spwndChild)
{
if ( spwndChild->style & WS_CHILD &&
spwndChild->head.pti != ptiCur)
{
spwndChild = spwndChild->spwndNext;
continue;
}
IntSendSyncPaint( spwndChild, Flags );
spwndChild = spwndChild->spwndNext;
}
}
}
}
/*
* @name IntCalcWindowRgn
*
* Get a window or client region.
*/
HRGN FASTCALL
IntCalcWindowRgn(PWND Wnd, BOOL Client)
{
HRGN hRgnWindow;
if (Client)
{
hRgnWindow = NtGdiCreateRectRgn(
Wnd->rcClient.left,
Wnd->rcClient.top,
Wnd->rcClient.right,
Wnd->rcClient.bottom);
}
else
{
hRgnWindow = NtGdiCreateRectRgn(
Wnd->rcWindow.left,
Wnd->rcWindow.top,
Wnd->rcWindow.right,
Wnd->rcWindow.bottom);
}
if (Wnd->hrgnClip != NULL && !(Wnd->style & WS_MINIMIZE))
{
NtGdiOffsetRgn(hRgnWindow,
-Wnd->rcWindow.left,
-Wnd->rcWindow.top);
NtGdiCombineRgn(hRgnWindow, hRgnWindow, Wnd->hrgnClip, RGN_AND);
NtGdiOffsetRgn(hRgnWindow,
Wnd->rcWindow.left,
Wnd->rcWindow.top);
}
return hRgnWindow;
}
/*
* @name IntGetNCUpdateRgn
*
* Get non-client update region of a window and optionally validate it.
*
* @param Window
* Pointer to window to get the NC update region from.
* @param Validate
* Set to TRUE to force validating the NC update region.
*
* @return
* Handle to NC update region. The caller is responsible for deleting
* it.
*/
HRGN FASTCALL
IntGetNCUpdateRgn(PWND Window, BOOL Validate)
{
HRGN hRgnNonClient;
HRGN hRgnWindow;
UINT RgnType, NcType;
RECT update;
if (Window->hrgnUpdate != NULL &&
Window->hrgnUpdate != HRGN_WINDOW)
{
hRgnNonClient = IntCalcWindowRgn(Window, FALSE);
/*
* If region creation fails it's safe to fallback to whole
* window region.
*/
if (hRgnNonClient == NULL)
{
return HRGN_WINDOW;
}
hRgnWindow = IntCalcWindowRgn(Window, TRUE);
if (hRgnWindow == NULL)
{
GreDeleteObject(hRgnNonClient);
return HRGN_WINDOW;
}
NcType = IntGdiGetRgnBox(hRgnNonClient, &update);
RgnType = NtGdiCombineRgn(hRgnNonClient, hRgnNonClient, hRgnWindow, RGN_DIFF);
if (RgnType == ERROR)
{
GreDeleteObject(hRgnWindow);
GreDeleteObject(hRgnNonClient);
return HRGN_WINDOW;
}
else if (RgnType == NULLREGION)
{
GreDeleteObject(hRgnWindow);
GreDeleteObject(hRgnNonClient);
Window->state &= ~WNDS_UPDATEDIRTY;
return NULL;
}
/*
* Remove the nonclient region from the standard update region if
* we were asked for it.
*/
if (Validate)
{
if (NtGdiCombineRgn(Window->hrgnUpdate, Window->hrgnUpdate, hRgnWindow, RGN_AND) == NULLREGION)
{
IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
GreDeleteObject(Window->hrgnUpdate);
Window->state &= ~WNDS_UPDATEDIRTY;
Window->hrgnUpdate = NULL;
if (!(Window->state & WNDS_INTERNALPAINT))
MsqDecPaintCountQueue(Window->head.pti);
}
}
/* check if update rgn contains complete nonclient area */
if (NcType == SIMPLEREGION)
{
RECT window;
IntGetWindowRect( Window, &window );
if (IntEqualRect( &window, &update ))
{
GreDeleteObject(hRgnNonClient);
hRgnNonClient = HRGN_WINDOW;
}
}
GreDeleteObject(hRgnWindow);
return hRgnNonClient;
}
else
{
return Window->hrgnUpdate;
}
}
VOID FASTCALL
IntSendNCPaint(PWND pWnd, HRGN hRgn)
{
pWnd->state &= ~WNDS_SENDNCPAINT;
if ( pWnd == GetW32ThreadInfo()->MessageQueue->spwndActive &&
!(pWnd->state & WNDS_ACTIVEFRAME))
{
pWnd->state |= WNDS_ACTIVEFRAME;
pWnd->state &= ~WNDS_NONCPAINT;
hRgn = HRGN_WINDOW;
}
if (pWnd->state2 & WNDS2_FORCEFULLNCPAINTCLIPRGN)
{
pWnd->state2 &= ~WNDS2_FORCEFULLNCPAINTCLIPRGN;
hRgn = HRGN_WINDOW;
}
if (hRgn) co_IntSendMessage(UserHMGetHandle(pWnd), WM_NCPAINT, (WPARAM)hRgn, 0);
}
VOID FASTCALL
IntSendChildNCPaint(PWND pWnd)
{
pWnd = pWnd->spwndChild;
while (pWnd)
{
if ((pWnd->hrgnUpdate == NULL) && (pWnd->state & WNDS_SENDNCPAINT))
{
PWND Next;
USER_REFERENCE_ENTRY Ref;
/* Reference, IntSendNCPaint leaves win32k */
UserRefObjectCo(pWnd, &Ref);
IntSendNCPaint(pWnd, HRGN_WINDOW);
/* Make sure to grab next one before dereferencing/freeing */
Next = pWnd->spwndNext;
UserDerefObjectCo(pWnd);
pWnd = Next;
}
else
{
pWnd = pWnd->spwndNext;
}
}
}
/*
* IntPaintWindows
*
* Internal function used by IntRedrawWindow.
*/
VOID FASTCALL
co_IntPaintWindows(PWND Wnd, ULONG Flags, BOOL Recurse)
{
HDC hDC;
HWND hWnd = UserHMGetHandle(Wnd);
HRGN TempRegion = NULL;
Wnd->state &= ~WNDS_PAINTNOTPROCESSED;
if (Wnd->state & WNDS_SENDNCPAINT ||
Wnd->state & WNDS_SENDERASEBACKGROUND)
{
if (!(Wnd->style & WS_VISIBLE))
{
Wnd->state &= ~(WNDS_SENDNCPAINT|WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
return;
}
else
{
if (Wnd->hrgnUpdate == NULL)
{
Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
}
if (Wnd->head.pti == PsGetCurrentThreadWin32Thread())
{
if (Wnd->state & WNDS_SENDNCPAINT)
{
TempRegion = IntGetNCUpdateRgn(Wnd, TRUE);
IntSendNCPaint(Wnd, TempRegion);
if (TempRegion > HRGN_WINDOW && GreIsHandleValid(TempRegion))
{
/* NOTE: The region can already be deleted! */
GreDeleteObject(TempRegion);
}
}
if (Wnd->state & WNDS_SENDERASEBACKGROUND)
{
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
if (Wnd->hrgnUpdate)
{
hDC = UserGetDCEx( Wnd,
Wnd->hrgnUpdate,
DCX_CACHE|DCX_USESTYLE|DCX_INTERSECTRGN|DCX_KEEPCLIPRGN);
if (Wnd->head.pti->ppi != pti->ppi)
{
ERR("Sending DC to another Process!!!\n");
}
Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
// Kill the loop, so Clear before we send.
if (!co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
{
Wnd->state |= (WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
}
UserReleaseDC(Wnd, hDC, FALSE);
}
}
}
}
}
/*
* Check that the window is still valid at this point
*/
if (!IntIsWindow(hWnd))
{
return;
}
/*
* Paint child windows.
*/
if (!(Flags & RDW_NOCHILDREN) &&
!(Wnd->style & WS_MINIMIZE) &&
( Flags & RDW_ALLCHILDREN ||
(Flags & RDW_CLIPCHILDREN && Wnd->style & WS_CLIPCHILDREN) ) )
{
HWND *List, *phWnd;
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
if ((List = IntWinListChildren(Wnd)))
{
for (phWnd = List; *phWnd; ++phWnd)
{
if ((Wnd = UserGetWindowObject(*phWnd)) == NULL)
continue;
if (Wnd->head.pti != pti && Wnd->style & WS_CHILD)
continue;
if (Wnd->style & WS_VISIBLE)
{
USER_REFERENCE_ENTRY Ref;
UserRefObjectCo(Wnd, &Ref);
co_IntPaintWindows(Wnd, Flags, TRUE);
UserDerefObjectCo(Wnd);
}
}
ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
}
}
}
/*
* IntUpdateWindows
*
* Internal function used by IntRedrawWindow, simplecall.
*/
VOID FASTCALL
co_IntUpdateWindows(PWND Wnd, ULONG Flags, BOOL Recurse)
{
HWND hWnd = UserHMGetHandle(Wnd);
if ( Wnd->hrgnUpdate != NULL || Wnd->state & WNDS_INTERNALPAINT )
{
if (Wnd->hrgnUpdate)
{
if (!IntValidateParents(Wnd, Recurse))
{
return;
}
}
if (Wnd->state & WNDS_INTERNALPAINT)
{
Wnd->state &= ~WNDS_INTERNALPAINT;
if (Wnd->hrgnUpdate == NULL)
MsqDecPaintCountQueue(Wnd->head.pti);
}
Wnd->state |= WNDS_PAINTNOTPROCESSED;
Wnd->state &= ~WNDS_UPDATEDIRTY;
Wnd->state2 |= WNDS2_WMPAINTSENT;
co_IntSendMessage(hWnd, WM_PAINT, 0, 0);
if (Wnd->state & WNDS_PAINTNOTPROCESSED)
{
USER_REFERENCE_ENTRY Ref;
UserRefObjectCo(Wnd, &Ref);
co_IntPaintWindows(Wnd, RDW_NOCHILDREN, FALSE);
UserDerefObjectCo(Wnd);
}
}
// Force flags as a toggle. Fixes msg:test_paint_messages:WmChildPaintNc.
Flags = (Flags & RDW_NOCHILDREN) ? RDW_NOCHILDREN : RDW_ALLCHILDREN; // All children is the default.
/*
* Update child windows.
*/
if (!(Flags & RDW_NOCHILDREN) &&
(Flags & RDW_ALLCHILDREN) &&
!UserIsDesktopWindow(Wnd))
{
PWND Child;
for (Child = Wnd->spwndChild; Child; Child = Child->spwndNext)
{
/* transparent window, check for non-transparent sibling to paint first, then skip it */
if ( Child->ExStyle & WS_EX_TRANSPARENT &&
( Child->hrgnUpdate != NULL || Child->state & WNDS_INTERNALPAINT ) )
{
PWND Next = Child->spwndNext;
while (Next)
{
if ( Next->hrgnUpdate != NULL || Next->state & WNDS_INTERNALPAINT ) break;
Next = Next->spwndNext;
}
if (Next) continue;
}
if (Child->style & WS_VISIBLE)
{
USER_REFERENCE_ENTRY Ref;
UserRefObjectCo(Child, &Ref);
co_IntUpdateWindows(Child, Flags, TRUE);
UserDerefObjectCo(Child);
}
}
}
}
VOID FASTCALL
UserUpdateWindows(PWND pWnd, ULONG Flags)
{
// If transparent and any sibling windows below needs to be painted, leave.
if (pWnd->ExStyle & WS_EX_TRANSPARENT)
{
PWND Next = pWnd->spwndNext;
while(Next)
{
if ( Next->head.pti == pWnd->head.pti &&
( Next->hrgnUpdate != NULL || Next->state & WNDS_INTERNALPAINT) )
{
return;
}
Next = Next->spwndNext;
}
}
co_IntUpdateWindows(pWnd, Flags, FALSE);
}
VOID FASTCALL
UserSyncAndPaintWindows(PWND pWnd, ULONG Flags)
{
PWND Parent = pWnd;
// Find parent, if it needs to be painted, leave.
while(TRUE)
{
if ((Parent = Parent->spwndParent) == NULL) break;
if ( Parent->style & WS_CLIPCHILDREN ) break;
if ( Parent->hrgnUpdate != NULL || Parent->state & WNDS_INTERNALPAINT ) return;
}
IntSendSyncPaint(pWnd, Flags);
co_IntPaintWindows(pWnd, Flags, FALSE);
}
/*
* IntInvalidateWindows
*
* Internal function used by IntRedrawWindow, UserRedrawDesktop,
* co_WinPosSetWindowPos, co_UserRedrawWindow.
*/
VOID FASTCALL
IntInvalidateWindows(PWND Wnd, PREGION Rgn, ULONG Flags)
{
INT RgnType = NULLREGION;
BOOL HadPaintMessage;
TRACE("IntInvalidateWindows start Rgn %p\n",Rgn);
if ( Rgn > PRGN_WINDOW )
{
/*
* If the nonclient is not to be redrawn, clip the region to the client
* rect
*/
if ((Flags & RDW_INVALIDATE) != 0 && (Flags & RDW_FRAME) == 0)
{
PREGION RgnClient;
RgnClient = IntSysCreateRectpRgnIndirect(&Wnd->rcClient);
if (RgnClient)
{
RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnClient, RGN_AND);
REGION_Delete(RgnClient);
}
}
/*
* Clip the given region with window rectangle (or region)
*/
if (!Wnd->hrgnClip || (Wnd->style & WS_MINIMIZE))
{
PREGION RgnWindow = IntSysCreateRectpRgnIndirect(&Wnd->rcWindow);
if (RgnWindow)
{
RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnWindow, RGN_AND);
REGION_Delete(RgnWindow);
}
}
else
{
PREGION RgnClip = REGION_LockRgn(Wnd->hrgnClip);
if (RgnClip)
{
REGION_bOffsetRgn(Rgn,
-Wnd->rcWindow.left,
-Wnd->rcWindow.top);
RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnClip, RGN_AND);
REGION_bOffsetRgn(Rgn,
Wnd->rcWindow.left,
Wnd->rcWindow.top);
REGION_UnlockRgn(RgnClip);
}
}
}
else
{
RgnType = NULLREGION;
}
/* Nothing to paint, just return */
if ((RgnType == NULLREGION && (Flags & RDW_INVALIDATE)) || RgnType == ERROR)
{
return;
}
/*
* Save current state of pending updates
*/
HadPaintMessage = IntIsWindowDirty(Wnd);
/*
* Update the region and flags
*/
// The following flags are used to invalidate the window.
if (Flags & (RDW_INVALIDATE|RDW_INTERNALPAINT|RDW_ERASE|RDW_FRAME))
{
if (Flags & RDW_INTERNALPAINT)
{
Wnd->state |= WNDS_INTERNALPAINT;
}
if (Flags & RDW_INVALIDATE )
{
PREGION RgnUpdate;
Wnd->state &= ~WNDS_NONCPAINT;
/* If not the same thread set it dirty. */
if (Wnd->head.pti != PsGetCurrentThreadWin32Thread())
{
Wnd->state |= WNDS_UPDATEDIRTY;
if (Wnd->state2 & WNDS2_WMPAINTSENT)
Wnd->state2 |= WNDS2_ENDPAINTINVALIDATE;
}
if (Flags & RDW_FRAME)
Wnd->state |= WNDS_SENDNCPAINT;
if (Flags & RDW_ERASE)
Wnd->state |= WNDS_SENDERASEBACKGROUND;
if (RgnType != NULLREGION && Rgn > PRGN_WINDOW)
{
if (Wnd->hrgnUpdate == NULL)
{
Wnd->hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0);
IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_PUBLIC);
}
if (Wnd->hrgnUpdate != HRGN_WINDOW)
{
RgnUpdate = REGION_LockRgn(Wnd->hrgnUpdate);
if (RgnUpdate)
{
RgnType = IntGdiCombineRgn(RgnUpdate, RgnUpdate, Rgn, RGN_OR);
REGION_UnlockRgn(RgnUpdate);
if (RgnType == NULLREGION)
{
IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
GreDeleteObject(Wnd->hrgnUpdate);
Wnd->hrgnUpdate = NULL;
}
}
}
}
Flags |= RDW_ERASE|RDW_FRAME; // For children.
}
if (!HadPaintMessage && IntIsWindowDirty(Wnd))
{
MsqIncPaintCountQueue(Wnd->head.pti);
}
} // The following flags are used to validate the window.
else if (Flags & (RDW_VALIDATE|RDW_NOINTERNALPAINT|RDW_NOERASE|RDW_NOFRAME))
{
if (Wnd->state & WNDS_UPDATEDIRTY && !(Flags & RDW_NOUPDATEDIRTY))
return;
if (Flags & RDW_NOINTERNALPAINT)
{
Wnd->state &= ~WNDS_INTERNALPAINT;
}
if (Flags & RDW_VALIDATE)
{
if (Flags & RDW_NOFRAME)
Wnd->state &= ~WNDS_SENDNCPAINT;
if (Flags & RDW_NOERASE)
Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
if (Wnd->hrgnUpdate > HRGN_WINDOW && RgnType != NULLREGION && Rgn > PRGN_WINDOW)
{
PREGION RgnUpdate = REGION_LockRgn(Wnd->hrgnUpdate);
if (RgnUpdate)
{
RgnType = IntGdiCombineRgn(RgnUpdate, RgnUpdate, Rgn, RGN_DIFF);
REGION_UnlockRgn(RgnUpdate);
if (RgnType == NULLREGION)
{
IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
GreDeleteObject(Wnd->hrgnUpdate);
Wnd->hrgnUpdate = NULL;
}
}
}
// If update is null, do not erase.
if (Wnd->hrgnUpdate == NULL)
{
Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
}
}
if (HadPaintMessage && !IntIsWindowDirty(Wnd))
{
MsqDecPaintCountQueue(Wnd->head.pti);
}
}
/*
* Process children if needed
*/
if (!(Flags & RDW_NOCHILDREN) &&
!(Wnd->style & WS_MINIMIZE) &&
((Flags & RDW_ALLCHILDREN) || !(Wnd->style & WS_CLIPCHILDREN)))
{
PWND Child;
for (Child = Wnd->spwndChild; Child; Child = Child->spwndNext)
{
if (Child->style & WS_VISIBLE)
{
/*
* Recursive call to update children hrgnUpdate
*/
PREGION RgnTemp = IntSysCreateRectpRgn(0, 0, 0, 0);
if (RgnTemp)
{
if (Rgn > PRGN_WINDOW) IntGdiCombineRgn(RgnTemp, Rgn, 0, RGN_COPY);
IntInvalidateWindows(Child, ((Rgn > PRGN_WINDOW)?RgnTemp:Rgn), Flags);
REGION_Delete(RgnTemp);
}
}
}
}
TRACE("IntInvalidateWindows exit\n");
}
/*
* IntIsWindowDrawable
*
* Remarks
* Window is drawable when it is visible and all parents are not
* minimized.
*/
BOOL FASTCALL
IntIsWindowDrawable(PWND Wnd)
{
PWND WndObject;
for (WndObject = Wnd; WndObject != NULL; WndObject = WndObject->spwndParent)
{
if ( WndObject->state2 & WNDS2_INDESTROY ||
WndObject->state & WNDS_DESTROYED ||
!WndObject ||
!(WndObject->style & WS_VISIBLE) ||
((WndObject->style & WS_MINIMIZE) && (WndObject != Wnd)))
{
return FALSE;
}
}
return TRUE;
}
/*
* IntRedrawWindow
*
* Internal version of NtUserRedrawWindow that takes WND as
* first parameter.
*/
BOOL FASTCALL
co_UserRedrawWindow(
PWND Window,
const RECTL* UpdateRect,
PREGION UpdateRgn,
ULONG Flags)
{
PREGION TmpRgn = NULL;
TRACE("co_UserRedrawWindow start Rgn %p\n",UpdateRgn);
/*
* Step 1.
* Validation of passed parameters.
*/
if (!IntIsWindowDrawable(Window))
{
return TRUE; // Just do nothing!!!
}
if (Window == NULL)
{
Window = UserGetDesktopWindow();
}
/*
* Step 2.
* Transform the parameters UpdateRgn and UpdateRect into
* a region hRgn specified in screen coordinates.
*/
if (Flags & (RDW_INVALIDATE | RDW_VALIDATE)) // Both are OKAY!
{
/* We can't hold lock on GDI objects while doing roundtrips to user mode,
* so use a copy instead */
if (UpdateRgn)
{
TmpRgn = IntSysCreateRectpRgn(0, 0, 0, 0);
if (UpdateRgn > PRGN_WINDOW)
{
IntGdiCombineRgn(TmpRgn, UpdateRgn, NULL, RGN_COPY);
}
if (Window != UserGetDesktopWindow())
{
REGION_bOffsetRgn(TmpRgn, Window->rcClient.left, Window->rcClient.top);
}
}
else
{
if (UpdateRect != NULL)
{
if (Window == UserGetDesktopWindow())
{
TmpRgn = IntSysCreateRectpRgnIndirect(UpdateRect);
}
else
{
TmpRgn = IntSysCreateRectpRgn(Window->rcClient.left + UpdateRect->left,
Window->rcClient.top + UpdateRect->top,
Window->rcClient.left + UpdateRect->right,
Window->rcClient.top + UpdateRect->bottom);
}
}
else
{
if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
(Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
{
if (!RECTL_bIsEmptyRect(&Window->rcWindow))
TmpRgn = IntSysCreateRectpRgnIndirect(&Window->rcWindow);
}
else
{
if (!RECTL_bIsEmptyRect(&Window->rcClient))
TmpRgn = IntSysCreateRectpRgnIndirect(&Window->rcClient);
}
}
}
}
/* Fixes test RDW_INTERNALPAINT behavior */
if (TmpRgn == NULL)
{
TmpRgn = PRGN_WINDOW; // Need a region so the bits can be set!!!
}
/*
* Step 3.
* Adjust the window update region depending on hRgn and flags.
*/
if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT) &&
TmpRgn != NULL)
{
IntInvalidateWindows(Window, TmpRgn, Flags);
}
/*
* Step 4.
* Repaint and erase windows if needed.
*/
if (Flags & RDW_UPDATENOW)
{
UserUpdateWindows(Window, Flags);
}
else if (Flags & RDW_ERASENOW)
{
if ((Flags & (RDW_NOCHILDREN|RDW_ALLCHILDREN)) == 0)
Flags |= RDW_CLIPCHILDREN;
UserSyncAndPaintWindows(Window, Flags);
}
/*
* Step 5.
* Cleanup ;-)
*/
if (TmpRgn > PRGN_WINDOW)
{
REGION_Delete(TmpRgn);
}
TRACE("co_UserRedrawWindow exit\n");
return TRUE;
}
VOID FASTCALL
PaintSuspendedWindow(PWND pwnd, HRGN hrgnOrig)
{
if (pwnd->hrgnUpdate)
{
HDC hDC;
INT Flags = DC_NC|DC_NOSENDMSG;
HRGN hrgnTemp;
RECT Rect;
INT type;
PREGION prgn;
if (pwnd->hrgnUpdate > HRGN_WINDOW)
{
hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
type = NtGdiCombineRgn( hrgnTemp, pwnd->hrgnUpdate, 0, RGN_COPY);
if (type == ERROR)
{
GreDeleteObject(hrgnTemp);
hrgnTemp = HRGN_WINDOW;
}
}
else
{
hrgnTemp = GreCreateRectRgnIndirect(&pwnd->rcWindow);
}
if ( hrgnOrig &&
hrgnTemp > HRGN_WINDOW &&
NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnOrig, RGN_AND) == NULLREGION)
{
GreDeleteObject(hrgnTemp);
return;
}
hDC = UserGetDCEx(pwnd, hrgnTemp, DCX_WINDOW|DCX_INTERSECTRGN|DCX_USESTYLE|DCX_KEEPCLIPRGN);
Rect = pwnd->rcWindow;
RECTL_vOffsetRect(&Rect, -pwnd->rcWindow.left, -pwnd->rcWindow.top);
// Clear out client area!
FillRect(hDC, &Rect, IntGetSysColorBrush(COLOR_WINDOW));
NC_DoNCPaint(pwnd, hDC, Flags); // Redraw without MENUs.
UserReleaseDC(pwnd, hDC, FALSE);
prgn = REGION_LockRgn(hrgnTemp);
IntInvalidateWindows(pwnd, prgn, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN);
REGION_UnlockRgn(prgn);
// Set updates for this window.
pwnd->state |= WNDS_SENDNCPAINT|WNDS_SENDERASEBACKGROUND|WNDS_UPDATEDIRTY;
// DCX_KEEPCLIPRGN is set. Check it anyway.
if (hrgnTemp > HRGN_WINDOW && GreIsHandleValid(hrgnTemp)) GreDeleteObject(hrgnTemp);
}
}
VOID FASTCALL
UpdateTheadChildren(PWND pWnd, HRGN hRgn)
{
PaintSuspendedWindow( pWnd, hRgn );
if (!(pWnd->style & WS_CLIPCHILDREN))
return;
pWnd = pWnd->spwndChild; // invalidate children if any.
while (pWnd)
{
UpdateTheadChildren( pWnd, hRgn );
pWnd = pWnd->spwndNext;
}
}
VOID FASTCALL
UpdateThreadWindows(PWND pWnd, PTHREADINFO pti, HRGN hRgn)
{
PWND pwndTemp;
for ( pwndTemp = pWnd;
pwndTemp;
pwndTemp = pwndTemp->spwndNext )
{
if (pwndTemp->head.pti == pti)
{
UserUpdateWindows(pwndTemp, RDW_ALLCHILDREN);
}
else
{
if (IsThreadSuspended(pwndTemp->head.pti) || MsqIsHung(pwndTemp->head.pti, MSQ_HUNG))
{
UpdateTheadChildren(pwndTemp, hRgn);
}
else
UserUpdateWindows(pwndTemp, RDW_ALLCHILDREN);
}
}
}
BOOL FASTCALL
IntIsWindowDirty(PWND Wnd)
{
return ( Wnd->style & WS_VISIBLE &&
( Wnd->hrgnUpdate != NULL ||
Wnd->state & WNDS_INTERNALPAINT ) );
}
/*
Conditions to paint any window:
1. Update region is not null.
2. Internal paint flag is set.
3. Paint count is not zero.
*/
PWND FASTCALL
IntFindWindowToRepaint(PWND Window, PTHREADINFO Thread)
{
PWND hChild;
PWND TempWindow;
for (; Window != NULL; Window = Window->spwndNext)
{
if (IntWndBelongsToThread(Window, Thread))
{
if (IntIsWindowDirty(Window))
{
/* Make sure all non-transparent siblings are already drawn. */
if (Window->ExStyle & WS_EX_TRANSPARENT)
{
for (TempWindow = Window->spwndNext; TempWindow != NULL;
TempWindow = TempWindow->spwndNext)
{
if (!(TempWindow->ExStyle & WS_EX_TRANSPARENT) &&
IntWndBelongsToThread(TempWindow, Thread) &&
IntIsWindowDirty(TempWindow))
{
return TempWindow;
}
}
}
return Window;
}
}
/* find a child of the specified window that needs repainting */
if (Window->spwndChild)
{
hChild = IntFindWindowToRepaint(Window->spwndChild, Thread);
if (hChild != NULL)
return hChild;
}
}
return Window;
}
//
// Internal painting of windows.
//
VOID FASTCALL
IntPaintWindow( PWND Window )
{
// Handle normal painting.
co_IntPaintWindows( Window, RDW_NOCHILDREN, FALSE );
}
BOOL FASTCALL
IntGetPaintMessage(
PWND Window,
UINT MsgFilterMin,
UINT MsgFilterMax,
PTHREADINFO Thread,
MSG *Message,
BOOL Remove)
{
PWND PaintWnd, StartWnd;
if ((MsgFilterMin != 0 || MsgFilterMax != 0) &&
(MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT))
return FALSE;
if (Thread->TIF_flags & TIF_SYSTEMTHREAD )
{
ERR("WM_PAINT is in a System Thread!\n");
}
StartWnd = UserGetDesktopWindow();
PaintWnd = IntFindWindowToRepaint(StartWnd, Thread);
Message->hwnd = PaintWnd ? UserHMGetHandle(PaintWnd) : NULL;
if (Message->hwnd == NULL && Thread->cPaintsReady)
{
// Find note in window.c:"PAINTING BUG".
ERR("WARNING SOMETHING HAS GONE WRONG: Thread marked as containing dirty windows, but no dirty windows found! Counts %u\n",Thread->cPaintsReady);
/* Hack to stop spamming the debug log ! */
Thread->cPaintsReady = 0;
return FALSE;
}
if (Message->hwnd == NULL)
return FALSE;
if (!(Window == NULL ||
PaintWnd == Window ||
IntIsChildWindow(Window, PaintWnd))) /* check that it is a child of the specified parent */
return FALSE;
if (PaintWnd->state & WNDS_INTERNALPAINT)
{
PaintWnd->state &= ~WNDS_INTERNALPAINT;
if (!PaintWnd->hrgnUpdate)
MsqDecPaintCountQueue(Thread);
}
PaintWnd->state2 &= ~WNDS2_STARTPAINT;
PaintWnd->state &= ~WNDS_UPDATEDIRTY;
Window = PaintWnd;
while (Window && !UserIsDesktopWindow(Window))
{
// Role back and check for clip children, do not set if any.
if (Window->spwndParent && !(Window->spwndParent->style & WS_CLIPCHILDREN))
{
PaintWnd->state2 |= WNDS2_WMPAINTSENT;
}
Window = Window->spwndParent;
}
Message->wParam = Message->lParam = 0;
Message->message = WM_PAINT;
return TRUE;
}
BOOL
FASTCALL
IntPrintWindow(
PWND pwnd,
HDC hdcBlt,
UINT nFlags)
{
HDC hdcSrc;
INT cx, cy, xSrc, ySrc;
if ( nFlags & PW_CLIENTONLY)
{
cx = pwnd->rcClient.right - pwnd->rcClient.left;
cy = pwnd->rcClient.bottom - pwnd->rcClient.top;
xSrc = pwnd->rcClient.left - pwnd->rcWindow.left;
ySrc = pwnd->rcClient.top - pwnd->rcWindow.top;
}
else
{
cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
xSrc = 0;
ySrc = 0;
}
// TODO: Setup Redirection for Print.
return FALSE;
/* Update the window just incase. */
co_IntUpdateWindows( pwnd, RDW_ALLCHILDREN, FALSE);
hdcSrc = UserGetDCEx( pwnd, NULL, DCX_CACHE|DCX_WINDOW);
/* Print window to printer context. */
NtGdiBitBlt( hdcBlt,
0,
0,
cx,
cy,
hdcSrc,
xSrc,
ySrc,
SRCCOPY,
0,
0);
UserReleaseDC( pwnd, hdcSrc, FALSE);
// TODO: Release Redirection from Print.
return TRUE;
}
BOOL
FASTCALL
IntFlashWindowEx(PWND pWnd, PFLASHWINFO pfwi)
{
DWORD_PTR FlashState;
UINT uCount = pfwi->uCount;
BOOL Activate = FALSE, Ret = FALSE;
ASSERT(pfwi);
FlashState = (DWORD_PTR)UserGetProp(pWnd, AtomFlashWndState, TRUE);
if (FlashState == FLASHW_FINISHED)
{
// Cycle has finished, kill timer and set this to Stop.
FlashState |= FLASHW_KILLSYSTIMER;
pfwi->dwFlags = FLASHW_STOP;
}
else
{
if (FlashState)
{
if (pfwi->dwFlags == FLASHW_SYSTIMER)
{
// Called from system timer, restore flags, counts and state.
pfwi->dwFlags = LOWORD(FlashState);
uCount = HIWORD(FlashState);
FlashState = MAKELONG(LOWORD(FlashState),0);
}
else
{
// Clean out the trash! Fix SeaMonkey crash after restart.
FlashState = 0;
}
}
if (FlashState == 0)
{ // First time in cycle, setup flash state.
if ( pWnd->state & WNDS_ACTIVEFRAME ||
(pfwi->dwFlags & FLASHW_CAPTION && pWnd->style & (WS_BORDER|WS_DLGFRAME)))
{
FlashState = FLASHW_STARTED|FLASHW_ACTIVE;
}
}
// Set previous window state.
Ret = !!(FlashState & FLASHW_ACTIVE);
if ( (pfwi->dwFlags & FLASHW_TIMERNOFG) == FLASHW_TIMERNOFG &&
gpqForeground == pWnd->head.pti->MessageQueue )
{
// Flashing until foreground, set this to Stop.
pfwi->dwFlags = FLASHW_STOP;
}
}
// Toggle activate flag.
if ( pfwi->dwFlags == FLASHW_STOP )
{
if (gpqForeground && gpqForeground->spwndActive == pWnd)
Activate = TRUE;
else
Activate = FALSE;
}
else
{
Activate = (FlashState & FLASHW_ACTIVE) == 0;
}
if ( pfwi->dwFlags == FLASHW_STOP || pfwi->dwFlags & FLASHW_CAPTION )
{
co_IntSendMessage(UserHMGetHandle(pWnd), WM_NCACTIVATE, Activate, 0);
}
// FIXME: Check for a Stop Sign here.
if ( pfwi->dwFlags & FLASHW_TRAY )
{
// Need some shell work here too.
TRACE("FIXME: Flash window no Tray support!\n");
}
if ( pfwi->dwFlags == FLASHW_STOP )
{
if (FlashState & FLASHW_KILLSYSTIMER)
{
IntKillTimer(pWnd, ID_EVENT_SYSTIMER_FLASHWIN, TRUE);
}
UserRemoveProp(pWnd, AtomFlashWndState, TRUE);
}
else
{ // Have a count and started, set timer.
if ( uCount )
{
FlashState |= FLASHW_COUNT;
if (!(Activate ^ !!(FlashState & FLASHW_STARTED)))
uCount--;
if (!(FlashState & FLASHW_KILLSYSTIMER))
pfwi->dwFlags |= FLASHW_TIMER;
}
if (pfwi->dwFlags & FLASHW_TIMER)
{
FlashState |= FLASHW_KILLSYSTIMER;
IntSetTimer( pWnd,
ID_EVENT_SYSTIMER_FLASHWIN,
pfwi->dwTimeout ? pfwi->dwTimeout : gpsi->dtCaretBlink,
SystemTimerProc,
TMRF_SYSTEM );
}
if (FlashState & FLASHW_COUNT && uCount == 0)
{
// Keep spinning? Nothing else to do.
FlashState = FLASHW_FINISHED;
}
else
{
// Save state and flags so this can be restored next time through.
FlashState ^= (FlashState ^ -!!(Activate)) & FLASHW_ACTIVE;
FlashState ^= (FlashState ^ pfwi->dwFlags) & (FLASHW_MASK & ~FLASHW_TIMER);
}
FlashState = MAKELONG(LOWORD(FlashState),uCount);
UserSetProp(pWnd, AtomFlashWndState, (HANDLE)FlashState, TRUE);
}
return Ret;
}
// Win: xxxBeginPaint
HDC FASTCALL
IntBeginPaint(PWND Window, PPAINTSTRUCT Ps)
{
RECT Rect;
INT type;
BOOL Erase = FALSE;
co_UserHideCaret(Window);
Window->state2 |= WNDS2_STARTPAINT;
Window->state &= ~WNDS_PAINTNOTPROCESSED;
if (Window->state & WNDS_SENDNCPAINT)
{
HRGN hRgn;
// Application can keep update dirty.
do
{
Window->state &= ~WNDS_UPDATEDIRTY;
hRgn = IntGetNCUpdateRgn(Window, FALSE);
IntSendNCPaint(Window, hRgn);
if (hRgn > HRGN_WINDOW && GreIsHandleValid(hRgn))
{
/* NOTE: The region can already be deleted! */
GreDeleteObject(hRgn);
}
}
while(Window->state & WNDS_UPDATEDIRTY);
}
else
{
Window->state &= ~WNDS_UPDATEDIRTY;
}
RtlZeroMemory(Ps, sizeof(PAINTSTRUCT));
if (Window->state2 & WNDS2_ENDPAINTINVALIDATE)
{
ERR("BP: Another thread invalidated this window\n");
}
Ps->hdc = UserGetDCEx( Window,
Window->hrgnUpdate,
DCX_INTERSECTRGN | DCX_USESTYLE);
if (!Ps->hdc)
{
return NULL;
}
// If set, always clear flags out due to the conditions later on for sending the message.
if (Window->state & WNDS_SENDERASEBACKGROUND)
{
Window->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
Erase = TRUE;
}
if (Window->hrgnUpdate != NULL)
{
MsqDecPaintCountQueue(Window->head.pti);
IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
/* The region is part of the dc now and belongs to the process! */
Window->hrgnUpdate = NULL;
}
else
{
if (Window->state & WNDS_INTERNALPAINT)
MsqDecPaintCountQueue(Window->head.pti);
}
type = GdiGetClipBox(Ps->hdc, &Ps->rcPaint);
IntGetClientRect(Window, &Rect);
Window->state &= ~WNDS_INTERNALPAINT;
if ( Erase && // Set to erase,
type != NULLREGION && // don't erase if the clip box is empty,
(!(Window->pcls->style & CS_PARENTDC) || // not parent dc or
RECTL_bIntersectRect( &Rect, &Rect, &Ps->rcPaint) ) ) // intersecting.
{
Ps->fErase = !co_IntSendMessage(UserHMGetHandle(Window), WM_ERASEBKGND, (WPARAM)Ps->hdc, 0);
if ( Ps->fErase )
{
Window->state |= (WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
}
}
else
{
Ps->fErase = FALSE;
}
IntSendChildNCPaint(Window);
return Ps->hdc;
}
// Win: xxxEndPaint
BOOL FASTCALL
IntEndPaint(PWND Wnd, PPAINTSTRUCT Ps)
{
HDC hdc = NULL;
hdc = Ps->hdc;
UserReleaseDC(Wnd, hdc, TRUE);
if (Wnd->state2 & WNDS2_ENDPAINTINVALIDATE)
{
ERR("EP: Another thread invalidated this window\n");
Wnd->state2 &= ~WNDS2_ENDPAINTINVALIDATE;
}
Wnd->state2 &= ~(WNDS2_WMPAINTSENT|WNDS2_STARTPAINT);
co_UserShowCaret(Wnd);
return TRUE;
}
// Win: xxxFillWindow
BOOL FASTCALL
IntFillWindow(PWND pWndParent,
PWND pWnd,
HDC hDC,
HBRUSH hBrush)
{
RECT Rect, Rect1;
INT type;
if (!pWndParent)
pWndParent = pWnd;
type = GdiGetClipBox(hDC, &Rect);
IntGetClientRect(pWnd, &Rect1);
if ( type != NULLREGION && // Clip box is not empty,
(!(pWnd->pcls->style & CS_PARENTDC) || // not parent dc or
RECTL_bIntersectRect( &Rect, &Rect, &Rect1) ) ) // intersecting.
{
POINT ppt;
INT x = 0, y = 0;
if (!UserIsDesktopWindow(pWndParent))
{
x = pWndParent->rcClient.left - pWnd->rcClient.left;
y = pWndParent->rcClient.top - pWnd->rcClient.top;
}
GreSetBrushOrg(hDC, x, y, &ppt);
if ( hBrush < (HBRUSH)CTLCOLOR_MAX )
hBrush = GetControlColor( pWndParent, pWnd, hDC, HandleToUlong(hBrush) + WM_CTLCOLORMSGBOX);
FillRect(hDC, &Rect, hBrush);
GreSetBrushOrg(hDC, ppt.x, ppt.y, NULL);
return TRUE;
}
else
return FALSE;
}
/* PUBLIC FUNCTIONS ***********************************************************/
/*
* NtUserBeginPaint
*
* Status
* @implemented
*/
HDC APIENTRY
NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
{
PWND Window;
PAINTSTRUCT Ps;
NTSTATUS Status;
HDC hDC;
USER_REFERENCE_ENTRY Ref;
HDC Ret = NULL;
TRACE("Enter NtUserBeginPaint\n");
UserEnterExclusive();
if (!(Window = UserGetWindowObject(hWnd)))
{
goto Cleanup; // Return NULL
}
UserRefObjectCo(Window, &Ref);
hDC = IntBeginPaint(Window, &Ps);
Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
if (! NT_SUCCESS(Status))
{
SetLastNtError(Status);
goto Cleanup; // Return NULL
}
Ret = hDC;
Cleanup:
if (Window) UserDerefObjectCo(Window);
TRACE("Leave NtUserBeginPaint, ret=%p\n", Ret);
UserLeave();
return Ret;
}
/*
* NtUserEndPaint
*
* Status
* @implemented
*/
BOOL APIENTRY
NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* pUnsafePs)
{
NTSTATUS Status = STATUS_SUCCESS;
PWND Window;
PAINTSTRUCT Ps;
USER_REFERENCE_ENTRY Ref;
BOOL Ret = FALSE;
TRACE("Enter NtUserEndPaint\n");
UserEnterExclusive();
if (!(Window = UserGetWindowObject(hWnd)))
{
goto Cleanup; // Return FALSE
}
UserRefObjectCo(Window, &Ref); // Here for the exception.
_SEH2_TRY
{
ProbeForRead(pUnsafePs, sizeof(*pUnsafePs), 1);
RtlCopyMemory(&Ps, pUnsafePs, sizeof(PAINTSTRUCT));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if (!NT_SUCCESS(Status))
{
goto Cleanup; // Return FALSE
}
Ret = IntEndPaint(Window, &Ps);
Cleanup:
if (Window) UserDerefObjectCo(Window);
TRACE("Leave NtUserEndPaint, ret=%i\n", Ret);
UserLeave();
return Ret;
}
/*
* FillWindow: Called from User; Dialog, Edit and ListBox procs during a WM_ERASEBKGND.
*/
/*
* @implemented
*/
BOOL APIENTRY
NtUserFillWindow(HWND hWndParent,
HWND hWnd,
HDC hDC,
HBRUSH hBrush)
{
BOOL ret = FALSE;
PWND pWnd, pWndParent = NULL;
USER_REFERENCE_ENTRY Ref;
TRACE("Enter NtUserFillWindow\n");
UserEnterExclusive();
if (!hDC)
{
goto Exit;
}
if (!(pWnd = UserGetWindowObject(hWnd)))
{
goto Exit;
}
if (hWndParent && !(pWndParent = UserGetWindowObject(hWndParent)))
{
goto Exit;
}
UserRefObjectCo(pWnd, &Ref);
ret = IntFillWindow( pWndParent, pWnd, hDC, hBrush );
UserDerefObjectCo(pWnd);
Exit:
TRACE("Leave NtUserFillWindow, ret=%i\n",ret);
UserLeave();
return ret;
}
/*
* @implemented
*/
BOOL APIENTRY
NtUserFlashWindowEx(IN PFLASHWINFO pfwi)
{
PWND pWnd;
FLASHWINFO finfo = {0};
BOOL Ret = FALSE;
UserEnterExclusive();
_SEH2_TRY
{
ProbeForRead(pfwi, sizeof(FLASHWINFO), 1);
RtlCopyMemory(&finfo, pfwi, sizeof(FLASHWINFO));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
_SEH2_YIELD(goto Exit);
}
_SEH2_END
if (!( pWnd = ValidateHwndNoErr(finfo.hwnd)) ||
finfo.cbSize != sizeof(FLASHWINFO) ||
finfo.dwFlags & ~(FLASHW_ALL|FLASHW_TIMER|FLASHW_TIMERNOFG) )
{
EngSetLastError(ERROR_INVALID_PARAMETER);
goto Exit;
}
Ret = IntFlashWindowEx(pWnd, &finfo);
Exit:
UserLeave();
return Ret;
}
/*
GetUpdateRgn, this fails the same as the old one.
*/
INT FASTCALL
co_UserGetUpdateRgn(PWND Window, HRGN hRgn, BOOL bErase)
{
int RegionType;
BOOL Type;
RECTL Rect;
ASSERT_REFS_CO(Window);
if (bErase)
{
USER_REFERENCE_ENTRY Ref;
UserRefObjectCo(Window, &Ref);
co_IntPaintWindows(Window, RDW_NOCHILDREN, FALSE);
UserDerefObjectCo(Window);
}
Window->state &= ~WNDS_UPDATEDIRTY;
if (Window->hrgnUpdate == NULL)
{
NtGdiSetRectRgn(hRgn, 0, 0, 0, 0);
return NULLREGION;
}
Rect = Window->rcClient;
Type = IntIntersectWithParents(Window, &Rect);
if (Window->hrgnUpdate == HRGN_WINDOW)
{
// Trap it out.
ERR("GURn: Caller is passing Window Region 1\n");
if (!Type)
{
NtGdiSetRectRgn(hRgn, 0, 0, 0, 0);
return NULLREGION;
}
RegionType = SIMPLEREGION;
if (!UserIsDesktopWindow(Window))
{
RECTL_vOffsetRect(&Rect,
-Window->rcClient.left,
-Window->rcClient.top);
}
GreSetRectRgnIndirect(hRgn, &Rect);
}
else
{
HRGN hrgnTemp = GreCreateRectRgnIndirect(&Rect);
RegionType = NtGdiCombineRgn(hRgn, hrgnTemp, Window->hrgnUpdate, RGN_AND);
if (RegionType == ERROR || RegionType == NULLREGION)
{
if (hrgnTemp) GreDeleteObject(hrgnTemp);
NtGdiSetRectRgn(hRgn, 0, 0, 0, 0);
return RegionType;
}
if (!UserIsDesktopWindow(Window))
{
NtGdiOffsetRgn(hRgn,
-Window->rcClient.left,
-Window->rcClient.top);
}
if (hrgnTemp) GreDeleteObject(hrgnTemp);
}
return RegionType;
}
BOOL FASTCALL
co_UserGetUpdateRect(PWND Window, PRECT pRect, BOOL bErase)
{
INT RegionType;
BOOL Ret = TRUE;
if (bErase)
{
USER_REFERENCE_ENTRY Ref;
UserRefObjectCo(Window, &Ref);
co_IntPaintWindows(Window, RDW_NOCHILDREN, FALSE);
UserDerefObjectCo(Window);
}
Window->state &= ~WNDS_UPDATEDIRTY;
if (Window->hrgnUpdate == NULL)
{
pRect->left = pRect->top = pRect->right = pRect->bottom = 0;
Ret = FALSE;
}
else
{
/* Get the update region bounding box. */
if (Window->hrgnUpdate == HRGN_WINDOW)
{
*pRect = Window->rcClient;
ERR("GURt: Caller is retrieving Window Region 1\n");
}
else
{
RegionType = IntGdiGetRgnBox(Window->hrgnUpdate, pRect);
if (RegionType != ERROR && RegionType != NULLREGION)
RECTL_bIntersectRect(pRect, pRect, &Window->rcClient);
}
if (IntIntersectWithParents(Window, pRect))
{
if (!UserIsDesktopWindow(Window))
{
RECTL_vOffsetRect(pRect,
-Window->rcClient.left,
-Window->rcClient.top);
}
if (Window->pcls->style & CS_OWNDC)
{
HDC hdc;
//DWORD layout;
hdc = UserGetDCEx(Window, NULL, DCX_USESTYLE);
//layout = NtGdiSetLayout(hdc, -1, 0);
//IntMapWindowPoints( 0, Window, (LPPOINT)pRect, 2 );
GreDPtoLP( hdc, (LPPOINT)pRect, 2 );
//NtGdiSetLayout(hdc, -1, layout);
UserReleaseDC(Window, hdc, FALSE);
}
}
else
{
pRect->left = pRect->top = pRect->right = pRect->bottom = 0;
}
}
return Ret;
}
/*
* NtUserGetUpdateRgn
*
* Status
* @implemented
*/
INT APIENTRY
NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
{
PWND Window;
INT ret = ERROR;
TRACE("Enter NtUserGetUpdateRgn\n");
UserEnterExclusive();
Window = UserGetWindowObject(hWnd);
if (Window)
{
ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
}
TRACE("Leave NtUserGetUpdateRgn, ret=%i\n", ret);
UserLeave();
return ret;
}
/*
* NtUserGetUpdateRect
*
* Status
* @implemented
*/
BOOL APIENTRY
NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
{
PWND Window;
RECTL Rect;
NTSTATUS Status;
BOOL Ret = FALSE;
TRACE("Enter NtUserGetUpdateRect\n");
UserEnterExclusive();
if (!(Window = UserGetWindowObject(hWnd)))
{
goto Exit; // Return FALSE
}
Ret = co_UserGetUpdateRect(Window, &Rect, bErase);
if (UnsafeRect != NULL)
{
Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECTL));
if (!NT_SUCCESS(Status))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
Ret = FALSE;
}
}
Exit:
TRACE("Leave NtUserGetUpdateRect, ret=%i\n", Ret);
UserLeave();
return Ret;
}
/*
* NtUserRedrawWindow
*
* Status
* @implemented
*/
BOOL APIENTRY
NtUserRedrawWindow(
HWND hWnd,
CONST RECT *lprcUpdate,
HRGN hrgnUpdate,
UINT flags)
{
RECTL SafeUpdateRect;
PWND Wnd;
BOOL Ret = FALSE;
USER_REFERENCE_ENTRY Ref;
NTSTATUS Status = STATUS_SUCCESS;
PREGION RgnUpdate = NULL;
TRACE("Enter NtUserRedrawWindow\n");
UserEnterExclusive();
if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
{
goto Exit; // Return FALSE
}
if (lprcUpdate)
{
_SEH2_TRY
{
ProbeForRead(lprcUpdate, sizeof(RECTL), 1);
RtlCopyMemory(&SafeUpdateRect, lprcUpdate, sizeof(RECTL));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if (!NT_SUCCESS(Status))
{
EngSetLastError(RtlNtStatusToDosError(Status));
goto Exit; // Return FALSE
}
}
if ( flags & ~(RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE|
RDW_NOERASE|RDW_NOFRAME|RDW_NOINTERNALPAINT|RDW_VALIDATE|
RDW_ERASENOW|RDW_UPDATENOW|RDW_ALLCHILDREN|RDW_NOCHILDREN) )
{
/* RedrawWindow fails only in case that flags are invalid */
EngSetLastError(ERROR_INVALID_FLAGS);
goto Exit; // Return FALSE
}
/* We can't hold lock on GDI objects while doing roundtrips to user mode,
* so it will be copied.
*/
if (hrgnUpdate > HRGN_WINDOW)
{
RgnUpdate = REGION_LockRgn(hrgnUpdate);
if (!RgnUpdate)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto Exit; // Return FALSE
}
REGION_UnlockRgn(RgnUpdate);
}
else if (hrgnUpdate == HRGN_WINDOW) // Trap it out.
{
ERR("NTRW: Caller is passing Window Region 1\n");
}
UserRefObjectCo(Wnd, &Ref);
Ret = co_UserRedrawWindow( Wnd,
lprcUpdate ? &SafeUpdateRect : NULL,
RgnUpdate,
flags);
UserDerefObjectCo(Wnd);
Exit:
TRACE("Leave NtUserRedrawWindow, ret=%i\n", Ret);
UserLeave();
return Ret;
}
BOOL
UserDrawCaptionText(
PWND pWnd,
HDC hDc,
const PUNICODE_STRING Text,
const RECTL *lpRc,
UINT uFlags,
HFONT hFont)
{
HFONT hOldFont = NULL;
COLORREF OldTextColor;
NONCLIENTMETRICSW nclm;
NTSTATUS Status;
BOOLEAN bDeleteFont = FALSE;
SIZE Size;
BOOL Ret = TRUE;
ULONG fit = 0, Length;
RECTL r = *lpRc;
TRACE("UserDrawCaptionText: %wZ\n", Text);
nclm.cbSize = sizeof(nclm);
if (!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS, nclm.cbSize, &nclm, 0))
{
ERR("UserSystemParametersInfo() failed!\n");
return FALSE;
}
if (!hFont)
{
if(uFlags & DC_SMALLCAP)
Status = TextIntCreateFontIndirect(&nclm.lfSmCaptionFont, &hFont);
else
Status = TextIntCreateFontIndirect(&nclm.lfCaptionFont, &hFont);
if(!NT_SUCCESS(Status))
{
ERR("TextIntCreateFontIndirect() failed! Status: 0x%x\n", Status);
return FALSE;
}
bDeleteFont = TRUE;
}
IntGdiSetBkMode(hDc, TRANSPARENT);
hOldFont = NtGdiSelectFont(hDc, hFont);
if(uFlags & DC_INBUTTON)
OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT));
else
OldTextColor = IntGdiSetTextColor(hDc,
IntGetSysColor(uFlags & DC_ACTIVE ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
// Adjust for system menu.
if (pWnd && pWnd->style & WS_SYSMENU)
{
r.right -= UserGetSystemMetrics(SM_CYCAPTION) - 1;
if ((pWnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX)) && !(pWnd->ExStyle & WS_EX_TOOLWINDOW))
{
r.right -= UserGetSystemMetrics(SM_CXSIZE) + 1;
r.right -= UserGetSystemMetrics(SM_CXSIZE) + 1;
}
}
GreGetTextExtentExW(hDc, Text->Buffer, Text->Length/sizeof(WCHAR), r.right - r.left, &fit, 0, &Size, 0);
Length = (Text->Length/sizeof(WCHAR) == fit ? fit : fit+1);
if (Text->Length/sizeof(WCHAR) > Length)
{
Ret = FALSE;
}
if (Ret)
{ // Faster while in setup.
UserExtTextOutW( hDc,
lpRc->left,
lpRc->top + (lpRc->bottom - lpRc->top - Size.cy) / 2, // DT_SINGLELINE && DT_VCENTER
ETO_CLIPPED,
(RECTL *)lpRc,
Text->Buffer,
Length);
}
else
{
DrawTextW( hDc,
Text->Buffer,
Text->Length/sizeof(WCHAR),
(RECTL *)&r,
DT_END_ELLIPSIS|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_LEFT);
}
IntGdiSetTextColor(hDc, OldTextColor);
if (hOldFont)
NtGdiSelectFont(hDc, hOldFont);
if (bDeleteFont)
GreDeleteObject(hFont);
return Ret;
}
//
// This draws Buttons, Icons and Text...
//
BOOL UserDrawCaption(
PWND pWnd,
HDC hDc,
RECTL *lpRc,
HFONT hFont,
HICON hIcon,
const PUNICODE_STRING Str,
UINT uFlags)
{
BOOL Ret = FALSE;
HBRUSH hBgBrush, hOldBrush = NULL;
RECTL Rect = *lpRc;
BOOL HasIcon;
RECTL_vMakeWellOrdered(lpRc);
/* Determine whether the icon needs to be displayed */
if (!hIcon && pWnd != NULL)
{
HasIcon = (uFlags & DC_ICON) && !(uFlags & DC_SMALLCAP) &&
(pWnd->style & WS_SYSMENU) && !(pWnd->ExStyle & WS_EX_TOOLWINDOW);
}
else
HasIcon = (hIcon != NULL);
// Draw the caption background
if((uFlags & DC_GRADIENT) && !(uFlags & DC_INBUTTON))
{
static GRADIENT_RECT gcap = {0, 1};
TRIVERTEX Vertices[2];
COLORREF Colors[2];
Colors[0] = IntGetSysColor((uFlags & DC_ACTIVE) ?
COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION);
Colors[1] = IntGetSysColor((uFlags & DC_ACTIVE) ?
COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION);
Vertices[0].x = Rect.left;
Vertices[0].y = Rect.top;
Vertices[0].Red = (WORD)Colors[0]<<8;
Vertices[0].Green = (WORD)Colors[0] & 0xFF00;
Vertices[0].Blue = (WORD)(Colors[0]>>8) & 0xFF00;
Vertices[0].Alpha = 0;
Vertices[1].x = Rect.right;
Vertices[1].y = Rect.bottom;
Vertices[1].Red = (WORD)Colors[1]<<8;
Vertices[1].Green = (WORD)Colors[1] & 0xFF00;
Vertices[1].Blue = (WORD)(Colors[1]>>8) & 0xFF00;
Vertices[1].Alpha = 0;
if(!GreGradientFill(hDc, Vertices, 2, &gcap, 1, GRADIENT_FILL_RECT_H))
{
ERR("GreGradientFill() failed!\n");
goto cleanup;
}
}
else
{
if(uFlags & DC_INBUTTON)
hBgBrush = IntGetSysColorBrush(COLOR_3DFACE);
else if(uFlags & DC_ACTIVE)
hBgBrush = IntGetSysColorBrush(COLOR_ACTIVECAPTION);
else
hBgBrush = IntGetSysColorBrush(COLOR_INACTIVECAPTION);
hOldBrush = NtGdiSelectBrush(hDc, hBgBrush);
if(!hOldBrush)
{
ERR("NtGdiSelectBrush() failed!\n");
goto cleanup;
}
if(!NtGdiPatBlt(hDc, Rect.left, Rect.top,
Rect.right - Rect.left,
Rect.bottom - Rect.top,
PATCOPY))
{
ERR("NtGdiPatBlt() failed!\n");
goto cleanup;
}
}
/* Draw icon */
if (HasIcon)
{
PCURICON_OBJECT pIcon = NULL;
if (hIcon)
{
pIcon = UserGetCurIconObject(hIcon);
}
else if (pWnd)
{
pIcon = NC_IconForWindow(pWnd);
// FIXME: NC_IconForWindow should reference it for us */
if (pIcon)
UserReferenceObject(pIcon);
}
if (pIcon)
{
LONG cx = UserGetSystemMetrics(SM_CXSMICON);
LONG cy = UserGetSystemMetrics(SM_CYSMICON);
LONG x = Rect.left - cx/2 + 1 + (Rect.bottom - Rect.top)/2; // this is really what Window does
LONG y = (Rect.top + Rect.bottom - cy)/2; // center
UserDrawIconEx(hDc, x, y, pIcon, cx, cy, 0, NULL, DI_NORMAL);
UserDereferenceObject(pIcon);
}
else
{
HasIcon = FALSE;
}
}
if (HasIcon)
Rect.left += Rect.bottom - Rect.top;
if((uFlags & DC_TEXT))
{
BOOL Set = FALSE;
Rect.left += 2;
if (Str)
Set = UserDrawCaptionText(pWnd, hDc, Str, &Rect, uFlags, hFont);
else if (pWnd != NULL) // FIXME: Windows does not do that
{
UNICODE_STRING ustr;
ustr.Buffer = pWnd->strName.Buffer; // FIXME: LARGE_STRING truncated!
ustr.Length = (USHORT)min(pWnd->strName.Length, MAXUSHORT);
ustr.MaximumLength = (USHORT)min(pWnd->strName.MaximumLength, MAXUSHORT);
Set = UserDrawCaptionText(pWnd, hDc, &ustr, &Rect, uFlags, hFont);
}
if (pWnd)
{
if (Set)
pWnd->state2 &= ~WNDS2_CAPTIONTEXTTRUNCATED;
else
pWnd->state2 |= WNDS2_CAPTIONTEXTTRUNCATED;
}
}
Ret = TRUE;
cleanup:
if (hOldBrush) NtGdiSelectBrush(hDc, hOldBrush);
return Ret;
}
INT
FASTCALL
UserRealizePalette(HDC hdc)
{
HWND hWnd, hWndDesktop;
DWORD Ret;
Ret = IntGdiRealizePalette(hdc);
if (Ret) // There was a change.
{
hWnd = IntWindowFromDC(hdc);
if (hWnd) // Send broadcast if dc is associated with a window.
{ // FYI: Thread locked in CallOneParam.
hWndDesktop = IntGetDesktopWindow();
if ( hWndDesktop != hWnd )
{
PWND pWnd = UserGetWindowObject(hWndDesktop);
ERR("RealizePalette Desktop.\n");
hdc = UserGetWindowDC(pWnd);
IntPaintDesktop(hdc);
UserReleaseDC(pWnd,hdc,FALSE);
}
UserSendNotifyMessage((HWND)HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0);
}
}
return Ret;
}
BOOL
APIENTRY
NtUserDrawCaptionTemp(
HWND hWnd,
HDC hDC,
LPCRECT lpRc,
HFONT hFont,
HICON hIcon,
const PUNICODE_STRING str,
UINT uFlags)
{
PWND pWnd = NULL;
UNICODE_STRING SafeStr = {0};
NTSTATUS Status = STATUS_SUCCESS;
RECTL SafeRect;
BOOL Ret;
UserEnterExclusive();
if (hWnd != NULL)
{
if(!(pWnd = UserGetWindowObject(hWnd)))
{
UserLeave();
return FALSE;
}
}
_SEH2_TRY
{
ProbeForRead(lpRc, sizeof(RECTL), sizeof(ULONG));
RtlCopyMemory(&SafeRect, lpRc, sizeof(RECTL));
if (str != NULL)
{
SafeStr = ProbeForReadUnicodeString(str);
if (SafeStr.Length != 0)
{
ProbeForRead( SafeStr.Buffer,
SafeStr.Length,
sizeof(WCHAR));
}
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
if (Status != STATUS_SUCCESS)
{
SetLastNtError(Status);
UserLeave();
return FALSE;
}
if (str != NULL)
Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags);
else
{
if ( RECTL_bIsEmptyRect(&SafeRect) && hFont == 0 && hIcon == 0 )
{
Ret = TRUE;
if (uFlags & DC_DRAWCAPTIONMD)
{
ERR("NC Caption Mode\n");
UserDrawCaptionBar(pWnd, hDC, uFlags);
goto Exit;
}
else if (uFlags & DC_DRAWFRAMEMD)
{
ERR("NC Paint Mode\n");
NC_DoNCPaint(pWnd, hDC, uFlags); // Update Menus too!
goto Exit;
}
}
Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags);
}
Exit:
UserLeave();
return Ret;
}
BOOL
APIENTRY
NtUserDrawCaption(HWND hWnd,
HDC hDC,
LPCRECT lpRc,
UINT uFlags)
{
return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
}
INT FASTCALL
co_UserExcludeUpdateRgn(HDC hDC, PWND Window)
{
POINT pt;
RECT rc;
if (Window->hrgnUpdate)
{
if (Window->hrgnUpdate == HRGN_WINDOW)
{
return NtGdiIntersectClipRect(hDC, 0, 0, 0, 0);
}
else
{
INT ret = ERROR;
HRGN hrgn = NtGdiCreateRectRgn(0,0,0,0);
if ( hrgn && GreGetDCPoint( hDC, GdiGetDCOrg, &pt) )
{
if ( NtGdiGetRandomRgn( hDC, hrgn, CLIPRGN) == NULLREGION )
{
NtGdiOffsetRgn(hrgn, pt.x, pt.y);
}
else
{
HRGN hrgnScreen;
PMONITOR pm = UserGetPrimaryMonitor();
hrgnScreen = NtGdiCreateRectRgn(0,0,0,0);
NtGdiCombineRgn(hrgnScreen, hrgnScreen, pm->hrgnMonitor, RGN_OR);
NtGdiCombineRgn(hrgn, hrgnScreen, NULL, RGN_COPY);
GreDeleteObject(hrgnScreen);
}
NtGdiCombineRgn(hrgn, hrgn, Window->hrgnUpdate, RGN_DIFF);
NtGdiOffsetRgn(hrgn, -pt.x, -pt.y);
ret = NtGdiExtSelectClipRgn(hDC, hrgn, RGN_COPY);
GreDeleteObject(hrgn);
}
return ret;
}
}
else
{
return GdiGetClipBox( hDC, &rc);
}
}
INT
APIENTRY
NtUserExcludeUpdateRgn(
HDC hDC,
HWND hWnd)
{
INT ret = ERROR;
PWND pWnd;
TRACE("Enter NtUserExcludeUpdateRgn\n");
UserEnterExclusive();
pWnd = UserGetWindowObject(hWnd);
if (hDC && pWnd)
ret = co_UserExcludeUpdateRgn(hDC, pWnd);
TRACE("Leave NtUserExcludeUpdateRgn, ret=%i\n", ret);
UserLeave();
return ret;
}
BOOL
APIENTRY
NtUserInvalidateRect(
HWND hWnd,
CONST RECT *lpUnsafeRect,
BOOL bErase)
{
UINT flags = RDW_INVALIDATE | (bErase ? RDW_ERASE : 0);
if (!hWnd)
{
flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
lpUnsafeRect = NULL;
}
return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, flags);
}
BOOL
APIENTRY
NtUserInvalidateRgn(
HWND hWnd,
HRGN hRgn,
BOOL bErase)
{
if (!hWnd)
{
EngSetLastError( ERROR_INVALID_WINDOW_HANDLE );
return FALSE;
}
return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
}
BOOL
APIENTRY
NtUserPrintWindow(
HWND hwnd,
HDC hdcBlt,
UINT nFlags)
{
PWND Window;
BOOL Ret = FALSE;
UserEnterExclusive();
if (hwnd)
{
if (!(Window = UserGetWindowObject(hwnd)) ||
UserIsDesktopWindow(Window) || UserIsMessageWindow(Window))
{
goto Exit;
}
if ( Window )
{
/* Validate flags and check it as a mask for 0 or 1. */
if ( (nFlags & PW_CLIENTONLY) == nFlags)
Ret = IntPrintWindow( Window, hdcBlt, nFlags);
else
EngSetLastError(ERROR_INVALID_PARAMETER);
}
}
Exit:
UserLeave();
return Ret;
}
/* ValidateRect gets redirected to NtUserValidateRect:
http://blog.csdn.net/ntdll/archive/2005/10/19/509299.aspx */
BOOL
APIENTRY
NtUserValidateRect(
HWND hWnd,
const RECT *lpRect)
{
UINT flags = RDW_VALIDATE;
if (!hWnd)
{
flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
lpRect = NULL;
}
return NtUserRedrawWindow(hWnd, lpRect, NULL, flags);
}
/* EOF */