mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 03:37:59 +01:00
[Launcher] Use a keyboard hook in the runner to invoke the Launcher (#6660)
* Added a keyboard hook to the runner * Update RootKeyboardHook * Enable reading the whole JsonObject property * Renamed RootKeyboardHook to CentralizedKeyboardHook * Fixed build break, changed callback return type to bool * Added Hotkey struct which somehow went missing + Cherry-pick fixes * Reorganized the kb hook * Basic version works * Various fixes * Finishing touches * Fix potential threading issue * int -> size_t * Add default initializers to the Hotkey struct * Added a suggested comment * Unified a constant * Use C# classes instead of native calls for sync * Added a claryfing comment * Use std::move * Renamed a method * Possible fix for compilation errors * Fix a regression * Show a message on failure * Added DISABLE_LOWLEVEL_HOOK support * Allow running Launcher as standalone * Rename string constants
This commit is contained in:
131
src/runner/centralized_kb_hook.cpp
Normal file
131
src/runner/centralized_kb_hook.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "pch.h"
|
||||
#include "centralized_kb_hook.h"
|
||||
#include "common/common.h"
|
||||
|
||||
namespace CentralizedKeyboardHook
|
||||
{
|
||||
struct HotkeyDescriptor
|
||||
{
|
||||
Hotkey hotkey;
|
||||
std::wstring moduleName;
|
||||
std::function<bool()> action;
|
||||
|
||||
bool operator<(const HotkeyDescriptor& other) const
|
||||
{
|
||||
return hotkey < other.hotkey;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
std::multiset<HotkeyDescriptor> hotkeyDescriptors;
|
||||
std::mutex mutex;
|
||||
HHOOK hHook{};
|
||||
|
||||
struct DestroyOnExit
|
||||
{
|
||||
~DestroyOnExit()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
} destroyOnExitObj;
|
||||
|
||||
LRESULT CALLBACK KeyboardHookProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
|
||||
{
|
||||
if (nCode < 0 || ((wParam != WM_KEYDOWN) && (wParam != WM_SYSKEYDOWN)))
|
||||
{
|
||||
return CallNextHookEx(hHook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
const auto& keyPressInfo = *reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
||||
|
||||
Hotkey hotkey{
|
||||
.win = (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000),
|
||||
.ctrl = static_cast<bool>(GetAsyncKeyState(VK_CONTROL) & 0x8000),
|
||||
.shift = static_cast<bool>(GetAsyncKeyState(VK_SHIFT) & 0x8000),
|
||||
.alt = static_cast<bool>(GetAsyncKeyState(VK_MENU) & 0x8000),
|
||||
.key = static_cast<unsigned char>(keyPressInfo.vkCode)
|
||||
};
|
||||
|
||||
std::function<bool()> action;
|
||||
{
|
||||
// Hold the lock for the shortest possible duration
|
||||
std::unique_lock lock{ mutex };
|
||||
HotkeyDescriptor dummy{ .hotkey = hotkey };
|
||||
auto it = hotkeyDescriptors.find(dummy);
|
||||
if (it != hotkeyDescriptors.end())
|
||||
{
|
||||
action = it->action;
|
||||
}
|
||||
}
|
||||
|
||||
if (action)
|
||||
{
|
||||
if (action())
|
||||
{
|
||||
// After invoking the hotkey send a dummy key to prevent Start Menu from activating
|
||||
INPUT dummyEvent[1] = {};
|
||||
dummyEvent[0].type = INPUT_KEYBOARD;
|
||||
dummyEvent[0].ki.wVk = 0xFF;
|
||||
dummyEvent[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
SendInput(1, dummyEvent, sizeof(INPUT));
|
||||
|
||||
// Swallow the key press
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return CallNextHookEx(hHook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
void SetHotkeyAction(const std::wstring& moduleName, const Hotkey& hotkey, std::function<bool()>&& action) noexcept
|
||||
{
|
||||
std::unique_lock lock{ mutex };
|
||||
hotkeyDescriptors.insert({ .hotkey = hotkey, .moduleName = moduleName, .action = std::move(action) });
|
||||
}
|
||||
|
||||
void ClearModuleHotkeys(const std::wstring& moduleName) noexcept
|
||||
{
|
||||
std::unique_lock lock{ mutex };
|
||||
auto it = hotkeyDescriptors.begin();
|
||||
while (it != hotkeyDescriptors.end())
|
||||
{
|
||||
if (it->moduleName == moduleName)
|
||||
{
|
||||
it = hotkeyDescriptors.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Start() noexcept
|
||||
{
|
||||
#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED)
|
||||
const bool hook_disabled = IsDebuggerPresent();
|
||||
#else
|
||||
const bool hook_disabled = false;
|
||||
#endif
|
||||
if (!hook_disabled)
|
||||
{
|
||||
if (!hHook)
|
||||
{
|
||||
hHook = SetWindowsHookExW(WH_KEYBOARD_LL, KeyboardHookProc, NULL, NULL);
|
||||
if (!hHook)
|
||||
{
|
||||
DWORD errorCode = GetLastError();
|
||||
show_last_error_message(L"SetWindowsHookEx", errorCode, L"centralized_kb_hook");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Stop() noexcept
|
||||
{
|
||||
if (hHook && UnhookWindowsHookEx(hHook))
|
||||
{
|
||||
hHook = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/runner/centralized_kb_hook.h
Normal file
13
src/runner/centralized_kb_hook.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "../modules/interface/powertoy_module_interface.h"
|
||||
|
||||
namespace CentralizedKeyboardHook
|
||||
{
|
||||
using Hotkey = PowertoyModuleIface::Hotkey;
|
||||
|
||||
void Start() noexcept;
|
||||
void Stop() noexcept;
|
||||
void SetHotkeyAction(const std::wstring& moduleName, const Hotkey& hotkey, std::function<bool()>&& action) noexcept;
|
||||
void ClearModuleHotkeys(const std::wstring& moduleName) noexcept;
|
||||
};
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <Psapi.h>
|
||||
#include <RestartManager.h>
|
||||
#include "centralized_kb_hook.h"
|
||||
|
||||
#if _DEBUG && _WIN64
|
||||
#include "unhandled_exception_handler.h"
|
||||
@@ -89,6 +90,7 @@ int runner(bool isProcessElevated)
|
||||
#endif
|
||||
Trace::RegisterProvider();
|
||||
start_tray_icon();
|
||||
CentralizedKeyboardHook::Start();
|
||||
|
||||
int result = -1;
|
||||
try
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <ProjectTelemetry.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "pch.h"
|
||||
#include "powertoy_module.h"
|
||||
#include "centralized_kb_hook.h"
|
||||
|
||||
std::map<std::wstring, PowertoyModule>& modules()
|
||||
{
|
||||
@@ -42,4 +43,24 @@ PowertoyModule::PowertoyModule(PowertoyModuleIface* module, HMODULE handle) :
|
||||
{
|
||||
throw std::runtime_error("Module not initialized");
|
||||
}
|
||||
|
||||
update_hotkeys();
|
||||
}
|
||||
|
||||
void PowertoyModule::update_hotkeys()
|
||||
{
|
||||
CentralizedKeyboardHook::ClearModuleHotkeys(module->get_name());
|
||||
|
||||
size_t hotkeyCount = module->get_hotkeys(nullptr, 0);
|
||||
std::vector<PowertoyModuleIface::Hotkey> hotkeys(hotkeyCount);
|
||||
module->get_hotkeys(hotkeys.data(), hotkeyCount);
|
||||
|
||||
auto modulePtr = module.get();
|
||||
|
||||
for (size_t i = 0; i < hotkeyCount; i++)
|
||||
{
|
||||
CentralizedKeyboardHook::SetHotkeyAction(module->get_name(), hotkeys[i], [modulePtr, i] {
|
||||
return modulePtr->on_hotkey(i);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ public:
|
||||
|
||||
json::JsonObject json_config() const;
|
||||
|
||||
void update_hotkeys();
|
||||
|
||||
private:
|
||||
std::unique_ptr<HMODULE, PowertoyModuleDLLDeleter> handle;
|
||||
std::unique_ptr<PowertoyModuleIface, PowertoyModuleDeleter> module;
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
<ClCompile Include="powertoy_module.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="restart_elevated.cpp" />
|
||||
<ClCompile Include="centralized_kb_hook.cpp" />
|
||||
<ClCompile Include="settings_window.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<ClCompile Include="tray_icon.cpp" />
|
||||
@@ -132,6 +133,7 @@
|
||||
<ClInclude Include="auto_start_helper.h" />
|
||||
<ClInclude Include="general_settings.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="centralized_kb_hook.h" />
|
||||
<ClInclude Include="update_utils.h" />
|
||||
<ClInclude Include="update_state.h" />
|
||||
<ClInclude Include="powertoy_module.h" />
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
<ClCompile Include="action_runner_utils.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="centralized_kb_hook.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
@@ -73,7 +76,10 @@
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="centralized_kb_hook.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "common/common.h"
|
||||
#include "restart_elevated.h"
|
||||
#include "update_utils.h"
|
||||
#include "centralized_kb_hook.h"
|
||||
|
||||
#include <common/json.h>
|
||||
#include <common\settings_helpers.cpp>
|
||||
@@ -106,9 +107,11 @@ std::optional<std::wstring> dispatch_json_action_to_module(const json::JsonObjec
|
||||
|
||||
void send_json_config_to_module(const std::wstring& module_key, const std::wstring& settings)
|
||||
{
|
||||
if (modules().find(module_key) != modules().end())
|
||||
auto moduleIt = modules().find(module_key);
|
||||
if (moduleIt != modules().end())
|
||||
{
|
||||
modules().at(module_key)->set_config(settings.c_str());
|
||||
moduleIt->second->set_config(settings.c_str());
|
||||
moduleIt->second.update_hotkeys();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user