/ src / modules / keyboardmanager / KeyboardManagerEditorLibrary / LoadingAndSavingRemappingHelper.cpp
LoadingAndSavingRemappingHelper.cpp
1 #include "pch.h" 2 #include "LoadingAndSavingRemappingHelper.h" 3 4 #include <set> 5 #include <variant> 6 #include <common/interop/shared_constants.h> 7 #include <keyboardmanager/common/MappingConfiguration.h> 8 9 #include "KeyboardManagerState.h" 10 #include "keyboardmanager/KeyboardManagerEditorLibrary/trace.h" 11 #include "EditorHelpers.h" 12 #include "ShortcutErrorType.h" 13 14 namespace LoadingAndSavingRemappingHelper 15 { 16 // Function to check if the set of remappings in the buffer are valid 17 ShortcutErrorType CheckIfRemappingsAreValid(const RemapBuffer& remappings) 18 { 19 ShortcutErrorType isSuccess = ShortcutErrorType::NoError; 20 std::map<std::wstring, std::set<KeyShortcutTextUnion>> ogKeys; 21 for (int i = 0; i < remappings.size(); i++) 22 { 23 KeyShortcutTextUnion ogKey = remappings[i].mapping[0]; 24 KeyShortcutTextUnion newKey = remappings[i].mapping[1]; 25 std::wstring appName = remappings[i].appName; 26 27 const bool ogKeyValidity = (ogKey.index() == 0 && std::get<DWORD>(ogKey) != NULL) || (ogKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(ogKey))); 28 const bool newKeyValidity = (newKey.index() == 0 && std::get<DWORD>(newKey) != NULL) || (newKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey))) || (newKey.index() == 2 && !std::get<std::wstring>(newKey).empty()); 29 30 // Add new set for a new target app name 31 if (ogKeys.find(appName) == ogKeys.end()) 32 { 33 ogKeys[appName] = std::set<KeyShortcutTextUnion>(); 34 } 35 36 if (ogKeyValidity && newKeyValidity && ogKeys[appName].find(ogKey) == ogKeys[appName].end()) 37 { 38 ogKeys[appName].insert(ogKey); 39 } 40 else if (ogKeyValidity && newKeyValidity && ogKeys[appName].find(ogKey) != ogKeys[appName].end()) 41 { 42 isSuccess = ShortcutErrorType::RemapUnsuccessful; 43 } 44 else 45 { 46 isSuccess = ShortcutErrorType::RemapUnsuccessful; 47 } 48 } 49 50 return isSuccess; 51 } 52 53 // Function to return the set of keys that have been orphaned from the remap buffer 54 std::vector<DWORD> GetOrphanedKeys(const RemapBuffer& remappings) 55 { 56 std::set<DWORD> ogKeys; 57 std::set<DWORD> newKeys; 58 59 for (int i = 0; i < remappings.size(); i++) 60 { 61 DWORD ogKey = std::get<DWORD>(remappings[i].mapping[0]); 62 KeyShortcutTextUnion newKey = remappings[i].mapping[1]; 63 64 const bool hasValidKeyRemapping = newKey.index() == 0 && std::get<DWORD>(newKey) != 0; 65 const bool hasValidShortcutRemapping = newKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey)); 66 const bool hasValidTextRemapping = newKey.index() == 2 && !std::get<std::wstring>(newKey).empty(); 67 if (ogKey != NULL && (hasValidKeyRemapping || hasValidShortcutRemapping || hasValidTextRemapping)) 68 { 69 ogKeys.insert(ogKey); 70 71 // newKey should be added only if the target is a key 72 if (remappings[i].mapping[1].index() == 0) 73 { 74 newKeys.insert(std::get<DWORD>(newKey)); 75 } 76 } 77 } 78 79 for (auto& k : newKeys) 80 { 81 ogKeys.erase(k); 82 } 83 84 return std::vector(ogKeys.begin(), ogKeys.end()); 85 } 86 87 // Function to combine remappings if the L and R version of the modifier is mapped to the same key 88 void CombineRemappings(SingleKeyRemapTable& table, DWORD leftKey, DWORD rightKey, DWORD combinedKey) 89 { 90 if (table.find(leftKey) != table.end() && table.find(rightKey) != table.end()) 91 { 92 // If they are mapped to the same key, delete those entries and set the common version 93 if (table[leftKey] == table[rightKey]) 94 { 95 if (std::holds_alternative<DWORD>(table[leftKey]) && std::get<DWORD>(table[leftKey]) == combinedKey) 96 { 97 // Avoid mapping a key to itself when the combined key is equal to the resulting mapping. 98 return; 99 } 100 table[combinedKey] = table[leftKey]; 101 table.erase(leftKey); 102 table.erase(rightKey); 103 } 104 } 105 } 106 107 // Function to pre process the remap table before loading it into the UI 108 void PreProcessRemapTable(SingleKeyRemapTable& table) 109 { 110 // Pre process the table to combine L and R versions of Ctrl/Alt/Shift/Win that are mapped to the same key 111 CombineRemappings(table, VK_LCONTROL, VK_RCONTROL, VK_CONTROL); 112 CombineRemappings(table, VK_LMENU, VK_RMENU, VK_MENU); 113 CombineRemappings(table, VK_LSHIFT, VK_RSHIFT, VK_SHIFT); 114 CombineRemappings(table, VK_LWIN, VK_RWIN, CommonSharedConstants::VK_WIN_BOTH); 115 } 116 117 // Function to apply the single key remappings from the buffer to the KeyboardManagerState variable 118 void ApplySingleKeyRemappings(MappingConfiguration& mappingConfiguration, const RemapBuffer& remappings, bool isTelemetryRequired) 119 { 120 // Clear existing Key Remaps 121 mappingConfiguration.ClearSingleKeyRemaps(); 122 mappingConfiguration.ClearSingleKeyToTextRemaps(); 123 DWORD successfulKeyToKeyRemapCount = 0; 124 DWORD successfulKeyToShortcutRemapCount = 0; 125 DWORD successfulKeyToTextRemapCount = 0; 126 for (int i = 0; i < remappings.size(); i++) 127 { 128 const DWORD originalKey = std::get<DWORD>(remappings[i].mapping[0]); 129 KeyShortcutTextUnion newKey = remappings[i].mapping[1]; 130 131 if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey))) && !(newKey.index() == 2 && std::get<std::wstring>(newKey).empty())) 132 { 133 // If Ctrl/Alt/Shift are added, add their L and R versions instead to the same key 134 bool result = false; 135 std::vector<DWORD> originalKeysWithModifiers; 136 if (originalKey == VK_CONTROL) 137 { 138 originalKeysWithModifiers.push_back(VK_LCONTROL); 139 originalKeysWithModifiers.push_back(VK_RCONTROL); 140 } 141 else if (originalKey == VK_MENU) 142 { 143 originalKeysWithModifiers.push_back(VK_LMENU); 144 originalKeysWithModifiers.push_back(VK_RMENU); 145 } 146 else if (originalKey == VK_SHIFT) 147 { 148 originalKeysWithModifiers.push_back(VK_LSHIFT); 149 originalKeysWithModifiers.push_back(VK_RSHIFT); 150 } 151 else if (originalKey == CommonSharedConstants::VK_WIN_BOTH) 152 { 153 originalKeysWithModifiers.push_back(VK_LWIN); 154 originalKeysWithModifiers.push_back(VK_RWIN); 155 } 156 else 157 { 158 originalKeysWithModifiers.push_back(originalKey); 159 } 160 161 for (const DWORD key : originalKeysWithModifiers) 162 { 163 const bool mappedToText = newKey.index() == 2; 164 result = mappedToText ? mappingConfiguration.AddSingleKeyToTextRemap(key, std::get<std::wstring>(newKey)) : mappingConfiguration.AddSingleKeyRemap(key, newKey) && result; 165 } 166 167 if (result) 168 { 169 if (newKey.index() == 0) 170 { 171 ++successfulKeyToKeyRemapCount; 172 } 173 else if (newKey.index() == 1) 174 { 175 ++successfulKeyToShortcutRemapCount; 176 } 177 else if (newKey.index() == 2) 178 { 179 ++successfulKeyToTextRemapCount; 180 } 181 } 182 } 183 } 184 185 // If telemetry is to be logged, log the key remap counts 186 if (isTelemetryRequired) 187 { 188 Trace::KeyRemapCount(successfulKeyToKeyRemapCount, successfulKeyToShortcutRemapCount, successfulKeyToTextRemapCount); 189 } 190 } 191 192 // Function to apply the shortcut remappings from the buffer to the KeyboardManagerState variable 193 void ApplyShortcutRemappings(MappingConfiguration& mappingConfiguration, const RemapBuffer& remappings, bool isTelemetryRequired) 194 { 195 // Clear existing shortcuts 196 mappingConfiguration.ClearOSLevelShortcuts(); 197 mappingConfiguration.ClearAppSpecificShortcuts(); 198 DWORD successfulOSLevelShortcutToShortcutRemapCount = 0; 199 DWORD successfulOSLevelShortcutToKeyRemapCount = 0; 200 DWORD successfulAppSpecificShortcutToShortcutRemapCount = 0; 201 DWORD successfulAppSpecificShortcutToKeyRemapCount = 0; 202 203 // Save the shortcuts that are valid and report if any of them were invalid 204 for (int i = 0; i < remappings.size(); i++) 205 { 206 Shortcut originalShortcut = std::get<Shortcut>(remappings[i].mapping[0]); 207 KeyShortcutTextUnion newShortcut = remappings[i].mapping[1]; 208 209 if (EditorHelpers::IsValidShortcut(originalShortcut) && ((newShortcut.index() == 0 && std::get<DWORD>(newShortcut) != NULL) || (newShortcut.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newShortcut))) || (newShortcut.index() == 2 && !std::get<std::wstring>(newShortcut).empty()))) 210 { 211 if (remappings[i].appName == L"") 212 { 213 bool result = mappingConfiguration.AddOSLevelShortcut(originalShortcut, newShortcut); 214 if (result) 215 { 216 if (newShortcut.index() == 0) 217 { 218 successfulOSLevelShortcutToKeyRemapCount += 1; 219 } 220 else 221 { 222 successfulOSLevelShortcutToShortcutRemapCount += 1; 223 } 224 } 225 } 226 else 227 { 228 bool result = mappingConfiguration.AddAppSpecificShortcut(remappings[i].appName, originalShortcut, newShortcut); 229 if (result) 230 { 231 if (newShortcut.index() == 0) 232 { 233 successfulAppSpecificShortcutToKeyRemapCount += 1; 234 } 235 else 236 { 237 successfulAppSpecificShortcutToShortcutRemapCount += 1; 238 } 239 } 240 } 241 } 242 } 243 244 // If telemetry is to be logged, log the shortcut remap counts 245 if (isTelemetryRequired) 246 { 247 Trace::OSLevelShortcutRemapCount(successfulOSLevelShortcutToShortcutRemapCount, successfulOSLevelShortcutToKeyRemapCount); 248 Trace::AppSpecificShortcutRemapCount(successfulAppSpecificShortcutToShortcutRemapCount, successfulAppSpecificShortcutToKeyRemapCount); 249 } 250 } 251 }