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 }