[Keyboard Manager] Added in Shortcut to Key and Key to Shortcut remapping (#5070)

* Added union class

* Added key to shortcut backend implementation

* Added tests

* Added tests for CapsLock/modifier workaround for key to shortcut

* Added correct JSON loading step

* Cleaned shortcut remap code to use helper function for modifier keys

* Removed RemapKey class

* Enable Key to Shortcut in UI along with Type Shortcut in Remap key window

* Fixed orphaning and unsuccessful remap dialog

* Fixed column width

* Renamed second type key button

* Fixed Type Shortcut issues

* Fixed shortcut to key backend logic and manually tested most scenarios

* Added s2k in UI, manually verified its working

* Added one more k2s test

* Added tests for s2k

* Added tests for Caps Lock workaround in shortcut remaps

* Fixed formatting

* Fixed formatting

* Removed safety code since it can cause issues with code generated key up events

* Added test for key up scenario

* Tweaked warning text

* Tweaked text

* Tweaked text to fit in two lines

* telemetry additions
This commit is contained in:
Arjun Balgovind
2020-07-23 16:43:49 -07:00
committed by GitHub
parent 53c4c6cbb8
commit ff1e04b957
32 changed files with 2322 additions and 686 deletions

View File

@@ -1,7 +1,10 @@
#include "pch.h"
#include "KeyboardManagerState.h"
#include "Shortcut.h"
#include "RemapShortcut.h"
#include <../common/settings_helpers.h>
#include "KeyDelay.h"
#include "Helpers.h"
// Constructor
KeyboardManagerState::KeyboardManagerState() :
@@ -103,6 +106,7 @@ void KeyboardManagerState::ClearOSLevelShortcuts()
{
std::lock_guard<std::mutex> lock(osLevelShortcutReMap_mutex);
osLevelShortcutReMap.clear();
osLevelShortcutReMapSortedKeys.clear();
}
// Function to clear the Keys remapping table.
@@ -117,10 +121,11 @@ void KeyboardManagerState::ClearAppSpecificShortcuts()
{
std::lock_guard<std::mutex> lock(appSpecificShortcutReMap_mutex);
appSpecificShortcutReMap.clear();
appSpecificShortcutReMapSortedKeys.clear();
}
// Function to add a new OS level shortcut remapping
bool KeyboardManagerState::AddOSLevelShortcut(const Shortcut& originalSC, const Shortcut& newSC)
bool KeyboardManagerState::AddOSLevelShortcut(const Shortcut& originalSC, const std::variant<DWORD, Shortcut>& newSC)
{
std::lock_guard<std::mutex> lock(osLevelShortcutReMap_mutex);
@@ -132,11 +137,14 @@ bool KeyboardManagerState::AddOSLevelShortcut(const Shortcut& originalSC, const
}
osLevelShortcutReMap[originalSC] = RemapShortcut(newSC);
osLevelShortcutReMapSortedKeys.push_back(originalSC);
KeyboardManagerHelper::SortShortcutVectorBasedOnSize(osLevelShortcutReMapSortedKeys);
return true;
}
// Function to add a new OS level shortcut remapping
bool KeyboardManagerState::AddSingleKeyRemap(const DWORD& originalKey, const DWORD& newRemapKey)
// Function to add a new single key to key/shortcut remapping
bool KeyboardManagerState::AddSingleKeyRemap(const DWORD& originalKey, const std::variant<DWORD, Shortcut>& newRemapKey)
{
std::lock_guard<std::mutex> lock(singleKeyReMap_mutex);
@@ -152,28 +160,34 @@ bool KeyboardManagerState::AddSingleKeyRemap(const DWORD& originalKey, const DWO
}
// Function to add a new App specific shortcut remapping
bool KeyboardManagerState::AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const Shortcut& newSC)
bool KeyboardManagerState::AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const std::variant<DWORD, Shortcut>& newSC)
{
std::lock_guard<std::mutex> lock(appSpecificShortcutReMap_mutex);
// Check if there are any app specific shortcuts for this app
auto appIt = appSpecificShortcutReMap.find(app);
if (appIt != appSpecificShortcutReMap.end())
{
// Check if the shortcut is already remapped
auto shortcutIt = appSpecificShortcutReMap[app].find(originalSC);
if (shortcutIt != appSpecificShortcutReMap[app].end())
{
return false;
}
}
// Convert app name to lower case
std::wstring process_name;
process_name.resize(app.length());
std::transform(app.begin(), app.end(), process_name.begin(), towlower);
// Check if there are any app specific shortcuts for this app
auto appIt = appSpecificShortcutReMap.find(process_name);
if (appIt != appSpecificShortcutReMap.end())
{
// Check if the shortcut is already remapped
auto shortcutIt = appSpecificShortcutReMap[process_name].find(originalSC);
if (shortcutIt != appSpecificShortcutReMap[process_name].end())
{
return false;
}
}
else
{
appSpecificShortcutReMapSortedKeys[process_name] = std::vector<Shortcut>();
}
appSpecificShortcutReMap[process_name][originalSC] = RemapShortcut(newSC);
appSpecificShortcutReMapSortedKeys[process_name].push_back(originalSC);
KeyboardManagerHelper::SortShortcutVectorBasedOnSize(appSpecificShortcutReMapSortedKeys[process_name]);
return true;
}
@@ -349,10 +363,10 @@ KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectSingleRe
}
// 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.
KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectShortcutUIBackend(LowlevelKeyboardEvent* data)
KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectShortcutUIBackend(LowlevelKeyboardEvent* data, bool isRemapKey)
{
// Check if the detect shortcut UI window has been activated
if (CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated))
if ((!isRemapKey && CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated)) || (isRemapKey && CheckUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated)))
{
if (HandleKeyDelayEvent(data))
{
@@ -375,7 +389,7 @@ KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectShortcut
}
// If the detect shortcut UI window is not activated, then clear the shortcut buffer if it isn't empty
else
else if (!CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated) && !CheckUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated))
{
std::lock_guard<std::mutex> lock(detectedShortcut_mutex);
if (!detectedShortcut.IsEmpty())
@@ -385,7 +399,7 @@ KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectShortcut
}
// 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))
if (!isRemapKey && (CheckUIState(KeyboardManagerUIState::EditShortcutsWindowActivated)) || (isRemapKey && uiState == KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated))
{
return KeyboardManagerHelper::KeyboardHookDecision::SkipHook;
}
@@ -452,7 +466,18 @@ bool KeyboardManagerState::SaveConfigToFile()
{
json::JsonObject keys;
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(winrt::to_hstring((unsigned int)it.first)));
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)it.second)));
// For key to key remapping
if (it.second.index() == 0)
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)std::get<DWORD>(it.second))));
}
// For key to shortcut remapping
else
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(it.second).ToHstringVK()));
}
inProcessRemapKeysArray.Append(keys);
}
@@ -463,12 +488,23 @@ bool KeyboardManagerState::SaveConfigToFile()
{
json::JsonObject keys;
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(it.first.ToHstringVK()));
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(it.second.targetShortcut.ToHstringVK()));
// For shortcut to key remapping
if (it.second.targetShortcut.index() == 0)
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)std::get<DWORD>(it.second.targetShortcut))));
}
// For shortcut to shortcut remapping
else
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(it.second.targetShortcut).ToHstringVK()));
}
globalRemapShortcutsArray.Append(keys);
}
lockOsLevelShortcutReMap.unlock();
std::unique_lock<std::mutex> lockAppSpecificShortcutReMap(appSpecificShortcutReMap_mutex);
for (const auto& itApp : appSpecificShortcutReMap)
{
@@ -477,12 +513,23 @@ bool KeyboardManagerState::SaveConfigToFile()
{
json::JsonObject keys;
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(itKeys.first.ToHstringVK()));
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(itKeys.second.targetShortcut.ToHstringVK()));
// For shortcut to key remapping
if (itKeys.second.targetShortcut.index() == 0)
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)std::get<DWORD>(itKeys.second.targetShortcut))));
}
// For shortcut to shortcut remapping
else
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(itKeys.second.targetShortcut).ToHstringVK()));
}
keys.SetNamedValue(KeyboardManagerConstants::TargetAppSettingName, json::value(itApp.first));
appSpecificRemapShortcutsArray.Append(keys);
appSpecificRemapShortcutsArray.Append(keys);
}
}
lockAppSpecificShortcutReMap.unlock();