HotkeyManager.cpp
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 #include "HotkeyManager.h" 6 #include <iostream> 7 #include <sstream> 8 9 HotkeyManager::HotkeyManager() 10 : m_nextHotkeyId(1) // Start from 1 11 , m_hotkeyExRegistered(false) 12 , m_hotkeyExId(0) 13 { 14 } 15 16 HotkeyManager::~HotkeyManager() 17 { 18 UnregisterAll(); 19 } 20 21 UINT HotkeyManager::ConvertModifiers(bool win, bool ctrl, bool alt, bool shift) const 22 { 23 UINT modifiers = MOD_NOREPEAT; // Prevent repeat events 24 if (win) modifiers |= MOD_WIN; 25 if (ctrl) modifiers |= MOD_CONTROL; 26 if (alt) modifiers |= MOD_ALT; 27 if (shift) modifiers |= MOD_SHIFT; 28 return modifiers; 29 } 30 31 bool HotkeyManager::RegisterModuleHotkeys(ModuleLoader& moduleLoader) 32 { 33 if (!moduleLoader.IsLoaded()) 34 { 35 std::wcerr << L"Error: Module not loaded\n"; 36 return false; 37 } 38 39 bool anyRegistered = false; 40 41 // First, try the newer GetHotkeyEx() API 42 auto hotkeyEx = moduleLoader.GetHotkeyEx(); 43 if (hotkeyEx.has_value()) 44 { 45 std::wcout << L"Module has HotkeyEx activation hotkey\n"; 46 47 UINT modifiers = hotkeyEx->modifiersMask | MOD_NOREPEAT; 48 UINT vkCode = hotkeyEx->vkCode; 49 50 if (vkCode != 0) 51 { 52 int hotkeyId = m_nextHotkeyId++; 53 54 std::wcout << L" Registering HotkeyEx: "; 55 std::wcout << ModifiersToString(modifiers) << L"+" << VKeyToString(vkCode); 56 57 if (RegisterHotKey(nullptr, hotkeyId, modifiers, vkCode)) 58 { 59 m_hotkeyExRegistered = true; 60 m_hotkeyExId = hotkeyId; 61 62 std::wcout << L" - OK (Activation/Toggle)\n"; 63 anyRegistered = true; 64 } 65 else 66 { 67 DWORD error = GetLastError(); 68 std::wcout << L" - FAILED (Error: " << error << L")\n"; 69 70 if (error == ERROR_HOTKEY_ALREADY_REGISTERED) 71 { 72 std::wcout << L" (Hotkey is already registered by another application)\n"; 73 } 74 } 75 } 76 } 77 78 // Also check the legacy get_hotkeys() API 79 size_t hotkeyCount = moduleLoader.GetHotkeys(nullptr, 0); 80 if (hotkeyCount > 0) 81 { 82 std::wcout << L"Module reports " << hotkeyCount << L" legacy hotkey(s)\n"; 83 84 // Allocate buffer and get the hotkeys 85 std::vector<PowertoyModuleIface::Hotkey> hotkeys(hotkeyCount); 86 size_t actualCount = moduleLoader.GetHotkeys(hotkeys.data(), hotkeyCount); 87 88 // Register each hotkey 89 for (size_t i = 0; i < actualCount; i++) 90 { 91 const auto& hotkey = hotkeys[i]; 92 93 UINT modifiers = ConvertModifiers(hotkey.win, hotkey.ctrl, hotkey.alt, hotkey.shift); 94 UINT vkCode = hotkey.key; 95 96 if (vkCode == 0) 97 { 98 std::wcout << L" Skipping hotkey " << i << L" (no key code)\n"; 99 continue; 100 } 101 102 int hotkeyId = m_nextHotkeyId++; 103 104 std::wcout << L" Registering hotkey " << i << L": "; 105 std::wcout << ModifiersToString(modifiers) << L"+" << VKeyToString(vkCode); 106 107 if (RegisterHotKey(nullptr, hotkeyId, modifiers, vkCode)) 108 { 109 HotkeyInfo info; 110 info.id = hotkeyId; 111 info.moduleHotkeyId = i; 112 info.modifiers = modifiers; 113 info.vkCode = vkCode; 114 info.description = ModifiersToString(modifiers) + L"+" + VKeyToString(vkCode); 115 116 m_registeredHotkeys.push_back(info); 117 std::wcout << L" - OK\n"; 118 anyRegistered = true; 119 } 120 else 121 { 122 DWORD error = GetLastError(); 123 std::wcout << L" - FAILED (Error: " << error << L")\n"; 124 125 if (error == ERROR_HOTKEY_ALREADY_REGISTERED) 126 { 127 std::wcout << L" (Hotkey is already registered by another application)\n"; 128 } 129 } 130 } 131 } 132 133 if (!anyRegistered && hotkeyCount == 0 && !hotkeyEx.has_value()) 134 { 135 std::wcout << L"Module has no hotkeys\n"; 136 } 137 138 return anyRegistered; 139 } 140 141 void HotkeyManager::UnregisterAll() 142 { 143 for (const auto& hotkey : m_registeredHotkeys) 144 { 145 UnregisterHotKey(nullptr, hotkey.id); 146 } 147 m_registeredHotkeys.clear(); 148 149 if (m_hotkeyExRegistered) 150 { 151 UnregisterHotKey(nullptr, m_hotkeyExId); 152 m_hotkeyExRegistered = false; 153 m_hotkeyExId = 0; 154 } 155 } 156 157 bool HotkeyManager::HandleHotkey(int hotkeyId, ModuleLoader& moduleLoader) 158 { 159 // Check if it's the HotkeyEx activation hotkey 160 if (m_hotkeyExRegistered && hotkeyId == m_hotkeyExId) 161 { 162 std::wcout << L"\nActivation hotkey triggered (HotkeyEx)\n"; 163 164 moduleLoader.OnHotkeyEx(); 165 166 std::wcout << L"Module toggled via activation hotkey\n"; 167 std::wcout << L"Module enabled: " << (moduleLoader.IsEnabled() ? L"Yes" : L"No") << L"\n\n"; 168 169 return true; 170 } 171 172 // Check legacy hotkeys 173 for (const auto& hotkey : m_registeredHotkeys) 174 { 175 if (hotkey.id == hotkeyId) 176 { 177 std::wcout << L"\nHotkey triggered: " << hotkey.description << L"\n"; 178 179 bool result = moduleLoader.OnHotkey(hotkey.moduleHotkeyId); 180 181 std::wcout << L"Module handled hotkey: " << (result ? L"Swallowed" : L"Not swallowed") << L"\n"; 182 std::wcout << L"Module enabled: " << (moduleLoader.IsEnabled() ? L"Yes" : L"No") << L"\n\n"; 183 184 return true; 185 } 186 } 187 188 return false; 189 } 190 191 void HotkeyManager::PrintHotkeys() const 192 { 193 for (const auto& hotkey : m_registeredHotkeys) 194 { 195 std::wcout << L" " << hotkey.description << L"\n"; 196 } 197 } 198 199 std::wstring HotkeyManager::ModifiersToString(UINT modifiers) const 200 { 201 std::wstringstream ss; 202 bool first = true; 203 204 if (modifiers & MOD_WIN) 205 { 206 if (!first) ss << L"+"; 207 ss << L"Win"; 208 first = false; 209 } 210 if (modifiers & MOD_CONTROL) 211 { 212 if (!first) ss << L"+"; 213 ss << L"Ctrl"; 214 first = false; 215 } 216 if (modifiers & MOD_ALT) 217 { 218 if (!first) ss << L"+"; 219 ss << L"Alt"; 220 first = false; 221 } 222 if (modifiers & MOD_SHIFT) 223 { 224 if (!first) ss << L"+"; 225 ss << L"Shift"; 226 first = false; 227 } 228 229 return ss.str(); 230 } 231 232 std::wstring HotkeyManager::VKeyToString(UINT vkCode) const 233 { 234 // Handle special keys 235 switch (vkCode) 236 { 237 case VK_SPACE: return L"Space"; 238 case VK_RETURN: return L"Enter"; 239 case VK_ESCAPE: return L"Esc"; 240 case VK_TAB: return L"Tab"; 241 case VK_BACK: return L"Backspace"; 242 case VK_DELETE: return L"Del"; 243 case VK_INSERT: return L"Ins"; 244 case VK_HOME: return L"Home"; 245 case VK_END: return L"End"; 246 case VK_PRIOR: return L"PgUp"; 247 case VK_NEXT: return L"PgDn"; 248 case VK_LEFT: return L"Left"; 249 case VK_RIGHT: return L"Right"; 250 case VK_UP: return L"Up"; 251 case VK_DOWN: return L"Down"; 252 case VK_F1: return L"F1"; 253 case VK_F2: return L"F2"; 254 case VK_F3: return L"F3"; 255 case VK_F4: return L"F4"; 256 case VK_F5: return L"F5"; 257 case VK_F6: return L"F6"; 258 case VK_F7: return L"F7"; 259 case VK_F8: return L"F8"; 260 case VK_F9: return L"F9"; 261 case VK_F10: return L"F10"; 262 case VK_F11: return L"F11"; 263 case VK_F12: return L"F12"; 264 } 265 266 // For alphanumeric keys, use MapVirtualKey 267 wchar_t keyName[256]; 268 UINT scanCode = MapVirtualKeyW(vkCode, MAPVK_VK_TO_VSC); 269 270 if (GetKeyNameTextW(scanCode << 16, keyName, 256) > 0) 271 { 272 return keyName; 273 } 274 275 // Fallback to hex code 276 std::wstringstream ss; 277 ss << L"0x" << std::hex << vkCode; 278 return ss.str(); 279 }