-
Notifications
You must be signed in to change notification settings - Fork 253
fix(input): Prevent modified key releases from firing plain hotkeys #3233
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3311ee3
d4687be
23e1ebc
3b71ae4
167bbc8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -96,6 +96,23 @@ void Keyboard::createStreamMessages() | |
|
|
||
| } | ||
|
|
||
| //------------------------------------------------------------------------------------------------- | ||
| 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; | ||
| } | ||
|
|
||
| //------------------------------------------------------------------------------------------------- | ||
| /** update all our key state data */ | ||
| //------------------------------------------------------------------------------------------------- | ||
|
|
@@ -138,65 +155,64 @@ 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) || key == m_shift2Key; | ||
| const UnsignedShort lastPressedKeyState = m_lastPressedKeyState[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 || isModifier ) | ||
|
|
||
| { | ||
|
|
||
| // | ||
| // 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 reuse an action key's press state on its release. | ||
| BitSet( m_keys[ index ].state, m_modifiers ); | ||
| if( BitIsSet( m_keys[ index ].state, KEY_STATE_DOWN ) ) | ||
| { | ||
| m_lastPressedKeyState[key] = isModifier ? KEY_STATE_NONE : m_modifiers & KEY_STATE_MODIFIERS; | ||
| } | ||
| else | ||
| { | ||
| if( !isModifier ) | ||
| { | ||
| BitClear( m_keys[ index ].state, KEY_STATE_MODIFIERS ); | ||
| BitSet( m_keys[ index ].state, lastPressedKeyState ); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this overwrites the key modifiers for a Key up event, right? It looks like the UP event is only triggered when a non-modifier key is released. Is that right? In So what happens is: CTRL + SHIFT + F Down = triggers event and remembers that this combo was pressed. The state is tracked with If these are functionally not the same, I suggest to look into making them behave the same. As far as I am aware the implementation in I suggest to ask the LLM how Meta Event implements the Key up events with modifiers, then how it fundamentally differs with the current Keyboard implementation, and which implementation direction makes more sense for the Keyboard. To me it looks suspiciuos that the key UP event gets its state overwritten. It means it is not truthful anymore. |
||
| } | ||
| m_lastPressedKeyState[key] = KEY_STATE_NONE; | ||
| } | ||
|
|
||
| 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++; | ||
|
|
||
| } | ||
|
|
||
| } | ||
|
|
||
| } | ||
|
|
||
| //------------------------------------------------------------------------------------------------- | ||
|
|
@@ -233,7 +249,8 @@ 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_keys[ index ].status = KeyboardIO::STATUS_UNUSED; | ||
|
|
||
| // Set End Flag | ||
|
|
@@ -699,6 +716,7 @@ Keyboard::Keyboard() | |
|
|
||
| memset( m_keys, 0, sizeof( m_keys ) ); | ||
| memset( m_keyStatus, 0, sizeof( m_keyStatus ) ); | ||
| memset( m_lastPressedKeyState, 0, sizeof( m_lastPressedKeyState ) ); | ||
| m_modifiers = KEY_STATE_NONE; | ||
| m_shift2Key = KEY_NONE; | ||
|
|
||
|
|
@@ -749,13 +767,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_lastPressedKeyState until that release or a new press arrives. | ||
| m_modifiers = KEY_STATE_NONE; | ||
| if( getCapsState() ) | ||
| { | ||
|
|
@@ -765,24 +785,31 @@ 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); | ||
| } | ||
| } | ||
|
|
||
| //------------------------------------------------------------------------------------------------- | ||
| // 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); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is a Shift 2 missing? Key State has one.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shift2 uses |
||
| if (m_shift2Key != KEY_NONE && !isCtrlShiftAltKey(m_shift2Key)) | ||
| emitRawKeyUpIfDown(m_keyStatus, m_shift2Key); | ||
| } | ||
|
|
||
| //------------------------------------------------------------------------------------------------- | ||
| /** get the first key in our current state of the keyboard */ | ||
| //------------------------------------------------------------------------------------------------- | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this save KEY_STATE_NONE if isModifier is true?
Can we do:
if (!isModifier) { m_lastPressedKeyState[key] = m_modifiers; }