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.
623 lines
20 KiB
623 lines
20 KiB
/**************************** Module Header ********************************\
|
|
* Module Name: syscmd.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* System Command Routines
|
|
*
|
|
* History:
|
|
* 01-25-91 IanJa Added handle revalidation
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxHandleNCMouseGuys
|
|
*
|
|
* History:
|
|
* 11-09-90 DavidPe Ported.
|
|
\***************************************************************************/
|
|
|
|
void xxxHandleNCMouseGuys(
|
|
PWND pwnd,
|
|
UINT message,
|
|
int htArea,
|
|
LPARAM lParam)
|
|
{
|
|
UINT syscmd;
|
|
PWND pwndT;
|
|
TL tlpwndT;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
syscmd = 0xFFFF;
|
|
|
|
switch (htArea) {
|
|
|
|
case HTCAPTION:
|
|
switch (message) {
|
|
|
|
case WM_NCLBUTTONDBLCLK:
|
|
if (TestWF(pwnd, WFMINIMIZED) || TestWF(pwnd, WFMAXIMIZED)) {
|
|
syscmd = SC_RESTORE;
|
|
} else if (TestWF(pwnd, WFMAXBOX)) {
|
|
syscmd = SC_MAXIMIZE;
|
|
}
|
|
break;
|
|
|
|
case WM_NCLBUTTONDOWN:
|
|
pwndT = GetTopLevelWindow(pwnd);
|
|
ThreadLock(pwndT, &tlpwndT);
|
|
xxxActivateWindow(pwndT, AW_USE2);
|
|
ThreadUnlock(&tlpwndT);
|
|
syscmd = SC_MOVE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case HTSYSMENU:
|
|
case HTMENU:
|
|
case HTHSCROLL:
|
|
case HTVSCROLL:
|
|
if (message == WM_NCLBUTTONDOWN || message == WM_NCLBUTTONDBLCLK) {
|
|
switch (htArea) {
|
|
case HTSYSMENU:
|
|
if (message == WM_NCLBUTTONDBLCLK) {
|
|
syscmd = SC_CLOSE;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
*** FALL THRU **
|
|
*/
|
|
|
|
case HTMENU:
|
|
syscmd = SC_MOUSEMENU;
|
|
break;
|
|
|
|
case HTHSCROLL:
|
|
syscmd = SC_HSCROLL;
|
|
break;
|
|
|
|
case HTVSCROLL:
|
|
syscmd = SC_VSCROLL;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (syscmd) {
|
|
|
|
case SC_MINIMIZE:
|
|
case SC_MAXIMIZE:
|
|
case SC_CLOSE:
|
|
|
|
/*
|
|
* Only do double click commands on an upclick.
|
|
* This code is very sensitive to changes from this state.
|
|
* Eat any mouse messages.
|
|
*/
|
|
|
|
/*
|
|
* Bug #152: WM_NCLBUTTONUP message missing from double click.
|
|
* This code was broken in Windows 3.x and the test for whether
|
|
* the mouse button was down always failed, so no mouse messages
|
|
* were ever eaten. We'll emulate this by not even doing the test.
|
|
*
|
|
*
|
|
* {
|
|
* PQ pqCurrent;
|
|
* MSG msg;
|
|
*
|
|
* pqCurrent = PtiCurrent()->pq;
|
|
* if (TestKeyStateDown(pqCurrent, VK_LBUTTON)) {
|
|
* xxxCapture(PtiCurrent(), pwnd, WINDOW_CAPTURE);
|
|
*
|
|
* while (TestKeyStateDown(pqCurrent, VK_LBUTTON)) {
|
|
* if (!xxxPeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST,
|
|
* PM_REMOVE)) {
|
|
* if (!xxxSleepThread(QS_MOUSE, 0, TRUE))
|
|
* break;
|
|
* }
|
|
* }
|
|
*
|
|
* xxxReleaseCapture();
|
|
*
|
|
* }
|
|
* }
|
|
*
|
|
*/
|
|
|
|
/*
|
|
** FALL THRU **
|
|
*/
|
|
case SC_SIZE:
|
|
case SC_MOVE:
|
|
/*
|
|
* For SysCommands on system menu, don't do if menu item is
|
|
* disabled.
|
|
*/
|
|
if (TestWF(pwnd, WFSYSMENU)) {
|
|
xxxSetSysMenu(pwnd);
|
|
if (_GetMenuState(xxxGetSysMenuHandle(pwnd), (syscmd & 0xFFF0),
|
|
MF_BYCOMMAND) & MFS_GRAYED) {
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (syscmd != 0xFFFF) {
|
|
xxxSendMessage(pwnd, WM_SYSCOMMAND, syscmd | htArea, lParam);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* StartScreenSaver
|
|
*
|
|
* History:
|
|
* 11-12-90 MikeHar ported.
|
|
\***************************************************************************/
|
|
|
|
void StartScreenSaver(
|
|
BOOL bOnlyIfSecure)
|
|
{
|
|
/*
|
|
* If a screen saver is already running or we're in the midst of powering
|
|
* down the machine, ignore this request.
|
|
*/
|
|
if (gppiScreenSaver != NULL || gPowerState.fInProgress)
|
|
return;
|
|
|
|
if (gspwndLogonNotify != NULL) {
|
|
|
|
if( glinp.dwFlags & LINP_POWEROFF ) {
|
|
|
|
/*
|
|
* If the monitor is turned off. Tell winlogon to handle the
|
|
* screen saver in a special manner.
|
|
*/
|
|
_PostMessage(gspwndLogonNotify,
|
|
WM_LOGONNOTIFY, LOGON_INPUT_TIMEOUT, 2);
|
|
} else {
|
|
|
|
|
|
/*
|
|
* Let the logon process take care of the screen saver
|
|
*/
|
|
_PostMessage(gspwndLogonNotify,
|
|
WM_LOGONNOTIFY, LOGON_INPUT_TIMEOUT, bOnlyIfSecure);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxSysCommand
|
|
*
|
|
* History:
|
|
* 11-12-90 MikeHar ported.
|
|
* 02-07-91 DavidPe Added Win 3.1 WH_CBT support.
|
|
\***************************************************************************/
|
|
|
|
void xxxSysCommand(
|
|
PWND pwnd,
|
|
DWORD cmd,
|
|
LPARAM lParam)
|
|
{
|
|
UINT htArea;
|
|
PWND pwndSwitch;
|
|
PMENUSTATE pMenuState;
|
|
TL tlpwnd;
|
|
POINT pt;
|
|
DWORD dw;
|
|
PWND pwndCapture;
|
|
PTHREADINFO pti;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
htArea = (UINT)(cmd & 0x0F);
|
|
cmd -= htArea;
|
|
|
|
/*
|
|
* Intense hack o' death.
|
|
*/
|
|
if (lParam == 0x00010000L)
|
|
lParam = 0L;
|
|
|
|
/*
|
|
* If the system doesn't have capture (ie CLENT_CAPTURE_INTERNAL)
|
|
* do the sys command. Also, do the sys command for the special case
|
|
* where the window receiving the sys command is a console window that
|
|
* is in full screen mode. In this case we let the sys command through.
|
|
*
|
|
* Also if this a SC_SCREENSAVE then we handle it anyway and
|
|
* switching desktops will do the cancel. SC_SCREENSAVER
|
|
* is special so we can start the screen saver even if we are in
|
|
* menu mode for security so NT bug 10975 Banker's Trust
|
|
*/
|
|
pti = GETPTI(pwnd);
|
|
|
|
/*
|
|
* For 32bit apps (and apps on seperate queues), we need to check
|
|
* the capture in the queue. Otherwise, on MDI child-destruction
|
|
* we would get the restore when they shouldn't. This broke MSGOLF
|
|
* who during the restore, AV'd because they assumed this wouldn't
|
|
* happen. On 16bit shared apps, we want to check the internal
|
|
* capture. Otherwise, when doing 16bit drag-and-drop, we would
|
|
* not restore the minimized window if we had a queue-capture-window.
|
|
*/
|
|
|
|
/*
|
|
* But... it is too broad a change to just check internal capture for all WoW apps. Some
|
|
* apps depend on bailing out when they have capture set. (Adobe Persuasion, NT bug 68794,
|
|
* for SC_MOVE). So, let's restrict the hack to SC_RESTORE to keep Ole drag-and-drop working.
|
|
* See NT bug 6109. FritzS
|
|
*/
|
|
|
|
pwndCapture = ((pti->TIF_flags & TIF_16BIT) && (cmd == SC_RESTORE)) ? gspwndInternalCapture :
|
|
pti->pq->spwndCapture;
|
|
|
|
if ((!pwndCapture && !TestWF(pwnd, WFDISABLED)) ||
|
|
(pwnd == gspwndFullScreen) ||
|
|
(cmd == SC_SCREENSAVE) ||
|
|
(cmd == SC_MONITORPOWER) ||
|
|
(cmd == SC_TASKLIST)) {
|
|
|
|
/*
|
|
* Perform the sys command
|
|
*/
|
|
|
|
#ifdef SYSMODALWINDOWS
|
|
if (gspwndSysModal != NULL) {
|
|
switch (cmd) {
|
|
case SC_SIZE:
|
|
case SC_MOVE:
|
|
case SC_MINIMIZE:
|
|
case SC_MAXIMIZE:
|
|
case SC_NEXTWINDOW:
|
|
case SC_PREVWINDOW:
|
|
case SC_SCREENSAVE:
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Call the CBT hook asking if it's okay to do this command.
|
|
* If not, return from here.
|
|
*/
|
|
if (IsHooked(PtiCurrent(), WHF_CBT) && xxxCallHook(HCBT_SYSCOMMAND,
|
|
(DWORD)cmd, (DWORD)lParam, WH_CBT)) {
|
|
return;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case SC_RESTORE:
|
|
cmd = SW_RESTORE;
|
|
if (TestWF(pwnd, WFMINIMIZED) || !TestWF(pwnd, WFMAXIMIZED))
|
|
PlayEventSound(USER_SOUND_RESTOREUP);
|
|
else
|
|
PlayEventSound(USER_SOUND_RESTOREDOWN);
|
|
goto MinMax;
|
|
|
|
|
|
case SC_MINIMIZE:
|
|
cmd = SW_MINIMIZE;
|
|
|
|
/*
|
|
* Are we already minimized?
|
|
*/
|
|
if (TestWF(pwnd, WFMINIMIZED))
|
|
break;
|
|
|
|
PlayEventSound(USER_SOUND_MINIMIZE);
|
|
|
|
goto MinMax;
|
|
case SC_MAXIMIZE:
|
|
cmd = SW_SHOWMAXIMIZED;
|
|
|
|
/*
|
|
* Are we already maximized?
|
|
*/
|
|
if (TestWF(pwnd, WFMAXIMIZED))
|
|
break;
|
|
|
|
PlayEventSound(USER_SOUND_MAXIMIZE);
|
|
MinMax:
|
|
xxxShowWindow(pwnd, cmd | TEST_PUDF(PUDF_ANIMATE));
|
|
return;
|
|
|
|
case SC_SIZE:
|
|
{
|
|
xxxMoveSize(pwnd, htArea, _GetMessagePos());
|
|
}
|
|
return;
|
|
|
|
case SC_MOVE:
|
|
//
|
|
// Don't enter movesize loop unless the user is actually
|
|
// dragging from the caption. Otherwise, put up the system
|
|
// menu on a minimized window.
|
|
//
|
|
|
|
//
|
|
// Are we dragging with the left mouse button?
|
|
//
|
|
dw = _GetMessagePos();
|
|
POINTSTOPOINT( pt, MAKEPOINTS(dw));
|
|
if ( !htArea ||
|
|
xxxIsDragging(pwnd, pt, WM_LBUTTONUP)) {
|
|
|
|
/*
|
|
* We are moving. Enter move/size loop.
|
|
*/
|
|
{
|
|
xxxMoveSize(pwnd, (htArea == 0) ? WMSZ_KEYMOVE : WMSZ_MOVE, dw);
|
|
}
|
|
} else {
|
|
|
|
/*
|
|
* Activate our window, just like we would have in
|
|
* MoveSize().
|
|
*/
|
|
xxxSetWindowPos(pwnd, PWND_TOP, 0, 0, 0, 0,
|
|
SWP_NOMOVE | SWP_NOSIZE);
|
|
if (TestWF(pwnd, WFMINIMIZED)) {
|
|
|
|
/*
|
|
* Try to popup the system menu
|
|
*/
|
|
xxxSendMessage(pwnd, WM_SYSCOMMAND, SC_KEYMENU,
|
|
(DWORD) (TestWF(pwnd, WFCHILD) ? '-' : MENUSYSMENU));
|
|
}
|
|
}
|
|
return;
|
|
|
|
case SC_CLOSE:
|
|
xxxSendMessage(pwnd, WM_CLOSE, 0L, 0L);
|
|
return;
|
|
|
|
case SC_NEXTWINDOW:
|
|
case SC_PREVWINDOW:
|
|
xxxOldNextWindow((UINT)lParam);
|
|
break;
|
|
|
|
case SC_CONTEXTHELP:
|
|
xxxHelpLoop(pwnd);
|
|
break;
|
|
|
|
case SC_KEYMENU:
|
|
|
|
/*
|
|
* A menu was selected via keyboard
|
|
*/
|
|
pMenuState = xxxMNStartMenuState(pwnd, cmd, lParam);
|
|
if (pMenuState != NULL) {
|
|
UserAssert(PtiCurrent() == pMenuState->ptiMenuStateOwner);
|
|
|
|
/*
|
|
* Make sure we are not fullscreen
|
|
*/
|
|
if (gspwndFullScreen == pwnd) {
|
|
PWND pwndT;
|
|
TL tlpwndT;
|
|
|
|
pwndT = _GetDesktopWindow();
|
|
ThreadLock(pwndT, &tlpwndT);
|
|
xxxMakeWindowForegroundWithState(pwndT, GDIFULLSCREEN);
|
|
ThreadUnlock(&tlpwndT);
|
|
}
|
|
|
|
pMenuState->fUnderline = TRUE;
|
|
xxxMNKeyFilter(pMenuState->pGlobalPopupMenu, pMenuState, (UINT)lParam);
|
|
if (!pMenuState->fModelessMenu) {
|
|
xxxMNEndMenuState (TRUE);
|
|
}
|
|
}
|
|
/*
|
|
* Capture must have been unlocked
|
|
*/
|
|
UserAssert(!(PtiCurrent()->pq->QF_flags & QF_CAPTURELOCKED));
|
|
return;
|
|
|
|
case SC_MOUSEMENU:
|
|
case SC_DEFAULT:
|
|
|
|
/*
|
|
* If the window is not foreground, eat the command to avoid
|
|
* wasting time flashing the system menu.
|
|
*
|
|
* We used to check if the top level window was WFFRAMEON (so a
|
|
* child window's system menu works like Win 3.1) but Excel's
|
|
* (SDM) dialogs allow you to access their menus even though
|
|
* the child and parent appear to be inactive.
|
|
*/
|
|
if (!(GETPTI(pwnd)->pq == gpqForeground))
|
|
return;
|
|
|
|
/*
|
|
* A mouse click occurred on a toplevel menu.
|
|
*/
|
|
pMenuState = xxxMNStartMenuState(pwnd, cmd, lParam);
|
|
if (pMenuState != NULL) {
|
|
UserAssert(PtiCurrent() == pMenuState->ptiMenuStateOwner);
|
|
xxxMNLoop(pMenuState->pGlobalPopupMenu, pMenuState, lParam, (cmd==SC_DEFAULT));
|
|
if (!pMenuState->fModelessMenu) {
|
|
xxxMNEndMenuState (TRUE);
|
|
}
|
|
}
|
|
/*
|
|
* Capture must have been unlocked
|
|
*/
|
|
UserAssert(!(PtiCurrent()->pq->QF_flags & QF_CAPTURELOCKED));
|
|
return;
|
|
|
|
case SC_VSCROLL:
|
|
case SC_HSCROLL:
|
|
xxxSBTrackInit(pwnd, lParam, htArea, (_GetKeyState(VK_SHIFT) < 0) ? SCROLL_DIRECT : SCROLL_NORMAL);
|
|
return;
|
|
|
|
case SC_TASKLIST:
|
|
// _PostThreadMessage(gptiTasklist, WM_SYSCOMMAND, SC_TASKLIST, 0);
|
|
// if (!FCallTray() ||
|
|
// !CallHook(HSHELL_TASKMAN, (WPARAM) HW16(hwnd), (LPARAM) 0, WH_SHELL))
|
|
|
|
/*
|
|
* Winlogon will set lParam to -1 to indicate that we really want a task list,
|
|
* not just the start menu. We indicate this to the shell by passing a NULL
|
|
* window ptr
|
|
* This message is really intended for the SHELL, so give them the right
|
|
* to set the foreground.
|
|
*/
|
|
if (FDoTray() && (FCallHookTray() || FPostTray(pwnd->head.rpdesk))) {
|
|
PWND pwndTaskman = pwnd->head.rpdesk->pDeskInfo->spwndTaskman;
|
|
if (FCallHookTray()) {
|
|
xxxCallHook(HSHELL_TASKMAN, (WPARAM)HWq(pwnd), (LPARAM) 0, WH_SHELL);
|
|
}
|
|
if ((FPostTray(pwnd->head.rpdesk)) && (pwndTaskman != NULL)) {
|
|
glinp.ptiLastWoken = GETPTI(pwndTaskman);
|
|
_PostMessage(pwndTaskman, gpsi->uiShellMsg, HSHELL_TASKMAN,
|
|
lParam == (ULONG)(-1) ? (LPARAM) -1 :(LPARAM)HWq(pwnd));
|
|
}
|
|
} else if (gptiTasklist != NULL) {
|
|
glinp.ptiLastWoken = gptiTasklist;
|
|
_PostThreadMessage(gptiTasklist, WM_SYSCOMMAND, SC_TASKLIST, 0);
|
|
// LATER -- FritzS
|
|
// HCURSOR hCursorLast;
|
|
// static char CODESEG szTask[] = " %d %d";
|
|
|
|
// ShowCursor(TRUE);
|
|
// hCursorLast = SetCursor32(hCursWait, TRUE);
|
|
|
|
// Try in the windows directory first.
|
|
// GetWindowsDirectory(szBuff, sizeof(szBuff));
|
|
// if (szBuff[lstrlen(szBuff) - 1] != '\\')
|
|
// lstrcatn(szBuff, "\\", sizeof(szBuff));
|
|
// lstrcatn(szBuff, (LPSTR)pTaskManName, sizeof(szBuff));
|
|
// wvsprintf(szBuff+lstrlen(szBuff), (LPSTR)szTask, (LPSTR)&lParam);
|
|
|
|
// if (WinExec((LPSTR)szBuff, SW_SHOWNORMAL) <= 32)
|
|
// {
|
|
// // If it wasn't in the windows directory then try
|
|
// // searching the full path.
|
|
// lstrcpyn(szBuff, pTaskManName, sizeof(szBuff));
|
|
// wvsprintf(szBuff+lstrlen(szBuff), (LPSTR)szTask, (LPSTR)&lParam);
|
|
// WinExec((LPSTR)szBuff, SW_SHOWNORMAL);
|
|
// }
|
|
//
|
|
// ShowCursor(FALSE);
|
|
// SetCursor32(hCursorLast, TRUE);
|
|
}
|
|
|
|
break;
|
|
|
|
case SC_MONITORPOWER:
|
|
/*
|
|
* If we're powering down the machine, or if we are switching protocol,ignore this request.
|
|
*/
|
|
|
|
if (gPowerState.fInProgress || gfSwitchInProgress) {
|
|
break;
|
|
}
|
|
|
|
switch (lParam) {
|
|
case POWERON_PHASE:
|
|
if ( (glinp.dwFlags & LINP_POWERTIMEOUTS) ||
|
|
(glinp.dwFlags & LINP_POWEROFF) ) {
|
|
glinp.dwFlags &= ~LINP_POWEROFF;
|
|
glinp.dwFlags &= ~LINP_POWERTIMEOUTS;
|
|
DrvSetMonitorPowerState(gpDispInfo->pmdev,
|
|
PowerDeviceD0);
|
|
}
|
|
break;
|
|
case LOWPOWER_PHASE:
|
|
if ((glinp.dwFlags & LINP_LOWPOWER) == 0) {
|
|
glinp.dwFlags |= LINP_LOWPOWER;
|
|
DrvSetMonitorPowerState(gpDispInfo->pmdev,
|
|
PowerDeviceD1);
|
|
}
|
|
break;
|
|
case POWEROFF_PHASE:
|
|
if ((glinp.dwFlags & LINP_POWEROFF) == 0) {
|
|
glinp.dwFlags |= LINP_POWEROFF;
|
|
DrvSetMonitorPowerState(gpDispInfo->pmdev,
|
|
PowerDeviceD3);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SC_SCREENSAVE:
|
|
pwndSwitch = RevalidateHwnd(ghwndSwitch);
|
|
|
|
// Lock out screen save until we get another input message.
|
|
|
|
if (pwndSwitch != NULL && pwnd != pwndSwitch) {
|
|
_PostMessage(pwndSwitch, WM_SYSCOMMAND, SC_SCREENSAVE, 0L);
|
|
} else {
|
|
StartScreenSaver(FALSE);
|
|
}
|
|
break;
|
|
|
|
case SC_HOTKEY:
|
|
|
|
/*
|
|
* Loword of the lparam is window to switch to
|
|
*/
|
|
pwnd = ValidateHwnd((HWND)lParam);
|
|
if (pwnd != NULL) {
|
|
pwndSwitch = _GetLastActivePopup(pwnd);
|
|
|
|
if (pwndSwitch != NULL)
|
|
pwnd = pwndSwitch;
|
|
|
|
ThreadLockAlways(pwnd, &tlpwnd);
|
|
xxxSetForegroundWindow(pwnd, FALSE);
|
|
ThreadUnlock(&tlpwnd);
|
|
|
|
if (TestWF(pwnd, WFMINIMIZED))
|
|
_PostMessage(pwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _RegisterTasklist (Private API)
|
|
*
|
|
* History:
|
|
* 05-01-91 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
BOOL _RegisterTasklist(
|
|
PWND pwndTasklist)
|
|
{
|
|
#ifdef LATER
|
|
//
|
|
// JimA - ??? Why do this?
|
|
//
|
|
PETHREAD Thread;
|
|
|
|
Thread = PsGetCurrentThread();
|
|
pRitCSRThread->ThreadHandle = Thread->ThreadHandle;
|
|
#endif
|
|
|
|
gptiTasklist = GETPTI(pwndTasklist);
|
|
ghwndSwitch = HWq(pwndTasklist);
|
|
|
|
/*
|
|
* Don't allow an app to call AttachThreadInput() on task man -
|
|
* we want taskman to be unsynchronized at all times (so the user
|
|
* can bring it up and kill other apps).
|
|
*/
|
|
GETPTI(pwndTasklist)->TIF_flags |= TIF_DONTATTACHQUEUE;
|
|
|
|
return TRUE;
|
|
}
|