/****************************************************************************/ /* hotkey.c */ /* */ /* RDP Shadow hotkey handling functions. */ /* */ /* Copyright(C) Microsoft Corporation 1997-2000 */ /****************************************************************************/ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #define TRC_FILE "hotkey" #include #pragma hdrstop #define pTRCWd pWd #include #include #include #include "kbd.h" //TODO: Good Grief! typedef struct { DWORD dwVersion; DWORD dwFlags; DWORD dwMapCount; DWORD dwMap[0]; } SCANCODEMAP, *PSCANCODEMAP; /***************************************************************************\ * How some Virtual Key values change when a SHIFT key is held down. \***************************************************************************/ #define VK_MULTIPLY 0x6A #define VK_SNAPSHOT 0x2C const ULONG aulShiftCvt_VK[] = { MAKELONG(VK_MULTIPLY, VK_SNAPSHOT), MAKELONG(0,0) }; /***************************************************************************\ * How some Virtual Key values change when a CONTROL key is held down. \***************************************************************************/ #define VK_LSHIFT 0xA0 #define VK_RSHIFT 0xA1 #define VK_LCONTROL 0xA2 #define VK_RCONTROL 0xA3 #define VK_LMENU 0xA4 #define VK_RMENU 0xA5 #define VK_NUMLOCK 0x90 #define VK_SCROLL 0x91 #define VK_PAUSE 0x13 #define VK_CANCEL 0x03 //#define KBDEXT (USHORT)0x0100 const ULONG aulControlCvt_VK[] = { MAKELONG(VK_NUMLOCK, VK_PAUSE | KBDEXT), MAKELONG(VK_SCROLL, VK_CANCEL), MAKELONG(0,0) }; /***************************************************************************\ * How some Virtual Key values change when an ALT key is held down. * The SHIFT and ALT keys both alter VK values the same way!! \***************************************************************************/ #define aulAltCvt_VK aulShiftCvt_VK /***************************************************************************\ * This table list keys that may affect Virtual Key values when held down. * * See kbd.h for a full description. * * 101/102key keyboard (type 4): * Virtual Key values vary only if CTRL is held down. * 84-86 key keyboards (type 3): * Virtual Key values vary if one of SHIFT, CTRL or ALT is held down. \***************************************************************************/ #define VK_SHIFT 0x10 #define VK_CONTROL 0x11 #define VK_MENU 0x12 //#define KBDSHIFT 1 //#define KBDCTRL 2 //#define KBDALT 4 const VK_TO_BIT aVkToBits_VK[] = { { VK_SHIFT, KBDSHIFT }, // 0x01 { VK_CONTROL, KBDCTRL }, // 0x02 { VK_MENU, KBDALT }, // 0x04 { 0, 0 } }; /***************************************************************************\ * Tables defining how some Virtual Key values are modified when other keys * are held down. * Translates key combinations into indices for gapulCvt_VK_101[] or for * gapulCvt_VK_84[] or for * * See kbd.h for a full description. * \***************************************************************************/ //#define SHFT_INVALID 0x0F const MODIFIERS Modifiers_VK = { (PVK_TO_BIT)&aVkToBits_VK[0], 4, // Maximum modifier bitmask/index { SHFT_INVALID, // no keys held down (no VKs are modified) 0, // SHIFT held down 84-86 key kbd 1, // CTRL held down 101/102 key kbd SHFT_INVALID, // CTRL-SHIFT held down (no VKs are modified) 2 // ALT held down 84-86 key kbd } }; /***************************************************************************\ * A tables of pointers indexed by the number obtained from Modify_VK. * If a pointer is non-NULL then the table it points to is searched for * Virtual Key that should have their values changed. * There are two versions: one for 84-86 key kbds, one for 101/102 key kbds. * gapulCvt_VK is initialized with the default (101/102 key kbd). \***************************************************************************/ const ULONG *const gapulCvt_VK_101[] = { NULL, // No VKs are changed by SHIFT being held down aulControlCvt_VK, // Some VKs are changed by CTRL being held down NULL // No VKs are changed by ALT being held down }; const ULONG *const gapulCvt_VK_84[] = { aulShiftCvt_VK, // Some VKs are changed by SHIFT being held down aulControlCvt_VK, // Some VKs are changed by CTRL being held down aulAltCvt_VK // Some VKs are changed by ALT being held down }; /* * Determine the state of all the Modifier Keys (a Modifier Key * is any key that may modify values produced by other keys: these are * commonly SHIFT, CTRL and/or ALT) * Build a bit-mask (wModBits) to encode which modifier keys are depressed. */ #define KEY_BYTE(pb, vk) pb[((BYTE)(vk)) >> 2] #define KEY_DOWN_BIT(vk) (1 << ((((BYTE)(vk)) & 3) << 1)) #define KEY_TOGGLE_BIT(vk) (1 << (((((BYTE)(vk)) & 3) << 1) + 1)) #define TestKeyDownBit(pb, vk) (KEY_BYTE(pb,vk) & KEY_DOWN_BIT(vk)) #define SetKeyDownBit(pb, vk) (KEY_BYTE(pb,vk) |= KEY_DOWN_BIT(vk)) #define ClearKeyDownBit(pb, vk) (KEY_BYTE(pb,vk) &= ~KEY_DOWN_BIT(vk)) #define TestKeyToggleBit(pb, vk) (KEY_BYTE(pb,vk) & KEY_TOGGLE_BIT(vk)) #define SetKeyToggleBit(pb, vk) (KEY_BYTE(pb,vk) |= KEY_TOGGLE_BIT(vk)) #define ClearKeyToggleBit(pb, vk) (KEY_BYTE(pb,vk) &= ~KEY_TOGGLE_BIT(vk)) #define ToggleKeyToggleBit(pb, vk) (KEY_BYTE(pb,vk) ^= KEY_TOGGLE_BIT(vk)) WORD GetModifierBits( PMODIFIERS pModifiers, LPBYTE afKeyState) { PVK_TO_BIT pVkToBit = pModifiers->pVkToBit; WORD wModBits = 0; while (pVkToBit->Vk) { if (TestKeyDownBit(afKeyState, pVkToBit->Vk)) { wModBits |= pVkToBit->ModBits; } pVkToBit++; } return wModBits; } /***************************************************************************\ * MapScancode * * Converts a scancode (and it's prefix, if any) to a different scancode * and prefix. * * Parameters: * pbScanCode = address of Scancode byte, the scancode may be changed * pbPrefix = address of Prefix byte, The prefix may be changed * * Return value: * TRUE - mapping was found, scancode was altered. * FALSE - no mapping fouind, scancode was not altered. * * Note on scancode map table format: * A table entry DWORD of 0xE0450075 means scancode 0x45, prefix 0xE0 * gets mapped to scancode 0x75, no prefix * * History: * 96-04-18 IanJa Created. \***************************************************************************/ BOOL MapScancode( PSCANCODEMAP gpScancodeMap, PBYTE pbScanCode, PBYTE pbPrefix ) { DWORD *pdw; WORD wT = MAKEWORD(*pbScanCode, *pbPrefix); ASSERT(gpScancodeMap != NULL); for (pdw = &(gpScancodeMap->dwMap[0]); *pdw; pdw++) { if (HIWORD(*pdw) == wT) { wT = LOWORD(*pdw); *pbScanCode = LOBYTE(wT); *pbPrefix = HIBYTE(wT); return TRUE; } } return FALSE; } /* * Given modifier bits, return the modification number. */ WORD GetModificationNumber( PMODIFIERS pModifiers, WORD wModBits) { if (wModBits > pModifiers->wMaxModBits) { return SHFT_INVALID; } return pModifiers->ModNumber[wModBits]; } /***************************************************************************\ * UpdatePhysKeyState * * A helper routine for KeyboardApcProcedure. * Based on a VK and a make/break flag, this function will update the physical * keystate table. * * History: * 10-13-91 IanJa Created. \***************************************************************************/ void UpdatePhysKeyState( BYTE Vk, BOOL fBreak, LPBYTE gafPhysKeyState ) { if (fBreak) { ClearKeyDownBit(gafPhysKeyState, Vk); } else { /* * This is a key make. If the key was not already down, update the * physical toggle bit. */ if (!TestKeyDownBit(gafPhysKeyState, Vk)) { if (TestKeyToggleBit(gafPhysKeyState, Vk)) { ClearKeyToggleBit(gafPhysKeyState, Vk); } else { SetKeyToggleBit(gafPhysKeyState, Vk); } } /* * This is a make, so turn on the physical key down bit. */ SetKeyDownBit(gafPhysKeyState, Vk); } } /*****************************************************************************\ * VKFromVSC * * This function is called from KeyEvent() after each call to VSCFromSC. The * keyboard input data passed in is translated to a virtual key code. * This translation is dependent upon the currently depressed modifier keys. * * For instance, scan codes representing the number pad keys may be * translated into VK_NUMPAD codes or cursor movement codes depending * upon the state of NumLock and the modifier keys. * * History: * \*****************************************************************************/ BYTE WD_VKFromVSC( PKBDTABLES pKbdTbl, PKE pke, BYTE bPrefix, LPBYTE gafPhysKeyState, BOOLEAN KeyboardType101 ) { USHORT usVKey = 0xFF; PVSC_VK pVscVk = NULL; static BOOL fVkPause; PULONG *gapulCvt_VK; // DBG_UNREFERENCED_PARAMETER(afKeyState); if (pke->bScanCode == 0xFF) { /* * Kbd overrun (kbd hardware and/or keyboard driver) : Beep! * (some DELL keyboards send 0xFF if keys are hit hard enough, * presumably due to keybounce) */ // xxxMessageBeep(0); return 0; } pke->bScanCode &= 0x7F; // if (gptiForeground == NULL) { // RIPMSG0(RIP_VERBOSE, "VKFromVSC: NULL gptiForeground\n"); // pKbdTbl = gpKbdTbl; // } else { // if (gptiForeground->spklActive) { // pKbdTbl = gptiForeground->spklActive->spkf->pKbdTbl; // } else { // RIPMSG0(RIP_VERBOSE, "VKFromVSC: NULL spklActive\n"); // pKbdTbl = gpKbdTbl; // } // } if (bPrefix == 0) { if (pke->bScanCode < pKbdTbl->bMaxVSCtoVK) { /* * direct index into non-prefix table */ usVKey = pKbdTbl->pusVSCtoVK[pke->bScanCode]; if (usVKey == 0) { return 0xFF; } } else { /* * unexpected scancode */ // RIPMSG2(RIP_VERBOSE, "unrecognized scancode 0x%x, prefix %x", // pke->bScanCode, bPrefix); return 0xFF; } } else { /* * Scan the E0 or E1 prefix table for a match */ if (bPrefix == 0xE0) { /* * Ignore the SHIFT keystrokes generated by the hardware */ if ((pke->bScanCode == SCANCODE_LSHIFT) || (pke->bScanCode == SCANCODE_RSHIFT)) { return 0; } pVscVk = pKbdTbl->pVSCtoVK_E0; } else if (bPrefix == 0xE1) { pVscVk = pKbdTbl->pVSCtoVK_E1; } while (pVscVk != NULL && pVscVk->Vk) { if (pVscVk->Vsc == pke->bScanCode) { usVKey = pVscVk->Vk; break; } pVscVk++; } } /* * Scancode set 1 returns PAUSE button as E1 1D 45 (E1 Ctrl NumLock) * so convert E1 Ctrl to VK_PAUSE, and remember to discard the NumLock */ if (fVkPause) { /* * This is the "45" part of the Pause scancode sequence. * Discard this key event: it is a false NumLock */ fVkPause = FALSE; return 0; } if (usVKey == VK_PAUSE) { /* * This is the "E1 1D" part of the Pause scancode sequence. * Alter the scancode to the value Windows expects for Pause, * and remember to discard the "45" scancode that will follow */ pke->bScanCode = 0x45; fVkPause = TRUE; } /* * Convert to a different VK if some modifier keys are depressed. */ if (usVKey & KBDMULTIVK) { WORD nMod; PULONG pul; nMod = GetModificationNumber( (MODIFIERS *)&Modifiers_VK, GetModifierBits((MODIFIERS *)&Modifiers_VK, gafPhysKeyState)); /* * Scan gapulCvt_VK[nMod] for matching VK. */ if ( KeyboardType101 ) gapulCvt_VK = (PULONG *)gapulCvt_VK_101; else gapulCvt_VK = (PULONG *)gapulCvt_VK_84; if ((nMod != SHFT_INVALID) && ((pul = gapulCvt_VK[nMod]) != NULL)) { while (*pul != 0) { if (LOBYTE(*pul) == LOBYTE(usVKey)) { pke->usFlaggedVk = (USHORT)HIWORD(*pul); return (BYTE)pke->usFlaggedVk; } pul++; } } } pke->usFlaggedVk = usVKey; return (BYTE)usVKey; } /***************************************************************************\ * KeyboardHotKeyProcedure * * return TRUE if the hotkey is detected, false otherwise * * HotkeyVk (input) * - hotkey to look for * HotkeyModifiers (input) * - hotkey to look for * pkei (input) * - scan code * gpScancodeMap (input) * - scan code map from WIN32K * pKbdTbl (input) * - keyboard layout from WIN32K * KeyboardType101 (input) * - keyboard type from WIN32K * gafPhysKeyState (input/output) * - key states * \***************************************************************************/ BOOLEAN KeyboardHotKeyProcedure( BYTE HotkeyVk, USHORT HotkeyModifiers, PKEYBOARD_INPUT_DATA pkei, PVOID gpScancodeMap, PVOID pKbdTbl, BOOLEAN KeyboardType101, PVOID gafPhysKeyState ) { BYTE Vk; BYTE bPrefix; KE ke; WORD ModBits; if ( !pKbdTbl || !gafPhysKeyState ) { return FALSE; } if (pkei->Flags & KEY_E0) { bPrefix = 0xE0; } else if (pkei->Flags & KEY_E1) { bPrefix = 0xE1; } else { bPrefix = 0; } ke.bScanCode = (BYTE)(pkei->MakeCode & 0x7F); if (gpScancodeMap) { MapScancode(gpScancodeMap, &ke.bScanCode, &bPrefix); } Vk = WD_VKFromVSC(pKbdTbl, &ke, bPrefix, gafPhysKeyState, KeyboardType101); if ((Vk == 0) || (Vk == VK__none_)) { return FALSE; } if (pkei->Flags & KEY_BREAK) { ke.usFlaggedVk |= KBDBREAK; } // Vk = (BYTE)ke.usFlaggedVk; UpdatePhysKeyState(Vk, ke.usFlaggedVk & KBDBREAK, gafPhysKeyState); /* * Convert Left/Right Ctrl/Shift/Alt key to "unhanded" key. * ie: if VK_LCONTROL or VK_RCONTROL, convert to VK_CONTROL etc. */ if ((Vk >= VK_LSHIFT) && (Vk <= VK_RMENU)) { Vk = (BYTE)((Vk - VK_LSHIFT) / 2 + VK_SHIFT); UpdatePhysKeyState(Vk, ke.usFlaggedVk & KBDBREAK, gafPhysKeyState); } /* * Now check if the shadow hotkey has been hit */ if ( Vk == HotkeyVk && !(ke.usFlaggedVk & KBDBREAK) ) { ModBits = GetModifierBits( (MODIFIERS *)&Modifiers_VK, gafPhysKeyState ); if ( ModBits == HotkeyModifiers ) return( TRUE ); } return( FALSE ); } /******************************************************************************* * * KeyboardSetKeyState * * Initialize keyboard state * * ENTRY: * pgafPhysKeyState (input/output) * - buffer to allocate or clear * * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ #define CVKKEYSTATE 256 #define CBKEYSTATE (CVKKEYSTATE >> 2) NTSTATUS KeyboardSetKeyState( PTSHARE_WD pWd, PVOID *pgafPhysKeyState ) { NTSTATUS Status = STATUS_SUCCESS; if ( *pgafPhysKeyState == NULL ) { *pgafPhysKeyState = COM_Malloc(CBKEYSTATE); if ( *pgafPhysKeyState == NULL ) return STATUS_NO_MEMORY; } RtlZeroMemory( *pgafPhysKeyState, CBKEYSTATE ); return Status; } /******************************************************************************* * * KeyboardFixupLayout * * Fixup the pointers inside the keyboard layout * * ENTRY: * pLayout (input/output) * - buffer to fixup * pOriginal (input) * - pointer to original layout buffer * Length (input) * - length of layout buffer * pKbdTblOriginal (input) * - pointer to original KbdTbl table * ppKbdTbl (output) * - pointer to location to save ptr to new KbdTbl table * * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ #define FIXUP_PTR(p, pBase, pOldBase) ((p) ? (p) = (PVOID) ( (PBYTE)pBase + (ULONG) ( (PBYTE)p - (PBYTE)pOldBase ) ) : 0) //#define CHECK_PTR( p, Limit) { if ( (PVOID)p > Limit ) { ASSERT(FALSE); return STATUS_BUFFER_TOO_SMALL; } } #define CHECK_PTR( ptr, Limit) \ { if ( (PBYTE) (ptr) > (PBYTE) (Limit) ) { \ KdPrint(("Bad Ptr, Line %ld: %p > %p \n", __LINE__, ptr, Limit)); \ /* ASSERT(FALSE); */ \ /* return STATUS_BUFFER_TOO_SMALL; */ } } NTSTATUS KeyboardFixupLayout( PVOID pKbdLayout, PVOID pOriginal, ULONG Length, PVOID pKbdTblOrig, PVOID *ppKbdTbl ) { NTSTATUS Status = STATUS_SUCCESS; VK_TO_WCHAR_TABLE *pVkToWcharTable; VSC_LPWSTR *pKeyName; LPWSTR *lpDeadKey; PKBDTABLES pKbdTbl; PVOID pLimit; if ( Length < sizeof(KBDTABLES) ) { Status = STATUS_BUFFER_TOO_SMALL; goto error; } pLimit = (PBYTE)pKbdLayout + Length; pKbdTbl = pKbdTblOrig; FIXUP_PTR(pKbdTbl, pKbdLayout, pOriginal); FIXUP_PTR(pKbdTbl->pCharModifiers, pKbdLayout, pOriginal); CHECK_PTR(pKbdTbl->pCharModifiers, pLimit); FIXUP_PTR(pKbdTbl->pCharModifiers->pVkToBit, pKbdLayout, pOriginal); CHECK_PTR(pKbdTbl->pCharModifiers->pVkToBit, pLimit); if (FIXUP_PTR(pKbdTbl->pVkToWcharTable, pKbdLayout, pOriginal)) { CHECK_PTR(pKbdTbl->pVkToWcharTable, pLimit); for (pVkToWcharTable = pKbdTbl->pVkToWcharTable; pVkToWcharTable->pVkToWchars != NULL; pVkToWcharTable++) { FIXUP_PTR(pVkToWcharTable->pVkToWchars, pKbdLayout, pOriginal); CHECK_PTR(pVkToWcharTable->pVkToWchars, pLimit); } } FIXUP_PTR(pKbdTbl->pDeadKey, pKbdLayout, pOriginal); CHECK_PTR(pKbdTbl->pDeadKey, pLimit); if (FIXUP_PTR(pKbdTbl->pKeyNames, pKbdLayout, pOriginal)) { CHECK_PTR(pKbdTbl->pKeyNames, pLimit); for (pKeyName = pKbdTbl->pKeyNames; pKeyName->vsc != 0; pKeyName++) { FIXUP_PTR(pKeyName->pwsz, pKbdLayout, pOriginal); CHECK_PTR(pKeyName->pwsz, pLimit); } } if (FIXUP_PTR(pKbdTbl->pKeyNamesExt, pKbdLayout, pOriginal)) { CHECK_PTR(pKbdTbl->pKeyNamesExt, pLimit); for (pKeyName = pKbdTbl->pKeyNamesExt; pKeyName->vsc != 0; pKeyName++) { FIXUP_PTR(pKeyName->pwsz, pKbdLayout, pOriginal); CHECK_PTR(pKeyName->pwsz, pLimit); } } if (FIXUP_PTR(pKbdTbl->pKeyNamesDead, pKbdLayout, pOriginal)) { CHECK_PTR(pKbdTbl->pKeyNamesDead, pLimit); for (lpDeadKey = pKbdTbl->pKeyNamesDead; *lpDeadKey != NULL; lpDeadKey++) { FIXUP_PTR(*lpDeadKey, pKbdLayout, pOriginal); CHECK_PTR(*lpDeadKey, pLimit); } } FIXUP_PTR(pKbdTbl->pusVSCtoVK, pKbdLayout, pOriginal); CHECK_PTR(pKbdTbl->pusVSCtoVK, pLimit); FIXUP_PTR(pKbdTbl->pVSCtoVK_E0, pKbdLayout, pOriginal); CHECK_PTR(pKbdTbl->pVSCtoVK_E0, pLimit); FIXUP_PTR(pKbdTbl->pVSCtoVK_E1, pKbdLayout, pOriginal); CHECK_PTR(pKbdTbl->pVSCtoVK_E1, pLimit); *ppKbdTbl = pKbdTbl; error: return( Status ); } #ifdef __cplusplus } #endif /* __cplusplus */