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
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);
|
|
}
|