Change behavior on Edit Keyboard screen to be physical keys (#2666)

* Commented out ToggleToMod and AppSpecific function calls

* Added logic to make sure key remaps don't occur on Edit Keyboard Window

* Changed behavior such that remappings only apply after window is closed
This commit is contained in:
Arjun Balgovind 2020-05-04 15:49:37 -07:00 committed by GitHub
parent 855f3d74fe
commit c4f884f104
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 82 additions and 28 deletions

View File

@ -37,6 +37,14 @@ namespace KeyboardManagerHelper
ShortcutNotMoreThanOneActionKey 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 // Function to split a wstring based on a delimiter and return a vector of split strings
std::vector<std::wstring> splitwstring(const std::wstring& input, wchar_t delimiter); std::vector<std::wstring> splitwstring(const std::wstring& input, wchar_t delimiter);

View File

@ -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) bool KeyboardManagerState::CheckUIState(KeyboardManagerUIState state)
{ {
std::lock_guard<std::mutex> lock(uiState_mutex); std::lock_guard<std::mutex> lock(uiState_mutex);
if (uiState == state) if (uiState == state)
{ {
std::unique_lock<std::mutex> lock(currentUIWindow_mutex); std::unique_lock<std::mutex> lock(currentUIWindow_mutex);
if (uiState == KeyboardManagerUIState::Deactivated) if (uiState == KeyboardManagerUIState::Deactivated || uiState == KeyboardManagerUIState::EditKeyboardWindowActivated || uiState == KeyboardManagerUIState::EditShortcutsWindowActivated)
{ {
return true; 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. // 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()) 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. // 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 // Check if the detect key UI window has been activated
if (CheckUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated)) if (CheckUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated))
{ {
if (HandleKeyDelayEvent(data)) if (HandleKeyDelayEvent(data))
{ {
return true; return KeyboardManagerHelper::KeyboardHookDecision::Suppress;
} }
// detect the key if it is pressed down // detect the key if it is pressed down
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN) if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
@ -270,21 +270,27 @@ bool KeyboardManagerState::DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent*
} }
// Suppress the keyboard event // 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. // 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 // Check if the detect shortcut UI window has been activated
if (CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated)) if (CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated))
{ {
if (HandleKeyDelayEvent(data)) if (HandleKeyDelayEvent(data))
{ {
return true; return KeyboardManagerHelper::KeyboardHookDecision::Suppress;
} }
// Add the key if it is pressed down // Add the key if it is pressed down
@ -299,7 +305,7 @@ bool KeyboardManagerState::DetectShortcutUIBackend(LowlevelKeyboardEvent* data)
} }
// Suppress the keyboard event // 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 // 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( void KeyboardManagerState::RegisterKeyDelay(

View File

@ -19,8 +19,12 @@ enum class KeyboardManagerUIState
Deactivated, Deactivated,
// If set to this value then the detect key window is currently active and it requires a hook // If set to this value then the detect key window is currently active and it requires a hook
DetectSingleKeyRemapWindowActivated, 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 // 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 // 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 // Function to reset the UI state members
void ResetUIState(); 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); bool CheckUIState(KeyboardManagerUIState state);
// Function to set the window handle of the current UI window that is activated // Function to set the window handle of the current UI window that is activated
@ -140,10 +144,10 @@ public:
DWORD GetDetectedSingleRemapKey(); 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. // 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. // 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 // 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. // NOTE: this will throw an exception if a virtual key is registered twice.

View File

@ -345,10 +345,15 @@ public:
intptr_t HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept intptr_t HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept
{ {
// If the Detect Key Window is currently activated, then suppress the keyboard event // 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; return 1;
} }
else if (singleKeyRemapUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook)
{
return 0;
}
// Remap a key // Remap a key
intptr_t SingleKeyRemapResult = HandleSingleKeyRemapEvent(data); intptr_t SingleKeyRemapResult = HandleSingleKeyRemapEvent(data);
@ -360,28 +365,33 @@ public:
} }
// If the Detect Shortcut Window is currently activated, then suppress the keyboard event // 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; return 1;
} }
else if (shortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook)
// 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; 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 // Handle an os-level shortcut remapping
intptr_t OSLevelShortcutRemapResult = HandleOSLevelShortcutRemapEvent(data); intptr_t OSLevelShortcutRemapResult = HandleOSLevelShortcutRemapEvent(data);
// If any of the supported types of remappings took place, then suppress the key event // 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; return 1;
} }

