reactos/win32ss/user/ntuser/painting.c
Hermès Bélusca-Maïto a8e7defb01
[WIN32SS:USER] Some minimal work and fixes concerning message queues timeouts.
CORE-15147

- Rename CLIENTTHREADINFO::tickLastMsgChecked into timeLastRead as
  documented in https://reactos.org/wiki/Techwiki:Win32k/CLIENTTHREADINFO .
  This is the last time the message queue was read.

- This is the structure member one must compare against the current tick
  count timestamp in order to heuristically determine whether a message
  queue thread is hung!! Fix MsqIsHung() in accordance, add extra debug
  logging in order to help us determining which of our code present
  regular GUI hangs, and add as well an extra "TimeOut" parameter so as
  not to hardcode a fixed value within that function but instead
  allowing its caller to specify possible different values.

- THREADINFO::timeLast is on the contrary the last message time stamp,
  and will definitively differ from CLIENTTHREADINFO::timeLastRead .
  It should only be used for information purposes!

- Accordingly, in NtUserGetThreadState()::THREADSTATE_UPTIMELASTREAD
  and in InitThreadCallback(), only (re-)initialize the timeLastRead
  member of the CLIENTTHREADINFO structure of the THREADINFO of interest.

- In co_IntPeekMessage(), update more often the timeLastRead timestamp
  whenever the current message queue has been read (but NOT timeLast!!
  That one will be updated ONLY WHEN a message is found!).

- In co_IntSendMessageTimeoutSingle() first check whether the window to
  which we send the message is being destroyed, before checking for
  queue hangs etc. Collapse the logic checks for queue hang and increase
  the hang timeout check to 4 times MSQ_HUNG (== 4 * 5 seconds) and
  display a debug trace.
2019-12-29 16:14:15 +01:00

2613 lines
63 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)
{
for (pWnd = pWnd->spwndChild; pWnd; pWnd = pWnd->spwndNext)
{
if ((pWnd->hrgnUpdate == NULL) && (pWnd->state & WNDS_SENDNCPAINT))
{
USER_REFERENCE_ENTRY Ref;
UserRefObjectCo(pWnd, &Ref);
IntSendNCPaint(pWnd, HRGN_WINDOW);
UserDerefObjectCo(pWnd);
}
}
}
/*
* IntPaintWindows
*
* Internal function used by IntRedrawWindow.
*/
VOID FASTCALL
co_IntPaintWindows(PWND Wnd, ULONG Flags, BOOL Recurse)
{
HDC hDC;
HWND hWnd = Wnd->head.h;
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 = Wnd->head.h;
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;
}
/*
* 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;
}
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;
}
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;
}
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 = NULL;
PAINTSTRUCT Ps;
NTSTATUS Status;
HDC hDC;
USER_REFERENCE_ENTRY Ref;
DECLARE_RETURN(HDC);
TRACE("Enter NtUserBeginPaint\n");
UserEnterExclusive();
if (!(Window = UserGetWindowObject(hWnd)))
{
RETURN( NULL);
}
UserRefObjectCo(Window, &Ref);
hDC = IntBeginPaint(Window, &Ps);
Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
if (! NT_SUCCESS(Status))
{
SetLastNtError(Status);
RETURN(NULL);
}
RETURN(hDC);
CLEANUP:
if (Window) UserDerefObjectCo(Window);
TRACE("Leave NtUserBeginPaint, ret=%p\n",_ret_);
UserLeave();
END_CLEANUP;
}
/*
* NtUserEndPaint
*
* Status
* @implemented
*/
BOOL APIENTRY
NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* pUnsafePs)
{
NTSTATUS Status = STATUS_SUCCESS;
PWND Window = NULL;
PAINTSTRUCT Ps;
USER_REFERENCE_ENTRY Ref;
DECLARE_RETURN(BOOL);
TRACE("Enter NtUserEndPaint\n");
UserEnterExclusive();
if (!(Window = UserGetWindowObject(hWnd)))
{
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))
{
RETURN(FALSE);
}
RETURN(IntEndPaint(Window, &Ps));
CLEANUP:
if (Window) UserDerefObjectCo(Window);
TRACE("Leave NtUserEndPaint, ret=%i\n",_ret_);
UserLeave();
END_CLEANUP;
}
/*
* 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)
{
DECLARE_RETURN(INT);
PWND Window;
INT ret;
TRACE("Enter NtUserGetUpdateRgn\n");
UserEnterExclusive();
if (!(Window = UserGetWindowObject(hWnd)))
{
RETURN(ERROR);
}
ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
RETURN(ret);
CLEANUP:
TRACE("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
UserLeave();
END_CLEANUP;
}
/*
* NtUserGetUpdateRect
*
* Status
* @implemented
*/
BOOL APIENTRY
NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
{
PWND Window;
RECTL Rect;
NTSTATUS Status;
BOOL Ret;
DECLARE_RETURN(BOOL);
TRACE("Enter NtUserGetUpdateRect\n");
UserEnterExclusive();
if (!(Window = UserGetWindowObject(hWnd)))
{
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);
RETURN(FALSE);
}
}
RETURN(Ret);
CLEANUP:
TRACE("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
UserLeave();
END_CLEANUP;
}
/*
* NtUserRedrawWindow
*
* Status
* @implemented
*/
BOOL APIENTRY
NtUserRedrawWindow(
HWND hWnd,
CONST RECT *lprcUpdate,
HRGN hrgnUpdate,
UINT flags)
{
RECTL SafeUpdateRect;
PWND Wnd;
BOOL Ret;
USER_REFERENCE_ENTRY Ref;
NTSTATUS Status = STATUS_SUCCESS;
PREGION RgnUpdate = NULL;
DECLARE_RETURN(BOOL);
TRACE("Enter NtUserRedrawWindow\n");
UserEnterExclusive();
if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
{
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));
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);
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);
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);
RETURN( Ret);
CLEANUP:
TRACE("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
UserLeave();
END_CLEANUP;
}
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.");
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 */