Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

733 lines
20 KiB

/****************************** Module Header ******************************\
* Module Name: hotkeys.c
*
* Copyright (c) 1985-95, Microsoft Corporation
*
* This module contains the core functions of hotkey processing.
*
* History:
* 12-04-90 DavidPe Created.
* 02-12-91 JimA Added access checks
* 13-Feb-1991 mikeke Added Revalidation code (None)
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
#define IDHOT_DEBUG (-5)
#define IDHOT_DEBUGSERVER (-6)
#define IDHOT_WINDOWS (-7)
#define MOD_RSHIFT MAKELONG(0, MOD_SHIFT)
#define MOD_RCONTROL MAKELONG(0, MOD_CONTROL)
#define MOD_RALT MAKELONG(0, MOD_ALT)
/***************************************************************************\
* InitSystemHotKeys
*
* Called from InitWindows(), this routine registers the default system
* hotkeys.
*
* History:
* 12-04-90 DavidPe Created.
\***************************************************************************/
VOID InitSystemHotKeys(VOID)
{
#if DEVL // only on "development" checked and free builds
{
extern KEYBOARD_ATTRIBUTES KeyboardInfo;
UINT VkDebug;
VkDebug = FastGetProfileDwordW(PMAP_AEDEBUG, L"UserDebuggerHotkey", 0);
if (VkDebug == 0) {
if (ENHANCED_KEYBOARD(KeyboardInfo.KeyboardIdentifier)) {
VkDebug = VK_F12;
} else {
VkDebug = VK_SUBTRACT;
}
} else {
UserAssert((0xFFFFFF00 & VkDebug) == 0);
}
_RegisterHotKey(PWND_INPUTOWNER, IDHOT_DEBUG, 0, VkDebug);
_RegisterHotKey(PWND_INPUTOWNER, IDHOT_DEBUGSERVER, MOD_SHIFT, VkDebug);
}
#endif
/*
* Tapping a Windows key does something to the shell
*/
_RegisterHotKey(PWND_INPUTOWNER, IDHOT_WINDOWS, MOD_WIN, VK_NONE);
}
/***************************************************************************\
* DestroyThreadsHotKeys
*
* History:
* 26-Feb-1991 mikeke Created.
\***************************************************************************/
VOID DestroyThreadsHotKeys()
{
PHOTKEY *pphk;
PHOTKEY phk;
PTHREADINFO ptiCurrent = PtiCurrent();
pphk = &gphkFirst;
while (*pphk) {
if ((*pphk)->pti == ptiCurrent) {
phk = *pphk;
*pphk = (*pphk)->phkNext;
/*
* Unlock the object stored here.
*/
if ((phk->spwnd != PWND_FOCUS) && (phk->spwnd != PWND_INPUTOWNER)) {
Unlock(&phk->spwnd);
}
UserFreePool(phk);
} else {
pphk = &((*pphk)->phkNext);
}
}
}
/***************************************************************************\
* DestroyWindowsHotKeys
*
* Called from xxxFreeWindow.
* Hotkeys not unregistered properly by the app must be destroyed when the
* window is destroyed. Keeps things clean (problems with bad apps #5553)
*
* History:
* 23-Sep-1992 IanJa Created.
\***************************************************************************/
VOID DestroyWindowsHotKeys(
PWND pwnd)
{
PHOTKEY *pphk;
PHOTKEY phk;
pphk = &gphkFirst;
while (*pphk) {
if ((*pphk)->spwnd == pwnd) {
phk = *pphk;
*pphk = (*pphk)->phkNext;
Unlock(&phk->spwnd);
UserFreePool(phk);
} else {
pphk = &((*pphk)->phkNext);
}
}
}
/***************************************************************************\
* _RegisterHotKey (API)
*
* This API registers the hotkey specified. If the specified key sequence
* has already been registered we return FALSE. If the specified hwnd
* and id have already been registered, fsModifiers and vk are reset for
* the HOTKEY structure.
*
* History:
* 12-04-90 DavidPe Created.
* 02-12-91 JimA Added access check
\***************************************************************************/
BOOL _RegisterHotKey(
PWND pwnd,
int id,
UINT fsModifiers,
UINT vk)
{
PHOTKEY phk;
BOOL fKeysExist;
PTHREADINFO ptiCurrent;
PWINDOWSTATION pwinsta = _GetProcessWindowStation(NULL);
ptiCurrent = PtiCurrent();
/*
* Blow it off if the caller is not the windowstation init thread
* and doesn't have the proper access rights
*/
if (grpwinstaList && !CheckWinstaWriteAttributesAccess()) {
return FALSE;
}
/*
* Can't register hotkey for a window of another queue.
* Return FALSE in this case.
*/
if ((pwnd != PWND_FOCUS) && (pwnd != PWND_INPUTOWNER)) {
if (GETPTI(pwnd) != ptiCurrent) {
RIPERR0(ERROR_WINDOW_OF_OTHER_THREAD, RIP_VERBOSE, "");
return FALSE;
}
}
phk = FindHotKey(ptiCurrent, pwnd, id, fsModifiers, vk, FALSE, &fKeysExist);
/*
* If the keys have already been registered, return FALSE.
*/
if (fKeysExist) {
RIPERR0(ERROR_HOTKEY_ALREADY_REGISTERED, RIP_VERBOSE, "");
return FALSE;
}
if (phk == NULL) {
/*
* This hotkey doesn't exist yet.
*/
phk = (PHOTKEY)UserAllocPool(sizeof(HOTKEY), TAG_HOTKEY);
/*
* If the allocation failed, bail out.
*/
if (phk == NULL) {
return FALSE;
}
phk->pti = ptiCurrent;
if ((pwnd != PWND_FOCUS) && (pwnd != PWND_INPUTOWNER)) {
phk->spwnd = NULL;
Lock(&phk->spwnd, pwnd);
} else {
phk->spwnd = pwnd;
}
phk->fsModifiers = fsModifiers;
phk->vk = vk;
phk->id = id;
/*
* Link the new hotkey to the front of the list.
*/
phk->phkNext = gphkFirst;
gphkFirst = phk;
} else {
/*
* Hotkey already exists, reset the keys.
*/
phk->fsModifiers = fsModifiers;
phk->vk = vk;
}
return TRUE;
}
/***************************************************************************\
* _UnregisterHotKey (API)
*
* This API will 'unregister' the specified hwnd/id hotkey so that the
* WM_HOTKEY message will not be generated for it.
*
* History:
* 12-04-90 DavidPe Created.
\***************************************************************************/
BOOL _UnregisterHotKey(
PWND pwnd,
int id)
{
PHOTKEY phk;
BOOL fKeysExist;
phk = FindHotKey(PtiCurrent(), pwnd, id, 0, 0, TRUE, &fKeysExist);
/*
* No hotkey to unregister, return FALSE.
*/
if (phk == NULL) {
RIPERR0(ERROR_HOTKEY_NOT_REGISTERED, RIP_VERBOSE, "");
return FALSE;
}
return TRUE;
}
/***************************************************************************\
* FindHotKey
*
* Both RegisterHotKey() and UnregisterHotKey() call this function to search
* for hotkeys that already exist. If a HOTKEY is found that matches
* fsModifiers and vk, *pfKeysExist is set to TRUE. If a HOTKEY is found that
* matches pwnd and id, a pointer to it is returned.
*
* If fUnregister is TRUE, we remove the HOTKEY from the list if we find
* one that matches pwnd and id and return (PHOTKEY)1.
*
* History:
* 12-04-90 DavidPe Created.
\***************************************************************************/
PHOTKEY FindHotKey(
PTHREADINFO ptiCurrent,
PWND pwnd,
int id,
UINT fsModifiers,
UINT vk,
BOOL fUnregister,
PBOOL pfKeysExist)
{
PHOTKEY phk, phkRet, phkPrev;
/*
* Initialize out 'return' values.
*/
*pfKeysExist = FALSE;
phkRet = NULL;
phk = gphkFirst;
while (phk) {
/*
* If all this matches up then we've found it.
*/
if ((phk->pti == ptiCurrent) && (phk->spwnd == pwnd) && (phk->id == id)) {
if (fUnregister) {
/*
* Unlink the HOTKEY from the list.
*/
if (phk == gphkFirst) {
gphkFirst = phk->phkNext;
} else {
phkPrev->phkNext = phk->phkNext;
}
if ((pwnd != PWND_FOCUS) && (pwnd != PWND_INPUTOWNER)) {
Unlock(&phk->spwnd);
}
UserFreePool((PVOID)phk);
return((PHOTKEY)1);
}
phkRet = phk;
}
/*
* If the key is already registered, set the exists flag so
* the app knows it can't use this hotkey sequence.
*/
if ((phk->fsModifiers == fsModifiers) && (phk->vk == vk)) {
/*
* In the case of PWND_FOCUS, we need to check that the queues
* are the same since PWND_FOCUS is local to the queue it was
* registered under.
*/
if (phk->spwnd == PWND_FOCUS) {
if (phk->pti == ptiCurrent) {
*pfKeysExist = TRUE;
}
} else {
*pfKeysExist = TRUE;
}
}
phkPrev = phk;
phk = phk->phkNext;
}
return phkRet;
}
/***************************************************************************\
* xxxDoHotKeyStuff
*
* This function gets called for every key event from low-level input
* processing. It keeps track of the current state of modifier keys
* and when fsModifiers and vk match up with one of the registered
* hotkeys, a WM_HOTKEY message is generated. DoHotKeyStuff() will
* tell the input system to eat both the make and break for the 'vk'
* event. This prevents apps from getting input that wasn't really
* intended for them. DoHotKeyStuff() returns TRUE if it wants to 'eat'
* the event, FALSE if the system can pass on the event like it normally
* would.
*
* A Note on Modifier-Only Hotkeys
* Some hotkeys involve VK_SHIFT, VK_CONTROL, VK_MENU and/or VK_WINDOWS only.
* These are called Modifier-Only hotkeys.
* In order to distinguish hotkeys such as Alt-Shift-S and and Alt-Shift alone,
* modifier-only hotkeys must operate on a break, not a make.
* In order to prevent Alt-Shift-S from activating the Alt-Shift hotkey when
* the keys are released, modifier-only hotkeys are only activated when a
* modifier keyup (break) was immediately preceded by a modifier keydown (break)
* This also lets Alt-Shift,Shift,Shift activate the Alt-Shift hotkey 3 times.
*
* History:
* 12-05-90 DavidPe Created.
* 4-15-93 Sanfords Added code to return TRUE for Ctrl-Alt-Del events.
\***************************************************************************/
BOOL xxxDoHotKeyStuff(
UINT vk,
BOOL fBreak,
DWORD fsReserveKeys)
{
#ifdef WINDOWS_NOT_HOT
static BOOL fOnlyWinKey = FALSE;
#endif
static UINT fsModifiers = 0;
static UINT fsModOnlyCandidate = 0;
UINT fsModOnlyHotkey;
UINT fs;
PHOTKEY phk;
PWND pwnd;
BOOL fCancel;
BOOL fEatDebugKeyBreak = FALSE;
CheckCritIn();
/*
* Update fsModifiers.
*/
fs = 0;
fsModOnlyHotkey = 0;
switch (vk) {
case VK_RSHIFT:
fs = MOD_RSHIFT;
case VK_LSHIFT:
case VK_SHIFT:
vk = VK_SHIFT;
fs |= MOD_SHIFT;
break;
case VK_RCONTROL:
fs = MOD_RCONTROL;
case VK_LCONTROL:
vk = VK_CONTROL;
case VK_CONTROL:
fs |= MOD_CONTROL;
break;
case VK_RMENU:
fs = MOD_RALT;
case VK_LMENU:
vk = VK_MENU;
case VK_MENU:
fs |= MOD_ALT;
break;
case VK_LWIN:
case VK_RWIN:
fs = MOD_WIN;
break;
default:
/*
* A non-modifier key rules out Modifier-Only hotkeys
*/
fsModOnlyCandidate = 0;
break;
}
if (fBreak) {
fsModifiers &= ~fs;
/*
* If a modifier key is coming up, the current modifier only hotkey
* candidate must be tested to see if it is a hotkey. Store this
* in fsModOnlyHotkey, and prevent the next key release from
* being a candidate by clearing fsModOnlyCandidate.
*/
if (fs != 0) {
fsModOnlyHotkey = fsModOnlyCandidate;
fsModOnlyCandidate = 0;
}
} else {
fsModifiers |= fs;
/*
* If a modifier key is going down, we have a modifier-only hotkey
* candidate. Save current modifier state until the following break.
*/
if (fs != 0) {
fsModOnlyCandidate = fsModifiers;
}
}
if (vk == VK_DELETE) {
/*
* Special case for SAS (Ctrl+Alt+Del) - examine physical key state!
*
* An evil daemon process can fool convincingly pretend to be winlogon
* by registering Alt+Del as a hotkey, and spinning another thread that
* continually calls keybd_event() to send the Ctrl key up: when the
* user types Ctrl+Alt+Del, only Alt+Del will be seen by the system,
* the evil daemon will get woken by WM_HOTKEY and can pretend to be
* winlogon. So look at gafPhysKeyState in this case, to see what keys
* were physically pressed.
* NOTE: If hotkeys are ever made to work under journal playback, make
* sure they don't affect the gafPhysKeyState! - IanJa.
*/
UINT fPhysMods =
(TestKeyDownBit(gafPhysKeyState, VK_MENU) ? MOD_ALT : 0) |
(TestKeyDownBit(gafPhysKeyState, VK_SHIFT) ? MOD_SHIFT : 0) |
(TestKeyDownBit(gafPhysKeyState, VK_CONTROL) ? MOD_CONTROL : 0);
if ((fPhysMods & (MOD_CONTROL|MOD_ALT)) == MOD_CONTROL|MOD_ALT) {
/*
* Use physical modifiers keys
*/
fsModifiers = fPhysMods;
}
}
#ifdef WINDOWS_NOT_HOT
/*
* track whether any key has been pressed since a Windows key went down
*/
if (vk == VK_LWIN || vk == VK_RWIN) {
if (!fBreak) {
fOnlyWinKey = TRUE;
} else if (fOnlyWinKey) {
fOnlyWinKey = FALSE;
CancelJournalling();
pwnd = GETDESKINFO(PtiCurrent())->spwndShell;
if (pwnd != NULL) {
_PostMessage(pwnd, WM_SYSCOMMAND, SC_TASKLIST, 0);
return TRUE;
}
}
} else {
fOnlyWinKey = FALSE;
}
#endif
/*
* If the key is not a hotkey then we're done but first check if the
* key is an Alt-Escape if so we need to cancel journalling.
*
* NOTE: Support for Alt+Esc to cancel journalling dropped in NT 4.0
*/
if (fsModOnlyHotkey && fBreak) {
/*
* A hotkey involving only VK_SHIFT, VK_CONTROL, VK_MENU or VK_WINDOWS
* must only operate on a key release.
*/
if ((phk = IsHotKey(LOWORD(fsModOnlyHotkey), VK_NONE)) == NULL) {
return FALSE;
}
} else if ((phk = IsHotKey(LOWORD(fsModifiers), vk)) == NULL) {
return FALSE;
}
if (phk->id == IDHOT_WINDOWS) {
pwnd = GETDESKINFO(PtiCurrent())->spwndShell;
if (pwnd != NULL) {
_PostMessage(pwnd, WM_SYSCOMMAND, SC_TASKLIST, 0);
return TRUE;
}
}
if ((phk->id == IDHOT_DEBUG) || (phk->id == IDHOT_DEBUGSERVER)) {
if (!fBreak) {
/*
* The DEBUG key has been pressed. Break the appropriate
* thread into the debugger.
*/
fEatDebugKeyBreak = xxxActivateDebugger(phk->fsModifiers);
}
/*
* This'll eat the debug key down and break if we broke into
* the debugger on the server only on the down.
*/
return fEatDebugKeyBreak;
}
/*
* don't allow hotkeys(except for ones owned by the logon process)
* if the window station is locked.
*/
if (((grpdeskRitInput->rpwinstaParent->dwFlags & WSF_SWITCHLOCK) != 0) &&
(phk->pti->Thread->Cid.UniqueProcess != gpidLogon)) {
return FALSE;
}
if ((fsModOnlyHotkey == 0) && fBreak) {
/*
* Do Modifier-Only hotkeys on break events, else return here.
*/
return FALSE;
}
/*
* Unhook hooks if a control-escape, alt-escape, or control-alt-del
* comes through, so the user can cancel if the system seems hung.
*
* Note the hook may be locked so even if the unhook succeeds it
* won't remove the hook from the global asphkStart array. So
* we have to walk the list manually. This code works because
* we are in the critical section and we know other hooks won't
* be deleted.
*
* Once we've unhooked, post a WM_CANCELJOURNAL message to the app
* that set the hook so it knows we did this.
*
* NOTE: Support for Alt+Esc to cancel journalling dropped in NT 4.0
*/
fCancel = FALSE;
if (vk == VK_ESCAPE && LOWORD(fsModifiers) == MOD_CONTROL) {
fCancel = TRUE;
}
if (vk == VK_DELETE && (fsModifiers & (MOD_CONTROL | MOD_ALT)) ==
(MOD_CONTROL | MOD_ALT)) {
fCancel = TRUE;
}
if (fCancel)
CancelJournalling();
/*
* See if the key is reserved by a console window. If it is,
* return FALSE so the key will be passed to the console.
*/
if (fsReserveKeys != 0) {
switch (vk) {
case VK_TAB:
if ((fsReserveKeys & CONSOLE_ALTTAB) &&
((fsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) {
return FALSE;
}
break;
case VK_ESCAPE:
if ((fsReserveKeys & CONSOLE_ALTESC) &&
((fsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) {
return FALSE;
}
if ((fsReserveKeys & CONSOLE_CTRLESC) &&
((fsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_CONTROL)) {
return FALSE;
}
break;
case VK_RETURN:
if ((fsReserveKeys & CONSOLE_ALTENTER) &&
((fsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) {
return FALSE;
}
break;
case VK_SNAPSHOT:
if ((fsReserveKeys & CONSOLE_PRTSC) &&
((fsModifiers & (MOD_CONTROL | MOD_ALT)) == 0)) {
return FALSE;
}
if ((fsReserveKeys & CONSOLE_ALTPRTSC) &&
((fsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) {
return FALSE;
}
break;
case VK_SPACE:
if ((fsReserveKeys & CONSOLE_ALTSPACE) &&
((fsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) {
return FALSE;
}
break;
}
}
/*
* If this is the task-list hotkey, go ahead and set foreground
* status to the task-list queue right now. This prevents problems
* where the user hits ctrl-esc and types-ahead before the task-list
* processes the hotkey and brings up the task-list window.
*/
if ((LOWORD(fsModifiers) == MOD_CONTROL) && (vk == VK_ESCAPE) && !fBreak) {
PWND pwndSwitch;
TL tlpwndSwitch;
if (ghwndSwitch != NULL) {
pwndSwitch = PW(ghwndSwitch);
ThreadLock(pwndSwitch, &tlpwndSwitch);
xxxSetForegroundWindow2(pwndSwitch, NULL, 0);
ThreadUnlock(&tlpwndSwitch);
}
}
/*
* Get the hot key contents.
*/
if (phk->spwnd == NULL) {
_PostThreadMessage(
phk->pti, WM_HOTKEY, phk->id,
MAKELONG(LOWORD(fsModifiers), vk));
} else {
if (phk->spwnd == PWND_INPUTOWNER) {
if (gpqForeground != NULL) {
pwnd = gpqForeground->spwndFocus;
} else {
return FALSE;
}
} else {
pwnd = phk->spwnd;
}
if (pwnd) {
if (pwnd == pwnd->head.rpdesk->pDeskInfo->spwndShell && phk->id == SC_TASKLIST) {
_PostMessage(pwnd, WM_SYSCOMMAND, SC_TASKLIST, 0);
} else {
_PostMessage(pwnd, WM_HOTKEY, phk->id, MAKELONG(LOWORD(fsModifiers), vk));
}
}
}
/*
* If this is a Modifier-Only hotkey, let the modifier break through
* by returning FALSE, otherwise we will have modifier keys stuck down.
*/
return (fsModOnlyHotkey == 0);
}
/***************************************************************************\
* IsHotKey
*
*
* History:
* 03-10-91 DavidPe Created.
\***************************************************************************/
PHOTKEY IsHotKey(
UINT fsModifiers,
UINT vk)
{
PHOTKEY phk;
CheckCritIn();
phk = gphkFirst;
while (phk != NULL) {
/*
* Do the modifiers and vk for this hotkey match the current state?
*/
if ((phk->fsModifiers == fsModifiers) && (phk->vk == vk)) {
return phk;
}
phk = phk->phkNext;
}
return NULL;
}