mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 18:26:39 +02:00
* Analyzers CPP Changing the warning level from 3 to 4. change some project files to make them use the warning config in cpp props file. * Analyzers C++ turn on warning 4706 Change Cpp.Build.props file to enable 4706 fix BugReportTool code to get rid of 4706 * Turn on warning 4100 and fix the code * Follow c++ core guidelines * Adapting to PR comments
223 lines
7.2 KiB
C++
223 lines
7.2 KiB
C++
// KeyboardManagerEditor.cpp : Defines the entry point for the application.
|
|
//
|
|
|
|
#include "pch.h"
|
|
#include "KeyboardManagerEditor.h"
|
|
|
|
#include <common/utils/winapi_error.h>
|
|
#include <common/utils/logger_helper.h>
|
|
#include <common/utils/ProcessWaiter.h>
|
|
#include <common/utils/UnhandledExceptionHandler.h>
|
|
#include <common/utils/gpo.h>
|
|
|
|
#include <trace.h>
|
|
|
|
#include <keyboardmanager/common/KeyboardEventHandlers.h>
|
|
|
|
#include <EditKeyboardWindow.h>
|
|
#include <EditShortcutsWindow.h>
|
|
#include <KeyboardManagerState.h>
|
|
|
|
std::unique_ptr<KeyboardManagerEditor> editor = nullptr;
|
|
const std::wstring instanceMutexName = L"Local\\PowerToys_KBMEditor_InstanceMutex";
|
|
|
|
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
|
|
_In_opt_ HINSTANCE /*hPrevInstance*/,
|
|
_In_ LPWSTR /*lpCmdLine*/,
|
|
_In_ int /*nCmdShow*/)
|
|
{
|
|
LoggerHelpers::init_logger(KeyboardManagerConstants::ModuleName, L"Editor", LogSettings::keyboardManagerLoggerName);
|
|
|
|
if (powertoys_gpo::getConfiguredKeyboardManagerEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled)
|
|
{
|
|
Logger::warn(L"Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
|
|
return 0;
|
|
}
|
|
|
|
InitUnhandledExceptionHandler();
|
|
Trace::RegisterProvider();
|
|
|
|
auto mutex = CreateMutex(nullptr, true, instanceMutexName.c_str());
|
|
if (mutex == nullptr)
|
|
{
|
|
Logger::error(L"Failed to create mutex. {}", get_last_error_or_default(GetLastError()));
|
|
}
|
|
else
|
|
{
|
|
Logger::trace(L"Created/Opened {} mutex", instanceMutexName);
|
|
}
|
|
|
|
if (GetLastError() == ERROR_ALREADY_EXISTS)
|
|
{
|
|
Logger::info(L"KBM editor instance is already running");
|
|
return 0;
|
|
}
|
|
|
|
int numArgs;
|
|
LPWSTR* cmdArgs = CommandLineToArgvW(GetCommandLineW(), &numArgs);
|
|
|
|
KeyboardManagerEditorType type = KeyboardManagerEditorType::KeyEditor;
|
|
if (!IsDebuggerPresent())
|
|
{
|
|
if (cmdArgs == nullptr)
|
|
{
|
|
Logger::error(L"Keyboard Manager Editor cannot start as a standalone application");
|
|
return -1;
|
|
}
|
|
|
|
if (numArgs < 2)
|
|
{
|
|
Logger::error(L"Invalid arguments on Keyboard Manager Editor start");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (numArgs > 1)
|
|
{
|
|
type = static_cast<KeyboardManagerEditorType>(_wtoi(cmdArgs[1]));
|
|
}
|
|
|
|
if (numArgs == 3)
|
|
{
|
|
std::wstring pid = std::wstring(cmdArgs[2]);
|
|
Logger::trace(L"Editor started from the settings with pid {}", pid);
|
|
if (!pid.empty())
|
|
{
|
|
auto mainThreadId = GetCurrentThreadId();
|
|
ProcessWaiter::OnProcessTerminate(pid, [mainThreadId](int err) {
|
|
if (err != ERROR_SUCCESS)
|
|
{
|
|
Logger::error(L"Failed to wait for parent process exit. {}", get_last_error_or_default(err));
|
|
}
|
|
|
|
Logger::trace(L"Parent process exited. Exiting KeyboardManager editor");
|
|
PostThreadMessage(mainThreadId, WM_QUIT, 0, 0);
|
|
});
|
|
}
|
|
}
|
|
|
|
editor = std::make_unique<KeyboardManagerEditor>(hInstance);
|
|
if (!editor->StartLowLevelKeyboardHook())
|
|
{
|
|
DWORD errorCode = GetLastError();
|
|
show_last_error_message(L"SetWindowsHookEx", errorCode, L"PowerToys - Keyboard Manager Editor");
|
|
auto errorMessage = get_last_error_message(errorCode);
|
|
Logger::error(L"Unable to start keyboard hook: {}", errorMessage.has_value() ? errorMessage.value() : L"");
|
|
Trace::Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"start_lowlevel_keyboard_hook.SetWindowsHookEx");
|
|
|
|
return -1;
|
|
}
|
|
|
|
editor->OpenEditorWindow(type);
|
|
|
|
editor = nullptr;
|
|
|
|
Trace::UnregisterProvider();
|
|
return 0;
|
|
}
|
|
|
|
KeyboardManagerEditor::KeyboardManagerEditor(HINSTANCE hInst) :
|
|
hInstance(hInst)
|
|
{
|
|
bool loadedSuccessful = mappingConfiguration.LoadSettings();
|
|
if (!loadedSuccessful)
|
|
{
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
|
|
|
// retry once
|
|
mappingConfiguration.LoadSettings();
|
|
}
|
|
|
|
StartLowLevelKeyboardHook();
|
|
}
|
|
|
|
KeyboardManagerEditor::~KeyboardManagerEditor()
|
|
{
|
|
UnhookWindowsHookEx(hook);
|
|
}
|
|
|
|
bool KeyboardManagerEditor::StartLowLevelKeyboardHook()
|
|
{
|
|
#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED)
|
|
if (IsDebuggerPresent())
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
hook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyHookProc, GetModuleHandle(NULL), NULL);
|
|
return (hook != nullptr);
|
|
}
|
|
|
|
void KeyboardManagerEditor::OpenEditorWindow(KeyboardManagerEditorType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case KeyboardManagerEditorType::KeyEditor:
|
|
CreateEditKeyboardWindow(hInstance, keyboardManagerState, mappingConfiguration);
|
|
break;
|
|
case KeyboardManagerEditorType::ShortcutEditor:
|
|
CreateEditShortcutsWindow(hInstance, keyboardManagerState, mappingConfiguration);
|
|
}
|
|
}
|
|
|
|
intptr_t KeyboardManagerEditor::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept
|
|
{
|
|
// If the Detect Key Window is currently activated, then suppress the keyboard event
|
|
Helpers::KeyboardHookDecision singleKeyRemapUIDetected = keyboardManagerState.DetectSingleRemapKeyUIBackend(data);
|
|
if (singleKeyRemapUIDetected == Helpers::KeyboardHookDecision::Suppress)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (singleKeyRemapUIDetected == Helpers::KeyboardHookDecision::SkipHook)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// If the Detect Shortcut Window from Remap Keys is currently activated, then suppress the keyboard event
|
|
Helpers::KeyboardHookDecision remapKeyShortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, true);
|
|
if (remapKeyShortcutUIDetected == Helpers::KeyboardHookDecision::Suppress)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (remapKeyShortcutUIDetected == Helpers::KeyboardHookDecision::SkipHook)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// If the Detect Shortcut Window is currently activated, then suppress the keyboard event
|
|
Helpers::KeyboardHookDecision shortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, false);
|
|
if (shortcutUIDetected == Helpers::KeyboardHookDecision::Suppress)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (shortcutUIDetected == Helpers::KeyboardHookDecision::SkipHook)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Hook procedure definition
|
|
LRESULT KeyboardManagerEditor::KeyHookProc(int nCode, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LowlevelKeyboardEvent event;
|
|
if (nCode == HC_ACTION)
|
|
{
|
|
event.lParam = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
|
event.wParam = wParam;
|
|
if (editor->HandleKeyboardHookEvent(&event) == 1)
|
|
{
|
|
// Reset Num Lock whenever a NumLock key down event is suppressed since Num Lock key state change occurs before it is intercepted by low level hooks
|
|
if (event.lParam->vkCode == VK_NUMLOCK && (event.wParam == WM_KEYDOWN || event.wParam == WM_SYSKEYDOWN) && event.lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
|
{
|
|
KeyboardEventHandlers::SetNumLockToPreviousState(editor->GetInputHandler());
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return CallNextHookEx(hook, nCode, wParam, lParam);
|
|
}
|