/ tools / module_loader / src / HotkeyManager.cpp
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  }