mirror of
https://github.com/reactos/reactos.git
synced 2024-09-06 19:02:32 +00:00
2211 lines
53 KiB
C
2211 lines
53 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Win32k subsystem
|
|
* PURPOSE: Window painting function
|
|
* FILE: subsystems/win32/win32k/ntuser/painting.c
|
|
* PROGRAMER: Filip Navara (xnavara@volny.cz)
|
|
*/
|
|
|
|
#include <win32k.h>
|
|
DBG_DEFAULT_CHANNEL(UserPainting);
|
|
|
|
/* 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;
|
|
|
|
ParentWnd = Child->spwndParent;
|
|
while (ParentWnd != NULL)
|
|
{
|
|
if (!(ParentWnd->style & WS_VISIBLE) ||
|
|
(ParentWnd->style & WS_MINIMIZE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!RECTL_bIntersectRect(WindowRect, WindowRect, &ParentWnd->rcClient))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* FIXME: Layered windows. */
|
|
|
|
ParentWnd = ParentWnd->spwndParent;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL FASTCALL
|
|
IntValidateParent(PWND Child, HRGN hValidateRgn, BOOL Recurse)
|
|
{
|
|
PWND ParentWnd = Child;
|
|
|
|
if (ParentWnd->style & WS_CHILD)
|
|
{
|
|
do
|
|
ParentWnd = ParentWnd->spwndParent;
|
|
while (ParentWnd->style & WS_CHILD);
|
|
}
|
|
// Hax out for drawing issues.
|
|
// if (!(ParentWnd->state & WNDS_SYNCPAINTPENDING)) Recurse = FALSE;
|
|
|
|
ParentWnd = Child->spwndParent;
|
|
while (ParentWnd)
|
|
{
|
|
if (ParentWnd->style & WS_CLIPCHILDREN)
|
|
break;
|
|
|
|
if (ParentWnd->hrgnUpdate != 0)
|
|
{
|
|
if (Recurse)
|
|
return FALSE;
|
|
|
|
IntInvalidateWindows( ParentWnd,
|
|
hValidateRgn,
|
|
RDW_VALIDATE | RDW_NOCHILDREN);
|
|
}
|
|
|
|
ParentWnd = ParentWnd->spwndParent;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
Synchronize painting to the top-level windows of other threads.
|
|
*/
|
|
VOID FASTCALL
|
|
IntSendSyncPaint(PWND Wnd, ULONG Flags)
|
|
{
|
|
PTHREADINFO ptiCur;
|
|
PUSER_MESSAGE_QUEUE MessageQueue;
|
|
PUSER_SENT_MESSAGE Message;
|
|
PLIST_ENTRY Entry;
|
|
BOOL bSend = TRUE;
|
|
|
|
MessageQueue = Wnd->head.pti->MessageQueue;
|
|
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!
|
|
ERR("SendSyncPaint Wnd in State!\n");
|
|
if (!IsListEmpty(&MessageQueue->SentMessagesListHead))
|
|
{
|
|
// Scan sent queue messages to see if we received sync paint messages.
|
|
Entry = MessageQueue->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 != &MessageQueue->SentMessagesListHead);
|
|
}
|
|
if (bSend)
|
|
{
|
|
ERR("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 ( Wnd == UserGetDesktopWindow() )
|
|
{
|
|
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 = IntSysCreateRectRgnIndirect(&Wnd->rcClient);
|
|
else
|
|
hRgnWindow = IntSysCreateRectRgnIndirect(&Wnd->rcWindow);
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
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->MessageQueue);
|
|
}
|
|
}
|
|
|
|
GreDeleteObject(hRgnWindow);
|
|
|
|
return hRgnNonClient;
|
|
}
|
|
else
|
|
{
|
|
return Window->hrgnUpdate;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
Wnd->state &= ~WNDS_PAINTNOTPROCESSED;
|
|
|
|
if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
|
|
{
|
|
if (Wnd->hrgnUpdate)
|
|
{
|
|
if (!IntValidateParent(Wnd, Wnd->hrgnUpdate, Recurse))
|
|
return;
|
|
}
|
|
|
|
if (Flags & RDW_UPDATENOW)
|
|
{
|
|
if ((Wnd->hrgnUpdate != NULL ||
|
|
Wnd->state & WNDS_INTERNALPAINT))
|
|
{
|
|
Wnd->state2 |= WNDS2_WMPAINTSENT;
|
|
co_IntSendMessage(hWnd, WM_PAINT, 0, 0);
|
|
}
|
|
}
|
|
else if (Wnd->head.pti == PsGetCurrentThreadWin32Thread())
|
|
{
|
|
if (Wnd->state & WNDS_SENDNCPAINT)
|
|
{
|
|
TempRegion = IntGetNCUpdateRgn(Wnd, TRUE);
|
|
Wnd->state &= ~WNDS_SENDNCPAINT;
|
|
if ( Wnd == GetW32ThreadInfo()->MessageQueue->spwndActive &&
|
|
!(Wnd->state & WNDS_ACTIVEFRAME))
|
|
{
|
|
Wnd->state |= WNDS_ACTIVEFRAME;
|
|
Wnd->state &= ~WNDS_NONCPAINT;
|
|
}
|
|
if (TempRegion) co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)TempRegion, 0);
|
|
}
|
|
|
|
if (Wnd->state & WNDS_SENDERASEBACKGROUND)
|
|
{
|
|
if (Wnd->hrgnUpdate)
|
|
{
|
|
hDC = UserGetDCEx( Wnd,
|
|
Wnd->hrgnUpdate,
|
|
DCX_CACHE|DCX_USESTYLE|DCX_INTERSECTRGN|DCX_KEEPCLIPRGN);
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Wnd->state &= ~(WNDS_SENDNCPAINT|WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
|
|
}
|
|
|
|
/*
|
|
* 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) || !(Wnd->style & WS_CLIPCHILDREN)) )
|
|
{
|
|
HWND *List, *phWnd;
|
|
|
|
if ((List = IntWinListChildren(Wnd)))
|
|
{
|
|
/* FIXME: Handle WS_EX_TRANSPARENT */
|
|
for (phWnd = List; *phWnd; ++phWnd)
|
|
{
|
|
Wnd = UserGetWindowObject(*phWnd);
|
|
if (Wnd && (Wnd->style & WS_VISIBLE))
|
|
{
|
|
USER_REFERENCE_ENTRY Ref;
|
|
UserRefObjectCo(Wnd, &Ref);
|
|
co_IntPaintWindows(Wnd, Flags, TRUE);
|
|
UserDerefObjectCo(Wnd);
|
|
}
|
|
}
|
|
ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* IntInvalidateWindows
|
|
*
|
|
* Internal function used by IntRedrawWindow, UserRedrawDesktop,
|
|
* co_WinPosSetWindowPos, IntValidateParent, co_UserRedrawWindow.
|
|
*/
|
|
VOID FASTCALL
|
|
IntInvalidateWindows(PWND Wnd, HRGN hRgn, ULONG Flags)
|
|
{
|
|
INT RgnType;
|
|
BOOL HadPaintMessage;
|
|
|
|
TRACE("IntInvalidateWindows start\n");
|
|
|
|
Wnd->state |= WNDS_PAINTNOTPROCESSED;
|
|
|
|
/*
|
|
* If the nonclient is not to be redrawn, clip the region to the client
|
|
* rect
|
|
*/
|
|
if (0 != (Flags & RDW_INVALIDATE) && 0 == (Flags & RDW_FRAME))
|
|
{
|
|
HRGN hRgnClient;
|
|
|
|
hRgnClient = IntSysCreateRectRgnIndirect(&Wnd->rcClient);
|
|
RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnClient, RGN_AND);
|
|
GreDeleteObject(hRgnClient);
|
|
}
|
|
|
|
/*
|
|
* Clip the given region with window rectangle (or region)
|
|
*/
|
|
|
|
if (!Wnd->hrgnClip || (Wnd->style & WS_MINIMIZE))
|
|
{
|
|
HRGN hRgnWindow;
|
|
|
|
hRgnWindow = IntSysCreateRectRgnIndirect(&Wnd->rcWindow);
|
|
RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
|
|
GreDeleteObject(hRgnWindow);
|
|
}
|
|
else
|
|
{
|
|
NtGdiOffsetRgn( hRgn,
|
|
-Wnd->rcWindow.left,
|
|
-Wnd->rcWindow.top);
|
|
RgnType = NtGdiCombineRgn(hRgn, hRgn, Wnd->hrgnClip, RGN_AND);
|
|
NtGdiOffsetRgn( hRgn,
|
|
Wnd->rcWindow.left,
|
|
Wnd->rcWindow.top);
|
|
}
|
|
|
|
/*
|
|
* 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 && RgnType != NULLREGION)
|
|
{
|
|
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 (Wnd->hrgnUpdate == NULL)
|
|
{
|
|
Wnd->hrgnUpdate = IntSysCreateRectRgn(0, 0, 0, 0);
|
|
IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_PUBLIC);
|
|
}
|
|
|
|
if (NtGdiCombineRgn(Wnd->hrgnUpdate, Wnd->hrgnUpdate,
|
|
hRgn, RGN_OR) == NULLREGION)
|
|
{
|
|
IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
|
|
GreDeleteObject(Wnd->hrgnUpdate);
|
|
Wnd->hrgnUpdate = NULL;
|
|
}
|
|
Flags |= RDW_FRAME; // For children.
|
|
}
|
|
} // The following flags are used to validate the window.
|
|
else if (Flags & (RDW_VALIDATE|RDW_NOINTERNALPAINT|RDW_NOERASE|RDW_NOFRAME))
|
|
{
|
|
/* FIXME: Handle WNDS_UPDATEDIRTY */
|
|
|
|
if (Flags & RDW_NOINTERNALPAINT)
|
|
{
|
|
Wnd->state &= ~WNDS_INTERNALPAINT;
|
|
}
|
|
|
|
if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
|
|
{
|
|
if (Flags & RDW_NOFRAME)
|
|
Wnd->state &= ~WNDS_SENDNCPAINT;
|
|
if (Flags & RDW_NOERASE)
|
|
Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
|
|
|
|
if (Wnd->hrgnUpdate != NULL)
|
|
{
|
|
if (NtGdiCombineRgn(Wnd->hrgnUpdate, Wnd->hrgnUpdate,
|
|
hRgn, RGN_DIFF) == NULLREGION)
|
|
{
|
|
IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
|
|
GreDeleteObject(Wnd->hrgnUpdate);
|
|
Wnd->hrgnUpdate = NULL;
|
|
}
|
|
}
|
|
|
|
if (Wnd->hrgnUpdate == NULL)
|
|
Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
HRGN hRgnTemp = IntSysCreateRectRgn(0, 0, 0, 0);
|
|
NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
|
|
IntInvalidateWindows(Child, hRgnTemp, Flags);
|
|
GreDeleteObject(hRgnTemp);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fake post paint messages to window message queue if needed
|
|
*/
|
|
|
|
if (HadPaintMessage != IntIsWindowDirty(Wnd))
|
|
{
|
|
if (HadPaintMessage)
|
|
MsqDecPaintCountQueue(Wnd->head.pti->MessageQueue);
|
|
else
|
|
MsqIncPaintCountQueue(Wnd->head.pti->MessageQueue);
|
|
}
|
|
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,
|
|
HRGN UpdateRgn,
|
|
ULONG Flags)
|
|
{
|
|
HRGN hRgn = NULL;
|
|
TRACE("co_UserRedrawWindow start\n");
|
|
|
|
/*
|
|
* Step 1.
|
|
* Validation of passed parameters.
|
|
*/
|
|
|
|
if (!IntIsWindowDrawable(Window))
|
|
{
|
|
return TRUE; // Just do nothing!!!
|
|
}
|
|
|
|
/*
|
|
* 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!
|
|
{
|
|
if (UpdateRgn != NULL)
|
|
{
|
|
hRgn = IntSysCreateRectRgn(0, 0, 0, 0);
|
|
if (NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY) == NULLREGION)
|
|
{
|
|
GreDeleteObject(hRgn);
|
|
hRgn = NULL;
|
|
}
|
|
else
|
|
NtGdiOffsetRgn(hRgn, Window->rcClient.left, Window->rcClient.top);
|
|
}
|
|
else if (UpdateRect != NULL)
|
|
{
|
|
if (!RECTL_bIsEmptyRect(UpdateRect))
|
|
{
|
|
hRgn = IntSysCreateRectRgnIndirect((RECTL *)UpdateRect);
|
|
NtGdiOffsetRgn(hRgn, Window->rcClient.left, Window->rcClient.top);
|
|
}
|
|
}
|
|
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))
|
|
hRgn = IntSysCreateRectRgnIndirect(&Window->rcWindow);
|
|
}
|
|
else
|
|
{
|
|
if (!RECTL_bIsEmptyRect(&Window->rcClient))
|
|
hRgn = IntSysCreateRectRgnIndirect(&Window->rcClient);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Step 3.
|
|
* Adjust the window update region depending on hRgn and flags.
|
|
*/
|
|
|
|
if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT) &&
|
|
hRgn != NULL)
|
|
{
|
|
IntInvalidateWindows(Window, hRgn, Flags);
|
|
}
|
|
|
|
/*
|
|
* Step 4.
|
|
* Repaint and erase windows if needed.
|
|
*/
|
|
|
|
if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
|
|
{
|
|
if (Flags & RDW_ERASENOW) IntSendSyncPaint(Window, Flags);
|
|
co_IntPaintWindows(Window, Flags, FALSE);
|
|
}
|
|
|
|
/*
|
|
* Step 5.
|
|
* Cleanup ;-)
|
|
*/
|
|
|
|
if (hRgn != NULL)
|
|
{
|
|
GreDeleteObject(hRgn);
|
|
}
|
|
TRACE("co_UserRedrawWindow exit\n");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL FASTCALL
|
|
IntIsWindowDirty(PWND Wnd)
|
|
{
|
|
return ( Wnd->style & WS_VISIBLE &&
|
|
( Wnd->hrgnUpdate != NULL ||
|
|
Wnd->state & WNDS_INTERNALPAINT ) );
|
|
}
|
|
|
|
PWND FASTCALL
|
|
IntFindWindowToRepaint(PWND Window, PTHREADINFO Thread)
|
|
{
|
|
PWND hChild;
|
|
PWND TempWindow;
|
|
|
|
for (; Window != NULL; Window = Window->spwndNext)
|
|
{
|
|
if (IntWndBelongsToThread(Window, Thread) &&
|
|
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;
|
|
}
|
|
|
|
if (Window->spwndChild)
|
|
{
|
|
hChild = IntFindWindowToRepaint(Window->spwndChild, Thread);
|
|
if (hChild != NULL)
|
|
return hChild;
|
|
}
|
|
}
|
|
return Window;
|
|
}
|
|
|
|
BOOL FASTCALL
|
|
IntGetPaintMessage(
|
|
PWND Window,
|
|
UINT MsgFilterMin,
|
|
UINT MsgFilterMax,
|
|
PTHREADINFO Thread,
|
|
MSG *Message,
|
|
BOOL Remove)
|
|
{
|
|
PWND PaintWnd;
|
|
|
|
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");
|
|
}
|
|
|
|
PaintWnd = IntFindWindowToRepaint(UserGetDesktopWindow(), Thread);
|
|
|
|
Message->hwnd = PaintWnd ? UserHMGetHandle(PaintWnd) : NULL;
|
|
|
|
if (Message->hwnd == NULL)
|
|
{
|
|
ERR("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found! Counts %d\n",Thread->cPaintsReady);
|
|
/* Hack to stop spamming the debuglog ! */
|
|
Thread->cPaintsReady = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
if (Window != NULL && PaintWnd != Window)
|
|
return FALSE;
|
|
|
|
if (PaintWnd->state & WNDS_INTERNALPAINT)
|
|
{
|
|
PaintWnd->state &= ~WNDS_INTERNALPAINT;
|
|
if (!PaintWnd->hrgnUpdate)
|
|
MsqDecPaintCountQueue(Thread->MessageQueue);
|
|
}
|
|
PaintWnd->state2 &= ~WNDS2_WMPAINTSENT;
|
|
PaintWnd->state &= ~WNDS_UPDATEDIRTY;
|
|
Message->wParam = Message->lParam = 0;
|
|
Message->message = WM_PAINT;
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
HWND FASTCALL
|
|
co_IntFixCaret(PWND Window, RECTL *lprc, UINT flags)
|
|
{
|
|
PDESKTOP Desktop;
|
|
PTHRDCARETINFO CaretInfo;
|
|
PTHREADINFO pti;
|
|
PUSER_MESSAGE_QUEUE ActiveMessageQueue;
|
|
HWND hWndCaret;
|
|
PWND WndCaret;
|
|
|
|
ASSERT_REFS_CO(Window);
|
|
|
|
pti = PsGetCurrentThreadWin32Thread();
|
|
Desktop = pti->rpdesk;
|
|
ActiveMessageQueue = Desktop->ActiveMessageQueue;
|
|
if (!ActiveMessageQueue) return 0;
|
|
CaretInfo = ActiveMessageQueue->CaretInfo;
|
|
hWndCaret = CaretInfo->hWnd;
|
|
|
|
WndCaret = ValidateHwndNoErr(hWndCaret);
|
|
|
|
// FIXME: Check for WndCaret can be NULL
|
|
if (WndCaret == Window ||
|
|
((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(Window, WndCaret)))
|
|
{
|
|
POINT pt, FromOffset, ToOffset;
|
|
RECTL rcCaret;
|
|
|
|
pt.x = CaretInfo->Pos.x;
|
|
pt.y = CaretInfo->Pos.y;
|
|
IntGetClientOrigin(WndCaret, &FromOffset);
|
|
IntGetClientOrigin(Window, &ToOffset);
|
|
rcCaret.left = pt.x;
|
|
rcCaret.top = pt.y;
|
|
rcCaret.right = pt.x + CaretInfo->Size.cx;
|
|
rcCaret.bottom = pt.y + CaretInfo->Size.cy;
|
|
if (RECTL_bIntersectRect(lprc, lprc, &rcCaret))
|
|
{
|
|
co_UserHideCaret(0);
|
|
lprc->left = pt.x;
|
|
lprc->top = pt.y;
|
|
return hWndCaret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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_IntPaintWindows( pwnd, RDW_ERASENOW|RDW_UPDATENOW, 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)
|
|
{
|
|
PPROPERTY pprop;
|
|
DWORD FlashState;
|
|
BOOL Ret = FALSE;
|
|
|
|
pprop = IntGetProp(pWnd, AtomFlashWndState);
|
|
if (!pprop)
|
|
{
|
|
FlashState = pfwi->dwFlags;
|
|
IntSetProp(pWnd, AtomFlashWndState, (HANDLE) FlashState);
|
|
return TRUE;
|
|
}
|
|
|
|
FlashState = (DWORD)pprop->Data;
|
|
if ( pfwi->dwFlags == FLASHW_STOP )
|
|
{
|
|
IntRemoveProp(pWnd, AtomFlashWndState);
|
|
Ret = TRUE;
|
|
}
|
|
return Ret;
|
|
}
|
|
|
|
HDC FASTCALL
|
|
IntBeginPaint(PWND Window, PPAINTSTRUCT Ps)
|
|
{
|
|
co_UserHideCaret(Window);
|
|
|
|
Window->state2 |= WNDS2_STARTPAINT;
|
|
Window->state &= ~WNDS_PAINTNOTPROCESSED;
|
|
|
|
if (Window->state & WNDS_SENDNCPAINT)
|
|
{
|
|
HRGN hRgn;
|
|
|
|
Window->state &= ~WNDS_UPDATEDIRTY;
|
|
hRgn = IntGetNCUpdateRgn(Window, FALSE);
|
|
Window->state &= ~WNDS_SENDNCPAINT;
|
|
co_IntSendMessage(UserHMGetHandle(Window), WM_NCPAINT, (WPARAM)hRgn, 0);
|
|
if (hRgn != HRGN_WINDOW && hRgn != NULL && GreIsHandleValid(hRgn))
|
|
{
|
|
/* NOTE: The region can already be deleted! */
|
|
GreDeleteObject(hRgn);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Window->state &= ~WNDS_UPDATEDIRTY;
|
|
}
|
|
|
|
RtlZeroMemory(Ps, sizeof(PAINTSTRUCT));
|
|
|
|
Ps->hdc = UserGetDCEx( Window,
|
|
Window->hrgnUpdate,
|
|
DCX_INTERSECTRGN | DCX_USESTYLE);
|
|
if (!Ps->hdc)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (Window->hrgnUpdate != NULL)
|
|
{
|
|
MsqDecPaintCountQueue(Window->head.pti->MessageQueue);
|
|
GdiGetClipBox(Ps->hdc, &Ps->rcPaint);
|
|
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->MessageQueue);
|
|
|
|
IntGetClientRect(Window, &Ps->rcPaint);
|
|
}
|
|
|
|
Window->state &= ~WNDS_INTERNALPAINT;
|
|
|
|
if (Window->state & WNDS_SENDERASEBACKGROUND)
|
|
{
|
|
Window->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
|
|
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;
|
|
}
|
|
if (Window->hrgnUpdate)
|
|
{
|
|
if (!(Window->style & WS_CLIPCHILDREN))
|
|
{
|
|
PWND Child;
|
|
for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
|
|
{
|
|
if (Child->hrgnUpdate == NULL && Child->state & WNDS_SENDNCPAINT) // Helped fixing test_redrawnow.
|
|
IntInvalidateWindows(Child, Window->hrgnUpdate, RDW_FRAME | RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
|
|
}
|
|
}
|
|
}
|
|
return Ps->hdc;
|
|
}
|
|
|
|
BOOL FASTCALL
|
|
IntEndPaint(PWND Wnd, PPAINTSTRUCT Ps)
|
|
{
|
|
HDC hdc = NULL;
|
|
|
|
hdc = Ps->hdc;
|
|
|
|
UserReleaseDC(Wnd, hdc, TRUE);
|
|
|
|
Wnd->state2 &= ~(WNDS2_WMPAINTSENT|WNDS2_STARTPAINT);
|
|
|
|
co_UserShowCaret(Wnd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL APIENTRY
|
|
NtUserFlashWindowEx(IN PFLASHWINFO pfwi)
|
|
{
|
|
PWND pWnd;
|
|
FLASHWINFO finfo = {0};
|
|
BOOL Ret = TRUE;
|
|
|
|
UserEnterExclusive();
|
|
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead(pfwi, sizeof(FLASHWINFO), sizeof(ULONG));
|
|
RtlCopyMemory(&finfo, pfwi, sizeof(FLASHWINFO));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
SetLastNtError(_SEH2_GetExceptionCode());
|
|
Ret = FALSE;
|
|
}
|
|
_SEH2_END
|
|
|
|
if (!Ret) goto Exit;
|
|
|
|
if (!( pWnd = (PWND)UserGetObject(gHandleTable, finfo.hwnd, otWindow)) ||
|
|
finfo.cbSize != sizeof(FLASHWINFO) ||
|
|
finfo.dwFlags & ~(FLASHW_ALL|FLASHW_TIMER|FLASHW_TIMERNOFG) )
|
|
{
|
|
EngSetLastError(ERROR_INVALID_PARAMETER);
|
|
Ret = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
Ret = IntFlashWindowEx(pWnd, &finfo);
|
|
|
|
Exit:
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
INT FASTCALL
|
|
co_UserGetUpdateRgn(PWND Window, HRGN hRgn, BOOL bErase)
|
|
{
|
|
int RegionType;
|
|
RECTL Rect;
|
|
|
|
ASSERT_REFS_CO(Window);
|
|
|
|
Window->state &= ~WNDS_UPDATEDIRTY;
|
|
|
|
if (Window->hrgnUpdate == NULL)
|
|
{
|
|
RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
|
|
}
|
|
else
|
|
{
|
|
Rect = Window->rcClient;
|
|
IntIntersectWithParents(Window, &Rect);
|
|
NtGdiSetRectRgn(hRgn, Rect.left, Rect.top, Rect.right, Rect.bottom);
|
|
RegionType = NtGdiCombineRgn(hRgn, hRgn, Window->hrgnUpdate, RGN_AND);
|
|
NtGdiOffsetRgn(hRgn, -Window->rcClient.left, -Window->rcClient.top);
|
|
}
|
|
|
|
if (bErase && RegionType != NULLREGION && RegionType != ERROR)
|
|
{
|
|
co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
|
|
}
|
|
|
|
return RegionType;
|
|
}
|
|
|
|
/*
|
|
* NtUserGetUpdateRgn
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
|
|
INT APIENTRY
|
|
NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
|
|
{
|
|
DECLARE_RETURN(INT);
|
|
PWND Window;
|
|
INT ret;
|
|
USER_REFERENCE_ENTRY Ref;
|
|
|
|
TRACE("Enter NtUserGetUpdateRgn\n");
|
|
UserEnterExclusive();
|
|
|
|
if (!(Window = UserGetWindowObject(hWnd)))
|
|
{
|
|
RETURN(ERROR);
|
|
}
|
|
|
|
UserRefObjectCo(Window, &Ref);
|
|
ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
|
|
UserDerefObjectCo(Window);
|
|
|
|
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;
|
|
INT RegionType;
|
|
PROSRGNDATA RgnData;
|
|
NTSTATUS Status;
|
|
DECLARE_RETURN(BOOL);
|
|
|
|
TRACE("Enter NtUserGetUpdateRect\n");
|
|
UserEnterExclusive();
|
|
|
|
if (!(Window = UserGetWindowObject(hWnd)))
|
|
{
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
Window->state &= ~WNDS_UPDATEDIRTY;
|
|
|
|
if (Window->hrgnUpdate == NULL)
|
|
{
|
|
Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Get the update region bounding box. */
|
|
if (Window->hrgnUpdate == HRGN_WINDOW)
|
|
{
|
|
Rect = Window->rcClient;
|
|
}
|
|
else
|
|
{
|
|
RgnData = RGNOBJAPI_Lock(Window->hrgnUpdate, NULL);
|
|
ASSERT(RgnData != NULL);
|
|
RegionType = REGION_GetRgnBox(RgnData, &Rect);
|
|
RGNOBJAPI_Unlock(RgnData);
|
|
|
|
if (RegionType != ERROR && RegionType != NULLREGION)
|
|
RECTL_bIntersectRect(&Rect, &Rect, &Window->rcClient);
|
|
}
|
|
|
|
if (IntIntersectWithParents(Window, &Rect))
|
|
{
|
|
RECTL_vOffsetRect(&Rect,
|
|
-Window->rcClient.left,
|
|
-Window->rcClient.top);
|
|
} else
|
|
{
|
|
Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
|
|
}
|
|
}
|
|
|
|
if (bErase && !RECTL_bIsEmptyRect(&Rect))
|
|
{
|
|
USER_REFERENCE_ENTRY Ref;
|
|
UserRefObjectCo(Window, &Ref);
|
|
co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
|
|
UserDerefObjectCo(Window);
|
|
}
|
|
|
|
if (UnsafeRect != NULL)
|
|
{
|
|
Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECTL));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EngSetLastError(ERROR_INVALID_PARAMETER);
|
|
RETURN(FALSE);
|
|
}
|
|
}
|
|
|
|
RETURN(!RECTL_bIsEmptyRect(&Rect));
|
|
|
|
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;
|
|
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);
|
|
}
|
|
|
|
UserRefObjectCo(Wnd, &Ref);
|
|
|
|
Ret = co_UserRedrawWindow( Wnd,
|
|
lprcUpdate ? &SafeUpdateRect : NULL,
|
|
hrgnUpdate,
|
|
flags);
|
|
|
|
UserDerefObjectCo(Wnd);
|
|
|
|
RETURN( Ret);
|
|
|
|
CLEANUP:
|
|
TRACE("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
static
|
|
INT FASTCALL
|
|
UserScrollDC(
|
|
HDC hDC,
|
|
INT dx,
|
|
INT dy,
|
|
const RECTL *prcScroll,
|
|
const RECTL *prcClip,
|
|
HRGN hrgnUpdate,
|
|
RECTL *prcUpdate)
|
|
{
|
|
PDC pDC;
|
|
RECTL rcScroll, rcClip, rcSrc, rcDst;
|
|
INT Result;
|
|
|
|
GdiGetClipBox(hDC, &rcClip);
|
|
rcScroll = rcClip;
|
|
if (prcClip)
|
|
{
|
|
RECTL_bIntersectRect(&rcClip, &rcClip, prcClip);
|
|
}
|
|
|
|
if (prcScroll)
|
|
{
|
|
rcScroll = *prcScroll;
|
|
RECTL_bIntersectRect(&rcSrc, &rcClip, prcScroll);
|
|
}
|
|
else
|
|
{
|
|
rcSrc = rcClip;
|
|
}
|
|
|
|
rcDst = rcSrc;
|
|
RECTL_vOffsetRect(&rcDst, dx, dy);
|
|
RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
|
|
|
|
if (!NtGdiBitBlt( hDC,
|
|
rcDst.left,
|
|
rcDst.top,
|
|
rcDst.right - rcDst.left,
|
|
rcDst.bottom - rcDst.top,
|
|
hDC,
|
|
rcDst.left - dx,
|
|
rcDst.top - dy,
|
|
SRCCOPY,
|
|
0,
|
|
0))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
/* Calculate the region that was invalidated by moving or
|
|
could not be copied, because it was not visible */
|
|
if (hrgnUpdate || prcUpdate)
|
|
{
|
|
HRGN hrgnOwn, hrgnTmp;
|
|
PREGION prgnTmp;
|
|
|
|
pDC = DC_LockDc(hDC);
|
|
if (!pDC)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Begin with the shifted and then clipped scroll rect */
|
|
rcDst = rcScroll;
|
|
RECTL_vOffsetRect(&rcDst, dx, dy);
|
|
RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
|
|
if (hrgnUpdate)
|
|
{
|
|
hrgnOwn = hrgnUpdate;
|
|
if (!NtGdiSetRectRgn(hrgnOwn, rcDst.left, rcDst.top, rcDst.right, rcDst.bottom))
|
|
{
|
|
DC_UnlockDc(pDC);
|
|
return ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hrgnOwn = IntSysCreateRectRgnIndirect(&rcDst);
|
|
}
|
|
|
|
/* Add the source rect */
|
|
hrgnTmp = IntSysCreateRectRgnIndirect(&rcSrc);
|
|
NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_OR);
|
|
|
|
/* Substract the part of the dest that was visible in source */
|
|
prgnTmp = RGNOBJAPI_Lock(hrgnTmp, NULL);
|
|
IntGdiCombineRgn(prgnTmp, prgnTmp, pDC->prgnVis, RGN_AND);
|
|
RGNOBJAPI_Unlock(prgnTmp);
|
|
NtGdiOffsetRgn(hrgnTmp, dx, dy);
|
|
Result = NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_DIFF);
|
|
|
|
/* DO NOT Unlock DC while messing with prgnVis! */
|
|
DC_UnlockDc(pDC);
|
|
|
|
GreDeleteObject(hrgnTmp);
|
|
|
|
if (prcUpdate)
|
|
{
|
|
IntGdiGetRgnBox(hrgnOwn, prcUpdate);
|
|
}
|
|
|
|
if (!hrgnUpdate)
|
|
{
|
|
GreDeleteObject(hrgnOwn);
|
|
}
|
|
}
|
|
else
|
|
Result = NULLREGION;
|
|
|
|
return Result;
|
|
}
|
|
|
|
/*
|
|
* NtUserScrollDC
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
BOOL APIENTRY
|
|
NtUserScrollDC(
|
|
HDC hDC,
|
|
INT dx,
|
|
INT dy,
|
|
const RECT *prcUnsafeScroll,
|
|
const RECT *prcUnsafeClip,
|
|
HRGN hrgnUpdate,
|
|
LPRECT prcUnsafeUpdate)
|
|
{
|
|
DECLARE_RETURN(DWORD);
|
|
RECTL rcScroll, rcClip, rcUpdate;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
DWORD Result;
|
|
|
|
TRACE("Enter NtUserScrollDC\n");
|
|
UserEnterExclusive();
|
|
|
|
_SEH2_TRY
|
|
{
|
|
if (prcUnsafeScroll)
|
|
{
|
|
ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
|
|
rcScroll = *prcUnsafeScroll;
|
|
}
|
|
if (prcUnsafeClip)
|
|
{
|
|
ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
|
|
rcClip = *prcUnsafeClip;
|
|
}
|
|
if (prcUnsafeUpdate)
|
|
{
|
|
ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
Result = UserScrollDC( hDC,
|
|
dx,
|
|
dy,
|
|
prcUnsafeScroll? &rcScroll : 0,
|
|
prcUnsafeClip? &rcClip : 0,
|
|
hrgnUpdate,
|
|
prcUnsafeUpdate? &rcUpdate : NULL);
|
|
if(Result == ERROR)
|
|
{
|
|
/* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
if (prcUnsafeUpdate)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
*prcUnsafeUpdate = rcUpdate;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* FIXME: SetLastError? */
|
|
/* FIXME: correct? We have already scrolled! */
|
|
RETURN(FALSE);
|
|
}
|
|
}
|
|
|
|
RETURN(TRUE);
|
|
|
|
CLEANUP:
|
|
TRACE("Leave NtUserScrollDC, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
/*
|
|
* NtUserScrollWindowEx
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
|
|
DWORD APIENTRY
|
|
NtUserScrollWindowEx(
|
|
HWND hWnd,
|
|
INT dx,
|
|
INT dy,
|
|
const RECT *prcUnsafeScroll,
|
|
const RECT *prcUnsafeClip,
|
|
HRGN hrgnUpdate,
|
|
LPRECT prcUnsafeUpdate,
|
|
UINT flags)
|
|
{
|
|
RECTL rcScroll, rcClip, rcCaret, rcUpdate;
|
|
INT Result;
|
|
PWND Window = NULL, CaretWnd;
|
|
HDC hDC;
|
|
HRGN hrgnOwn = NULL, hrgnTemp, hrgnWinupd = NULL;
|
|
HWND hwndCaret;
|
|
DWORD dcxflags = 0;
|
|
int rdw_flags;
|
|
BOOL bOwnRgn = TRUE;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
DECLARE_RETURN(DWORD);
|
|
USER_REFERENCE_ENTRY Ref, CaretRef;
|
|
|
|
TRACE("Enter NtUserScrollWindowEx\n");
|
|
UserEnterExclusive();
|
|
|
|
Window = UserGetWindowObject(hWnd);
|
|
if (!Window || !IntIsWindowDrawable(Window))
|
|
{
|
|
Window = NULL; /* prevent deref at cleanup */
|
|
RETURN( ERROR);
|
|
}
|
|
UserRefObjectCo(Window, &Ref);
|
|
|
|
IntGetClientRect(Window, &rcClip);
|
|
|
|
_SEH2_TRY
|
|
{
|
|
if (prcUnsafeScroll)
|
|
{
|
|
ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
|
|
RECTL_bIntersectRect(&rcScroll, &rcClip, prcUnsafeScroll);
|
|
}
|
|
else
|
|
rcScroll = rcClip;
|
|
|
|
if (prcUnsafeClip)
|
|
{
|
|
ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
|
|
RECTL_bIntersectRect(&rcClip, &rcClip, prcUnsafeClip);
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
RETURN(ERROR);
|
|
}
|
|
|
|
if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
|
|
(dx == 0 && dy == 0))
|
|
{
|
|
RETURN(NULLREGION);
|
|
}
|
|
|
|
if (hrgnUpdate)
|
|
{
|
|
hrgnOwn = hrgnUpdate;
|
|
bOwnRgn = FALSE;
|
|
}
|
|
else
|
|
hrgnOwn = IntSysCreateRectRgn(0, 0, 0, 0);
|
|
|
|
/* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
|
|
if (flags & SW_SCROLLWNDDCE)
|
|
{
|
|
dcxflags = DCX_USESTYLE;
|
|
|
|
if (!(Window->pcls->style & (CS_OWNDC|CS_CLASSDC)))
|
|
dcxflags |= DCX_CACHE; // AH??? wine~ If not Powned or with Class go Cheap!
|
|
|
|
if (flags & SW_SCROLLCHILDREN && Window->style & WS_CLIPCHILDREN)
|
|
dcxflags |= DCX_CACHE|DCX_NOCLIPCHILDREN;
|
|
}
|
|
else
|
|
{
|
|
/* So in this case ScrollWindowEx uses Cache DC. */
|
|
dcxflags = DCX_CACHE|DCX_USESTYLE;
|
|
if (flags & SW_SCROLLCHILDREN) dcxflags |= DCX_NOCLIPCHILDREN;
|
|
}
|
|
|
|
hDC = UserGetDCEx(Window, 0, dcxflags);
|
|
if (!hDC)
|
|
{
|
|
/* FIXME: SetLastError? */
|
|
RETURN(ERROR);
|
|
}
|
|
|
|
rdw_flags = (flags & SW_ERASE) && (flags & SW_INVALIDATE) ? RDW_INVALIDATE | RDW_ERASE : RDW_INVALIDATE ;
|
|
|
|
rcCaret = rcScroll;
|
|
hwndCaret = co_IntFixCaret(Window, &rcCaret, flags);
|
|
|
|
Result = UserScrollDC( hDC,
|
|
dx,
|
|
dy,
|
|
&rcScroll,
|
|
&rcClip,
|
|
hrgnOwn,
|
|
prcUnsafeUpdate? &rcUpdate : NULL);
|
|
|
|
UserReleaseDC(Window, hDC, FALSE);
|
|
|
|
/*
|
|
* Take into account the fact that some damage may have occurred during
|
|
* the scroll. Keep a copy in hrgnWinupd to be added to hrngUpdate at the end.
|
|
*/
|
|
|
|
hrgnTemp = IntSysCreateRectRgn(0, 0, 0, 0);
|
|
if (co_UserGetUpdateRgn(Window, hrgnTemp, FALSE) != NULLREGION)
|
|
{
|
|
HRGN hrgnClip = IntSysCreateRectRgnIndirect(&rcClip);
|
|
if (!bOwnRgn)
|
|
{
|
|
hrgnWinupd = IntSysCreateRectRgn( 0, 0, 0, 0);
|
|
NtGdiCombineRgn( hrgnWinupd, hrgnTemp, 0, RGN_COPY);
|
|
}
|
|
NtGdiOffsetRgn(hrgnTemp, dx, dy);
|
|
NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
|
|
if (!bOwnRgn) NtGdiCombineRgn( hrgnWinupd, hrgnWinupd, hrgnTemp, RGN_OR );
|
|
co_UserRedrawWindow(Window, NULL, hrgnTemp, rdw_flags );
|
|
GreDeleteObject(hrgnClip);
|
|
}
|
|
GreDeleteObject(hrgnTemp);
|
|
|
|
if (flags & SW_SCROLLCHILDREN)
|
|
{
|
|
PWND Child;
|
|
RECTL rcChild;
|
|
POINT ClientOrigin;
|
|
USER_REFERENCE_ENTRY WndRef;
|
|
RECTL rcDummy;
|
|
|
|
IntGetClientOrigin(Window, &ClientOrigin);
|
|
for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
|
|
{
|
|
rcChild = Child->rcWindow;
|
|
rcChild.left -= ClientOrigin.x;
|
|
rcChild.top -= ClientOrigin.y;
|
|
rcChild.right -= ClientOrigin.x;
|
|
rcChild.bottom -= ClientOrigin.y;
|
|
|
|
if (! prcUnsafeScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll))
|
|
{
|
|
UserRefObjectCo(Child, &WndRef);
|
|
co_WinPosSetWindowPos(Child, 0, rcChild.left + dx, rcChild.top + dy, 0, 0,
|
|
SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
|
|
SWP_NOREDRAW | SWP_DEFERERASE);
|
|
UserDerefObjectCo(Child);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flags & (SW_INVALIDATE | SW_ERASE))
|
|
{
|
|
co_UserRedrawWindow(Window, NULL, hrgnOwn, rdw_flags |
|
|
((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
|
|
((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
|
|
}
|
|
|
|
if (hwndCaret && (CaretWnd = UserGetWindowObject(hwndCaret)))
|
|
{
|
|
UserRefObjectCo(CaretWnd, &CaretRef);
|
|
|
|
co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
|
|
co_UserShowCaret(CaretWnd);
|
|
|
|
UserDerefObjectCo(CaretWnd);
|
|
}
|
|
|
|
if (prcUnsafeUpdate)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe here, to not fail on invalid pointer before scrolling */
|
|
ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
|
|
*prcUnsafeUpdate = rcUpdate;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
RETURN(ERROR);
|
|
}
|
|
}
|
|
|
|
RETURN(Result);
|
|
|
|
CLEANUP:
|
|
if (hrgnWinupd && !bOwnRgn)
|
|
{
|
|
NtGdiCombineRgn( hrgnOwn, hrgnOwn, hrgnWinupd, RGN_OR);
|
|
GreDeleteObject(hrgnWinupd);
|
|
}
|
|
|
|
if (hrgnOwn && !hrgnUpdate)
|
|
{
|
|
GreDeleteObject(hrgnOwn);
|
|
}
|
|
|
|
if (Window)
|
|
UserDerefObjectCo(Window);
|
|
|
|
TRACE("Leave NtUserScrollWindowEx, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
BOOL
|
|
UserDrawCaptionText(
|
|
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;
|
|
|
|
TRACE("UserDrawCaptionText: %wZ\n", Text);
|
|
|
|
nclm.cbSize = sizeof(nclm);
|
|
if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS,
|
|
sizeof(NONCLIENTMETRICS), &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(!hOldFont)
|
|
{
|
|
ERR("SelectFont() failed!\n");
|
|
/* Don't fail */
|
|
}
|
|
|
|
if(uFlags & DC_INBUTTON)
|
|
OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT));
|
|
else
|
|
OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(uFlags & DC_ACTIVE
|
|
? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
|
|
|
|
// FIXME: If string doesn't fit to rc, truncate it and add ellipsis.
|
|
GreGetTextExtentW(hDc, Text->Buffer, Text->Length/sizeof(WCHAR), &Size, 0);
|
|
GreExtTextOutW(hDc,
|
|
lpRc->left, (lpRc->top + lpRc->bottom)/2 - Size.cy/2,
|
|
0, NULL, Text->Buffer, Text->Length/sizeof(WCHAR), NULL, 0);
|
|
|
|
IntGdiSetTextColor(hDc, OldTextColor);
|
|
if (hOldFont)
|
|
NtGdiSelectFont(hDc, hOldFont);
|
|
if (bDeleteFont)
|
|
GreDeleteObject(hFont);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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);
|
|
|
|
if (!hIcon && pWnd != NULL)
|
|
{
|
|
HasIcon = (uFlags & DC_ICON) && (pWnd->style & WS_SYSMENU)
|
|
&& !(uFlags & DC_SMALLCAP) && !(pWnd->ExStyle & WS_EX_DLGMODALFRAME)
|
|
&& !(pWnd->ExStyle & WS_EX_TOOLWINDOW);
|
|
}
|
|
else
|
|
HasIcon = (hIcon != 0);
|
|
|
|
// 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 && pWnd)
|
|
{
|
|
hIcon = pWnd->pcls->hIconSm; // FIXME: Windows does not do that
|
|
if(!hIcon)
|
|
hIcon = pWnd->pcls->hIcon;
|
|
}
|
|
|
|
if (hIcon)
|
|
pIcon = UserGetCurIconObject(hIcon);
|
|
|
|
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)/2 - cy/2; // center
|
|
UserDrawIconEx(hDc, x, y, pIcon, cx, cy, 0, NULL, DI_NORMAL);
|
|
UserDereferenceObject(pIcon);
|
|
}
|
|
}
|
|
|
|
if (hIcon)
|
|
Rect.left += Rect.bottom - Rect.top;
|
|
|
|
if((uFlags & DC_TEXT))
|
|
{
|
|
Rect.left += 2;
|
|
|
|
if (Str)
|
|
UserDrawCaptionText(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);
|
|
UserDrawCaptionText(hDc, &ustr, &Rect, uFlags, hFont);
|
|
}
|
|
}
|
|
|
|
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
|
|
Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags);
|
|
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
NtUserDrawCaption(HWND hWnd,
|
|
HDC hDC,
|
|
LPCRECT lpRc,
|
|
UINT uFlags)
|
|
{
|
|
return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
NtUserInvalidateRect(
|
|
HWND hWnd,
|
|
CONST RECT *lpUnsafeRect,
|
|
BOOL bErase)
|
|
{
|
|
return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
NtUserInvalidateRgn(
|
|
HWND hWnd,
|
|
HRGN hRgn,
|
|
BOOL bErase)
|
|
{
|
|
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)) || // FIXME:
|
|
Window == UserGetDesktopWindow() || // pWnd->fnid == FNID_DESKTOP
|
|
Window == UserGetMessageWindow() ) // pWnd->fnid == FNID_MESSAGEWND
|
|
{
|
|
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)
|
|
{
|
|
if (hWnd)
|
|
{
|
|
return NtUserRedrawWindow(hWnd, lpRect, NULL, RDW_VALIDATE );
|
|
}
|
|
return NtUserRedrawWindow(hWnd, lpRect, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ERASENOW|RDW_ALLCHILDREN);
|
|
}
|
|
|
|
/* EOF */
|