/ src / modules / cmdpal / CmdPalKeyboardService / KeyboardListener.cpp
KeyboardListener.cpp
  1  #include "pch.h"
  2  #include "KeyboardListener.h"
  3  #include "KeyboardListener.g.cpp"
  4  
  5  // #include <common/logger/logger.h>
  6  // #include <common/utils/logger_helper.h>
  7  #include <common/utils/winapi_error.h>
  8  
  9  namespace
 10  {
 11  }
 12  
 13  namespace winrt::CmdPalKeyboardService::implementation
 14  {
 15      KeyboardListener::KeyboardListener()
 16      {
 17          s_instance = this;
 18      }
 19  
 20      void KeyboardListener::Start()
 21      {
 22  #if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED)
 23          const bool hook_disabled = IsDebuggerPresent();
 24  #else
 25          const bool hook_disabled = false;
 26  #endif
 27          if (!hook_disabled)
 28          {
 29              if (!s_llKeyboardHook)
 30              {
 31                  s_llKeyboardHook = SetWindowsHookExW(WH_KEYBOARD_LL, LowLevelKeyboardProc, NULL, NULL);
 32                  if (!s_llKeyboardHook)
 33                  {
 34                      DWORD errorCode = GetLastError();
 35                      show_last_error_message(L"SetWindowsHookEx", errorCode, L"CmdPalKeyboardService");
 36                  }
 37              }
 38          }
 39      }
 40  
 41      void KeyboardListener::Stop()
 42      {
 43          if (s_llKeyboardHook && UnhookWindowsHookEx(s_llKeyboardHook))
 44          {
 45              s_llKeyboardHook = NULL;
 46          }
 47      }
 48  
 49      void KeyboardListener::SetHotkeyAction(bool win, bool ctrl, bool shift, bool alt, uint8_t key, hstring const& id)
 50      {
 51          Hotkey hotkey = { .win = win, .ctrl = ctrl, .shift = shift, .alt = alt, .key = key };
 52          std::unique_lock lock{ mutex };
 53  
 54          HotkeyDescriptor desc = { .hotkey = hotkey, .id = std::wstring(id) };
 55          hotkeyDescriptors.insert(desc);
 56      }
 57  
 58      void KeyboardListener::ClearHotkey(hstring const& id)
 59      {
 60          {
 61              std::unique_lock lock{ mutex };
 62              auto it = hotkeyDescriptors.begin();
 63              while (it != hotkeyDescriptors.end())
 64              {
 65                  if (it->id == id)
 66                  {
 67                      it = hotkeyDescriptors.erase(it);
 68                  }
 69                  else
 70                  {
 71                      ++it;
 72                  }
 73              }
 74          }
 75      }
 76  
 77      void KeyboardListener::ClearHotkeys()
 78      {
 79          {
 80              std::unique_lock lock{ mutex };
 81              auto it = hotkeyDescriptors.begin();
 82              while (it != hotkeyDescriptors.end())
 83              {
 84                  it = hotkeyDescriptors.erase(it);
 85              }
 86          }
 87      }
 88  
 89      void KeyboardListener::SetProcessCommand(ProcessCommand processCommand)
 90      {
 91          m_processCommandCb = [trigger = std::move(processCommand)](hstring const& id) {
 92              trigger(id);
 93          };
 94      }
 95  
 96      LRESULT KeyboardListener::DoLowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
 97      {
 98          const auto& keyPressInfo = *reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
 99  
100          if ((wParam != WM_KEYDOWN) && (wParam != WM_SYSKEYDOWN))
101          {
102              return CallNextHookEx(NULL, nCode, wParam, lParam);
103          }
104  
105          Hotkey hotkey{
106              .win = (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000),
107              .ctrl = static_cast<bool>(GetAsyncKeyState(VK_CONTROL) & 0x8000),
108              .shift = static_cast<bool>(GetAsyncKeyState(VK_SHIFT) & 0x8000),
109              .alt = static_cast<bool>(GetAsyncKeyState(VK_MENU) & 0x8000),
110              .key = static_cast<unsigned char>(keyPressInfo.vkCode)
111          };
112  
113          if (hotkey == Hotkey{})
114          {
115              return CallNextHookEx(NULL, nCode, wParam, lParam);
116          }
117  
118          bool do_action = false;
119          std::wstring actionId{};
120  
121          {
122              // Hold the lock for the shortest possible duration
123              std::unique_lock lock{ mutex };
124              HotkeyDescriptor dummy{ .hotkey = hotkey };
125              auto it = hotkeyDescriptors.find(dummy);
126              if (it != hotkeyDescriptors.end())
127              {
128                  do_action = true;
129                  actionId = it->id;
130              }
131          }
132  
133          if (do_action)
134          {
135              m_processCommandCb(hstring{ actionId });
136  
137              // After invoking the hotkey send a dummy key to prevent Start Menu from activating
138              INPUT dummyEvent[1] = {};
139              dummyEvent[0].type = INPUT_KEYBOARD;
140              dummyEvent[0].ki.wVk = 0xFF;
141              dummyEvent[0].ki.dwFlags = KEYEVENTF_KEYUP;
142              SendInput(1, dummyEvent, sizeof(INPUT));
143  
144              // Swallow the key press
145              return 1;
146          }
147  
148          return CallNextHookEx(NULL, nCode, wParam, lParam);
149      }
150  
151      LRESULT KeyboardListener::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
152      {
153          if (s_instance == nullptr)
154          {
155              return CallNextHookEx(NULL, nCode, wParam, lParam);
156          }
157  
158          if (nCode < 0)
159          {
160              return CallNextHookEx(NULL, nCode, wParam, lParam);
161          }
162  
163          return s_instance->DoLowLevelKeyboardProc(nCode, wParam, lParam);
164      }
165  }