[KBM] Migrate Engine and Editor into separate processes (#10774)

* Move KBM engine into separate process (#10672)

* [KBM] Migrate KBM UI out of the runner (#10709)

* Clean up keyboard hook handles (#10817)

* [C++ common] Unhandled exception handler (#10821)

* [KBM] Use icon in the KeyboardManagerEditor (#10845)

* [KBM] Move resources from the Common project to the Editor. (#10844)

* KBM Editor tests (#10858)

* Rename engine executable (#10868)

* clean up (#10870)

* [KBM] Changed Editor and libraries output folders (#10871)

* [KBM] New logs structure (#10872)

* Add unhandled exception handling to the editor (#10874)

* [KBM] Trace for edit keyboard window

* Logging for XamlBridge message loop

* [KBM] Added Editor and Engine to the installer (#10876)

* Fix spelling

* Interprocess communication logs, remove unnecessary windows message logs

* [KBM] Separated telemetry for the engine and editor. (#10889)

* [KBM] Editor test project (#10891)

* Versions for the engine and the editor (#10897)

* Add the editor's and the engine's executables to signing process (#10900)

* [KBM editor] Run only one instance, exit when parent process exits (#10890)

* [KBM] Force kill editor process to avoid XAML crash (#10907)

* [KBM] Force kill editor process to avoid XAML crash

* Fix event releasing

Co-authored-by: mykhailopylyp <17161067+mykhailopylyp@users.noreply.github.com>

* Make the editor dpi aware (#10908)

* [KBM] KeyboardManagerCommon refactoring (#10909)

* Do not start the process if it is already started (#10910)

* logs

* Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp

* Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp

* [KBM] Rename InitUnhandledExceptionHandler
to make it explicit that is for x64 only.
We will fix it properly when adding support for ARM64 and add a header with
the proper conditional building.

* [KBM] rename file/class/variables using camel case

* [KBM] Rename "event_locker" -> "EventLocker"

* [KBM] rename process_waiter
Add a TODO comment

* [KBM] rename methods
Add TODO comment

* [KBM] use uppercase for function names

* [KBM] use uppercase for methos, lowercase for properties

* [KBM] rename method, make methods private, formatting

* [KBM] rename private variables

* [KBM] use uppercase for function names

* [KBM] Added support to run the editor stand-alone when built in debug mode

* Update src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp

* Check success of event creation, comment (#10947)

* [KBM] code formatting (#10951)

* [KBM] code formatting

* Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp

* [KBM] tracing

* [KBM] Remappings not showing fix. (#10954)

* removed mutex

* retry loop for reading

* retry on reading config once

* log error

Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com>

Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com>

Co-authored-by: Seraphima Zykova <zykovas91@gmail.com>
Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com>
Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com>
This commit is contained in:
Mykhailo Pylyp
2021-04-26 22:01:38 +03:00
committed by GitHub
parent e9a0b58796
commit a8c99e9513
141 changed files with 5124 additions and 2374 deletions

View File

@@ -0,0 +1,217 @@
// 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/UnhandledExceptionHandler_x64.h>
#include <trace.h>
#include <KeyboardEventHandlers.h>
#include <KeyboardManagerState.h>
#include <SettingsHelper.h>
#include <EditKeyboardWindow.h>
#include <EditShortcutsWindow.h>
#include <common/utils/ProcessWaiter.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)
{
UNREFERENCED_PARAMETER(hPrevInstance);
LoggerHelpers::init_logger(KeyboardManagerConstants::ModuleName, L"Editor", LogSettings::keyboardManagerLoggerName);
InitUnhandledExceptionHandler_x64();
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 = SettingsHelper::LoadSettings(keyboardManagerState);
if (!loadedSuccessful)
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
// retry once
SettingsHelper::LoadSettings(keyboardManagerState);
}
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);
break;
case KeyboardManagerEditorType::ShortcutEditor:
CreateEditShortcutsWindow(hInstance, keyboardManagerState);
}
}
intptr_t KeyboardManagerEditor::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept
{
// If the Detect Key Window is currently activated, then suppress the keyboard event
KeyboardManagerHelper::KeyboardHookDecision singleKeyRemapUIDetected = keyboardManagerState.DetectSingleRemapKeyUIBackend(data);
if (singleKeyRemapUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress)
{
return 1;
}
else if (singleKeyRemapUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook)
{
return 0;
}
// If the Detect Shortcut Window from Remap Keys is currently activated, then suppress the keyboard event
KeyboardManagerHelper::KeyboardHookDecision remapKeyShortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, true);
if (remapKeyShortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress)
{
return 1;
}
else if (remapKeyShortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook)
{
return 0;
}
// If the Detect Shortcut Window is currently activated, then suppress the keyboard event
KeyboardManagerHelper::KeyboardHookDecision shortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, false);
if (shortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress)
{
return 1;
}
else if (shortcutUIDetected == KeyboardManagerHelper::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);
}