diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp index 13392bbb44..2bf9d302a7 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp @@ -247,6 +247,14 @@ namespace KeyboardEventHandlers bool isMatchOnChordEnd = false; bool isMatchOnChordStart = false; + static bool isAltRightKeyInvoked = false; + + // Check if the right Alt key (AltGr) is pressed. + if (data->lParam->vkCode == VK_RMENU && ii.GetVirtualKeyState(VK_LCONTROL)) + { + isAltRightKeyInvoked = true; + } + // If the shortcut has been pressed down if (!it->second.isShortcutInvoked && it->first.CheckModifiersKeyboardState(ii)) { @@ -522,6 +530,13 @@ namespace KeyboardEventHandlers // 5. The user presses any key apart from the action key or a modifier key in the original shortcut - revert the keyboard state to just the original modifiers being held down along with the current key press // 6. The user releases any key apart from original modifier or original action key - This can't happen since the key down would have to happen first, which is handled above + // Prevents the unintended release of the Ctrl part when AltGr is pressed. AltGr acts as both Ctrl and Alt being pressed. + // After triggering a shortcut involving AltGr, the system might attempt to release the Ctrl part. This code ensures Ctrl remains pressed, maintaining the AltGr state correctly. + if (isAltRightKeyInvoked && data->lParam->vkCode == VK_LCONTROL && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)) + { + break; + } + // Get the common keys between the two shortcuts int commonKeys = (remapToShortcut && !isRunProgram) ? it->first.GetCommonModifiersCount(std::get(it->second.targetShortcut)) : 0; @@ -543,8 +558,15 @@ namespace KeyboardEventHandlers Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first, data->lParam->vkCode); - // Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate it's own key message - Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get(it->second.targetShortcut), data->lParam->vkCode); + if (!isAltRightKeyInvoked) + { + // Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate it's own key message + Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get(it->second.targetShortcut), data->lParam->vkCode); + } + else + { + isAltRightKeyInvoked = false; + } // Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+Ctrl+A->Ctrl+V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); @@ -559,8 +581,16 @@ namespace KeyboardEventHandlers Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } - // Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate it's own key message - Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, Shortcut(), data->lParam->vkCode); + // Ensures that after releasing both the action key and AltGr, Ctrl does not remain falsely pressed. + if (!isAltRightKeyInvoked) + { + // Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate it's own key message + Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, Shortcut(), data->lParam->vkCode); + } + else + { + isAltRightKeyInvoked = false; + } // Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+Ctrl+A->V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); @@ -672,16 +702,22 @@ namespace KeyboardEventHandlers // Release new key state Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); - // Set original shortcut key down state except the action key - Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + if (!isAltRightKeyInvoked) + { + // Set original shortcut key down state except the action key + Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + } // Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->V, press Shift+Win+A and release A, since Win will be pressed here we need to send a dummy event after it Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); - // Reset the remap state - it->second.isShortcutInvoked = false; - it->second.winKeyInvoked = ModifierKey::Disabled; - it->second.isOriginalActionKeyPressed = false; + if (!isAltRightKeyInvoked) + { + // Reset the remap state + it->second.isShortcutInvoked = false; + it->second.winKeyInvoked = ModifierKey::Disabled; + it->second.isOriginalActionKeyPressed = false; + } // If app specific shortcut has finished invoking, reset the target application if (activatedApp != KeyboardManagerConstants::NoActivatedApp) @@ -741,7 +777,10 @@ namespace KeyboardEventHandlers if (newRemapping.RemapToKey()) { DWORD to = std::get<0>(newRemapping.targetShortcut); - Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + if (!isAltRightKeyInvoked) + { + Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + } if (ii.GetVirtualKeyState(static_cast(from.actionKey))) { // If the action key from the last shortcut is still being pressed, release it. @@ -752,13 +791,19 @@ namespace KeyboardEventHandlers else { Shortcut to = std::get(newRemapping.targetShortcut); - Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, to); + if (!isAltRightKeyInvoked) + { + Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, to); + } if (ii.GetVirtualKeyState(static_cast(from.actionKey))) { // If the action key from the last shortcut is still being pressed, release it. Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(from.actionKey), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } - Helpers::SetModifierKeyEvents(to, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, from); + if (!isAltRightKeyInvoked) + { + Helpers::SetModifierKeyEvents(to, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, from); + } Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(to.actionKey), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); newRemapping.isShortcutInvoked = true; } @@ -783,10 +828,13 @@ namespace KeyboardEventHandlers { Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } - Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); + if (!isAltRightKeyInvoked) + { + Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); - // Set old shortcut key down state - Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get(it->second.targetShortcut)); + // Set old shortcut key down state + Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get(it->second.targetShortcut)); + } // key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again if (isActionKeyPressed) @@ -800,10 +848,13 @@ namespace KeyboardEventHandlers // Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after shortcut to shortcut is released to open start menu } - // Reset the remap state - it->second.isShortcutInvoked = false; - it->second.winKeyInvoked = ModifierKey::Disabled; - it->second.isOriginalActionKeyPressed = false; + if (!isAltRightKeyInvoked) + { + // Reset the remap state + it->second.isShortcutInvoked = false; + it->second.winKeyInvoked = ModifierKey::Disabled; + it->second.isOriginalActionKeyPressed = false; + } // If app specific shortcut has finished invoking, reset the target application if (activatedApp) @@ -848,8 +899,11 @@ namespace KeyboardEventHandlers { std::vector keyEventList; - // Set original shortcut key down state - Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + if (!isAltRightKeyInvoked) + { + // Set original shortcut key down state + Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + } // Send the original action key only if it is physically pressed. For remappings to keys other than disabled we already check earlier that it is not pressed in this scenario. For remap to disable if (isRemapToDisable && isOriginalActionKeyPressed) @@ -863,10 +917,13 @@ namespace KeyboardEventHandlers // Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after another shortcut to key remap is released to open start menu - // Reset the remap state - it->second.isShortcutInvoked = false; - it->second.winKeyInvoked = ModifierKey::Disabled; - it->second.isOriginalActionKeyPressed = false; + if (!isAltRightKeyInvoked) + { + // Reset the remap state + it->second.isShortcutInvoked = false; + it->second.winKeyInvoked = ModifierKey::Disabled; + it->second.isOriginalActionKeyPressed = false; + } // If app specific shortcut has finished invoking, reset the target application if (activatedApp != KeyboardManagerConstants::NoActivatedApp)