From 06894d51061b610259f71e56e76674d742237262 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:48:21 +0100 Subject: [PATCH 1/8] Refactored HotKeyTranslator::translateGameMessage to accommodate the key repeat feature. --- .../GameClient/MessageStream/HotKey.cpp | 51 ++++++++----------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/HotKey.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/HotKey.cpp index 651512e4436..cd607a55d41 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/HotKey.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/HotKey.cpp @@ -69,44 +69,33 @@ //----------------------------------------------------------------------------- GameMessageDisposition HotKeyTranslator::translateGameMessage(const GameMessage *msg) { - GameMessageDisposition disp = KEEP_MESSAGE; - GameMessage::Type t = msg->getType(); - - if ( t == GameMessage::MSG_RAW_KEY_UP) + switch (msg->getType()) { + case GameMessage::MSG_RAW_KEY_DOWN: + if ((msg->getArgument(1)->integer & KEY_STATE_AUTOREPEAT) == 0) + return KEEP_MESSAGE; - //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; + FALLTHROUGH; + case GameMessage::MSG_RAW_KEY_UP: + if (msg->getArgument(1)->integer & (KEY_STATE_CONTROL | KEY_STATE_SHIFT | KEY_STATE_ALT)) + return KEEP_MESSAGE; - if( keyState & KEY_STATE_CONTROL ) + if (TheHotKeyManager) { - newModState |= CTRL; + WideChar key = TheKeyboard->getPrintableKey((KeyDefType)msg->getArgument(0)->integer, 0); + UnicodeString uKey; + uKey.concat(key); + AsciiString aKey; + aKey.translate(uKey); + + if (TheHotKeyManager->executeHotKey(aKey)) + return DESTROY_MESSAGE; } - if( keyState & KEY_STATE_SHIFT ) - { - newModState |= SHIFT; - } - - if( keyState & KEY_STATE_ALT ) - { - newModState |= ALT; - } - if(newModState != 0) - return disp; - WideChar key = TheKeyboard->getPrintableKey((KeyDefType)msg->getArgument(0)->integer, 0); - UnicodeString uKey; - uKey.concat(key); - AsciiString aKey; - aKey.translate(uKey); - if(TheHotKeyManager && TheHotKeyManager->executeHotKey(aKey)) - disp = DESTROY_MESSAGE; + return KEEP_MESSAGE; + default: + return KEEP_MESSAGE; } - return disp; } //----------------------------------------------------------------------------- From cf56aef5cf5db13d698c86eeac57eb3856c60405 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:50:58 +0100 Subject: [PATCH 2/8] Increased hotkey translator priority over meta events. e.g. for the 'S' button, which can be both a meta event and a hotkey. --- GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp index 1c6c5e8e9d1..086ae7ae7f2 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -289,8 +289,8 @@ void GameClient::init( void ) // since we only allocate one of each, don't bother pooling 'em m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") WindowTranslator, 10 ); - 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") HotKeyTranslator, 20 ); + m_translators[ m_numTranslators++ ] = TheMessageStream->attachTranslator( MSGNEW("GameClientSubsystem") MetaEventTranslator, 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 327307141ca3d6c10b2dbff2f02147e8bbb14553 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 30 Jan 2026 18:10:57 +0100 Subject: [PATCH 3/8] Modified Keyboard::checkKeyRepeat. --- .../GameEngine/Include/GameClient/Keyboard.h | 9 ++- .../Source/GameClient/Input/Keyboard.cpp | 63 +++++++++---------- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/Keyboard.h b/GeneralsMD/Code/GameEngine/Include/GameClient/Keyboard.h index 527af260552..a0eb0b42ca8 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/Keyboard.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/Keyboard.h @@ -77,6 +77,7 @@ struct KeyboardIO UnsignedByte status; // StatusType, above UnsignedShort state; // KEY_STATE_* in KeyDefs.h UnsignedInt keyDownTimeMsec; // real-time in milliseconds when key went down + UnsignedInt keyRepeatTimeMsecOffset; // shorten key repeat delay by offset }; @@ -88,8 +89,12 @@ class Keyboard : public SubsystemInterface enum { - KEY_REPEAT_DELAY_MSEC = 333, // 10 frames at 30 FPS - KEY_REPEAT_INTERVAL_MSEC = 67 // ~2 frames at 30 FPS + // TheSuperHackers @info Holding a button down requires 200 msec to register as a repeated key for the first time. + // After that the delay gets shorter and shorter, between 140 (200 - 60) and 15 (200 - 185) msec, with a decrease step of 15 msec. + KEY_REPEAT_DELAY_MSEC = 200, + KEY_REPEAT_OFFSET_DELAY_MIN_MSEC = 60, + KEY_REPEAT_OFFSET_DELAY_MAX_MSEC = 185, + KEY_REPEAT_OFFSET_DELAY_STEP_MSEC = 15 }; public: diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp index a4e087fb752..a9abbd8dc72 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp @@ -204,59 +204,58 @@ void Keyboard::updateKeys( void ) //------------------------------------------------------------------------------------------------- Bool Keyboard::checkKeyRepeat( void ) { - Bool retVal = FALSE; - Int index = 0; - Int key; - - /** @todo we shouldn't think about repeating any keys while we - don't have the focus */ -// if( currentFocus == FOCUS_OUT ) -// return FALSE; + // @todo we shouldn't think about repeating any keys while we don't have the focus - // Find end of real keys for this frame - while( m_keys[ index ].key != KEY_NONE ) - index++; + Bool retVal = FALSE; + size_t index = 0; - // Scan Keyboard status array for first key down - // long enough to repeat - for( key = 0; key < ARRAY_SIZE(m_keyStatus); key++ ) + for (size_t i = 0; i < ARRAY_SIZE(m_keys) - 1 && m_keys[i].key != KEY_NONE; ++i) { + ++index; + } + // Scan keyboard status array for first key down long enough to repeat + for( size_t key = 0; key < ARRAY_SIZE(m_keyStatus); ++key ) + { if( BitIsSet( m_keyStatus[ key ].state, KEY_STATE_DOWN ) ) { - const UnsignedInt now = timeGetTime(); - const UnsignedInt keyDownTime = m_keyStatus[ key ].keyDownTimeMsec; - const UnsignedInt elapsedMsec = now - keyDownTime; - if( elapsedMsec > Keyboard::KEY_REPEAT_DELAY_MSEC ) + if( m_keyStatus[ key ].keyDownTimeMsec > 0 && now - m_keyStatus[ key ].keyDownTimeMsec > Keyboard::KEY_REPEAT_DELAY_MSEC ) { // 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 ].status = KeyboardIO::STATUS_UNUSED; + if (index < ARRAY_SIZE(m_keys) - 2) + { + 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].status = KeyboardIO::STATUS_UNUSED; - // Set End Flag - m_keys[ ++index ].key = KEY_NONE; + // Set end flag + ++index; + m_keys[index].key = KEY_NONE; + } - // Set all keys as new to prevent multiple keys repeating - for( index = 0; index< NUM_KEYS; index++ ) - m_keyStatus[ index ].keyDownTimeMsec = now; + // Decrease the delay between registered key strokes the longer a key is held down + const UnsignedInt keyRepeatTimeMsecOffset = clamp( + static_cast(KEY_REPEAT_OFFSET_DELAY_STEP_MSEC) + m_keyStatus[ key ].keyRepeatTimeMsecOffset, + static_cast(KEY_REPEAT_OFFSET_DELAY_MIN_MSEC), + static_cast(KEY_REPEAT_OFFSET_DELAY_MAX_MSEC)); // Set repeated key so it will repeat again after the interval - m_keyStatus[ key ].keyDownTimeMsec = now - (Keyboard::KEY_REPEAT_DELAY_MSEC + Keyboard::KEY_REPEAT_INTERVAL_MSEC); + m_keyStatus[ key ].keyRepeatTimeMsecOffset = keyRepeatTimeMsecOffset; + m_keyStatus[ key ].keyDownTimeMsec = now - m_keyStatus[ key ].keyRepeatTimeMsecOffset; retVal = TRUE; - break; // exit for key - } - } - + else + { + m_keyStatus[ key ].keyDownTimeMsec = 0; + m_keyStatus[ key ].keyRepeatTimeMsecOffset = 0; + } } return retVal; - } //------------------------------------------------------------------------------------------------- From 5cd3e25b1a8a5a8f7cbae745177b7824a3a1911d Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 30 Jan 2026 18:12:53 +0100 Subject: [PATCH 4/8] Restored two EA comments. --- GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp index a9abbd8dc72..80599e70b73 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp @@ -209,6 +209,7 @@ Bool Keyboard::checkKeyRepeat( void ) Bool retVal = FALSE; size_t index = 0; + // Find end of real keys for this frame for (size_t i = 0; i < ARRAY_SIZE(m_keys) - 1 && m_keys[i].key != KEY_NONE; ++i) { ++index; @@ -226,6 +227,7 @@ Bool Keyboard::checkKeyRepeat( void ) // Add key to this frame if (index < ARRAY_SIZE(m_keys) - 2) { + // 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].status = KeyboardIO::STATUS_UNUSED; From ef0800085cdf9fc29ccd58f02e0881c18255f1af Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 30 Jan 2026 19:28:53 +0100 Subject: [PATCH 5/8] Fixed loop iterations. --- .../Code/GameEngine/Source/GameClient/Input/Keyboard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp index 80599e70b73..1f6268ea76e 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp @@ -210,7 +210,7 @@ Bool Keyboard::checkKeyRepeat( void ) size_t index = 0; // Find end of real keys for this frame - for (size_t i = 0; i < ARRAY_SIZE(m_keys) - 1 && m_keys[i].key != KEY_NONE; ++i) + for (size_t i = 0; i < ARRAY_SIZE(m_keys) && m_keys[i].key != KEY_NONE; ++i) { ++index; } @@ -225,7 +225,7 @@ Bool Keyboard::checkKeyRepeat( void ) if( m_keyStatus[ key ].keyDownTimeMsec > 0 && now - m_keyStatus[ key ].keyDownTimeMsec > Keyboard::KEY_REPEAT_DELAY_MSEC ) { // Add key to this frame - if (index < ARRAY_SIZE(m_keys) - 2) + if (index < ARRAY_SIZE(m_keys) - 1) { // Add key to this frame m_keys[index].key = (UnsignedByte)key; From a4ac9733feba26352d58e6f7a535cf79dd028aaf Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 30 Jan 2026 19:30:54 +0100 Subject: [PATCH 6/8] Moved code to improve accuracy of code comment. --- GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp index 1f6268ea76e..b9a06824005 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp @@ -231,9 +231,9 @@ Bool Keyboard::checkKeyRepeat( void ) 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].status = KeyboardIO::STATUS_UNUSED; + ++index; // Set end flag - ++index; m_keys[index].key = KEY_NONE; } From 89dea8b937a609157f5c0f0db864f1768a1e6bda Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 30 Jan 2026 19:31:50 +0100 Subject: [PATCH 7/8] Removed duplicate comment. --- GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp index b9a06824005..0dc6250fa4e 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp @@ -224,7 +224,6 @@ Bool Keyboard::checkKeyRepeat( void ) if( m_keyStatus[ key ].keyDownTimeMsec > 0 && now - m_keyStatus[ key ].keyDownTimeMsec > Keyboard::KEY_REPEAT_DELAY_MSEC ) { - // Add key to this frame if (index < ARRAY_SIZE(m_keys) - 1) { // Add key to this frame From 67202f0c3e41fe1902ccbb5a171e1cd6af550d94 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:40:01 +0100 Subject: [PATCH 8/8] Removed unnecessary EA comment. --- GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp index 0dc6250fa4e..22cdfab05b8 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/Input/Keyboard.cpp @@ -228,7 +228,7 @@ Bool Keyboard::checkKeyRepeat( void ) { // 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_keys[index].status = KeyboardIO::STATUS_UNUSED; ++index;