View File

@ -150,6 +150,9 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
// Vector to store dynamically allocated control objects to avoid early destruction // Vector to store dynamically allocated control objects to avoid early destruction
std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>> keyboardRemapControlObjects; std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>> 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 // Load existing remaps into UI
std::unique_lock<std::mutex> lock(keyboardManagerState.singleKeyReMap_mutex); std::unique_lock<std::mutex> lock(keyboardManagerState.singleKeyReMap_mutex);
std::unordered_map<DWORD, DWORD> singleKeyRemapCopy = keyboardManagerState.singleKeyReMap; std::unordered_map<DWORD, DWORD> singleKeyRemapCopy = keyboardManagerState.singleKeyReMap;
@ -328,6 +331,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
hWndXamlIslandEditKeyboardWindow = nullptr; hWndXamlIslandEditKeyboardWindow = nullptr;
hwndLock.lock(); hwndLock.lock();
hwndEditKeyboardNativeWindow = nullptr; hwndEditKeyboardNativeWindow = nullptr;
keyboardManagerState.ResetUIState();
// Cannot be done in WM_DESTROY because that causes crashes due to fatal app exit // Cannot be done in WM_DESTROY because that causes crashes due to fatal app exit
xamlBridge.ClearXamlIslands(); xamlBridge.ClearXamlIslands();

View File

@ -146,6 +146,9 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
// Vector to store dynamically allocated control objects to avoid early destruction // Vector to store dynamically allocated control objects to avoid early destruction
std::vector<std::vector<std::unique_ptr<ShortcutControl>>> keyboardRemapControlObjects; std::vector<std::vector<std::unique_ptr<ShortcutControl>>> 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 // Load existing shortcuts into UI
std::unique_lock<std::mutex> lock(keyboardManagerState.osLevelShortcutReMap_mutex); std::unique_lock<std::mutex> lock(keyboardManagerState.osLevelShortcutReMap_mutex);
for (const auto& it : keyboardManagerState.osLevelShortcutReMap) for (const auto& it : keyboardManagerState.osLevelShortcutReMap)
@ -252,6 +255,7 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
hWndXamlIslandEditShortcutsWindow = nullptr; hWndXamlIslandEditShortcutsWindow = nullptr;
hwndLock.lock(); hwndLock.lock();
hwndEditShortcutsNativeWindow = nullptr; hwndEditShortcutsNativeWindow = nullptr;
keyboardManagerState.ResetUIState();
// Cannot be done in WM_DESTROY because that causes crashes due to fatal app exit // Cannot be done in WM_DESTROY because that causes crashes due to fatal app exit
xamlBridge.ClearXamlIslands(); xamlBridge.ClearXamlIslands();

View File

@ -178,6 +178,8 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
// Reset the keyboard manager UI state // Reset the keyboard manager UI state
keyboardManagerState.ResetUIState(); keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Shortcut window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, EditShortcutsWindowHandle);
unregisterKeys(); unregisterKeys();
detectShortcutBox.Hide(); 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&) { cancelButton.Click([detectShortcutBox, unregisterKeys, &keyboardManagerState](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
// Reset the keyboard manager UI state // Reset the keyboard manager UI state
keyboardManagerState.ResetUIState(); keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Shortcut window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, EditShortcutsWindowHandle);
unregisterKeys(); unregisterKeys();
detectShortcutBox.Hide(); detectShortcutBox.Hide();
}); });
@ -240,6 +244,8 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
}); });
keyboardManagerState.ResetUIState(); keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Shortcut window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, EditShortcutsWindowHandle);
unregisterKeys(); unregisterKeys();
}, },
nullptr); nullptr);

View File

@ -154,6 +154,8 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
// Reset the keyboard manager UI state // Reset the keyboard manager UI state
keyboardManagerState.ResetUIState(); keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
unregisterKeys(); unregisterKeys();
detectRemapKeyBox.Hide(); 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&) { cancelButton.Click([detectRemapKeyBox, unregisterKeys, &keyboardManagerState](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
// Reset the keyboard manager UI state // Reset the keyboard manager UI state
keyboardManagerState.ResetUIState(); keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
unregisterKeys(); unregisterKeys();
detectRemapKeyBox.Hide(); detectRemapKeyBox.Hide();
}); });
@ -214,6 +218,8 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
}); });
keyboardManagerState.ResetUIState(); keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
unregisterKeys(); unregisterKeys();
}, },
nullptr); nullptr);