From 520d829352d9957461cae6154006c54c765ce3a4 Mon Sep 17 00:00:00 2001 From: Albert Date: Tue, 26 Jan 2021 17:30:16 +0100 Subject: [PATCH] =?UTF-8?q?Verbose=20output=20f=C3=BCr=20Windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/win32/ConfigFile.cpp | 4 +- src/win32/Settings.cpp | 7 ++- src/win32/Settings.h | 1 + src/win32/common.h | 3 +- src/win32/main.cpp | 97 +++++++++++++++++++++++++--------- src/win32/run_hook.cpp | 37 +++++++------ src/win32/run_interception.cpp | 11 ++-- 7 files changed, 111 insertions(+), 49 deletions(-) diff --git a/src/win32/ConfigFile.cpp b/src/win32/ConfigFile.cpp index e0410954..a39a8544 100644 --- a/src/win32/ConfigFile.cpp +++ b/src/win32/ConfigFile.cpp @@ -36,7 +36,7 @@ bool ConfigFile::update() { auto is = std::ifstream(std::string(cbegin(m_filename), cend(m_filename))); #endif if (!is.good()) { - print("opening configuration file failed\n"); + error("opening configuration file failed"); return false; } try { @@ -45,7 +45,7 @@ bool ConfigFile::update() { return true; } catch (const std::exception& ex) { - print((std::string("parsing configuration failed:\n") + ex.what() + ".\n").c_str()); + error("parsing configuration failed:\n\n%s", ex.what()); return false; } } diff --git a/src/win32/Settings.cpp b/src/win32/Settings.cpp index 1670e44b..aa582ab8 100644 --- a/src/win32/Settings.cpp +++ b/src/win32/Settings.cpp @@ -16,6 +16,9 @@ bool interpret_commandline(Settings& settings, int argc, wchar_t* argv[]) { else if (argument == L"-i" || argument == L"--interception") { settings.run_interception = true; } + else if (argument == L"-v" || argument == L"--verbose") { + settings.verbose = true; + } else { return false; } @@ -32,12 +35,14 @@ void print_help_message(const wchar_t* argv0) { ""); #endif - print(("keymapper " + version + "(c) 2019-2021 by Albert Kalchmair\n" + error(("keymapper " + version + "(c) 2019-2021 by Albert Kalchmair\n" "\n" "Usage: keymapper [-options]\n" " -c, --config configuration file.\n" " -u, --update reload configuration file when it changes.\n" " -i, --interception use interception.\n" + " -v, --verbose enable verbose output.\n" + " -h, --help print this help.\n" "\n" "All Rights Reserved.\n" "This program comes with absolutely no warranty.\n" diff --git a/src/win32/Settings.h b/src/win32/Settings.h index 51e0de9f..456b26ee 100644 --- a/src/win32/Settings.h +++ b/src/win32/Settings.h @@ -6,6 +6,7 @@ struct Settings { std::wstring config_file_path; bool auto_update_config; bool run_interception; + bool verbose; }; bool interpret_commandline(Settings& settings, int argc, wchar_t* argv[]); diff --git a/src/win32/common.h b/src/win32/common.h index f6f8ecdf..026d5466 100644 --- a/src/win32/common.h +++ b/src/win32/common.h @@ -13,4 +13,5 @@ void reuse_buffer(KeySequence&& buffer); int run_interception(); int run_hook(HINSTANCE instance); -void print(const char* message); +void error(const char* format, ...); +void verbose(const char* format, ...); diff --git a/src/win32/main.cpp b/src/win32/main.cpp index b2af25a2..67b8c749 100644 --- a/src/win32/main.cpp +++ b/src/win32/main.cpp @@ -5,6 +5,8 @@ #include "runtime/Stage.h" #include "LimitSingleInstance.h" #include "common.h" +#include +#include const auto config_filename = L"keymapper.conf"; const int update_interval_ms = 500; @@ -15,6 +17,37 @@ namespace { FocusedWindowPtr g_focused_window; std::unique_ptr g_stage; bool g_was_inaccessible; + + void vprint(bool notify, const char* format, va_list args) { +#if defined(NDEBUG) + static const auto s_has_console = [](){ + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + FILE *stream; + freopen_s(&stream, "CONOUT$", "w", stdout); + std::fputc('\n', stdout); + return true; + } + return false; + }(); + if (s_has_console) { + std::vfprintf(stdout, format, args); + std::fputc('\n', stdout); + std::fflush(stdout); + return; + } +#endif + + auto buffer = std::array(); + std::vsnprintf(buffer.data(), buffer.size(), format, args); + +#if !defined(NDEBUG) + OutputDebugStringA(buffer.data()); + OutputDebugStringA("\n"); +#else + if (notify) + MessageBoxA(nullptr, buffer.data(), "Keymapper", MB_ICONINFORMATION); +#endif + } } // namespace void reset_state() { @@ -42,42 +75,56 @@ void reset_state() { g_focused_window = create_focused_window(); } -void print(const char* message) { -#if !defined(NDEBUG) - OutputDebugStringA(message); -#else - static const auto has_console = [](){ - if (AttachConsole(ATTACH_PARENT_PROCESS)) { - FILE *stream; - freopen_s(&stream, "CONOUT$", "w", stdout); - return true; - } - return false; - }(); - if (has_console) { - std::fprintf(stdout, message); - } - else { - MessageBoxA(nullptr, message, "Keymapper", MB_ICONINFORMATION); +void error(const char* format, ...) { + va_list args; + va_start(args, format); + vprint(true, format, args); + va_end(args); +} + +void verbose(const char* format, ...) { + if (g_settings.verbose) { + va_list args; + va_start(args, format); + vprint(false, format, args); + va_end(args); } -#endif } void update_configuration() { if (!g_stage->is_output_down()) if (g_settings.auto_update_config) - if (g_config_file.update()) + if (g_config_file.update()) { + verbose("configuration updated"); reset_state(); + } } bool update_focused_window(bool validate_when_window_inaccessible) { if (!update_focused_window(*g_focused_window)) return false; - g_stage->activate_override_set( - find_context(g_config_file.config(), + verbose("detected focused window changed:"); + verbose(" class = '%s'", get_class(*g_focused_window).c_str()); + verbose(" title = '%s'", get_title(*g_focused_window).c_str()); + + const auto override_set = find_context(g_config_file.config(), get_class(*g_focused_window), - get_title(*g_focused_window))); + get_title(*g_focused_window)); + + if (override_set >= 0) { + verbose("setting active context #%i:", override_set + 1); + const auto& context = g_config_file.config().contexts[override_set]; + if (const auto& filter = context.window_class_filter) + verbose(" class filter = '%s'", filter.string.c_str()); + if (const auto& filter = context.window_title_filter) + verbose(" title filter = '%s'", filter.string.c_str()); + } + else { + verbose("setting no active context"); + } + + g_stage->activate_override_set(override_set); // validate internal state when a window of another user was focused if (validate_when_window_inaccessible) { @@ -111,12 +158,14 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, LPWSTR, int) { } LimitSingleInstance single_instance("Global\\{658914E7-CCA6-4425-89FF-EF4A13B75F31}"); - if (single_instance.is_another_instance_running()) + if (single_instance.is_another_instance_running()) { + error("another instance is already running"); return 1; + } SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); - // load initial configuration + verbose("loading configuration file '%ws'", g_settings.config_file_path.c_str()); g_config_file = ConfigFile(g_settings.config_file_path); if (!g_config_file.update()) return 1; diff --git a/src/win32/run_hook.cpp b/src/win32/run_hook.cpp index 6d070fb4..00f07a22 100644 --- a/src/win32/run_hook.cpp +++ b/src/win32/run_hook.cpp @@ -45,17 +45,23 @@ namespace { } #if !defined(NDEBUG) - void print_event(const KeyEvent& e) { - auto key_name = [](auto key) { + std::string format(const KeyEvent& e) { + const auto key_name = [](auto key) { auto name = get_key_name(static_cast(key)); return (name.empty() ? "???" : std::string(name)) + " (" + std::to_string(key) + ") "; }; - print(e.state == KeyState::Down ? "+" : - e.state == KeyState::Up ? "-" : "*"); - print(key_name(e.key).c_str()); + return (e.state == KeyState::Down ? "+" : + e.state == KeyState::Up ? "-" : "*") + key_name(e.key); } -#endif + + std::string format(const KeySequence& sequence) { + auto string = std::string(); + for (const auto& e : sequence) + string += format(e); + return string; + } +#endif // !defined(NDEBUG) void flush_send_buffer() { if (!g_send_buffer.empty()) { @@ -88,11 +94,7 @@ namespace { (output.front().state == KeyState::Up) != (input.state == KeyState::Up)) { #if !defined(NDEBUG) - print_event(input); - print("--> "); - for (const auto& event : output) - print_event(event); - print("\n"); + verbose("%s--> %s", format(input).c_str(), format(output).c_str()); #endif send_key_sequence(output); translated = true; @@ -100,10 +102,8 @@ namespace { reuse_buffer(std::move(output)); #if !defined(NDEBUG) - if (!translated) { - print_event(input); - print("\n"); - } + if (!translated) + verbose("%s", format(input).c_str()); #endif return translated; } @@ -141,7 +141,8 @@ namespace { if (update_focused_window(true)) { // reinsert hook in front of callchain unhook_keyboard(); - hook_keyboard(); + if (!hook_keyboard()) + verbose("resetting keyboard hook failed"); } break; } @@ -160,8 +161,9 @@ int run_hook(HINSTANCE instance) { if (!RegisterClassExW(&window_class)) return 1; + verbose("hooking keyboard"); if (!hook_keyboard()) { - print("hooking keyboard failed.\n"); + error("hooking keyboard failed"); UnregisterClassW(window_class_name, instance); return 1; } @@ -172,6 +174,7 @@ int run_hook(HINSTANCE instance) { SetTimer(window, 1, update_interval_ms, NULL); + verbose("entering update loop"); auto message = MSG{ }; while (GetMessageW(&message, window, 0, 0) > 0) { TranslateMessage(&message); diff --git a/src/win32/run_interception.cpp b/src/win32/run_interception.cpp index 050a00ef..4f60dba0 100644 --- a/src/win32/run_interception.cpp +++ b/src/win32/run_interception.cpp @@ -32,9 +32,10 @@ namespace { } // namespace int run_interception() { + verbose("loading interception.dll"); const auto handle = LoadLibraryA("interception.dll"); if (!handle) { - print("interception.dll missing.\n"); + error("interception.dll missing"); return 1; } @@ -51,19 +52,21 @@ int run_interception() { !interception_receive || !interception_send) { FreeLibrary(handle); - print("interception.dll invalid.\n"); + error("interception.dll invalid"); return 1; } + verbose("initializing interception"); auto context = interception_create_context(); if (!context) { - print("initializing interception failed.\n"); + error("initializing interception failed"); return false; } interception_set_filter(context, interception_is_keyboard, INTERCEPTION_FILTER_KEY_DOWN | INTERCEPTION_FILTER_KEY_UP | INTERCEPTION_FILTER_KEY_E0); + verbose("entering update loop"); InterceptionStroke stroke; for (;;) { auto device = interception_wait_with_timeout(context, @@ -94,7 +97,7 @@ int run_interception() { #else // !defined(ENABLE_INTERCEPTION) int run_interception() { - print("interception support not compiled in.\n"); + error("interception support not compiled in"); return 1; }