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