Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2147 lines
65 KiB

/****************************** 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.
\***************************************************************************/
FUNCLOG2(LOG_GENERAL, WORD, DUMMYCALLINGTYPE, GetWindowWord, HWND, hwnd, int, index)
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));
}
FUNCLOG4(LOG_GENERAL, BOOL, WINAPI, AdjustWindowRectEx, LPRECT, lpRect, DWORD, dwStyle, BOOL, bMenu, DWORD, dwExStyle)
BOOL WINAPI AdjustWindowRectEx(
LPRECT lpRect,
DWORD dwStyle,
BOOL bMenu,
DWORD dwExStyle)
{
ConnectIfNecessary(0);
return _AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle);
}
FUNCLOG3(LOG_GENERAL, int, WINAPI, GetClassNameW, HWND, hwnd, LPWSTR, lpClassName, int, nMaxCount)
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)
{
CLIENTTHREADINFO *pcti = GETCLIENTTHREADINFO();
if ((pcti == NULL) || (pcti->fsChangeBits & (QS_MOUSEBUTTON | QS_KEY)))
return (BOOL)NtUserGetThreadState(UserThreadStateInputState);
return FALSE;
}
/***************************************************************************\
* MapWindowPoints
*
*
*
* History:
\***************************************************************************/
FUNCLOG4(LOG_GENERAL, int, DUMMYCALLINGTYPE, MapWindowPoints, HWND, hwndFrom, HWND, hwndTo, LPPOINT, lppt, UINT, cpt)
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:
\***************************************************************************/
FUNCLOG1(LOG_GENERAL, HWND, DUMMYCALLINGTYPE, GetLastActivePopup, HWND, hwnd)
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.
\***************************************************************************/
FUNCLOG2(LOG_GENERAL, DWORD, DUMMYCALLINGTYPE, GetWindowThreadProcessId, HWND, hwnd, LPDWORD, lpdwProcessId)
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:
\***************************************************************************/
FUNCLOG2(LOG_GENERAL, int, DUMMYCALLINGTYPE, GetScrollPos, HWND, hwnd, int, code)
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
\***************************************************************************/
FUNCLOG4(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, GetScrollRange, HWND, hwnd, int, code, LPINT, lpposMin, LPINT, lpposMax)
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 = KPSBDATA_TO_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;
}
}
FUNCLOG4(LOG_GENERAL, int, DUMMYCALLINGTYPE, SetScrollInfo, HWND, hwnd, int, fnBar, LPCSCROLLINFO, lpsi, BOOL, fRedraw)
int SetScrollInfo(
HWND hwnd,
int fnBar,
LPCSCROLLINFO lpsi,
BOOL fRedraw)
{
int ret;
BEGIN_USERAPIHOOK()
ret = guah.pfnSetScrollInfo(hwnd, fnBar, lpsi, fRedraw);
END_USERAPIHOOK()
return ret;
}
int RealSetScrollInfo(
HWND hwnd,
int fnBar,
LPCSCROLLINFO lpsi,
BOOL fRedraw)
{
return NtUserSetScrollInfo(hwnd, fnBar, lpsi, fRedraw);
}
FUNCLOG3(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, GetScrollInfo, HWND, hwnd, int, code, LPSCROLLINFO, lpsi)
BOOL GetScrollInfo(
HWND hwnd,
int code,
LPSCROLLINFO lpsi)
{
int ret;
BEGIN_USERAPIHOOK()
ret = guah.pfnGetScrollInfo(hwnd, code, lpsi);
END_USERAPIHOOK()
return ret;
}
/***************************************************************************\
* RealGetScrollInfo
*
* !!! WARNING a similiar copy of this code is in server\winmgrc.c
*
\***************************************************************************/
BOOL RealGetScrollInfo(
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 = KPSBDATA_TO_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.
\***************************************************************************/
FUNCLOG1(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, IsMenu, HMENU, hMenu)
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.
\***************************************************************************/
FUNCLOG1(LOG_GENERAL, DWORD, DUMMYCALLINGTYPE, GetAppCompatFlags, PTHREADINFO, pti)
DWORD GetAppCompatFlags(
PTHREADINFO pti)
{
UNREFERENCED_PARAMETER(pti);
ConnectIfNecessary(0);
return GetClientInfo()->dwCompatFlags;
}
/***************************************************************************\
* GetAppCompatFlags2
*
* Compatibility flags for <= wVer apps. Newer apps will get no hacks
* from this DWORD.
*
* History:
* 06-29-98 MCostea Created.
\***************************************************************************/
FUNCLOG1(LOG_GENERAL, DWORD, DUMMYCALLINGTYPE, GetAppCompatFlags2, WORD, wVer)
DWORD GetAppCompatFlags2(
WORD wVer)
{
ConnectIfNecessary(0);
/*
* 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.
\**************************************************************************/
FUNCLOG1(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, IsHungAppWindow, HWND, hwnd)
BOOL IsHungAppWindow(
HWND hwnd)
{
return (NtUserQueryWindow(hwnd, WindowIsHung) != NULL);
}
/***************************************************************************\
* CreateSystemThreads
*
* Simply calls xxxCreateSystemThreads, which will call the appropriate
* thread routine (depending on uThreadID).
*
* History:
* 20-Aug-00 MSadek Created.
\***************************************************************************/
WINUSERAPI
DWORD
WINAPI
CreateSystemThreads (
LPVOID pUnused)
{
UNREFERENCED_PARAMETER(pUnused);
NtUserCallOneParam(TRUE, SFI_XXXCREATESYSTEMTHREADS);
ExitThread(0);
}
/***************************************************************************\
* 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(0);
return (PTHREADINFO)NtCurrentTebShared()->Win32ThreadInfo;
}
/***************************************************************************\
* _AdjustWindowRectEx (API)
*
*
*
* History:
* 10-24-90 darrinm Ported from Win 3.0.
\***************************************************************************/
BOOL _AdjustWindowRectEx(
LPRECT lprc,
DWORD style,
BOOL fMenu,
DWORD dwExStyle)
{
BOOL ret;
BEGIN_USERAPIHOOK()
ret = guah.pfnAdjustWindowRectEx(lprc, style, fMenu, dwExStyle);
END_USERAPIHOOK()
return ret;
}
BOOL RealAdjustWindowRectEx(
LPRECT lprc,
DWORD 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, BOOL fActivateWindow)
{
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;
BOOL fFirstFrame = TRUE;
if (QueryPerformanceFrequency(&liFreq) == 0)
return FALSE;
SetLastError(0);
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
if (GetLastError() != 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);
if (fFirstFrame && fActivateWindow) {
NtUserSetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER);
}
fFirstFrame = FALSE;
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;
}
/***************************************************************************\
* TakeWindowSnapshot
*
* Helper routine to grab the visual appearance of a window to a bitmap.
*
\***************************************************************************/
HBITMAP TakeWindowSnapshot(HWND hwnd, HDC hdcWindow, HDC hdcSnapshot)
{
PWND pwnd;
int cx;
int cy;
HBITMAP hbmOld, hbmSnapshot;
BOOL fOK = FALSE;
pwnd = ValidateHwnd(hwnd);
if (pwnd == NULL)
return NULL;
cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
hbmSnapshot = CreateCompatibleBitmap(hdcWindow, cx, cy);
if (hbmSnapshot == NULL) {
return NULL;
}
hbmOld = SelectObject(hdcSnapshot, hbmSnapshot);
/*
* Try redirection first.
*/
/*
if (NtUserPrintWindow(hwnd, hdcSnapshot, 0)) {
fOK = TRUE;
} else */ {
/*
* We failed to redirect the window! This can be caused by windows
* with class or parent DCs. Maybe other reasons as well. Revert to
* the old way of sending a WM_PRINT to the window.
*/
UINT uBounds;
RECT rcBounds;
DWORD dwOldLayout = GDI_ERROR;
BOOL fError = TRUE;
/*
* The WM_PRINT message expects a "normal" layout setting on the DC.
* The message will handle RTL stuff itself.
*/
dwOldLayout = SetLayout(hdcSnapshot, 0);
/*
* Clear the dirty bounds so we can tell if anything was painted.
*/
SetBoundsRect(hdcSnapshot, 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)hdcSnapshot, PRF_CLIENT | PRF_NONCLIENT | PRF_CHILDREN | PRF_ERASEBKGND);
/*
* Check to see if the app painted in our DC. We do this by checking to
* see if the bounding rect of operations performed on the DC is set.
*/
uBounds = GetBoundsRect(hdcSnapshot, &rcBounds, 0);
if ((uBounds & DCB_RESET) && (!(uBounds & DCB_ACCUMULATE))) {
goto Cleanup;
}
fOK = TRUE;
Cleanup:
SetLayout(hdcSnapshot, dwOldLayout);
}
SelectObject(hdcSnapshot, hbmOld);
if (!fOK) {
DeleteObject(hbmSnapshot);
hbmSnapshot = NULL;
}
return hbmSnapshot;
}
/***************************************************************************\
* 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);
}
FUNCLOG3(LOG_GENERAL, BOOL, WINAPI, AnimateWindow, HWND, hwnd, DWORD, dwTime, DWORD, dwFlags)
BOOL WINAPI AnimateWindow(HWND hwnd, DWORD dwTime, DWORD dwFlags)
{
PTHREADINFO ptiCurrent = PtiCurrent();
HDC hdc = NULL, hdcMem = NULL;
PCLS pcls = NULL;
HRGN hrgnOriginal = NULL, hrgnUpdate = NULL, hrgnOldAnim = NULL, hrgnAnim = NULL;
HBITMAP hbmMem = NULL, hbmOld;
BOOL fHide = dwFlags & AW_HIDE, fRet = FALSE, fSlide = dwFlags & AW_SLIDE;
BOOL fRestoreClipChildren = FALSE;
BOOL fRestoreOriginalRegion = FALSE;
BOOL fShowWindow = FALSE;
BOOL fHideWindow = FALSE;
BOOL fActivateWindow = FALSE;
BOOL fFirstFrame = TRUE;
BOOL fRedrawParentWindow = FALSE;
HWND hwndParent;
int x, y, nx, ny, cx, cy, ix, iy, ixLast, iyLast, xWin, yWin;
int xReal, yReal, xMem, yMem, xRgn, yRgn;
DWORD dwStart, dwElapsed;
RECT rcAnim, rcWin;
PWND pwnd;
BOOL fRTL = FALSE;
#if DBG
int cAnimationFrames = 0;
DWORD dwElapsed2 = 0;
#endif
/*
* Check to see if we have nothing to do or the flags didn't validate.
*/
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;
/*
* Convert the HWND to a PWND. Fail if this is an invalid window.
*/
pwnd = ValidateHwnd(hwnd);
if (pwnd == NULL)
return FALSE;
/*
* The animation effect is applied to a window that is changing from being
* hidden to being visible, or from being visible to being hidden. If the
* window is already in the final state, there is nothing to do.
*/
if (!IsWindowVisible(hwnd)) {
if (fHide) {
return FALSE;
}
} else {
if (!fHide) {
return FALSE;
}
}
/*
* Grab a DC for this window.
*/
if ((hdc = GetDCEx(hwnd, NULL, DCX_WINDOW | DCX_USESTYLE | DCX_CACHE)) == NULL) {
return FALSE;
}
fRTL = (GetLayout(hdc) & LAYOUT_RTL) ? TRUE : FALSE;
/*
* ----------------------------------------------------------------------
* After this point, we will not return directly. Instead, we will fall
* out through the cleanup section at the bottom! Up until this point
* we may have bailed out for any number of easily-detected problems.
* From now on, we have resources we'll need to clean up.
* ----------------------------------------------------------------------
*/
/*
* Remember to hide/show/activate the window as requested.
*/
if (dwFlags & AW_HIDE) {
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Need to hide window");
fHideWindow = TRUE;
} else {
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Need to show window");
fShowWindow = TRUE;
}
if (dwFlags & AW_ACTIVATE) {
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Need to activate window");
fActivateWindow = TRUE;
}
/*
* If this is a child window we are animating, then we may need to
* repaint the parent every time we move the child so that the
* background can be refreshed.
*/
if (TestWF(pwnd, WFCHILD) && (pwnd->spwndParent != NULL)) {
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Animating a child window" );
if (dwFlags & AW_BLEND) {
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Can not fade a child window!" );
goto Cleanup;
}
fRedrawParentWindow = TRUE;
hwndParent = HW(_GetParent(pwnd));
}
/*
* In the process of animating the window, we are going to draw directly
* on top of the window region ourselves. As such, we don't want any
* "holes" in the window region due to it clipping out the children. But
* we will need to restore this setting when we are all done, so we set
* a flag here and check it at the end.
*/
if (TestWF(pwnd, WFCLIPCHILDREN)) {
fRestoreClipChildren = TRUE;
ClearWindowState(pwnd, WFCLIPCHILDREN);
}
/*
* Remember the original window region. We will restore this when we are
* all done.
*/
if (pwnd->hrgnClip != NULL) {
hrgnOriginal = CreateRectRgn(0, 0, 0, 0);
if (hrgnOriginal == NULL) {
goto Cleanup;
}
if (GetWindowRgn(hwnd, hrgnOriginal) == ERROR) {
goto Cleanup;
}
}
fRestoreOriginalRegion = TRUE;
/*
* Precreate the regions we use.
*/
if (((hrgnUpdate = CreateRectRgn(0, 0, 0, 0)) == NULL) ||
((hrgnOldAnim = CreateRectRgn(0, 0, 0, 0)) == NULL)) {
goto Cleanup;
}
rcWin = pwnd->rcWindow;
xWin = rcWin.left;
yWin = rcWin.top;
cx = rcWin.right - rcWin.left;
cy = rcWin.bottom - rcWin.top;
/*
* Initialize the "old" animation region to be:
* 1) Empty, if the window is being show.
* 2) Full, if the window is being hiddem.
*/
if (fHide) {
if (hrgnOriginal != NULL) {
if (CombineRgn(hrgnOldAnim, hrgnOriginal, NULL, RGN_COPY) == ERROR) {
goto Cleanup;
}
} else {
if (SetRectRgn(hrgnOldAnim, 0, 0, cx, cy) == 0) {
goto Cleanup;
}
}
} else {
if (SetRectRgn(hrgnOldAnim, 0, 0, 0, 0) == 0) {
goto Cleanup;
}
}
/*
* The window needs to be visible since we are going to be drawing parts
* of it. If the window is being hidden, then it is currently visible.
* If the window is being shown, then we go ahead and make it visible
* now but we don't repaint it.
*/
if (!(dwFlags & AW_BLEND)) {
HRGN hrgnWin = NULL;
/*
* 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) {
goto Cleanup;
}
RealSetWindowRgn(hwnd, hrgnWin, FALSE);
if (!fHide) {
ShowWindowNoRepaint(pwnd);
fShowWindow = FALSE;
}
}
/*
* Set up an offscreen DC, and back it to a bitmap. We will use this to
* capture the visual representation of the window being animated.
*/
if ((hdcMem = CreateCompatibleDC(hdc)) == NULL) {
goto Cleanup;
}
hbmMem = TakeWindowSnapshot(hwnd, hdc, hdcMem);
if (hbmMem != NULL) {
/*
* If the window changed its size while we were taking a snapshot,
* we need to do it again. For instance, like RAID does with
* combo boxes by resizing them on WM_CTLCOLOR from WM_ERASEBKGND.
*/
if (!EqualRect(&rcWin, KPRECT_TO_PRECT(&pwnd->rcWindow))) {
/*
* Update all of our variables taking into account the new size.
*/
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Size change on paint!");
TAGMSG4(DBGTAG_AnimateWindow, "AnimateWindow: Old = (%d,%d)-(%d,%d)", rcWin.left, rcWin.top, rcWin.right, rcWin.bottom);
rcWin = pwnd->rcWindow;
TAGMSG4(DBGTAG_AnimateWindow, "AnimateWindow: New = (%d,%d)-(%d,%d)", rcWin.left, rcWin.top, rcWin.right, rcWin.bottom);
xWin = rcWin.left;
yWin = rcWin.top;
cx = rcWin.right - rcWin.left;
cy = rcWin.bottom - rcWin.top;
if (hrgnOriginal != NULL) {
if (GetWindowRgn(hwnd, hrgnOriginal) == ERROR) {
goto Cleanup;
}
}
/*
* Initialize the "old" animation region to be:
* 1) Empty, if the window is being show.
* 2) Full, if the window is being hiddem.
*/
if (fHide) {
if (hrgnOriginal != NULL) {
if (CombineRgn(hrgnOldAnim, hrgnOriginal, NULL, RGN_COPY) == ERROR) {
goto Cleanup;
}
} else {
if (SetRectRgn(hrgnOldAnim, 0, 0, cx, cy) == 0) {
goto Cleanup;
}
}
} else {
if (SetRectRgn(hrgnOldAnim, 0, 0, 0, 0) == 0) {
goto Cleanup;
}
}
DeleteObject(hbmMem);
hbmMem = TakeWindowSnapshot(hwnd, hdc, hdcMem);
}
if (hbmMem != NULL) {
hbmOld = SelectBitmap(hdcMem, hbmMem);
} else {
goto Cleanup;
}
} else {
goto Cleanup;
}
/*
* Use the default animation duration if the caller didn't specify it.
*/
if (dwTime == 0) {
dwTime = CMS_QANIMATION;
}
/*
* If we are doing an alpha blend animation, call a separate routine to
* do it and then return.
*/
if (dwFlags & AW_BLEND) {
fRet = AnimateBlend(pwnd, hdc, hdcMem, dwTime, fHide, fActivateWindow);
if (fRet) {
fHideWindow = FALSE;
fShowWindow = FALSE;
}
goto Cleanup;
}
/*
* Our central animation routine uses an equation to update the new
* position of the window during the animation. This equation uses some
* variables so that it is configurable.
*
* x and y describe where the left and top edges are caluclated relative
* to. xReal and yReal are the result of that calculation.
*
* nx and ny are used to control in which direction the the top and left
* edges are offset from x and y. The left/top edges are either fixed in
* place (nx and ny are set to 0), or are calculated as a negative offset
* from the right/bottom edges (nx and ny are set to -1).
*
* ix, and iy are the amount of the width and height that the
* animation should be showing at a particular iteration through the
* loop. If we are showing the window, this amount starts at
* 0 and increments towards the window's true dimension. If we are
* hiding a window, this amount starts at the window's true dimension
* and decrements towards 0.
*/
ix = iy = 0;
ixLast = fHide ? cx : 0; // The opposite condition of what signals we're done.
iyLast = fHide ? cy : 0; // The opposite condition of what signals we're done.
if (dwFlags & AW_CENTER) {
/*
* Expand the window from the center. The left edge is calculated as
* a negative offset from the center. As the width either grows or
* shrinks, the left edge will be repositioned.
*/
x = cx / 2;
nx = -1;
fSlide = FALSE;
} else if (dwFlags & AW_HOR_POSITIVE) {
if (fHide) {
/*
* Slide/Roll to the right. The left edge moves to the right, and
* the right edge stays put. Thus, the width gets smaller. The
* left edge is calculated as a negative offset from the right
* edge.
*/
x = cx;
nx = -1;
} else {
/*
* Slide/Roll to the right. The left edge stays put, and the right
* edge moves to the right. Thus, the width gets bigger. The
* left edge is always 0.
*/
x = 0;
nx = 0;
}
} else if (dwFlags & AW_HOR_NEGATIVE) {
if (fHide) {
/*
* Slide/Roll to the left. The left edge stays put, and the right
* edge moves to the left. Thus, the width gets smaller. The
* left edge is always 0.
*/
x = 0;
nx = 0;
} else {
/*
* Slide/Roll to the left. The left edge moves to the left, and
* the right edge stays put. Thus, the width gets bigger.
* The left edge is calculated as a negative offset from the right
* edge.
*/
x = cx;
nx = -1;
}
} else {
/*
* There is not supposed to be any horizontal animation. The
* animation is always as wide as the window.
*/
x = 0;
nx = 0;
ix = cx;
}
if (dwFlags & AW_CENTER) {
/*
* Expand the window from the center. The top edge is calculated as
* a negative offset from the center. As the height either grows or
* shrinks, the top edge will be repositioned.
*/
y = cy / 2;
ny = -1;
} else if (dwFlags & AW_VER_POSITIVE) {
if (fHide) {
/*
* Slide/Roll down. The top edge moves down, and the bottom
* edge stays put. Thus, the height gets smaller. The top edge
* is calculated as a negative offset from the bottom edge.
*/
y = cy;
ny = -1;
} else {
/*
* Slide/Roll down. The top edge stays put, and the bottom edge
* moves down. Thus, the height gets bigger. The top edge is
* always 0.
*/
y = 0;
ny = 0;
}
} else if (dwFlags & AW_VER_NEGATIVE) {
if (fHide) {
/*
* Slide/Roll up. The top edge stays put, and the bottom edge
* moves up. Thus, the height gets smaller. The top edge is
* always 0.
*/
y = 0;
ny = 0;
} else {
/*
* Slide/Roll up. The top edge moves up, and the bottom edge
* stays put. Thus, the height gets bigger. The top edge is
* calculated as a negative offset from the bottom edge.
*/
y = cy;
ny = -1;
}
} else {
/*
* There is not supposed to be any vertical animation. The
* animation is always as tall as the window.
*/
y = 0;
ny = 0;
iy = cy;
}
/*
* Summary of the animation loop:
*
* We sit in a tight loop and update the positions of the left and
* top edges of the window, as well as the width and height. We set
* a window region with these dimensions on the window so that windows
* behind it will be updated properly. Then we draw the cached snapshot
* of the window on top of (and clipped to) this region.
*
* dwTime is the amount of time the animation should take. dwStart
* was the value of the internal tick counter when we started the
* animation loop. dwElapsed counts how many ticks (nilliseconds)
* have passed at the start of each pass through the animation loop.
*
* ixLast and iyLast are simply the values of ix and iy the last
* time we went through the loop. If these are the same, there is
* no work to be done, and we force our thread to be rescheduled by
* calling Sleep(1).
*/
dwStart = GetTickCount();
#if DBG
cAnimationFrames = 0;
#endif
while (TRUE) {
dwElapsed = GetTickCount() - dwStart;
/*
* Calculate the amount of the window width we should be showing.
*/
if (dwFlags & AW_HOR) {
ix = (fHide ? AnimDec : AnimInc)(cx, dwElapsed, dwTime);
}
/*
* Calculate the amount of the window height we should be showing.
*/
if (dwFlags & AW_VER) {
iy = (fHide ? AnimDec : AnimInc)(cy, dwElapsed, dwTime);
}
/*
* We have exceeded our time, make sure we draw the final frame.
*/
if (dwElapsed > dwTime) {
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Exceeded animation time. Drawing fimal frame.");
ix = fHide ? 0 : cx;
iy = fHide ? 0 : cy;
}
if (ixLast == ix && iyLast == iy) {
/*
* There was no change in the amount of the window we are
* supposed to show since last time. Chances are we are
* being animated really slowly or a short distance. Either
* way, sitting in this tight loop is kind of a waste. So
* force the thread to get rescheduled.
*/
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Drawing frames faster than needed. Sleeping." );
Sleep(1);
} else {
/*
* Calculate the new positions of the left and top edges of the
* window being animated.
*/
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 animation dimensions on the screen.
*/
rcAnim.left = xReal;
rcAnim.top = yReal;
rcAnim.right = rcAnim.left + ix;
rcAnim.bottom = rcAnim.top + iy;
TAGMSG5(DBGTAG_AnimateWindow, "AnimateWindow: Frame %d = (%d,%d)-(%d,%d)", cAnimationFrames, rcAnim.left, rcAnim.top, rcAnim.right, rcAnim.bottom);
/*
* Calculate the offset of this animation rectangle in the bitmap.
*/
if (fSlide) {
if (dwFlags & AW_HOR_POSITIVE) {
xMem = fHide ? 0: cx - ix;
} else if (dwFlags & AW_HOR_NEGATIVE) {
xMem = fHide ? cx - ix : 0;
} else {
xMem = xReal;
}
xRgn = xMem ? -xMem : xReal;
if (dwFlags & AW_VER_POSITIVE) {
yMem = fHide ? 0 : cy - iy;
} else if (dwFlags & AW_VER_NEGATIVE) {
yMem = fHide ? cy - iy : 0;
} else {
yMem = yReal;
}
yRgn = yMem ? -yMem : yReal;
} else {
xMem = xReal;
yMem = yReal;
xRgn = 0;
yRgn = 0;
}
/*
* Create a new region that spans the animation rectangle. We
* have to create a new region every time because when we set
* it into the window, the system will take ownership of it.
*/
hrgnAnim = CreateRectRgnIndirect(&rcAnim);
if (hrgnAnim == NULL) {
goto Cleanup;
}
/*
* If the original window had a region, we need to merge it
* with the animation rectangle. We may have to offset the
* original region to accomplish effects like slides.
*/
if (hrgnOriginal != NULL) {
if (OffsetRgn(hrgnOriginal, xRgn, yRgn) == ERROR) {
goto Cleanup;
}
if (CombineRgn(hrgnAnim, hrgnOriginal, hrgnAnim, RGN_AND) == ERROR) {
goto Cleanup;
}
if (OffsetRgn(hrgnOriginal, -xRgn, -yRgn) == ERROR) {
goto Cleanup;
}
}
/*
* Now calculate how much of the screen (ie desktop window)
* we need to update. All we really need to paint is the
* difference in the new animation region and the old
* animation region. Note that we have to convert to
* coordinates in the regions to be relative to the desktop
* window instead of being relative to the window being
* animated.
*/
if (CombineRgn(hrgnUpdate, hrgnOldAnim, hrgnAnim, RGN_DIFF) == ERROR) {
goto Cleanup;
}
if (fRTL) {
MirrorRgn(hwnd, hrgnUpdate);
}
if (OffsetRgn(hrgnUpdate, xWin, yWin) == ERROR) {
goto Cleanup;
}
/*
* The system will own the region when we set it into the
* window. We need to keep it around so that we can
* calculate the update region on the next pass through
* the animation loop. So we make a copy.
*/
if (CombineRgn(hrgnOldAnim, hrgnAnim, NULL, RGN_COPY) == ERROR) {
goto Cleanup;
}
/*
* Set the window region. Note that we haven't actually moved
* the window. And that the coordinates in the region are all
* relative to the window. After this call, the system owns
* the hrgnAnim. Then repaint the update region of the
* DESKTOP window. This is the region under/around the window
* that we have exposed.
*
* Note: We use the RealSetWindowRgn to work around theming.
* The theming system will hook the standard SetWindowRgn API
* and revoke the theming of the window since it detects us
* setting our own region. The idea being that if we are setting
* a region, we must have a "custom" look in mind for the window.
* Which we dont, we just want to hide parts of it temporarily.
*/
if(0 == RealSetWindowRgn(hwnd, hrgnAnim, FALSE)) {
goto Cleanup;
} else {
/*
* The system now owns the region. Lets simply forget about
* it to be safe.
*/
hrgnAnim = NULL;
}
/*
* If we are supposed to activate the window, do so on the first
* frame of the animation. This will cause the window to be
* z-ordered properly. Note that we leave the flag set to
* true so that we will activate it again at the end. This will
* force a repaint since we are currently drawing the bits of the
* window that doesn't look activated.
*/
if (fFirstFrame && fActivateWindow) {
NtUserSetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOREDRAW);
}
fFirstFrame = FALSE;
if (RedrawWindow(NULL, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN) == 0) {
goto Cleanup;
}
if (fRedrawParentWindow) {
if (NtUserCallHwndParamLock(hwndParent, (ULONG_PTR)hrgnUpdate, SFI_XXXUPDATEWINDOWS) == 0) {
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Warning: xxxUpdateWindows failed!");
goto Cleanup;
}
}
/*
* Now draw the cached snapshot of the window on top of the window
* itself. We do this by drawing into the window's DC. Since we
* applied a region already, all clipping is done for us.
*/
if (BitBlt(hdc, xReal, yReal, ix, iy, hdcMem, xMem, yMem, SRCCOPY | NOMIRRORBITMAP) == 0) {
goto Cleanup;
}
#if DBG
cAnimationFrames++;
dwElapsed2 = GetTickCount() - dwStart;
dwElapsed2 -= dwElapsed;
#endif
TAGMSG2(DBGTAG_AnimateWindow, "AnimateWindow: Frame %d took %lums", cAnimationFrames, dwElapsed2 );
ixLast = ix;
iyLast = iy;
/*
* Break out of the animation loop when, either:
* 1) We've exceeded the animation time.
* 2) We're hiding the window and one of the dimensions is 0.
* The window is completely hidden now anyways,
* 3) We're showing the window and both dimensions are at their
* full size. The window is completely shown now anyways.
*/
if (dwElapsed > dwTime) {
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Done with the animation late!");
break;
}
if ((fHide && (ix == 0 || iy == 0)) ||
(!fHide && (ix == cx && iy == cy))) {
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Done with the animation on time or early!");
break;
}
}
}
TAGMSG2(DBGTAG_AnimateWindow, "AnimateWindow: Animation completed after %lums, drawing %d frames.", dwElapsed, cAnimationFrames);
fRet = TRUE;
if (fHide) {
UserAssert(ixLast == 0 || iyLast == 0);
/*
* We are supposed to be hiding the window. Go ahead and restore the
* child clipping setting, and hide the window.
*/
if (fRestoreClipChildren) {
SetWindowState(pwnd, WFCLIPCHILDREN);
fRestoreClipChildren = FALSE;
}
NtUserSetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_HIDEWINDOW | SWP_NOACTIVATE);
fHideWindow = FALSE;
} else {
UserAssert(ixLast == cx && iyLast == cy);
/*
* We successfully finished the animation loop! Validate the entire window since
* we claimed responsibility for drawing it correctly.
*/
RedrawWindow(hwnd, NULL, NULL, RDW_NOERASE | RDW_NOFRAME | RDW_NOINTERNALPAINT | RDW_VALIDATE);
}
Cleanup:
/*
* Things to do on cleanup. Make sure we restore the "children clipping"
* setting of the window if we removed it!
*/
if (fRestoreClipChildren) {
SetWindowState(pwnd, WFCLIPCHILDREN);
fRestoreClipChildren = FALSE;
}
/*
* Hide the window if needed before we reapply the window region.
*/
if (fHideWindow) {
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Hiding the window during cleanup" );
NtUserShowWindow(hwnd, SW_HIDE);
}
/*
* Restore the original window region. Note that the system now owns
* the handle, so we should not delete it. Also, if the original
* handle was NULL, this removes any regions we inflicted on the window
* in order to do the animation.
*/
if (fRestoreOriginalRegion) {
RealSetWindowRgn(hwnd, hrgnOriginal, FALSE);
hrgnOriginal = NULL;
fRestoreOriginalRegion = FALSE;
}
/*
* More things to do on cleanup. Make sure we show/activate the window
* if needed!
*/
if (fShowWindow && fActivateWindow) {
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Showing and activating the window during cleanup" );
NtUserSetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER);
fShowWindow = FALSE;
fActivateWindow = FALSE;
}
if (fShowWindow) {
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Showing the window during cleanup" );
NtUserSetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
fShowWindow = FALSE;
}
if (fActivateWindow) {
TAGMSG0(DBGTAG_AnimateWindow, "AnimateWindow: Activating the window during cleanup" );
NtUserSetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER);
fActivateWindow = FALSE;
}
if (hdcMem != NULL) {
DeleteDC(hdcMem);
}
if (hbmMem != NULL) {
DeleteObject(hbmMem);
}
if (hdc != NULL) {
ReleaseDC(hwnd, hdc);
}
if (hrgnAnim != NULL) {
DeleteObject(hrgnAnim);
hrgnAnim = NULL;
}
if (hrgnOldAnim != NULL) {
DeleteObject(hrgnOldAnim);
hrgnOldAnim = NULL;
}
if (hrgnUpdate != NULL) {
DeleteObject(hrgnUpdate);
hrgnUpdate = NULL;
}
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);
}
}
/***************************************************************************\
* IsGUIThread (API)
*
* Checks whether the current thread is a GUI thread. If bConvert is TRUE, will
* convert the current thread to GUI, if necessary.
*
* History:
* 22-Jun-2000 JasonSch Wrote.
\***************************************************************************/
FUNCLOG1(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, IsGUIThread, BOOL, bConvert)
BOOL IsGUIThread(BOOL bConvert)
{
BOOL bIsGUI = (NtCurrentTebShared()->Win32ThreadInfo != NULL);
if (!bIsGUI && bConvert) {
bIsGUI = (BOOL)USERTHREADCONNECT();
if (!bIsGUI) {
UserSetLastError(ERROR_OUTOFMEMORY);
}
}
return bIsGUI;
}
/***************************************************************************\
* IsWindowInDestroy (API)
*
* Checks whether the current window is in the process of being destroyed.
*
* History:
* 02-Jan-2001 Mohamed Wrote.
\***************************************************************************/
FUNCLOG1(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, IsWindowInDestroy, HWND, hwnd)
BOOL IsWindowInDestroy(IN HWND hwnd)
{
PWND pwnd;
pwnd = ValidateHwnd(hwnd);
if (pwnd == NULL) {
return FALSE;
}
return TestWF(pwnd, WFINDESTROY);
}
/***************************************************************************\
* IsServerSideWindow (API)
*
* Checks whether the current window is marked as having a server side WndProc.
*
* History:
* 13-Jun-2001 Mohamed Created.
\***************************************************************************/
FUNCLOG1(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, IsServerSideWindow, HWND, hwnd)
BOOL IsServerSideWindow(IN HWND hwnd)
{
PWND pwnd;
pwnd = ValidateHwnd(hwnd);
if (pwnd == NULL) {
return FALSE;
}
return TestWF(pwnd, WFSERVERSIDEPROC);
}