diff --git a/src/modules/keyboardmanager/common/Helpers.h b/src/modules/keyboardmanager/common/Helpers.h index 578064fe14..2a95dbb9bb 100644 --- a/src/modules/keyboardmanager/common/Helpers.h +++ b/src/modules/keyboardmanager/common/Helpers.h @@ -37,6 +37,14 @@ namespace KeyboardManagerHelper ShortcutNotMoreThanOneActionKey }; + // Enum type to store possible decision for input in the low level hook + enum class KeyboardHookDecision + { + ContinueExec, + Suppress, + SkipHook + }; + // Function to split a wstring based on a delimiter and return a vector of split strings std::vector splitwstring(const std::wstring& input, wchar_t delimiter); diff --git a/src/modules/keyboardmanager/common/KeyboardManagerState.cpp b/src/modules/keyboardmanager/common/KeyboardManagerState.cpp index f17f400946..7db6dfe977 100644 --- a/src/modules/keyboardmanager/common/KeyboardManagerState.cpp +++ b/src/modules/keyboardmanager/common/KeyboardManagerState.cpp @@ -20,18 +20,18 @@ KeyboardManagerState::~KeyboardManagerState() } } -// Function to check the if the UI state matches the argument state. For states with activated windows it also checks if the window is in focus. +// Function to check the if the UI state matches the argument state. For states with detect windows it also checks if the window is in focus. bool KeyboardManagerState::CheckUIState(KeyboardManagerUIState state) { std::lock_guard lock(uiState_mutex); if (uiState == state) { std::unique_lock lock(currentUIWindow_mutex); - if (uiState == KeyboardManagerUIState::Deactivated) + if (uiState == KeyboardManagerUIState::Deactivated || uiState == KeyboardManagerUIState::EditKeyboardWindowActivated || uiState == KeyboardManagerUIState::EditShortcutsWindowActivated) { return true; } - // If the UI state is activated then we also have to ensure that the UI window is in focus. + // If the UI state is a detect window then we also have to ensure that the UI window is in focus. // GetForegroundWindow can be used here since we only need to check the main parent window and not the sub windows within the content dialog. Using GUIThreadInfo will give more specific sub-windows within the XAML window which is not needed. else if (currentUIWindow == GetForegroundWindow()) { @@ -254,14 +254,14 @@ void KeyboardManagerState::ResetDetectedShortcutKey(DWORD key) } // Function which can be used in HandleKeyboardHookEvent before the single key remap event to use the UI and suppress events while the remap window is active. -bool KeyboardManagerState::DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent* data) +KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent* data) { // Check if the detect key UI window has been activated if (CheckUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated)) { if (HandleKeyDelayEvent(data)) { - return true; + return KeyboardManagerHelper::KeyboardHookDecision::Suppress; } // detect the key if it is pressed down if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN) @@ -270,21 +270,27 @@ bool KeyboardManagerState::DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent* } // Suppress the keyboard event - return true; + return KeyboardManagerHelper::KeyboardHookDecision::Suppress; } - return false; + // If the settings window is up, remappings should not be applied, but we should not suppress events in the hook + else if (CheckUIState(KeyboardManagerUIState::EditKeyboardWindowActivated)) + { + return KeyboardManagerHelper::KeyboardHookDecision::SkipHook; + } + + return KeyboardManagerHelper::KeyboardHookDecision::ContinueExec; } // Function which can be used in HandleKeyboardHookEvent before the os level shortcut remap event to use the UI and suppress events while the remap window is active. -bool KeyboardManagerState::DetectShortcutUIBackend(LowlevelKeyboardEvent* data) +KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectShortcutUIBackend(LowlevelKeyboardEvent* data) { // Check if the detect shortcut UI window has been activated if (CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated)) { if (HandleKeyDelayEvent(data)) { - return true; + return KeyboardManagerHelper::KeyboardHookDecision::Suppress; } // Add the key if it is pressed down @@ -299,7 +305,7 @@ bool KeyboardManagerState::DetectShortcutUIBackend(LowlevelKeyboardEvent* data) } // Suppress the keyboard event - return true; + return KeyboardManagerHelper::KeyboardHookDecision::Suppress; } // If the detect shortcut UI window is not activated, then clear the shortcut buffer if it isn't empty @@ -312,7 +318,13 @@ bool KeyboardManagerState::DetectShortcutUIBackend(LowlevelKeyboardEvent* data) } } - return false; + // If the settings window is up, shortcut remappings should not be applied, but we should not suppress events in the hook + if (CheckUIState(KeyboardManagerUIState::EditShortcutsWindowActivated)) + { + return KeyboardManagerHelper::KeyboardHookDecision::SkipHook; + } + + return KeyboardManagerHelper::KeyboardHookDecision::ContinueExec; } void KeyboardManagerState::RegisterKeyDelay( diff --git a/src/modules/keyboardmanager/common/KeyboardManagerState.h b/src/modules/keyboardmanager/common/KeyboardManagerState.h index 98938df4b0..ac4d8ccebd 100644 --- a/src/modules/keyboardmanager/common/KeyboardManagerState.h +++ b/src/modules/keyboardmanager/common/KeyboardManagerState.h @@ -19,8 +19,12 @@ enum class KeyboardManagerUIState Deactivated, // If set to this value then the detect key window is currently active and it requires a hook DetectSingleKeyRemapWindowActivated, + // If set to this value then the edit keyboard window is currently active and remaps should not be applied + EditKeyboardWindowActivated, // If set to this value then the detect shortcut window is currently active and it requires a hook - DetectShortcutWindowActivated + DetectShortcutWindowActivated, + // If set to this value then the edit shortcuts window is currently active and remaps should not be applied + EditShortcutsWindowActivated }; // Class to store the shared state of the keyboard manager between the UI and the hook @@ -100,7 +104,7 @@ public: // Function to reset the UI state members void ResetUIState(); - // Function to check the if the UI state matches the argument state. For states with activated windows it also checks if the window is in focus. + // Function to check the if the UI state matches the argument state. For states with detect windows it also checks if the window is in focus. bool CheckUIState(KeyboardManagerUIState state); // Function to set the window handle of the current UI window that is activated @@ -140,10 +144,10 @@ public: DWORD GetDetectedSingleRemapKey(); // Function which can be used in HandleKeyboardHookEvent before the single key remap event to use the UI and suppress events while the remap window is active. - bool DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent* data); + KeyboardManagerHelper::KeyboardHookDecision DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent* data); // Function which can be used in HandleKeyboardHookEvent before the os level shortcut remap event to use the UI and suppress events while the remap window is active. - bool DetectShortcutUIBackend(LowlevelKeyboardEvent* data); + KeyboardManagerHelper::KeyboardHookDecision DetectShortcutUIBackend(LowlevelKeyboardEvent* data); // Add a KeyDelay object to get delayed key presses events for a given virtual key // NOTE: this will throw an exception if a virtual key is registered twice. diff --git a/src/modules/keyboardmanager/dll/dllmain.cpp b/src/modules/keyboardmanager/dll/dllmain.cpp index 7749a8efd4..2adafaf598 100644 --- a/src/modules/keyboardmanager/dll/dllmain.cpp +++ b/src/modules/keyboardmanager/dll/dllmain.cpp @@ -345,10 +345,15 @@ public: intptr_t HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept { // If the Detect Key Window is currently activated, then suppress the keyboard event - if (keyboardManagerState.DetectSingleRemapKeyUIBackend(data)) + KeyboardManagerHelper::KeyboardHookDecision singleKeyRemapUIDetected = keyboardManagerState.DetectSingleRemapKeyUIBackend(data); + if (singleKeyRemapUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress) { return 1; } + else if (singleKeyRemapUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook) + { + return 0; + } // Remap a key intptr_t SingleKeyRemapResult = HandleSingleKeyRemapEvent(data); @@ -360,28 +365,33 @@ public: } // If the Detect Shortcut Window is currently activated, then suppress the keyboard event - if (keyboardManagerState.DetectShortcutUIBackend(data)) + KeyboardManagerHelper::KeyboardHookDecision shortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data); + if (shortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress) { return 1; } - - // Remap a key to behave like a modifier instead of a toggle - intptr_t SingleKeyToggleToModResult = HandleSingleKeyToggleToModEvent(data); - - // Handle an app-specific shortcut remapping - intptr_t AppSpecificShortcutRemapResult = HandleAppSpecificShortcutRemapEvent(data); - - // If an app-specific shortcut is remapped then the os-level shortcut remapping should be suppressed. - if (AppSpecificShortcutRemapResult == 1) + else if (shortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook) { - return 1; + return 0; } + //// Remap a key to behave like a modifier instead of a toggle + //intptr_t SingleKeyToggleToModResult = HandleSingleKeyToggleToModEvent(data); + + //// Handle an app-specific shortcut remapping + //intptr_t AppSpecificShortcutRemapResult = HandleAppSpecificShortcutRemapEvent(data); + + //// If an app-specific shortcut is remapped then the os-level shortcut remapping should be suppressed. + //if (AppSpecificShortcutRemapResult == 1) + //{ + // return 1; + //} + // Handle an os-level shortcut remapping intptr_t OSLevelShortcutRemapResult = HandleOSLevelShortcutRemapEvent(data); // If any of the supported types of remappings took place, then suppress the key event - if ((SingleKeyRemapResult + SingleKeyToggleToModResult + OSLevelShortcutRemapResult + AppSpecificShortcutRemapResult) > 0) + if ((SingleKeyRemapResult + OSLevelShortcutRemapResult) > 0) { return 1; } diff --git a/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp b/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp index 7b6d832d18..a24adddd8e 100644 --- a/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp +++ b/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp @@ -150,6 +150,9 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan // Vector to store dynamically allocated control objects to avoid early destruction std::vector>> keyboardRemapControlObjects; + // Set keyboard manager UI state so that remaps are not applied while on this window + keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, _hWndEditKeyboardWindow); + // Load existing remaps into UI std::unique_lock lock(keyboardManagerState.singleKeyReMap_mutex); std::unordered_map singleKeyRemapCopy = keyboardManagerState.singleKeyReMap; @@ -328,6 +331,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan hWndXamlIslandEditKeyboardWindow = nullptr; hwndLock.lock(); hwndEditKeyboardNativeWindow = nullptr; + keyboardManagerState.ResetUIState(); // Cannot be done in WM_DESTROY because that causes crashes due to fatal app exit xamlBridge.ClearXamlIslands(); diff --git a/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp b/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp index 1fec45f6bc..742584330c 100644 --- a/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp +++ b/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp @@ -146,6 +146,9 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa // Vector to store dynamically allocated control objects to avoid early destruction std::vector>> keyboardRemapControlObjects; + // Set keyboard manager UI state so that shortcut remaps are not applied while on this window + keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, _hWndEditShortcutsWindow); + // Load existing shortcuts into UI std::unique_lock lock(keyboardManagerState.osLevelShortcutReMap_mutex); for (const auto& it : keyboardManagerState.osLevelShortcutReMap) @@ -252,6 +255,7 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa hWndXamlIslandEditShortcutsWindow = nullptr; hwndLock.lock(); hwndEditShortcutsNativeWindow = nullptr; + keyboardManagerState.ResetUIState(); // Cannot be done in WM_DESTROY because that causes crashes due to fatal app exit xamlBridge.ClearXamlIslands(); diff --git a/src/modules/keyboardmanager/ui/ShortcutControl.cpp b/src/modules/keyboardmanager/ui/ShortcutControl.cpp index 0497725e0d..254cce47e6 100644 --- a/src/modules/keyboardmanager/ui/ShortcutControl.cpp +++ b/src/modules/keyboardmanager/ui/ShortcutControl.cpp @@ -178,6 +178,8 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn // Reset the keyboard manager UI state keyboardManagerState.ResetUIState(); + // Revert UI state back to Edit Shortcut window + keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, EditShortcutsWindowHandle); unregisterKeys(); detectShortcutBox.Hide(); }; @@ -225,6 +227,8 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn cancelButton.Click([detectShortcutBox, unregisterKeys, &keyboardManagerState](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { // Reset the keyboard manager UI state keyboardManagerState.ResetUIState(); + // Revert UI state back to Edit Shortcut window + keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, EditShortcutsWindowHandle); unregisterKeys(); detectShortcutBox.Hide(); }); @@ -240,6 +244,8 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn }); keyboardManagerState.ResetUIState(); + // Revert UI state back to Edit Shortcut window + keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, EditShortcutsWindowHandle); unregisterKeys(); }, nullptr); diff --git a/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp b/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp index 48dd548a09..422482e04a 100644 --- a/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp +++ b/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp @@ -154,6 +154,8 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II // Reset the keyboard manager UI state keyboardManagerState.ResetUIState(); + // Revert UI state back to Edit Keyboard window + keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle); unregisterKeys(); detectRemapKeyBox.Hide(); }; @@ -199,6 +201,8 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II cancelButton.Click([detectRemapKeyBox, unregisterKeys, &keyboardManagerState](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { // Reset the keyboard manager UI state keyboardManagerState.ResetUIState(); + // Revert UI state back to Edit Keyboard window + keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle); unregisterKeys(); detectRemapKeyBox.Hide(); }); @@ -214,6 +218,8 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II }); keyboardManagerState.ResetUIState(); + // Revert UI state back to Edit Keyboard window + keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle); unregisterKeys(); }, nullptr);