|
|
/****************************** Module Header ******************************\
* Module Name: hotkeys.c * * Copyright (c) 1985 - 1999, 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
static PHOTKEY gphkHashTable[128];
/*
* This is the hash function for vks. The vast majority of hotkeys will have * vk values < 128, so we limit our table to that size. Worst case (all vks * > 128) we'll have the same perf (essentially) as the old, linked list * code. */ __inline BYTE HKHashVK( UINT vk) { return (BYTE)(vk & (ARRAY_SIZE(gphkHashTable) - 1)); }
/***************************************************************************\
* HKGetHashHead * * This routine returns the start of the bucket keyed by the specified vk. * * History: * 08-13-2002 JasonSch Created. \***************************************************************************/ PHOTKEY HKGetHashHead( UINT vk) { return gphkHashTable[HKHashVK(vk)]; }
/***************************************************************************\
* HKInsertHashElement * * Inserts a HOTKEY structure into the hash table. * * History: * 08-13-2002 JasonSch Created. \***************************************************************************/ VOID HKInsertHashElement( PHOTKEY phk) { BYTE index = HKHashVK(phk->vk); PHOTKEY phkT;
phkT = gphkHashTable[index]; phk->phkNext = phkT; gphkHashTable[index] = phk; }
/***************************************************************************\
* SetDebugHotKeys * * This routine registers the default system hotkeys for debugging. * * History: * 12-04-90 DavidPe Created. \***************************************************************************/ VOID SetDebugHotKeys( VOID) { UINT VkDebug;
FastGetProfileDwordW(NULL, PMAP_AEDEBUG, L"UserDebuggerHotkey", 0, &VkDebug, 0); if (VkDebug == 0) { if (ENHANCED_KEYBOARD(gKeyboardInfo.KeyboardIdentifier)) { VkDebug = VK_F12; } else { VkDebug = VK_SUBTRACT; } } else { UserAssert((0xFFFFFF00 & VkDebug) == 0); }
_UnregisterHotKey(PWND_INPUTOWNER, IDHOT_DEBUG); _UnregisterHotKey(PWND_INPUTOWNER, IDHOT_DEBUGSERVER);
_RegisterHotKey(PWND_INPUTOWNER, IDHOT_DEBUG, 0, VkDebug); _RegisterHotKey(PWND_INPUTOWNER, IDHOT_DEBUGSERVER, MOD_SHIFT, VkDebug); }
/***************************************************************************\
* DestroyThreadsHotKeys * * History: * 26-Feb-1991 mikeke Created. \***************************************************************************/ VOID DestroyThreadsHotKeys( VOID) { PHOTKEY *pphk, phk; PTHREADINFO ptiCurrent = PtiCurrent(); int i = 0;
for (; i < ARRAY_SIZE(gphkHashTable); ++i) { pphk = &gphkHashTable[i]; 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 * * Frees hotkeys associated with the specified pwnd that were not explicitly * unregistered by the app. * * History: * 23-Sep-1992 IanJa Created. \***************************************************************************/ VOID DestroyWindowsHotKeys( PWND pwnd) { PHOTKEY *pphk, phk; int i = 0;
for (; i < ARRAY_SIZE(gphkHashTable); ++i) { pphk = &gphkHashTable[i]; 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. * * 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, bSAS; PTHREADINFO ptiCurrent; WORD wFlags;
wFlags = fsModifiers & MOD_SAS; fsModifiers &= ~MOD_SAS;
ptiCurrent = PtiCurrent();
/*
* Blow it off if the caller is not the windowstation init thread * and doesn't have the proper access rights */ if (PsGetCurrentProcess() != gpepCSRSS) { if (grpWinStaList && !CheckWinstaWriteAttributesAccess()) { return FALSE; } }
/*
* If VK_PACKET is specified, just bail out, since VK_PACKET is * not a real keyboard input. */ if (vk == VK_PACKET) { return FALSE; }
/*
* If this is the SAS check that winlogon is the one registering it. */ if ((wFlags & MOD_SAS) != 0 && PsGetCurrentProcessId() == gpidLogon) { bSAS = TRUE; } else { bSAS = FALSE; }
/*
* Can't register hotkey for a window of another queue. */ if (pwnd != PWND_FOCUS && pwnd != PWND_INPUTOWNER) { if (GETPTI(pwnd) != ptiCurrent) { RIPERR1(ERROR_WINDOW_OF_OTHER_THREAD, RIP_WARNING, "hwnd 0x%x belongs to a different thread", HWq(pwnd));
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_WARNING, "Hotkey already exists"); return FALSE; }
if (phk == NULL) {
/*
* This hotkey doesn't exist yet. */ phk = (PHOTKEY)UserAllocPool(sizeof(HOTKEY), TAG_HOTKEY); 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 = (WORD)fsModifiers; phk->wFlags = wFlags;
phk->vk = vk; phk->id = id;
/*
* Add the new hotkey to our global hash. */ HKInsertHashElement(phk); } else { /*
* Hotkey already exists, reset the keys. */ phk->fsModifiers = (WORD)fsModifiers; phk->wFlags = wFlags; phk->vk = vk; }
if (bSAS) { /*
* Store the SAS on the terminal. */ gvkSAS = vk; gfsSASModifiers = fsModifiers; }
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; PTHREADINFO ptiCurrent = PtiCurrent();
phk = FindHotKey(ptiCurrent, pwnd, id, 0, 0, TRUE, &fKeysExist); if (phk == NULL) { RIPERR2(ERROR_HOTKEY_NOT_REGISTERED, (pwnd == PWND_INPUTOWNER) ? RIP_VERBOSE : RIP_WARNING, "Hotkey 0x%x on pwnd 0x%p does not exist", id, pwnd);
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; BYTE index = HKHashVK(vk);
UserAssert(!fUnregister || vk == 0);
/*
* Initialize out 'return' values. */ *pfKeysExist = FALSE; phkRet = NULL;
phk = gphkHashTable[index];
hashloop: 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 == gphkHashTable[index]) { gphkHashTable[index] = phk->phkNext; } else { phkPrev->phkNext = phk->phkNext; }
if (pwnd != PWND_FOCUS && pwnd != PWND_INPUTOWNER) { Unlock(&phk->spwnd); } UserFreePool(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 == (WORD)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; }
/*
* This is needed because when called from unregister we specify 0 as * the VK so the hash is always 0 and we need to index through the * entire hash table to try to find it. */ if (fUnregister && ++index < ARRAY_SIZE(gphkHashTable)) { phk = gphkHashTable[index]; goto hashloop; }
return phkRet; }
/***************************************************************************\
* IsSAS * * Checks the physical state of keyboard modifiers that would effect SAS. \***************************************************************************/ BOOL IsSAS( BYTE vk, UINT *pfsModifiers) { CheckCritIn();
if (gvkSAS != vk) { return FALSE; }
/*
* Special case for SAS - examine real physical modifier-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 gfsSASModifiersDown 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 gfsSASModifiersDown! - IanJa. */ if (gfsSASModifiersDown == gfsSASModifiers) { *pfsModifiers = gfsSASModifiersDown; return TRUE; }
return FALSE; }
/*
* The below two states are used by xxxDoHotKeyStuff(). * Originally function-static variables, but as it's required * to clear those flags after the system wakes up from hybernation, * they are made global, */ UINT gfsModifiers; UINT gfsModOnlyCandidate;
VOID ClearCachedHotkeyModifiers( VOID) { /*
* Clear the cached modifiers. */ gfsModifiers = 0; gfsModOnlyCandidate = 0;
/*
* Clear the special modifier cache for the Ctrl+Alt+Del recognition. * (See comments in IsSAS()). */ gfsSASModifiersDown = 0; }
/***************************************************************************\
* 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 gfsModifiers 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) { UINT fsModOnlyHotkey; UINT fs; PHOTKEY phk; BOOL fCancel; BOOL fEatDebugKeyBreak; PWND pwnd; BOOL bSAS;
CheckCritIn(); UserAssert(IsWinEventNotifyDeferredOK());
if (gfInNumpadHexInput & NUMPAD_HEXMODE_LL) { RIPMSGF0(RIP_VERBOSE, "Since we're in gfInNumpadHexInput, just bail out."); return FALSE; }
/*
* Update gfsModifiers. */ fs = 0; fsModOnlyHotkey = 0;
switch (vk) { case VK_SHIFT: fs = MOD_SHIFT; break;
case VK_CONTROL: fs = MOD_CONTROL; break;
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 */ gfsModOnlyCandidate = 0; break; }
if (fBreak) { gfsModifiers &= ~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 = gfsModOnlyCandidate; gfsModOnlyCandidate = 0; } } else { gfsModifiers |= 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) { gfsModOnlyCandidate = gfsModifiers; } }
/*
* We look at the physical state for the modifiers because they cannot be * manipulated and this prevents someone from writing a trojan winlogon * look alike (see comment in AreModifiersIndicatingSAS). */ bSAS = IsSAS((BYTE)vk, &gfsModifiers);
/*
* 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(fsModOnlyHotkey, VK_NONE)) == NULL) { return FALSE; } } else if ((phk = IsHotKey(gfsModifiers, vk)) == NULL) { return FALSE; }
/*
* If we tripped a SAS hotkey, but it's not really the SAS, don't do it. */ if ((phk->wFlags & MOD_SAS) && !bSAS) { return FALSE;
}
#ifdef GENERIC_INPUT
if (gpqForeground && TestRawInputMode(PtiKbdFromQ(gpqForeground), NoHotKeys) && (phk->wFlags & MOD_SAS) == 0) { /*
* NOTE: * If the foreground thread does not want the hotkey handling, * just bail out. * * Exception: Ctrl+Alt+Del should be strictly handled by the system. */ return FALSE; } #endif
if (phk->id == IDHOT_WINDOWS) { pwnd = GETDESKINFO(PtiCurrent())->spwndShell; if (pwnd != NULL) { gfsModOnlyCandidate = 0; /* Make it return TRUE */ goto PostTaskListSysCmd; } }
if (phk->id == IDHOT_DEBUG || phk->id == IDHOT_DEBUGSERVER) { if (!fBreak) { /*
* The DEBUG key has been pressed. Break the appropriate thread * into the debugger. We won't need phk after this callback * because we return immediately. */ fEatDebugKeyBreak = xxxActivateDebugger(phk->fsModifiers); } else { fEatDebugKeyBreak = FALSE; }
/*
* 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->dwWSF_Flags & WSF_SWITCHLOCK) != 0) && (PsGetThreadProcessId(phk->pti->pEThread) != gpidLogon)) { RIPMSG0(RIP_WARNING, "Ignoring hotkey because Workstation locked"); 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 && (gfsModifiers == MOD_CONTROL)) { fCancel = TRUE; }
if (bSAS) { fCancel = TRUE; }
if (fCancel) { zzzCancelJournalling(); // BUG BUG phk might go away IANJA
}
/*
* 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) && ((gfsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) { return FALSE; } break; case VK_ESCAPE: if ((fsReserveKeys & CONSOLE_ALTESC) && ((gfsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) { return FALSE; } if ((fsReserveKeys & CONSOLE_CTRLESC) && ((gfsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_CONTROL)) { return FALSE; } break; case VK_RETURN: if ((fsReserveKeys & CONSOLE_ALTENTER) && ((gfsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) { return FALSE; } break; case VK_SNAPSHOT: if ((fsReserveKeys & CONSOLE_PRTSC) && ((gfsModifiers & (MOD_CONTROL | MOD_ALT)) == 0)) { return FALSE; } if ((fsReserveKeys & CONSOLE_ALTPRTSC) && ((gfsModifiers & (MOD_CONTROL | MOD_ALT)) == MOD_ALT)) { return FALSE; } break; case VK_SPACE: if ((fsReserveKeys & CONSOLE_ALTSPACE) && ((gfsModifiers & (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 ((gfsModifiers == MOD_CONTROL) && (vk == VK_ESCAPE) && !fBreak) { PWND pwndSwitch; TL tlpwndSwitch;
if (ghwndSwitch != NULL) { pwndSwitch = PW(ghwndSwitch); ThreadLock(pwndSwitch, &tlpwndSwitch); xxxSetForegroundWindow2(pwndSwitch, NULL, 0); // BUG BUG phk might go away IANJA
ThreadUnlock(&tlpwndSwitch); } }
/*
* Get the hot key contents. */ if (phk->spwnd == NULL) { _PostThreadMessage(phk->pti, WM_HOTKEY, phk->id, MAKELONG(gfsModifiers, vk)); /*
* Since this hotkey is for this guy, he owns the last input. */ glinp.ptiLastWoken = phk->pti;
} 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) { PostTaskListSysCmd: _PostMessage(pwnd, WM_SYSCOMMAND, SC_TASKLIST, 0); } else { _PostMessage(pwnd, WM_HOTKEY, phk->id, MAKELONG(gfsModifiers, vk)); }
/*
* Since this hotkey is for this guy, he owns the last input. */ glinp.ptiLastWoken = GETPTI(pwnd); } }
/*
* 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 = HKGetHashHead(vk); 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 phk; }
|