Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Core/GameEngine/Include/GameClient/Keyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; ///< emit key-ups for held CTRL/SHIFT/ALT after focus loss
protected:

/** get the key data for a single key, KEY_NONE should be returned when
Expand All @@ -140,6 +140,7 @@ class Keyboard : public SubsystemInterface
void setKeyStateData( KeyDefType key, UnsignedByte data ); ///< get key state

UnsignedShort m_modifiers;
Bool m_pressedWithModifier[KEY_COUNT];
// internal keyboard data members
//Bool m_capsState; // 1 if caps lock is on
//Bool m_shiftState; // 1 if either shift key is pressed
Expand Down
100 changes: 58 additions & 42 deletions Core/GameEngine/Source/GameClient/Input/Keyboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,65 +138,71 @@ 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 pressedWithModifier = m_pressedWithModifier[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 ) )
{
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 ||
key == KEY_LCTRL ||
key == KEY_RCTRL ||
key == KEY_LSHIFT ||
key == KEY_RSHIFT ||
key == KEY_LALT ||
key == KEY_RALT )

{

//
// 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 );

}

// TheSuperHackers @bugfix CryoTheRenegade 31/08/2026 Preserve modifier state for
// each buffered event and carry it from a key-down to its matching key-up.
BitSet( m_keys[ index ].state, m_modifiers );
if( BitIsSet( m_keys[ index ].state, KEY_STATE_DOWN ) )
{
const Int keyModifiers = KEY_STATE_CONTROL | KEY_STATE_SHIFT | KEY_STATE_ALT;
m_pressedWithModifier[key] = (m_modifiers & keyModifiers) != 0;
if( m_pressedWithModifier[key] )
{
BitSet( m_keys[ index ].state, KEY_STATE_MODIFIER_ON_DOWN );
}
}
else
{
m_pressedWithModifier[key] = FALSE;
if( pressedWithModifier )
BitSet( m_keys[ index ].state, KEY_STATE_MODIFIER_ON_DOWN );
}

index++;

}

// 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++;

}

}

}

//-------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -233,7 +239,9 @@ 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
// This is an assignment, not a bit set.
m_keys[ index ].state = KEY_STATE_DOWN | KEY_STATE_AUTOREPEAT | m_modifiers
| (m_pressedWithModifier[key] ? KEY_STATE_MODIFIER_ON_DOWN : 0);
m_keys[ index ].status = KeyboardIO::STATUS_UNUSED;

// Set End Flag
Expand Down Expand Up @@ -699,6 +707,7 @@ Keyboard::Keyboard()

memset( m_keys, 0, sizeof( m_keys ) );
memset( m_keyStatus, 0, sizeof( m_keyStatus ) );
memset( m_pressedWithModifier, 0, sizeof( m_pressedWithModifier ) );
m_modifiers = KEY_STATE_NONE;
m_shift2Key = KEY_NONE;

Expand Down Expand Up @@ -749,13 +758,15 @@ 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();
// CTRL and SHIFT have the same stuck-mode problem (force-attack, prefer-selection).
emitModifierKeyUps();

memset( m_keys, 0, sizeof( m_keys ) );
memset( m_keyStatus, 0, sizeof( m_keyStatus ) );
// A held key can still report its release after focus returns. Do not clear
// m_pressedWithModifier until that release or a new press arrives.
m_modifiers = KEY_STATE_NONE;
if( getCapsState() )
{
Expand All @@ -765,24 +776,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))
if (BitIsSet(keyStatus[key].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))
{
GameMessage* msg = TheMessageStream->appendMessage(GameMessage::MSG_RAW_KEY_UP);
msg->appendIntegerArgument(KEY_RALT);
msg->appendIntegerArgument(key);
msg->appendIntegerArgument(KEY_STATE_UP);
}
}

//-------------------------------------------------------------------------------------------------
// Emit RAW_KEY_UP for still-held modifiers so MetaEvent can end force-attack / waypoints / etc.
//-------------------------------------------------------------------------------------------------
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);
}

//-------------------------------------------------------------------------------------------------
/** get the first key in our current state of the keyboard */
//-------------------------------------------------------------------------------------------------
Expand Down
33 changes: 7 additions & 26 deletions Core/GameEngine/Source/GameClient/MessageStream/HotKey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -74,33 +73,15 @@ 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 Int keyState = msg->getArgument(1)->integer;
const Int ignoredModifiers = KEY_STATE_CONTROL | KEY_STATE_SHIFT | KEY_STATE_ALT | KEY_STATE_MODIFIER_ON_DOWN;
if( keyState & ignoredModifiers )
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))
Expand Down
1 change: 1 addition & 0 deletions Generals/Code/GameEngine/Include/GameClient/KeyDefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ enum
KEY_STATE_AUTOREPEAT = 0x0100, // Key is down due to autorepeat (only seen in conjunction with KEY_STATE_DOWN)
KEY_STATE_CAPSLOCK = 0x0200, // Caps Lock key is on.
KEY_STATE_SHIFT2 = 0x0400, // Alternate shift key is pressed (I think this is for foreign keyboards..)
KEY_STATE_MODIFIER_ON_DOWN = 0x0800, // CTRL, SHIFT, or ALT was held when this key was pressed.

// modifier combinations when left/right isn't a factor
KEY_STATE_CONTROL = (KEY_STATE_LCONTROL | KEY_STATE_RCONTROL),
Expand Down
1 change: 1 addition & 0 deletions GeneralsMD/Code/GameEngine/Include/GameClient/KeyDefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ enum
KEY_STATE_AUTOREPEAT = 0x0100, // Key is down due to autorepeat (only seen in conjunction with KEY_STATE_DOWN)
KEY_STATE_CAPSLOCK = 0x0200, // Caps Lock key is on.
KEY_STATE_SHIFT2 = 0x0400, // Alternate shift key is pressed (I think this is for foreign keyboards..)
KEY_STATE_MODIFIER_ON_DOWN = 0x0800, // CTRL, SHIFT, or ALT was held when this key was pressed.

// modifier combinations when left/right isn't a factor
KEY_STATE_CONTROL = (KEY_STATE_LCONTROL | KEY_STATE_RCONTROL),
Expand Down
Loading