Windows2000/private/ntos/w32/ntuser/client/winmgrc.c
2020-09-30 17:12:32 +02:00

1481 lines
36 KiB
C

/****************************** Module Header ******************************\
* Module Name: winmgrc.c
* Copyright (c) 1985 - 1999, Microsoft Corporation
* This module contains
* History:
* 20-Feb-1992 DarrinM Pulled functions from user\server.
*/
#include "precomp.h"
#pragma hdrstop
#define CONSOLE_WINDOW_CLASS (L"ConsoleWindowClass")
/*
* GetWindowWord (API)
* Return a window word. Positive index values return application window words
* while negative index values return system window words. The negative
* indices are published in WINDOWS.H.
* History:
* 20-Feb-1992 DarrinM Wrote.
*/
WORD GetWindowWord(
HWND hwnd,
int index)
{
PWND pwnd;
pwnd = ValidateHwnd(hwnd);
if (pwnd == NULL)
return 0;
/*
* If it's a dialog window the window data is on the server side
* We just call the "long" routine instead of have two thunks.
* We know there is enough data if its DWLP_USER so we won't fault.
*/
if (TestWF(pwnd, WFDIALOGWINDOW) && (index == DWLP_USER)) {
return (WORD)_GetWindowLong(pwnd, index, FALSE);
}
return _GetWindowWord(pwnd, index);
}
BOOL FChildVisible(
HWND hwnd)
{
PWND pwnd;
pwnd = ValidateHwnd(hwnd);
if (pwnd == NULL)
return 0;
return (_FChildVisible(pwnd));
}
BOOL WINAPI AdjustWindowRectEx(
LPRECT lpRect,
DWORD dwStyle,
BOOL bMenu,
DWORD dwExStyle)
{
ConnectIfNecessary();
return _AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle);
}
int WINAPI GetClassNameW(
HWND hwnd,
LPWSTR lpClassName,
int nMaxCount)
{
UNICODE_STRING strClassName;
strClassName.MaximumLength = (USHORT)(nMaxCount * sizeof(WCHAR));
strClassName.Buffer = lpClassName;
return NtUserGetClassName(hwnd, FALSE, &strClassName);
}
HWND GetFocus(VOID)
{
return (HWND)NtUserGetThreadState(UserThreadStateFocusWindow);
}
HWND GetCapture(VOID)
{
/*
* If no captures are currently taking place, just return NULL.
*/
if (gpsi->cCaptures == 0) {
return NULL;
}
return (HWND)NtUserGetThreadState(UserThreadStateCaptureWindow);
}
/*
* AnyPopup (API)
* History:
* 12-Nov-1990 DarrinM Ported.
*/
BOOL AnyPopup(VOID)
{
PWND pwnd = _GetDesktopWindow();
for (pwnd = REBASEPWND(pwnd, spwndChild); pwnd; pwnd = REBASEPWND(pwnd, spwndNext)) {
if ((pwnd->spwndOwner != NULL) && TestWF(pwnd, WFVISIBLE))
return TRUE;
}
return FALSE;
}
/*
* GetInputState
* History:
*/
BOOL GetInputState(VOID)
{
PCLIENTTHREADINFO pcti = GetClientInfo()->pClientThreadInfo;
if ((pcti == NULL) || (pcti->fsChangeBits & (QS_MOUSEBUTTON | QS_KEY)))
return (BOOL)NtUserGetThreadState(UserThreadStateInputState);
return FALSE;
}
/*
* MapWindowPoints
* History:
*/
int MapWindowPoints(
HWND hwndFrom,
HWND hwndTo,
LPPOINT lppt,
UINT cpt)
{
PWND pwndFrom;
PWND pwndTo;
if (hwndFrom != NULL) {
if ((pwndFrom = ValidateHwnd(hwndFrom)) == NULL)
return 0;
} else {
pwndFrom = NULL;
}
if (hwndTo != NULL) {
if ((pwndTo = ValidateHwnd(hwndTo)) == NULL)
return 0;
} else {
pwndTo = NULL;
}
return _MapWindowPoints(pwndFrom, pwndTo, lppt, cpt);
}
/*
* GetLastActivePopup
* History:
*/
HWND GetLastActivePopup(
HWND hwnd)
{
PWND pwnd = ValidateHwnd(hwnd);
if (pwnd == NULL)
return NULL;
pwnd = _GetLastActivePopup(pwnd);
return HW(pwnd);
}
/*
* PtiWindow
* Gets the PTHREADINFO of window or NULL if not valid.
* 12-Feb-1997 JerrySh Created.
*/
PTHREADINFO PtiWindow(
HWND hwnd)
{
PHE phe;
DWORD dw;
WORD uniq;
dw = HMIndexFromHandle(hwnd);
if (dw < gpsi->cHandleEntries) {
phe = &gSharedInfo.aheList[dw];
if ((phe->bType == TYPE_WINDOW) && !(phe->bFlags & HANDLEF_DESTROY)) {
uniq = HMUniqFromHandle(hwnd);
if ( uniq == phe->wUniq
#if !defined(_WIN64) && !defined(BUILD_WOW6432)
|| uniq == 0
|| uniq == HMUNIQBITS
#endif
) {
return phe->pOwner;
}
}
}
UserSetLastError(ERROR_INVALID_WINDOW_HANDLE);
return NULL;
}
/*
* GetWindowThreadProcessId
* Get's windows process and thread ids.
* 24-Jun-1991 ScottLu Created.
*/
DWORD GetWindowThreadProcessId(
HWND hwnd,
LPDWORD lpdwProcessId)
{
PTHREADINFO ptiWindow;
DWORD dwThreadId;
if ((ptiWindow = PtiWindow(hwnd)) == NULL)
return 0;
/*
* For non-system threads get the info from the thread info structure
*/
if (ptiWindow == PtiCurrent()) {
if (lpdwProcessId != NULL)
*lpdwProcessId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess);
dwThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread);
} else {
/*
* Make this better later on.
*/
if (lpdwProcessId != NULL)
*lpdwProcessId = HandleToUlong(NtUserQueryWindow(hwnd, WindowProcess));
dwThreadId = HandleToUlong(NtUserQueryWindow(hwnd, WindowThread));
}
return dwThreadId;
}
/*
* GetScrollPos
* Returns the current position of a scroll bar
* !!! WARNING a similiar copy of this code is in server\sbapi.c
* History:
*/
int GetScrollPos(
HWND hwnd,
int code)
{
PWND pwnd;
if ((pwnd = ValidateHwnd(hwnd)) == NULL)
return 0;
switch (code) {
case SB_CTL:
return (int)SendMessageWorker(pwnd, SBM_GETPOS, 0, 0, FALSE);
case SB_HORZ:
case SB_VERT:
if (pwnd->pSBInfo != NULL) {
PSBINFO pSBInfo = (PSBINFO)(REBASEALWAYS(pwnd, pSBInfo));
return (code == SB_VERT) ? pSBInfo->Vert.pos : pSBInfo->Horz.pos;
} else {
RIPERR0(ERROR_NO_SCROLLBARS, RIP_VERBOSE, "");
}
break;
default:
/*
* Win3.1 validation layer code.
*/
RIPERR0(ERROR_INVALID_PARAMETER, RIP_VERBOSE, "");
}
return 0;
}
/*
* GetScrollRange
* !!! WARNING a similiar copy of this code is in server\sbapi.c
* History:
* 16-May-1991 mikeke Changed to return BOOL
*/
BOOL GetScrollRange(
HWND hwnd,
int code,
LPINT lpposMin,
LPINT lpposMax)
{
PSBINFO pSBInfo;
PWND pwnd;
if ((pwnd = ValidateHwnd(hwnd)) == NULL)
return FALSE;
switch (code) {
case SB_CTL:
SendMessageWorker(pwnd, SBM_GETRANGE, (WPARAM)lpposMin, (LPARAM)lpposMax, FALSE);
return TRUE;
case SB_VERT:
case SB_HORZ:
if (pSBInfo = REBASE(pwnd, pSBInfo)) {
PSBDATA pSBData;
pSBData = (code == SB_VERT) ? &pSBInfo->Vert : &pSBInfo->Horz;
*lpposMin = pSBData->posMin;
*lpposMax = pSBData->posMax;
} else {
RIPERR0(ERROR_NO_SCROLLBARS, RIP_VERBOSE, "");
*lpposMin = 0;
*lpposMax = 0;
}
return TRUE;
default:
/*
* Win3.1 validation layer code.
*/
RIPERR0(ERROR_INVALID_PARAMETER, RIP_VERBOSE, "");
return FALSE;
}
}
/*
* GetScrollInfo
* !!! WARNING a similiar copy of this code is in server\winmgrc.c
*/
BOOL GetScrollInfo(
HWND hwnd,
int code,
LPSCROLLINFO lpsi)
{
PWND pwnd;
PSBINFO pSBInfo;
PSBDATA pSBData;
if (lpsi->cbSize != sizeof(SCROLLINFO)) {
if (lpsi->cbSize != sizeof(SCROLLINFO) - 4) {
RIPMSG0(RIP_WARNING, "SCROLLINFO: Invalid cbSize");
return FALSE;
} else {
RIPMSG0(RIP_WARNING, "SCROLLINFO: Invalid cbSize");
}
}
if (lpsi->fMask & ~SIF_MASK) {
RIPMSG0(RIP_WARNING, "SCROLLINFO: Invalid fMask");
return FALSE;
}
if ((pwnd = ValidateHwnd(hwnd)) == NULL)
return FALSE;
switch (code) {
case SB_CTL:
SendMessageWorker(pwnd, SBM_GETSCROLLINFO, 0, (LPARAM)lpsi, FALSE);
return TRUE;
case SB_HORZ:
case SB_VERT:
if (pwnd->pSBInfo == NULL) {
RIPERR0(ERROR_NO_SCROLLBARS, RIP_VERBOSE, "");
return FALSE;
}
/*
* Rebase rgwScroll so probing will work
*/
pSBInfo = (PSBINFO)REBASEALWAYS(pwnd, pSBInfo);
pSBData = (code == SB_VERT) ? &pSBInfo->Vert : &pSBInfo->Horz;
return(NtUserSBGetParms(hwnd, code, pSBData, lpsi));
default:
/*
* Win3.1 validation layer code.
*/
RIPERR0(ERROR_INVALID_PARAMETER, RIP_VERBOSE, "");
return FALSE;
}
}
/*
* _GetActiveWindow (API)
* 23-Oct-1990 MikeHar Ported from Windows.
* 12-Nov-1990 DarrinM Moved from getset.c to here.
*/
HWND GetActiveWindow(VOID)
{
return (HWND)NtUserGetThreadState(UserThreadStateActiveWindow);
}
/*
* GetCursor
* History:
*/
HCURSOR GetCursor(VOID)
{
return (HCURSOR)NtUserGetThreadState(UserThreadStateCursor);
}
/*
* BOOL IsMenu(HMENU);
* Verifies that the handle passed in is a menu handle.
* Histroy:
* 10-Jul-1992 MikeHar Created.
*/
BOOL IsMenu(
HMENU hMenu)
{
if (HMValidateHandle(hMenu, TYPE_MENU))
return TRUE;
return FALSE;
}
/*
* GetAppCompatFlags
* Compatibility flags for < Win 3.1 apps running on 3.1
* History:
* 01-Apr-1992 ScottLu Created.
* 04-May-1992 DarrinM Moved to USERRTL.DLL.
*/
DWORD GetAppCompatFlags(
PTHREADINFO pti)
{
UNREFERENCED_PARAMETER(pti);
ConnectIfNecessary();
return GetClientInfo()->dwCompatFlags;
}
/*
* GetAppCompatFlags2
* Compatibility flags for <= wVer apps. Newer apps will get no hacks
* from this DWORD.
* History:
* 06-29-98 MCostea Created.
*/
DWORD GetAppCompatFlags2(
WORD wVer)
{
ConnectIfNecessary();
/*
* Newer apps should behave, so they get no hacks
*/
if (wVer < GETAPPVER()) {
return 0;
}
return GetClientInfo()->dwCompatFlags2;
}
/*
* IsWindowUnicode
* 25-Feb-1992 IanJa Created
*/
BOOL IsWindowUnicode(
IN HWND hwnd)
{
PWND pwnd;
if ((pwnd = ValidateHwnd(hwnd)) == NULL)
return FALSE;
return !TestWF(pwnd, WFANSIPROC);
}
/*
* TestWindowProcess
* 14-Nov-1994 JimA Created.
*/
BOOL TestWindowProcess(
PWND pwnd)
{
/*
* If the threads are the same, don't bother going to the kernel
* to get the window's process id.
*/
if (GETPTI(pwnd) == PtiCurrent()) {
return TRUE;
}
return (GetWindowProcess(HW(pwnd)) == GETPROCESSID());
}
/*
* IsHungAppWindow
* 11-14-94 JimA Created.
*/
BOOL IsHungAppWindow(
HWND hwnd)
{
return (NtUserQueryWindow(hwnd, WindowIsHung) != NULL);
}
/*
* PtiCurrent
* Returns the THREADINFO structure for the current thread.
* LATER: Get DLL_THREAD_ATTACH initialization working right and we won't
* need this connect code.
* History:
* 10-28-90 DavidPe Created.
*/
PTHREADINFO PtiCurrent(VOID)
{
ConnectIfNecessary();
return (PTHREADINFO)NtCurrentTebShared()->Win32ThreadInfo;
}
/*
* _AdjustWindowRectEx (API)
* History:
* 10-24-90 darrinm Ported from Win 3.0.
*/
BOOL _AdjustWindowRectEx(
LPRECT lprc,
LONG style,
BOOL fMenu,
DWORD dwExStyle)
{
// Here we add on the appropriate 3D borders for old and new apps.
// Rules:
// (1) Do nothing for windows that have 3D border styles.
// (2) If the window has a dlgframe border (has a caption or is a
// a dialog), then add on the window edge style.
// (3) We NEVER add on the CLIENT STYLE. New apps can create
// it if they want. This is because it screws up alignment
// when the app doesn't know about it.
if (NeedsWindowEdge(style, dwExStyle, GETAPPVER() >= VER40))
dwExStyle |= WS_EX_WINDOWEDGE;
else
dwExStyle &= ~WS_EX_WINDOWEDGE;
// Space for a menu bar
if (fMenu)
lprc->top -= SYSMET(CYMENU);
// Space for a caption bar
if ((HIWORD(style) & HIWORD(WS_CAPTION)) == HIWORD(WS_CAPTION)) {
lprc->top -= (dwExStyle & WS_EX_TOOLWINDOW) ? SYSMET(CYSMCAPTION) : SYSMET(CYCAPTION);
}
// Space for borders (window AND client)
{
int cBorders;
// Window AND Client borders
if (cBorders = GetWindowBorders(style, dwExStyle, TRUE, TRUE))
InflateRect(lprc, cBorders*SYSMET(CXBORDER), cBorders*SYSMET(CYBORDER));
}
return TRUE;
}
/*
* ShowWindowNoRepaint
*/
void ShowWindowNoRepaint(PWND pwnd)
{
HWND hwnd = HWq(pwnd);
PCLS pcls = REBASE(pwnd, pcls);
NtUserSetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE |
SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER |
SWP_NOREDRAW | SWP_SHOWWINDOW | SWP_NOACTIVATE |
((pcls->style & CS_SAVEBITS) ? SWP_CREATESPB : 0));
}
/*
* AnimateBlend
* 6-Mar-1997 vadimg created
*/
#define ALPHASTART 40
#define ONEFRAME 10
BOOL AnimateBlend(PWND pwnd, HDC hdcScreen, HDC hdcImage, DWORD dwTime, BOOL fHide)
{
HWND hwnd = HWq(pwnd);
SIZE size;
POINT ptSrc = {0, 0}, ptDst;
BLENDFUNCTION blend;
DWORD dwElapsed;
BYTE bAlpha = ALPHASTART;
LARGE_INTEGER liFreq, liStart, liDiff;
LARGE_INTEGER liIter;
DWORD dwIter;
if (QueryPerformanceFrequency(&liFreq) == 0)
return FALSE;
if (SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) |
WS_EX_LAYERED) == 0)
return FALSE;
if (fHide) {
/*
* Give up the time slice and sleep just a touch to allow windows
* below invalidated by the SetWindowLong(WS_EX_LAYERED) call to
* repaint enough for the sprite to get good background image.
*/
Sleep(10);
}
ptDst.x = pwnd->rcWindow.left;
ptDst.y = pwnd->rcWindow.top;
size.cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
size.cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.AlphaFormat = 0;
blend.SourceConstantAlpha = fHide ? (255 - bAlpha) : bAlpha;
/*
* Copy the initial image with the initial alpha.
*/
NtUserUpdateLayeredWindow(hwnd, NULL, &ptDst, &size, hdcImage, &ptSrc, 0,
&blend, ULW_ALPHA);
if (!fHide) {
ShowWindowNoRepaint(pwnd);
}
/*
* Time and start the animation cycle.
*/
dwElapsed = (dwTime * ALPHASTART + 255) / 255 + 10;
QueryPerformanceCounter(&liStart);
liStart.QuadPart = liStart.QuadPart - dwElapsed * liFreq.QuadPart / 1000;
while (dwElapsed < dwTime) {
if (fHide) {
blend.SourceConstantAlpha = (BYTE)((255 * (dwTime - dwElapsed)) / dwTime);
} else {
blend.SourceConstantAlpha = (BYTE)((255 * dwElapsed) / dwTime);
}
QueryPerformanceCounter(&liIter);
NtUserUpdateLayeredWindow(hwnd, NULL, NULL, NULL, NULL, NULL, 0,
&blend, ULW_ALPHA);
QueryPerformanceCounter(&liDiff);
/*
* Calculate how long in ms the previous frame took.
*/
liIter.QuadPart = liDiff.QuadPart - liIter.QuadPart;
dwIter = (DWORD)((liIter.QuadPart * 1000) / liFreq.QuadPart);
if (dwIter < ONEFRAME) {
Sleep(ONEFRAME - dwIter);
}
liDiff.QuadPart -= liStart.QuadPart;
dwElapsed = (DWORD)((liDiff.QuadPart * 1000) / liFreq.QuadPart);
}
/*
* Hide the window before removing the layered bit to make sure that
* the bits for the window are not left on the screen.
*/
if (fHide) {
NtUserSetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW |
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) &
~WS_EX_LAYERED);
if (!fHide) {
BitBlt(hdcScreen, 0, 0, size.cx, size.cy, hdcImage, 0, 0, SRCCOPY | NOMIRRORBITMAP);
}
return TRUE;
}
/*
* AnimateWindow (API)
* Hide animations are done by updating a la full-drag. Uses window's window
* region to do some of the magic.
* We have to put in the CLIPCHILDREN hack to work around a bug with the
* DC cache resetting attributes even if DCX_USESTYLE is not used whe
* the DC cache is invalidated.
* History:
* 9-Sep-1996 vadimg created
*/
#define AW_HOR (AW_HOR_POSITIVE | AW_HOR_NEGATIVE | AW_CENTER)
#define AW_VER (AW_VER_POSITIVE | AW_VER_NEGATIVE | AW_CENTER)
__inline int AnimInc(int x, int y, int z)
{
return MultDiv(x, y, z);
}
__inline int AnimDec(int x, int y, int z)
{
return x - AnimInc(x, y, z);
}
BOOL WINAPI AnimateWindow(HWND hwnd, DWORD dwTime, DWORD dwFlags)
{
PTHREADINFO ptiCurrent = PtiCurrent();
HDC hdc = NULL, hdcMem = NULL;
HRGN hrgnUpdate = NULL, hrgnOld = NULL, hrgnWin;
HBITMAP hbmMem = NULL, hbmOld;
BOOL fHide = dwFlags & AW_HIDE, fRet = FALSE, fSlide = dwFlags & AW_SLIDE;
BOOL fRemoveClipChildren = FALSE;
int x, y, nx, ny, cx, cy, ix, iy, ixLast, iyLast, xLast, yLast, xWin, yWin;
int xReal, yReal, xMem, yMem;
DWORD dwStart, dwElapsed;
RECT rcOld, rcNew, rcWin;
PWND pwnd = ValidateHwnd(hwnd);
UINT uBounds;
RECT rcBounds;
#ifdef USE_MIRRORING
DWORD dwOldlayout = GDI_ERROR;
#endif
if (pwnd == NULL)
return FALSE;
/*
* Nothing to do or the flags didn't validate. Send the jerk to hell.
*/
if ((dwFlags & ~AW_VALID) != 0 || (dwFlags & (AW_HOR_POSITIVE |
AW_HOR_NEGATIVE | AW_CENTER | AW_VER_POSITIVE |
AW_VER_NEGATIVE | AW_BLEND)) == 0)
return FALSE;
if (!(dwFlags & AW_BLEND)) {
if (pwnd->hrgnClip != NULL) {
return FALSE;
}
}
/*
* If already hidden and tring to hide, just bail out of here.
* (or already shown and trying to show...)
*/
if (!IsWindowVisible(hwnd)) {
if (fHide) {
return FALSE;
}
} else {
if (!fHide) {
return FALSE;
}
}
if ((hdc = GetDCEx(hwnd, NULL, DCX_WINDOW | DCX_USESTYLE |
DCX_CACHE)) == NULL) {
return FALSE;
}
if (TestWF(pwnd, WFCLIPCHILDREN)) {
fRemoveClipChildren = TRUE;
ClearWindowState(pwnd, WFCLIPCHILDREN);
}
/*
* Precreate regions used for calculating paint updates.
*/
if (fHide) {
if ((hrgnUpdate = CreateRectRgn(0, 0, 0, 0)) == NULL) {
goto Cleanup;
}
if ((hrgnOld = CreateRectRgn(0, 0, 0, 0)) == NULL) {
goto Cleanup;
}
}
rcWin = pwnd->rcWindow;
cx = rcWin.right - rcWin.left;
cy = rcWin.bottom - rcWin.top;
/*
* Set up the offscreen dc.
*/
if ((hbmMem = CreateCompatibleBitmap(hdc, cx, cy | CCB_NOVIDEOMEMORY)) == NULL) {
goto Cleanup;
}
if ((hdcMem = CreateCompatibleDC(hdc)) == NULL) {
goto Cleanup;
}
#ifdef USE_MIRRORING
/*
* Turn off Mirroring.
*/
dwOldlayout = SetLayout(hdcMem, 0);
#endif
hbmOld = SelectBitmap(hdcMem, hbmMem);
if (!(dwFlags & AW_BLEND)) {
/*
* Set window region to nothing, so that if the window draws during
* callbacks in WM_PRINT, it doesn't happen on screen.
*/
if ((hrgnWin = CreateRectRgn(0, 0, 0, 0)) != NULL) {
SetWindowRgn(hwnd, hrgnWin, FALSE);
}
if (!fHide) {
ShowWindowNoRepaint(pwnd);
}
}
SetBoundsRect(hdcMem, NULL, DCB_RESET | DCB_ENABLE);
/*
* Get the actual image. The windows participating here must implement
* WM_PRINTCLIENT or they will look ugly.
*/
SendMessage(hwnd, WM_PRINT, (WPARAM)hdcMem, PRF_CLIENT | PRF_NONCLIENT |
PRF_CHILDREN | PRF_ERASEBKGND);
/*
* If the window changes size during callbacks, like RAID does with combo
* boxes by resizing them on WM_CTLCOLOR from WM_ERASEBKGND, send WM_PRINT
* again to get the correctly sized image.
*/
if (!EqualRect(&rcWin, &pwnd->rcWindow)) {
rcWin = pwnd->rcWindow;
cx = rcWin.right - rcWin.left;
cy = rcWin.bottom - rcWin.top;
SelectObject(hdcMem, hbmOld);
DeleteObject(hbmMem);
if ((hbmMem = CreateCompatibleBitmap(hdc, cx, cy)) == NULL) {
goto Cleanup;
}
SelectObject(hdcMem, hbmMem);
SendMessage(hwnd, WM_PRINT, (WPARAM)hdcMem, PRF_CLIENT |
PRF_NONCLIENT | PRF_CHILDREN | PRF_ERASEBKGND);
}
/*
* Check to see if the app painted in our DC. If not, do not animate the window
* SetBoundsRect() was called prior to SendMessage(WM_PRINT) for the hdcMem
* and in xxxLBPaint() (to fix bug#83743)
*/
uBounds = GetBoundsRect(hdcMem, &rcBounds, 0);
if ((uBounds & DCB_RESET) && (!(uBounds & DCB_ACCUMULATE))) {
if (fHide) {
NtUserShowWindow(hwnd, SW_HIDE);
} else {
RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME |
RDW_ALLCHILDREN);
}
goto Cleanup;
}
if (dwTime == 0) {
dwTime = CMS_QANIMATION;
}
if (dwFlags & AW_BLEND) {
fRet = AnimateBlend(pwnd, hdc, hdcMem, dwTime, fHide);
goto ShowActivate;
}
xWin = rcWin.left;
yWin = rcWin.top;
ix = iy = xLast = yLast = 0;
ixLast = cx;
iyLast = cy;
/*
* Calculate initial coordinates and multiples. x, y are the starting points
* for xReal, yReal calcs, nx, and ny are the directional multiples.
*/
if (dwFlags & AW_CENTER) {
x = cx / 2;
nx = -1;
fSlide = FALSE;
} else if (dwFlags & AW_HOR_POSITIVE) {
x = fHide ? cx : 0;
nx = fHide ? -1 : 0;
} else if (dwFlags & AW_HOR_NEGATIVE) {
x = fHide ? 0 : cx;
nx = fHide ? 0 : -1;
} else {
x = 0;
nx = 0;
ix = cx;
}
if (dwFlags & AW_CENTER) {
y = cy / 2;
ny = -1;
} else if (dwFlags & AW_VER_POSITIVE) {
y = fHide ? cy : 0;
ny = fHide ? -1 : 0;
} else if (dwFlags & AW_VER_NEGATIVE) {
y = fHide ? 0 : cy;
ny = fHide ? 0 : -1;
} else {
y = 0;
ny = 0;
iy = cy;
}
dwStart = GetTickCount();
while (TRUE) {
dwElapsed = GetTickCount() - dwStart;
if (dwFlags & AW_HOR) {
if (fHide) {
ix = AnimDec(cx, dwElapsed, dwTime);
} else {
ix = AnimInc(cx, dwElapsed, dwTime);
}
}
if (dwFlags & AW_VER) {
if (fHide) {
iy = AnimDec(cy, dwElapsed, dwTime);
} else {
iy = AnimInc(cy, dwElapsed, dwTime);
}
}
/*
* Terminate when we are out of time or we're hiding and either
* dimenion has reached zero (i.e. the window is not visible) or
* we're showing and the window is completely visible.
*/
if (dwElapsed > dwTime ||
(fHide && (ix == 0 || iy == 0)) ||
(!fHide && (ix == cx && iy == cy))) {
break;
} else if (ixLast == ix && iyLast == iy) {
Sleep(1);
} else {
if (dwFlags & AW_CENTER) {
xReal = x + nx * (ix / 2);
yReal = y + ny * (iy / 2);
} else {
xReal = x + nx * ix;
yReal = y + ny * iy;
}
/*
* Calculate new window area and set as the window rgn.
*/
rcNew.left = xReal;
rcNew.top = yReal;
rcNew.right = rcNew.left + ix;
rcNew.bottom = rcNew.top + iy;
/*
* Change the window region accordingly to the new rect to make
* sure that everything underneath paints properly. If a region
* was selected in before, it will be destroyed in SetWindowRgn.
*/
if ((hrgnWin = CreateRectRgnIndirect(&rcNew)) != NULL) {
SetWindowRgn(hwnd, hrgnWin, FALSE);
}
if (fHide) {
/*
* Calculate the smallest possible update region.
*/
rcOld.left = xLast;
rcOld.top = yLast;
rcOld.right = rcOld.left + ixLast;
rcOld.bottom = rcOld.top + iyLast;
SetRectRgn(hrgnOld, rcOld.left, rcOld.top, rcOld.right, rcOld.bottom);
SetRectRgn(hrgnUpdate, rcNew.left, rcNew.top, rcNew.right, rcNew.bottom);
CombineRgn(hrgnUpdate, hrgnOld, hrgnUpdate, RGN_DIFF);
OffsetRgn(hrgnUpdate, xWin, yWin);
RedrawWindow(NULL, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME |
RDW_ALLCHILDREN);
NtUserCallHwndParamLock(hwnd, (ULONG_PTR)hrgnUpdate, SFI_XXXUPDATEWINDOWS);
}
xMem = xReal;
yMem = yReal;
if (fSlide) {
if (dwFlags & AW_HOR_POSITIVE) {
xMem = fHide ? 0: cx - ix;
} else if (dwFlags & AW_HOR_NEGATIVE) {
xMem = fHide ? cx - ix : 0;
}
if (dwFlags & AW_VER_POSITIVE) {
yMem = fHide ? 0 : cy - iy;
} else if (dwFlags & AW_VER_NEGATIVE) {
yMem = fHide ? cy - iy : 0;
}
}
BitBlt(hdc, xReal, yReal, ix, iy, hdcMem, xMem, yMem, SRCCOPY | NOMIRRORBITMAP);
xLast = xReal;
yLast = yReal;
ixLast = ix;
iyLast = iy;
}
}
fRet = TRUE;
/*
* One last update to make sure that everything is repainted properly.
*/
if (fHide) {
if (fRemoveClipChildren) {
SetWindowState(pwnd, WFCLIPCHILDREN);
fRemoveClipChildren = FALSE;
}
NtUserSetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE |
SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER |
SWP_NOREDRAW | SWP_HIDEWINDOW |
(dwFlags & AW_ACTIVATE ? 0 : SWP_NOACTIVATE));
/*
* This way we won't get a flash, which we would if we allowed to draw
* in the call above.
*/
if (ixLast != 0 && iyLast != 0) {
rcWin.left = xLast + xWin;
rcWin.top = yLast + yWin;
rcWin.right = rcWin.left + ixLast;
rcWin.bottom = rcWin.top + iyLast;
SetRectRgn(hrgnUpdate, rcWin.left, rcWin.top, rcWin.right, rcWin.bottom);
RedrawWindow(NULL, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE |
RDW_FRAME);
NtUserCallHwndParamLock(hwnd, (ULONG_PTR)hrgnUpdate, SFI_XXXUPDATEWINDOWS);
}
} else {
if (ixLast != cx || iyLast != cy) {
if ((hrgnWin = CreateRectRgn(0, 0, cx, cy)) != NULL) {
SetWindowRgn(hwnd, hrgnWin, FALSE);
}
BitBlt(hdc, 0, 0, cx, cy, hdcMem, 0, 0, SRCCOPY | NOMIRRORBITMAP);
}
if (fRemoveClipChildren) {
SetWindowState(pwnd, WFCLIPCHILDREN);
fRemoveClipChildren = FALSE;
}
ShowActivate:
if (dwFlags & AW_ACTIVATE) {
NtUserSetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE |
SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
}
}
Cleanup:
if (fRemoveClipChildren) {
SetWindowState(pwnd, WFCLIPCHILDREN);
}
if (hdcMem != NULL) {
#ifdef USE_MIRRORING
/*
* Restore old layout value.
*/
if (dwOldlayout != GDI_ERROR)
SetLayout(hdcMem, dwOldlayout);
#endif
DeleteDC(hdcMem);
}
if (hbmMem != NULL) {
DeleteObject(hbmMem);
}
if (hdc != NULL) {
ReleaseDC(hwnd, hdc);
}
if (fHide) {
if (hrgnUpdate != NULL) {
DeleteObject(hrgnUpdate);
}
if (hrgnOld != NULL) {
DeleteObject(hrgnOld);
}
}
if (!(dwFlags & AW_BLEND)) {
SetWindowRgn(hwnd, NULL, FALSE);
}
return fRet;
}
/*
* SmoothScrollWindowEx
* History:
* 24-Sep-1996 vadimg wrote
*/
#define MINSCROLL 10
#define MAXSCROLLTIME 200
int SmoothScrollWindowEx(HWND hwnd, int dx, int dy, CONST RECT *prcScroll,
CONST RECT *prcClip, HRGN hrgnUpdate, LPRECT prcUpdate, DWORD dwFlags,
DWORD dwTime)
{
RECT rc, rcT, rcUpdate;
int dxStep, dyStep, dxDone, dyDone, xSrc, ySrc, xDst, yDst, dxBlt, dyBlt;
int nRet = ERROR, nClip;
BOOL fNegX = FALSE, fNegY = FALSE;
HDC hdc, hdcMem = NULL;
HBITMAP hbmMem = NULL, hbmOld;
DWORD dwSleep;
BOOL fCalcSubscroll = FALSE;
PWND pwnd = ValidateHwnd(hwnd);
HRGN hrgnScroll = NULL, hrgnErase = NULL;
MSG msg;
UINT uBounds;
RECT rcBounds;
if (pwnd == NULL)
return ERROR;
/*
* Keep track of the signs so we don't have to mess with abs all the time.
*/
if (dx < 0) {
fNegX = TRUE;
dx = -dx;
}
if (dy < 0) {
fNegY = TRUE;
dy = -dy;
}
/*
* Set up the client rectangle.
*/
if (prcScroll != NULL) {
rc = *prcScroll;
} else {
rc.left = rc.top = 0;
rc.right = pwnd->rcClient.right - pwnd->rcClient.left;
rc.bottom = pwnd->rcClient.bottom - pwnd->rcClient.top;
}
/*
* If they want to scroll less than we can let them, or more than
* one page, or need repainting send them to the API.
*/
if (pwnd->hrgnUpdate != NULL || (dx == 0 && dy == 0) ||
(dx != 0 && dx > rc.right) ||
(dy != 0 && dy > rc.bottom)) {
return NtUserScrollWindowEx(hwnd, fNegX ? -dx : dx, fNegY ? -dy : dy,
prcScroll, prcClip, hrgnUpdate, prcUpdate,
dwFlags | SW_ERASE | SW_INVALIDATE);
}
if ((hdc = GetDCEx(hwnd, NULL, DCX_USESTYLE | DCX_CACHE)) == NULL) {
return ERROR;
}
/*
* Part of the window may be obscured, which means that more may be
* invisible and may need to be bltted. Take that into account by
* gettting the clip box.
*/
nClip = GetClipBox(hdc, &rcT);
if (nClip == ERROR || nClip == NULLREGION) {
goto Cleanup;
}
/*
* Set up the offscreen dc and send WM_PRINT to get the image.
*/
if ((hbmMem = CreateCompatibleBitmap(hdc, rc.right, rc.bottom)) == NULL) {
goto Cleanup;
}
if ((hdcMem = CreateCompatibleDC(hdc)) == NULL) {
goto Cleanup;
}
hbmOld = SelectBitmap(hdcMem, hbmMem);
SetBoundsRect(hdcMem, NULL, DCB_RESET | DCB_ENABLE);
SendMessage(hwnd, WM_PRINT, (WPARAM)hdcMem, PRF_CLIENT |
PRF_ERASEBKGND | ((dwFlags & SW_SCROLLCHILDREN) ? PRF_CHILDREN : 0));
/*
* If the client rect changes during the callback, send WM_PRINT
* again to get the correctly sized image.
*/
if (prcScroll == NULL) {
rcT.left = rcT.top = 0;
rcT.right = pwnd->rcClient.right - pwnd->rcClient.left;
rcT.bottom = pwnd->rcClient.bottom - pwnd->rcClient.top;
if (!EqualRect(&rc, &rcT)) {
rc = rcT;
SelectObject(hdcMem, hbmOld);
DeleteObject(hbmMem);
if ((hbmMem = CreateCompatibleBitmap(hdc, rc.right, rc.bottom)) == NULL) {
goto Cleanup;
}
SelectObject(hdcMem, hbmMem);
SendMessage(hwnd, WM_PRINT, (WPARAM)hdcMem, PRF_CLIENT |
PRF_ERASEBKGND | ((dwFlags & SW_SCROLLCHILDREN) ? PRF_CHILDREN : 0));
}
}
/*
* Check to see if the app painted in our DC.
*/
uBounds = GetBoundsRect(hdcMem, &rcBounds, 0);
if ((uBounds & DCB_RESET) && (!(uBounds & DCB_ACCUMULATE))) {
goto Cleanup;
}
if ((hrgnScroll = CreateRectRgn(0, 0, 0, 0)) == NULL) {
goto Cleanup;
}
if ((hrgnErase = CreateRectRgn(0, 0, 0, 0)) == NULL) {
goto Cleanup;
}
SetRectEmpty(&rcUpdate);
/*
* Start off with MINSCROLL and adjust it based on available time after
* the first iteration. We should consider adding a NOTIMELIMIT flag.
*/
xDst = xSrc = 0;
yDst = ySrc = 0;
dxBlt = rc.right;
dyBlt = rc.bottom;
if (dx == 0) {
dxDone = rc.right;
dxStep = 0;
} else {
dxDone = 0;
dxStep = max(dx / MINSCROLL, 1);
}
if (dy == 0) {
dyDone = rc.bottom;
dyStep = 0;
} else {
dyDone = 0;
dyStep = max(dy / MINSCROLL, 1);
}
if (dwTime == 0) {
dwTime = MAXSCROLLTIME;
}
dwSleep = dwTime / MINSCROLL;
do {
/*
* When the dc is scrolled, the part that's revealed cannot be
* updated properly. We set up the variables to blt just the part that
* was just uncovered.
*/
if (dx != 0) {
if (dxDone + dxStep > dx) {
dxStep = dx - dxDone;
}
dxDone += dxStep;
xDst = dx - dxDone;
dxBlt = rc.right - xDst;
if (!fNegX) {
xSrc = xDst;
xDst = 0;
}
}
if (dy != 0) {
if (dyDone + dyStep > dy) {
dyStep = dy - dyDone;
}
dyDone += dyStep;
yDst = dy - dyDone;
dyBlt = rc.bottom - yDst;
if (!fNegY) {
ySrc = yDst;
yDst = 0;
}
}
/*
* This is a hack for ReaderMode to be smoothly continuous. We'll make an
* attempt for the scrolling to take as close to dwTime
* as possible. We'll also dispatch MOUSEMOVEs to the ReaderMode window, so it
* can update mouse cursor.
*/
if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwSleep, QS_MOUSEMOVE) == WAIT_OBJECT_0) {
if (PeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, MAKELONG(PM_NOREMOVE, QS_INPUT))) {
PWND pwndPeek = ValidateHwnd(msg.hwnd);
if (pwndPeek != NULL) {
PCLS pcls = (PCLS)REBASEALWAYS(pwndPeek, pcls);
if (pcls->atomClassName == gatomReaderMode) {
if (PeekMessage(&msg, msg.hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, MAKELONG(PM_REMOVE, QS_INPUT))) {
DispatchMessage(&msg);
}
}
}
}
}
if ((nRet = NtUserScrollWindowEx(hwnd, fNegX ? -dxStep : dxStep, fNegY ? -dyStep : dyStep, prcScroll, prcClip, hrgnScroll, &rcT, dwFlags)) == ERROR)
goto Cleanup;
UnionRect(&rcUpdate, &rcUpdate, &rcT);
/*
* Blt the uncovered part.
*/
BitBlt(hdc, xDst, yDst, dxBlt, dyBlt, hdcMem, xSrc, ySrc, SRCCOPY | NOMIRRORBITMAP);
SetRectRgn(hrgnErase, xDst, yDst, xDst + dxBlt, yDst + dyBlt);
CombineRgn(hrgnErase, hrgnScroll, hrgnErase, RGN_DIFF);
RedrawWindow(hwnd, NULL, hrgnErase, RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW);
} while (dxDone < dx || dyDone < dy);
if (prcUpdate != NULL) {
*prcUpdate = rcUpdate;
}
if (hrgnUpdate != NULL) {
SetRectRgn(hrgnUpdate, rcUpdate.left, rcUpdate.top, rcUpdate.right, rcUpdate.bottom);
}
Cleanup:
if (hdcMem != NULL) {
DeleteDC(hdcMem);
}
if (hbmMem != NULL) {
DeleteObject(hbmMem);
}
if (hdc != NULL) {
ReleaseDC(hwnd, hdc);
}
if (hrgnErase != NULL) {
DeleteObject(hrgnErase);
}
if (hrgnScroll != NULL) {
DeleteObject(hrgnScroll);
}
return nRet;
}
/*
* ScrollWindowEx (API)
*/
int ScrollWindowEx(HWND hwnd, int dx, int dy, CONST RECT *prcScroll,
CONST RECT *prcClip, HRGN hrgnUpdate, LPRECT prcUpdate,
UINT dwFlags)
{
if (dwFlags & SW_SMOOTHSCROLL) {
return SmoothScrollWindowEx(hwnd, dx, dy, prcScroll, prcClip,
hrgnUpdate, prcUpdate, LOWORD(dwFlags), HIWORD(dwFlags));
} else {
return NtUserScrollWindowEx(hwnd, dx, dy, prcScroll, prcClip,
hrgnUpdate, prcUpdate, dwFlags);
}
}