From 5470941ccb31c873c2093057261b4e78a607d08a Mon Sep 17 00:00:00 2001 From: Jacob Ledbetter Date: Tue, 29 Sep 2026 21:21:12 -0600 Subject: [PATCH 1/4] refactor(input): Use KeyState and simplify keyboard state handling Introduce KeyState and the combined modifier mask. Update the keyboard, meta-event, and hotkey types. Simplify the hotkey modifier check and name the buffered key and physical-modifier helper without changing event timing or modifier capture. --- .../GameEngine/Include/Common/MessageStream.h | 4 +- Core/GameEngine/Include/GameClient/Keyboard.h | 10 ++--- .../GameEngine/Include/GameClient/MetaEvent.h | 5 ++- .../Source/GameClient/Input/Keyboard.cpp | 42 ++++++++++++------- .../GameClient/MessageStream/HotKey.cpp | 32 +++----------- .../GameClient/MessageStream/MetaEvent.cpp | 6 +-- .../GameEngine/Include/GameClient/KeyDefs.h | 5 ++- .../GameEngine/Include/GameClient/KeyDefs.h | 5 ++- 8 files changed, 55 insertions(+), 54 deletions(-) diff --git a/Core/GameEngine/Include/Common/MessageStream.h b/Core/GameEngine/Include/Common/MessageStream.h index e5dc1fc0896..255f700d59c 100644 --- a/Core/GameEngine/Include/Common/MessageStream.h +++ b/Core/GameEngine/Include/Common/MessageStream.h @@ -133,8 +133,8 @@ class GameMessage : public MemoryPoolObject MSG_RAW_MOUSE_WHEEL, ///< (Real spin, + is away, - is toward user) MSG_RAW_MOUSE_END, - MSG_RAW_KEY_DOWN, ///< (KeyDefType) the given key was pressed (uses Microsoft VK_ codes) - MSG_RAW_KEY_UP, ///< (KeyDefType) the given key was released + MSG_RAW_KEY_DOWN, ///< (KeyDefType, KeyState) the given key was pressed + MSG_RAW_KEY_UP, ///< (KeyDefType, KeyState) the given key was released // Refined Mouse messages // NOTE: All processing should attempt to use these refined mouse messages, rather than the diff --git a/Core/GameEngine/Include/GameClient/Keyboard.h b/Core/GameEngine/Include/GameClient/Keyboard.h index 8c21aae40b0..44c6fae5bd2 100644 --- a/Core/GameEngine/Include/GameClient/Keyboard.h +++ b/Core/GameEngine/Include/GameClient/Keyboard.h @@ -75,7 +75,7 @@ struct KeyboardIO UnsignedByte key; // KeyDefType, key data UnsignedByte status; // StatusType, above - UnsignedShort state; // KEY_STATE_* in KeyDefs.h + KeyState state; // KEY_STATE_* in KeyDefs.h UnsignedInt keyDownTimeMsec; // real-time in milliseconds when key went down }; @@ -112,7 +112,7 @@ class Keyboard : public SubsystemInterface Bool isShift(); Bool isCtrl(); Bool isAlt(); - Int getModifierFlags() { return m_modifiers; } + KeyState getModifierFlags() { return m_modifiers; } // access methods for key data void resetKeys(); ///< reset the state of the keys @@ -136,10 +136,10 @@ class Keyboard : public SubsystemInterface void updateKeys(); ///< update the state of our key data Bool checkKeyRepeat(); ///< check for repeating keys UnsignedByte getKeyStatusData( KeyDefType key ); ///< get key status - Bool getKeyStateBit( KeyDefType key, Int bit ); ///< get key state bit - void setKeyStateData( KeyDefType key, UnsignedByte data ); ///< get key state + Bool getKeyStateBit( KeyDefType key, KeyState bit ); ///< get key state bit + void setKeyStateData( KeyDefType key, KeyState data ); ///< get key state - UnsignedShort m_modifiers; + KeyState m_modifiers; // internal keyboard data members //Bool m_capsState; // 1 if caps lock is on //Bool m_shiftState; // 1 if either shift key is pressed diff --git a/Core/GameEngine/Include/GameClient/MetaEvent.h b/Core/GameEngine/Include/GameClient/MetaEvent.h index a610c4f5e7d..742d5822f08 100644 --- a/Core/GameEngine/Include/GameClient/MetaEvent.h +++ b/Core/GameEngine/Include/GameClient/MetaEvent.h @@ -28,6 +28,7 @@ #pragma once #include "Common/SubsystemInterface.h" +#include "GameClient/KeyDefs.h" #include "GameClient/InGameUI.h" @@ -447,10 +448,10 @@ class MetaEventTranslator : public GameMessageTranslator void onKeyEvent(const GameMessage *msg, GameMessageDisposition &disp); void onKeyModStateRemoved(GameMessageDisposition &disp, MappableKeyModState keyModState); - void onKeyPressed(GameMessageDisposition &disp, Int systemKeyState, MappableKeyType keyType, MappableKeyModState keyModState); + void onKeyPressed(GameMessageDisposition &disp, KeyState systemKeyState, MappableKeyType keyType, MappableKeyModState keyModState); static MappableKeyType getActionKeyType(Int systemKey); ///< CRTL, ALT, SHIFT will be treated as MK_NONE - static MappableKeyModState getKeyModState(Int systemKeyState); ///< Extract CTRL, ALT, SHIFT key mod state + static MappableKeyModState getKeyModState(KeyState systemKeyState); ///< Extract CTRL, ALT, SHIFT key mod state }; //----------------------------------------------------------------------------- diff --git a/Core/GameEngine/Source/GameClient/Input/Keyboard.cpp b/Core/GameEngine/Source/GameClient/Input/Keyboard.cpp index ea1c5e4425a..4633bbe6ce1 100644 --- a/Core/GameEngine/Source/GameClient/Input/Keyboard.cpp +++ b/Core/GameEngine/Source/GameClient/Input/Keyboard.cpp @@ -43,6 +43,23 @@ Keyboard *TheKeyboard = nullptr; // PRIVATE PROTOTYPES ///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------------------------- +static Bool isCtrlShiftAltKey(KeyDefType key) +{ + switch (key) + { + case KEY_LCTRL: + case KEY_RCTRL: + case KEY_LSHIFT: + case KEY_RSHIFT: + case KEY_LALT: + case KEY_RALT: + return TRUE; + } + + return FALSE; +} + //------------------------------------------------------------------------------------------------- /** Given the state of the device, create messages from the input and * place them on the message stream */ @@ -138,17 +155,20 @@ void Keyboard::updateKeys() /** @todo -- if we don't have focus, we could destroy all the keys retrieved here so that we don't process anything */ - m_keyStatus[ m_keys[ index ].key ].state = m_keys[ index ].state; - m_keyStatus[ m_keys[ index ].key ].status = m_keys[ index ].status; + const KeyDefType key = (KeyDefType)m_keys[ index ].key; + const Bool isModifier = isCtrlShiftAltKey(key); + + m_keyStatus[ key ].state = m_keys[ index ].state; + m_keyStatus[ key ].status = m_keys[ index ].status; // Update key down time for new key presses if( BitIsSet( m_keys[ index ].state, KEY_STATE_DOWN ) ) { - m_keyStatus[ m_keys[ index ].key ].keyDownTimeMsec = m_keys[ index ].keyDownTimeMsec; + m_keyStatus[ key ].keyDownTimeMsec = m_keys[ index ].keyDownTimeMsec; } // prevent ALT-TAB from causing a TAB event - if( m_keys[ index ].key == KEY_TAB ) + if( key == KEY_TAB ) { if( BitIsSet( m_keyStatus[ KEY_LALT ].state, KEY_STATE_DOWN ) || BitIsSet( m_keyStatus[ KEY_RALT ].state, KEY_STATE_DOWN ) ) @@ -156,13 +176,7 @@ void Keyboard::updateKeys() m_keys[index].status = KeyboardIO::STATUS_USED; } } - else if( m_keys[ index ].key == KEY_CAPS || - m_keys[ index ].key == KEY_LCTRL || - m_keys[ index ].key == KEY_RCTRL || - m_keys[ index ].key == KEY_LSHIFT || - m_keys[ index ].key == KEY_RSHIFT || - m_keys[ index ].key == KEY_LALT || - m_keys[ index ].key == KEY_RALT ) + else if( key == KEY_CAPS || isModifier ) { @@ -170,7 +184,7 @@ void Keyboard::updateKeys() // this keeps our internal key state accurate event though we don't // use the returned translation ... kinda weird I think // - translateKey( m_keys[ index ].key ); + translateKey( key ); } @@ -816,7 +830,7 @@ UnsignedByte Keyboard::getKeyStatusData( KeyDefType key ) //------------------------------------------------------------------------------------------------- /** Get the key state data as a Bool for the specified key */ //------------------------------------------------------------------------------------------------- -Bool Keyboard::getKeyStateBit( KeyDefType key, Int bit ) +Bool Keyboard::getKeyStateBit( KeyDefType key, KeyState bit ) { return (m_keyStatus[ key ].state & bit) ? 1 : 0; } @@ -832,7 +846,7 @@ void Keyboard::setKeyStatusData( KeyDefType key, KeyboardIO::StatusType data ) //------------------------------------------------------------------------------------------------- /** set the key state data */ //------------------------------------------------------------------------------------------------- -void Keyboard::setKeyStateData( KeyDefType key, UnsignedByte data ) +void Keyboard::setKeyStateData( KeyDefType key, KeyState data ) { m_keyStatus[ key ].state = data; } diff --git a/Core/GameEngine/Source/GameClient/MessageStream/HotKey.cpp b/Core/GameEngine/Source/GameClient/MessageStream/HotKey.cpp index 0b7ecf22d1e..0fbf418c3fe 100644 --- a/Core/GameEngine/Source/GameClient/MessageStream/HotKey.cpp +++ b/Core/GameEngine/Source/GameClient/MessageStream/HotKey.cpp @@ -52,7 +52,6 @@ //----------------------------------------------------------------------------- #include "GameClient/HotKey.h" #include "GameClient/KeyDefs.h" -#include "GameClient/MetaEvent.h" #include "GameClient/GameWindow.h" #include "GameClient/GameWindowManager.h" #include "GameClient/Keyboard.h" @@ -74,33 +73,14 @@ GameMessageDisposition HotKeyTranslator::translateGameMessage(const GameMessage if ( t == GameMessage::MSG_RAW_KEY_UP) { - - //char key = msg->getArgument(0)->integer; - Int keyState = msg->getArgument(1)->integer; - - // for our purposes here, we don't care to distinguish between right and left keys, - // so just fudge a little to simplify things. - Int newModState = 0; - - if( keyState & KEY_STATE_CONTROL ) - { - newModState |= CTRL; - } - - if( keyState & KEY_STATE_SHIFT ) - { - newModState |= SHIFT; - } - - if( keyState & KEY_STATE_ALT ) - { - newModState |= ALT; - } - if(newModState != 0) + const KeyDefType key = (KeyDefType)msg->getArgument(0)->integer; + const KeyState keyState = (KeyState)msg->getArgument(1)->integer; + if( keyState & KEY_STATE_MODIFIERS ) return disp; - WideChar key = TheKeyboard->getPrintableKey((KeyDefType)msg->getArgument(0)->integer, 0); + + WideChar printableKey = TheKeyboard->getPrintableKey(key, 0); UnicodeString uKey; - uKey.concat(key); + uKey.concat(printableKey); AsciiString aKey; aKey.translate(uKey); if(TheHotKeyManager && TheHotKeyManager->executeHotKey(aKey)) diff --git a/Core/GameEngine/Source/GameClient/MessageStream/MetaEvent.cpp b/Core/GameEngine/Source/GameClient/MessageStream/MetaEvent.cpp index 31a954e67b5..c8471cd91e3 100644 --- a/Core/GameEngine/Source/GameClient/MessageStream/MetaEvent.cpp +++ b/Core/GameEngine/Source/GameClient/MessageStream/MetaEvent.cpp @@ -535,7 +535,7 @@ void MetaEventTranslator::onMouseEvent(const GameMessage *msg) void MetaEventTranslator::onKeyEvent(const GameMessage *msg, GameMessageDisposition &disp) { const Int systemKey = msg->getArgument(0)->integer; - const Int systemKeyState = msg->getArgument(1)->integer; + const KeyState systemKeyState = (KeyState)msg->getArgument(1)->integer; const MappableKeyType keyType = getActionKeyType(systemKey); const MappableKeyModState keyModState = getKeyModState(systemKeyState); @@ -598,7 +598,7 @@ void MetaEventTranslator::onKeyModStateRemoved(GameMessageDisposition &disp, Map } //------------------------------------------------------------------------------------------------- -void MetaEventTranslator::onKeyPressed(GameMessageDisposition &disp, Int systemKeyState, MappableKeyType keyType, MappableKeyModState keyModState) +void MetaEventTranslator::onKeyPressed(GameMessageDisposition &disp, KeyState systemKeyState, MappableKeyType keyType, MappableKeyModState keyModState) { // TheSuperHackers @info The regular key handler only triggers events when the mapped key is pressed, // not when the modifier (CTRL, ALT, SHIFT) is pressed, unless the key is MK_NONE. @@ -701,7 +701,7 @@ MappableKeyType MetaEventTranslator::getActionKeyType(Int systemKey) } //------------------------------------------------------------------------------------------------- -MappableKeyModState MetaEventTranslator::getKeyModState(Int systemKeyState) +MappableKeyModState MetaEventTranslator::getKeyModState(KeyState systemKeyState) { // for our purposes here, we don't care to distinguish between right and left keys, // so just fudge a little to simplify things. diff --git a/Generals/Code/GameEngine/Include/GameClient/KeyDefs.h b/Generals/Code/GameEngine/Include/GameClient/KeyDefs.h index 3c9f14394de..a37352e5aa5 100644 --- a/Generals/Code/GameEngine/Include/GameClient/KeyDefs.h +++ b/Generals/Code/GameEngine/Include/GameClient/KeyDefs.h @@ -252,10 +252,13 @@ enum // modifier combinations when left/right isn't a factor KEY_STATE_CONTROL = (KEY_STATE_LCONTROL | KEY_STATE_RCONTROL), KEY_STATE_SHIFT = (KEY_STATE_LSHIFT | KEY_STATE_RSHIFT | KEY_STATE_SHIFT2 ), - KEY_STATE_ALT = (KEY_STATE_LALT | KEY_STATE_RALT) + KEY_STATE_ALT = (KEY_STATE_LALT | KEY_STATE_RALT), + KEY_STATE_MODIFIERS = (KEY_STATE_CONTROL | KEY_STATE_SHIFT | KEY_STATE_ALT) }; +typedef UnsignedShort KeyState; + // INLINING /////////////////////////////////////////////////////////////////// // EXTERNALS ////////////////////////////////////////////////////////////////// diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/KeyDefs.h b/GeneralsMD/Code/GameEngine/Include/GameClient/KeyDefs.h index 9f1978d20e0..5fcab9933ee 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/KeyDefs.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/KeyDefs.h @@ -252,10 +252,13 @@ enum // modifier combinations when left/right isn't a factor KEY_STATE_CONTROL = (KEY_STATE_LCONTROL | KEY_STATE_RCONTROL), KEY_STATE_SHIFT = (KEY_STATE_LSHIFT | KEY_STATE_RSHIFT | KEY_STATE_SHIFT2 ), - KEY_STATE_ALT = (KEY_STATE_LALT | KEY_STATE_RALT) + KEY_STATE_ALT = (KEY_STATE_LALT | KEY_STATE_RALT), + KEY_STATE_MODIFIERS = (KEY_STATE_CONTROL | KEY_STATE_SHIFT | KEY_STATE_ALT) }; +typedef UnsignedShort KeyState; + // INLINING /////////////////////////////////////////////////////////////////// // EXTERNALS ////////////////////////////////////////////////////////////////// From 02e7cdb5e1f7365322ead576bd55274ed075075a Mon Sep 17 00:00:00 2001 From: Jacob Ledbetter Date: Tue, 29 Sep 2026 21:21:12 -0600 Subject: [PATCH 2/4] fix(input): Preserve modifiers for each buffered key event Capture modifiers for each buffered event instead of copying the final frame state to all events. For example, Ctrl-down, F-down, Ctrl-up in one frame must still mark F-down as modified. Include current modifiers on repeats and recognize the configured secondary shift key. --- .../Source/GameClient/Input/Keyboard.cpp | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/Core/GameEngine/Source/GameClient/Input/Keyboard.cpp b/Core/GameEngine/Source/GameClient/Input/Keyboard.cpp index 4633bbe6ce1..428d9cfe3dd 100644 --- a/Core/GameEngine/Source/GameClient/Input/Keyboard.cpp +++ b/Core/GameEngine/Source/GameClient/Input/Keyboard.cpp @@ -156,7 +156,7 @@ void Keyboard::updateKeys() here so that we don't process anything */ const KeyDefType key = (KeyDefType)m_keys[ index ].key; - const Bool isModifier = isCtrlShiftAltKey(key); + const Bool isModifier = isCtrlShiftAltKey(key) || key == m_shift2Key; m_keyStatus[ key ].state = m_keys[ index ].state; m_keyStatus[ key ].status = m_keys[ index ].status; @@ -188,6 +188,8 @@ void Keyboard::updateKeys() } + BitSet( m_keys[ index ].state, m_modifiers ); + index++; } @@ -195,22 +197,6 @@ void Keyboard::updateKeys() // check for key repeats checkKeyRepeat(); - if( m_modifiers ) - { - index = 0; - while( m_keys[ index ].key != KEY_NONE ) - { - - // set in the modifier data into the already existing up/down state - BitSet( m_keys[ index ].state, m_modifiers ); - - // next key - index++; - - } - - } - } //------------------------------------------------------------------------------------------------- @@ -247,7 +233,7 @@ Bool Keyboard::checkKeyRepeat() { // Add key to this frame m_keys[ index ].key = (UnsignedByte)key; - m_keys[ index ].state = KEY_STATE_DOWN | KEY_STATE_AUTOREPEAT; // note: not a bitset; this is an assignment + m_keys[ index ].state = KEY_STATE_DOWN | KEY_STATE_AUTOREPEAT | m_modifiers; // note: not a bitset; this is an assignment m_keys[ index ].status = KeyboardIO::STATUS_UNUSED; // Set End Flag From 5907158fe4bf3495382c2292555f01e3fd37b502 Mon Sep 17 00:00:00 2001 From: Jacob Ledbetter Date: Tue, 29 Sep 2026 21:21:13 -0600 Subject: [PATCH 3/4] fix(input): Trigger GUI hotkeys on unmodified key down Run plain hotkeys on press and ignore repeats. Give active GUI buttons priority over overlapping meta commands after window input. Document why the event timing and translator order matter. --- Core/GameEngine/Source/GameClient/MessageStream/HotKey.cpp | 5 +++-- Generals/Code/GameEngine/Source/GameClient/GameClient.cpp | 3 ++- GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Core/GameEngine/Source/GameClient/MessageStream/HotKey.cpp b/Core/GameEngine/Source/GameClient/MessageStream/HotKey.cpp index 0fbf418c3fe..b74bc9cbc16 100644 --- a/Core/GameEngine/Source/GameClient/MessageStream/HotKey.cpp +++ b/Core/GameEngine/Source/GameClient/MessageStream/HotKey.cpp @@ -71,11 +71,12 @@ GameMessageDisposition HotKeyTranslator::translateGameMessage(const GameMessage GameMessageDisposition disp = KEEP_MESSAGE; GameMessage::Type t = msg->getType(); - if ( t == GameMessage::MSG_RAW_KEY_UP) + // TheSuperHackers @fix Run hotkeys on press so releasing a modifier first cannot trigger a plain hotkey. + if ( t == GameMessage::MSG_RAW_KEY_DOWN) { const KeyDefType key = (KeyDefType)msg->getArgument(0)->integer; const KeyState keyState = (KeyState)msg->getArgument(1)->integer; - if( keyState & KEY_STATE_MODIFIERS ) + if( keyState & (KEY_STATE_MODIFIERS | KEY_STATE_AUTOREPEAT) ) return disp; WideChar printableKey = TheKeyboard->getPrintableKey(key, 0); diff --git a/Generals/Code/GameEngine/Source/GameClient/GameClient.cpp b/Generals/Code/GameEngine/Source/GameClient/GameClient.cpp index d556d8b4855..c9b5ebe4955 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -286,8 +286,9 @@ void GameClient::init() // since we only allocate one of each, don't bother pooling 'em m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") WindowTranslator, 10 ); + // TheSuperHackers @fix Let active GUI hotkeys handle keys before MetaEventTranslator consumes overlapping bindings. + m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") HotKeyTranslator, 15 ); m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") MetaEventTranslator, 20 ); - m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") HotKeyTranslator, 25 ); m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") PlaceEventTranslator, 30 ); m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") GUICommandTranslator, 40 ); m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") SelectionTranslator, 50 ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp index af5f407209b..0cd22b7dd9a 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -294,8 +294,9 @@ void GameClient::init() // since we only allocate one of each, don't bother pooling 'em m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") WindowTranslator, 10 ); + // TheSuperHackers @fix Let active GUI hotkeys handle keys before MetaEventTranslator consumes overlapping bindings. + m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") HotKeyTranslator, 15 ); m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") MetaEventTranslator, 20 ); - m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") HotKeyTranslator, 25 ); m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") PlaceEventTranslator, 30 ); m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") GUICommandTranslator, 40 ); m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") SelectionTranslator, 50 ); From e52d4a4fd98bd2fddeb0de2365fc7bd691900495 Mon Sep 17 00:00:00 2001 From: Jacob Ledbetter Date: Tue, 29 Sep 2026 21:21:13 -0600 Subject: [PATCH 4/4] fix(input): Release held modifiers when keyboard focus resets Extend Alt focus-reset handling to Ctrl, Shift, and a distinct secondary shift key. Emit releases before clearing the keyboard state. --- Core/GameEngine/Include/GameClient/Keyboard.h | 2 +- .../Source/GameClient/Input/Keyboard.cpp | 30 +++++++++++-------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Core/GameEngine/Include/GameClient/Keyboard.h b/Core/GameEngine/Include/GameClient/Keyboard.h index 44c6fae5bd2..a4c4874d7f2 100644 --- a/Core/GameEngine/Include/GameClient/Keyboard.h +++ b/Core/GameEngine/Include/GameClient/Keyboard.h @@ -124,7 +124,7 @@ class Keyboard : public SubsystemInterface WideChar getPrintableKey( KeyDefType key, Int state ); enum { MAX_KEY_STATES = 3}; private: - void refreshAltKeys() const; ///< refresh the state of the alt keys, necessary after alt tab + void emitModifierKeyUps() const; protected: /** get the key data for a single key, KEY_NONE should be returned when diff --git a/Core/GameEngine/Source/GameClient/Input/Keyboard.cpp b/Core/GameEngine/Source/GameClient/Input/Keyboard.cpp index 428d9cfe3dd..8fb554fcec9 100644 --- a/Core/GameEngine/Source/GameClient/Input/Keyboard.cpp +++ b/Core/GameEngine/Source/GameClient/Input/Keyboard.cpp @@ -749,10 +749,9 @@ void Keyboard::update() //------------------------------------------------------------------------------------------------- void Keyboard::resetKeys() { - // TheSuperHackers @fix Caball009 13/12/2025 Fix bug where game remains in waypoint mode // because the key up state for the alt key is not detected after alt tab. - refreshAltKeys(); + emitModifierKeyUps(); memset( m_keys, 0, sizeof( m_keys ) ); memset( m_keyStatus, 0, sizeof( m_keyStatus ) ); @@ -765,24 +764,29 @@ void Keyboard::resetKeys() } //------------------------------------------------------------------------------------------------- -// Refresh the state of the alt keys, necessary after alt tab -//------------------------------------------------------------------------------------------------- -void Keyboard::refreshAltKeys() const +static void emitRawKeyUpIfDown(const KeyboardIO *keyStatus, KeyDefType key) { - if (BitIsSet(m_keyStatus[KEY_LALT].state, KEY_STATE_DOWN)) - { - GameMessage* msg = TheMessageStream->appendMessage(GameMessage::MSG_RAW_KEY_UP); - msg->appendIntegerArgument(KEY_LALT); - msg->appendIntegerArgument(KEY_STATE_UP); - } - if (BitIsSet(m_keyStatus[KEY_RALT].state, KEY_STATE_DOWN)) + if (BitIsSet(keyStatus[key].state, KEY_STATE_DOWN)) { GameMessage* msg = TheMessageStream->appendMessage(GameMessage::MSG_RAW_KEY_UP); - msg->appendIntegerArgument(KEY_RALT); + msg->appendIntegerArgument(key); msg->appendIntegerArgument(KEY_STATE_UP); } } +//------------------------------------------------------------------------------------------------- +void Keyboard::emitModifierKeyUps() const +{ + emitRawKeyUpIfDown(m_keyStatus, KEY_LCTRL); + emitRawKeyUpIfDown(m_keyStatus, KEY_RCTRL); + emitRawKeyUpIfDown(m_keyStatus, KEY_LSHIFT); + emitRawKeyUpIfDown(m_keyStatus, KEY_RSHIFT); + emitRawKeyUpIfDown(m_keyStatus, KEY_LALT); + emitRawKeyUpIfDown(m_keyStatus, KEY_RALT); + if (m_shift2Key != KEY_NONE && !isCtrlShiftAltKey(m_shift2Key)) + emitRawKeyUpIfDown(m_keyStatus, m_shift2Key); +} + //------------------------------------------------------------------------------------------------- /** get the first key in our current state of the keyboard */ //-------------------------------------------------------------------------------------------------