//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "stdafx.h" #include "Keyboard.h" // memdbgon must be the last include file in a .cpp file!!! #include // // Defines key state bit masks. // #define KEYSTATE_DOWN 0x0000FFFF #define KEYSTATE_IMPULSE_DOWN 0x00010000 #define KEYSTATE_IMPULSE_UP 0x00020000 // // List of allowed modifier keys and their associated bit masks. // static KeyMap_t ModifierKeyTable[] = { { VK_SHIFT, KEY_MOD_SHIFT, 0 }, { VK_CONTROL, KEY_MOD_CONTROL, 0 }, { VK_MENU, KEY_MOD_ALT, 0 } }; //----------------------------------------------------------------------------- // Purpose: Constructor. //----------------------------------------------------------------------------- CKeyboard::CKeyboard(void) { g_uKeyMaps = 0; } //----------------------------------------------------------------------------- // Purpose: Destructor. //----------------------------------------------------------------------------- CKeyboard::~CKeyboard(void) { } //----------------------------------------------------------------------------- // Purpose: Adds a key binding to the // Input : uChar - The virtual keycode of the primary key that must be held down. // uModifierKeys - Bitflags specifying which modifier keys must be // held down along with the key specified by uChar. // uLogicalKey - An application-specific value that indicates which // logical function //----------------------------------------------------------------------------- void CKeyboard::AddKeyMap(unsigned int uChar, unsigned int uModifierKeys, unsigned int uLogicalKey) { g_uKeyMap[g_uKeyMaps].uChar = uChar; g_uKeyMap[g_uKeyMaps].uModifierKeys = uModifierKeys; g_uKeyMap[g_uKeyMaps].uLogicalKey = uLogicalKey; g_uKeyMaps++; } //----------------------------------------------------------------------------- // Purpose: Clears the KEYSTATE_IMPULSE_UP and KEYSTATE_IMPULSE_DOWN flags from // all physical and logical keys. //----------------------------------------------------------------------------- void CKeyboard::ClearImpulseFlags(void) { int nKey; // // Clear the impulse flags for all the physical keys. // for (nKey = 0; nKey < sizeof(g_uPhysicalKeyState) / sizeof(g_uPhysicalKeyState[0]); nKey++) { g_uPhysicalKeyState[nKey] &= ~(KEYSTATE_IMPULSE_DOWN | KEYSTATE_IMPULSE_UP); } // // Clear the impulse flags for all the logical keys. // for (nKey = 0; nKey < sizeof(g_uLogicalKeyState) / sizeof(g_uLogicalKeyState[0]); nKey++) { g_uLogicalKeyState[nKey] &= ~(KEYSTATE_IMPULSE_DOWN | KEYSTATE_IMPULSE_UP); } } //----------------------------------------------------------------------------- // Purpose: Zeros out the key state for all physical and logical keys. //----------------------------------------------------------------------------- void CKeyboard::ClearKeyStates(void) { int nKey; // // Clear the physical key states. // for (nKey = 0; nKey < sizeof(g_uPhysicalKeyState) / sizeof(g_uPhysicalKeyState[0]); nKey++) { g_uPhysicalKeyState[nKey] = 0; } // // Clear the logical key states. // for (nKey = 0; nKey < sizeof(g_uLogicalKeyState) / sizeof(g_uLogicalKeyState[0]); nKey++) { g_uLogicalKeyState[nKey] = 0; } } //----------------------------------------------------------------------------- // Purpose: Gets a floating point value indicating about how long the logical // key has been held down during this sample period. // Input : uLogicalKey - Logical key to check. // Output : Returns one of the following values: // 0.25 if a key was pressed and released during the sample period, // 0.5 if it was pressed and held, // 0 if held then released, and // 1.0 if held for the entire time. //----------------------------------------------------------------------------- float CKeyboard::GetKeyScale(unsigned int uLogicalKey) { if (uLogicalKey >= MAX_LOGICAL_KEYS) { return(0); } unsigned int uKeyState = g_uLogicalKeyState[uLogicalKey]; bool bImpulseDown = (uKeyState & KEYSTATE_IMPULSE_DOWN) != 0; bool bImpulseUp = (uKeyState & KEYSTATE_IMPULSE_UP) != 0; bool bDown = (uKeyState & KEYSTATE_DOWN) != 0; float fValue = 0; // // If we have a leading edge and no trailing edge, the key should be down. // if (bImpulseDown && !bImpulseUp) { if (bDown) { // // Pressed and held this frame. // fValue = 0.5; } } // // If we have a trailing edge and no leading edge, the key should be up. // if (bImpulseUp && !bImpulseDown) { if (!bDown) { // // Released this frame. // fValue = 0; } } // // If we have neither a leading edge nor a trailing edge, the key was either // up the whole frame or down the whole frame. // if (!bImpulseDown && !bImpulseUp) { if (bDown) { // // Held the entire frame // fValue = 1.0; } else { // // Up the entire frame. // fValue = 0; } } // // If we have both a leading and trailing edge, it was either released and repressed // this frame, or pressed and released this frame. // if (bImpulseDown && bImpulseUp) { if (bDown) { // // Released and re-pressed this frame. // fValue = 0.75; } else { // // Pressed and released this frame. // fValue = 0.25; } } return fValue; } //----------------------------------------------------------------------------- // Purpose: Returns the bit mask associated with the given modifier key. // Input : uModifierKey - The virtual key code corresponding to the modifier key. // Output : The modifier key's bitmask. //----------------------------------------------------------------------------- unsigned int CKeyboard::GetModifierKeyBit(unsigned int uChar) { for (int nKey = 0; nKey < sizeof(ModifierKeyTable) / sizeof(ModifierKeyTable[0]); nKey++) { if (ModifierKeyTable[nKey].uChar == uChar) { return(ModifierKeyTable[nKey].uModifierKeys); } } return(0); } //----------------------------------------------------------------------------- // Purpose: Checks to see if all of the modifier keys specified by bits in uModifierKeys // are currently held down. // Input : uModifierKeys - Contains bits indicating which modifier keys to check: // KEY_MOD_SHIFT // KEY_MOD_CONTROL // KEY_MOD_ALT // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CKeyboard::IsKeyPressed(unsigned int uChar, unsigned int uModifierKeys) { if (!(g_uPhysicalKeyState[uChar] & KEYSTATE_DOWN)) { return(false); } bool bKeyPressed = true; for (int nKey = 0; nKey < sizeof(ModifierKeyTable) / sizeof(ModifierKeyTable[0]); nKey++) { if (g_uPhysicalKeyState[ModifierKeyTable[nKey].uChar] & KEYSTATE_DOWN) { if (!(uModifierKeys & ModifierKeyTable[nKey].uModifierKeys)) { bKeyPressed = false; } } else if (uModifierKeys & ModifierKeyTable[nKey].uModifierKeys) { bKeyPressed = false; } } return(bKeyPressed); } //----------------------------------------------------------------------------- // Purpose: Determines whether a key is an allowed modifier key, ie, whether it // can be used in conjunction with other keys when performing key // bindings. // Input : uChar - Virtual key to check. // Output : Returns true if this key is a modifier key, false if not. //----------------------------------------------------------------------------- bool CKeyboard::IsModifierKey(unsigned int uChar) { return((uChar == VK_SHIFT) || (uChar == VK_CONTROL) || (uChar == VK_MENU)); } //----------------------------------------------------------------------------- // Purpose: Given a key press/release event, updates the status of all logical // keys. // Input : uChar - The key whose state has changed. // bPressed - True if the key was pressed, false if it was released. //----------------------------------------------------------------------------- void CKeyboard::UpdateLogicalKeys(unsigned int uChar, bool bPressed) { // // Determine whether the key is a modifier key. If so, find its modifier bit. // bool bIsModifierKey = IsModifierKey(uChar); unsigned int uModifierKeyBit = 0; if (bIsModifierKey) { uModifierKeyBit = GetModifierKeyBit(uChar); } // // For every key in the keymap that depends upon this physical key, update // the state of the corresponding logical key based on this event. // for (unsigned int nKey = 0; nKey < g_uKeyMaps; nKey++) { unsigned int uPhysicalKey = g_uKeyMap[nKey].uChar; unsigned int uLogicalKey = g_uKeyMap[nKey].uLogicalKey; unsigned int uModifierKeys = g_uKeyMap[nKey].uModifierKeys; if ((uPhysicalKey == uChar) || (uModifierKeys & uModifierKeyBit)) { // // Check the state of all modifier keys to which this logical key // is bound to determine whether the logical key is pressed or not. // bool bLogicalKeyPressed = IsKeyPressed(g_uKeyMap[nKey].uChar, g_uKeyMap[nKey].uModifierKeys); // // Update the logical key state. // if (bPressed) { if (bLogicalKeyPressed) { if (!(g_uLogicalKeyState[uLogicalKey] & KEYSTATE_DOWN)) { g_uLogicalKeyState[uLogicalKey] |= KEYSTATE_IMPULSE_DOWN; } g_uLogicalKeyState[uLogicalKey]++; } } else { if (g_uLogicalKeyState[uLogicalKey] & KEYSTATE_DOWN) { g_uLogicalKeyState[uLogicalKey]--; } if (!(g_uLogicalKeyState[uLogicalKey] & KEYSTATE_DOWN)) { g_uLogicalKeyState[uLogicalKey] |= KEYSTATE_IMPULSE_UP; } } } } } //----------------------------------------------------------------------------- // Purpose: Called by the client when a WM_KEYDOWN message is received. // Input : Per CWnd::OnKeyDown. //----------------------------------------------------------------------------- void CKeyboard::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if ((!(nFlags & 0x4000)) || (!(g_uPhysicalKeyState[nChar] & KEYSTATE_DOWN))) { g_uPhysicalKeyState[nChar] |= KEYSTATE_DOWN; g_uPhysicalKeyState[nChar] |= KEYSTATE_IMPULSE_DOWN; UpdateLogicalKeys(nChar, true); } } //----------------------------------------------------------------------------- // Purpose: Called by the client when a WM_KEYUP message is received. // Input : Per CWnd::OnKeyDown. //----------------------------------------------------------------------------- void CKeyboard::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { if (g_uPhysicalKeyState[nChar] & KEYSTATE_DOWN) { g_uPhysicalKeyState[nChar] &= ~KEYSTATE_DOWN; } g_uPhysicalKeyState[nChar] |= KEYSTATE_IMPULSE_UP; UpdateLogicalKeys(nChar, false); } //----------------------------------------------------------------------------- // Purpose: Deletes all key bindings. //----------------------------------------------------------------------------- void CKeyboard::RemoveAllKeyMaps(void) { g_uKeyMaps = 0; }