From ebbc10c929c9372ebf0b7d34a1c62ea91f00fb0b Mon Sep 17 00:00:00 2001 From: Alexander Barker Date: Thu, 17 Feb 2022 09:56:53 -0800 Subject: [PATCH 01/12] Adding a compile time option to use Unix epoch time for events. --- CMakeLists.txt | 4 ++ README.md | 1 + src/darwin/input_hook.c | 26 +++++++++++-- src/windows/input_hook.c | 79 +++++++++++++++++++++++++++++++++++----- src/x11/input_hook.c | 23 +++++++++++- 5 files changed, 119 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d45c6877..939b6897 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,10 @@ if(ENABLE_TEST) target_link_libraries(uiohook_tests uiohook "${CMAKE_THREAD_LIBS_INIT}") endif() +option(USE_EPOCH_TIME "Use Unix epoch time for event timestamps (default: OFF)" OFF) +if(USE_EPOCH_TIME) + add_compile_definitions(uiohook PRIVATE USE_EPOCH_TIME) +endif() if(UNIX AND NOT APPLE) find_package(PkgConfig REQUIRED) diff --git a/README.md b/README.md index 220dbcaf..5b49e4a0 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ $ cmake --build . --parallel 2 --target install | __all__ | BUILD_DEMO:BOOL | demo applications | OFF | | | BUILD_SHARED_LIBS:BOOL | shared library | ON | | | ENABLE_TEST:BOOL | testing | OFF | +| | USE_EPOCH_TIME:BOOL | unix epch event times | OFF | | __OSX__ | USE_APPLICATION_SERVICES:BOOL | framework | ON | | | USE_IOKIT:BOOL | framework | ON | | | USE_OBJC:BOOL | obj-c api | ON | diff --git a/src/darwin/input_hook.c b/src/darwin/input_hook.c index 875deb62..23289464 100644 --- a/src/darwin/input_hook.c +++ b/src/darwin/input_hook.c @@ -93,8 +93,10 @@ static CGEventTimestamp click_time = 0; static unsigned short int click_button = MOUSE_NOBUTTON; static bool mouse_dragged = false; +#ifdef USE_EPOCH_TIME // Structure for the current Unix epoch in milliseconds. static struct timeval system_time; +#endif // Virtual event pointer. static uiohook_event event; @@ -206,8 +208,24 @@ static void keycode_to_lookup(void *info) { } } +#ifdef USE_EPOCH_TIME +static inline uint64_t get_unix_timestamp() { + // Get the local system time in UTC. + gettimeofday(&system_time, NULL); + + // Convert the local system time to a Unix epoch in MS. + uint64_t timestamp = (system_time.tv_sec * 1000) + (system_time.tv_usec / 1000); + + return timestamp; +} +#endif + static void hook_status_proc(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { + #ifdef USE_EPOCH_TIME + uint64_t timestamp = get_unix_timestamp(); + #else uint64_t timestamp = mach_absolute_time(); + #endif switch (activity) { case kCFRunLoopEntry: @@ -885,11 +903,11 @@ static inline void process_mouse_wheel(uint64_t timestamp, CGEventRef event_ref) } CGEventRef hook_event_proc(CGEventTapProxy tap_proxy, CGEventType type, CGEventRef event_ref, void *refcon) { - // Get the local system time in UTC. - gettimeofday(&system_time, NULL); - - // Grab the native event timestamp for use later.. + #ifdef USE_EPOCH_TIME + uint64_t timestamp = get_unix_timestamp(); + #else uint64_t timestamp = (uint64_t) CGEventGetTimestamp(event_ref); + #endif // Get the event class. switch (type) { diff --git a/src/windows/input_hook.c b/src/windows/input_hook.c index b9e9161a..f28d1f80 100644 --- a/src/windows/input_hook.c +++ b/src/windows/input_hook.c @@ -34,9 +34,14 @@ extern HINSTANCE hInst; // Modifiers for tracking key masks. static unsigned short int current_modifiers = 0x0000; +#ifdef USE_EPOCH_TIME +// Structure for the current Unix epoch in milliseconds. +static FILETIME system_time; +#endif + // Click count globals. static unsigned short click_count = 0; -static DWORD click_time = 0; +static uint64_t click_time = 0; static unsigned short int click_button = MOUSE_NOBUTTON; static POINT last_click; @@ -142,6 +147,22 @@ static uint16_t get_scroll_wheel_amount() { return value; } +#ifdef USE_EPOCH_TIME +static inline uint64_t get_unix_timestamp() { + // Get the local system time in UTC. + GetSystemTimeAsFileTime(&system_time); + + // Convert the local system time to a Unix epoch in MS. + // milliseconds = 100-nanoseconds / 10000 + uint64_t timestamp = (((uint64_t) system_time.dwHighDateTime << 32) | system_time.dwLowDateTime) / 10000; + + // Convert Windows epoch to Unix epoch. (1970 - 1601 in milliseconds) + timestamp -= 11644473600000; + + return timestamp; +} +#endif + void unregister_running_hooks() { // Stop the event hook and any timer still running. if (win_event_hhook != NULL) { @@ -166,7 +187,11 @@ void hook_start_proc() { load_input_helper(); // Get the local system time in UNIX epoch form. + #ifdef USE_EPOCH_TIME + uint64_t timestamp = get_unix_timestamp(); + #else uint64_t timestamp = GetMessageTime(); + #endif // Populate the hook start event. event.time = timestamp; @@ -181,7 +206,11 @@ void hook_start_proc() { void hook_stop_proc() { // Get the local system time in UNIX epoch form. + #ifdef USE_EPOCH_TIME + uint64_t timestamp = get_unix_timestamp(); + #else uint64_t timestamp = GetMessageTime(); + #endif // Populate the hook stop event. event.time = timestamp; @@ -198,6 +227,12 @@ void hook_stop_proc() { } static void process_key_pressed(KBDLLHOOKSTRUCT *kbhook) { + #ifdef USE_EPOCH_TIME + uint64_t timestamp = get_unix_timestamp(); + #else + uint64_t timestamp = kbhook->time; + #endif + // Check and setup modifiers. if (kbhook->vkCode == VK_LSHIFT) { set_modifier_mask(MASK_SHIFT_L); } else if (kbhook->vkCode == VK_RSHIFT) { set_modifier_mask(MASK_SHIFT_R); } @@ -212,7 +247,7 @@ static void process_key_pressed(KBDLLHOOKSTRUCT *kbhook) { else if (kbhook->vkCode == VK_SCROLL) { set_modifier_mask(MASK_SCROLL_LOCK); } // Populate key pressed event. - event.time = kbhook->time; + event.time = timestamp; event.reserved = 0x00; event.type = EVENT_KEY_PRESSED; @@ -237,7 +272,7 @@ static void process_key_pressed(KBDLLHOOKSTRUCT *kbhook) { SIZE_T count = keycode_to_unicode(kbhook->vkCode, buffer, sizeof(buffer)); for (unsigned int i = 0; i < count; i++) { // Populate key typed event. - event.time = kbhook->time; + event.time = timestamp; event.reserved = 0x00; event.type = EVENT_KEY_TYPED; @@ -257,6 +292,12 @@ static void process_key_pressed(KBDLLHOOKSTRUCT *kbhook) { } static void process_key_released(KBDLLHOOKSTRUCT *kbhook) { + #ifdef USE_EPOCH_TIME + uint64_t timestamp = get_unix_timestamp(); + #else + uint64_t timestamp = kbhook->time; + #endif + // Check and setup modifiers. if (kbhook->vkCode == VK_LSHIFT) { unset_modifier_mask(MASK_SHIFT_L); } else if (kbhook->vkCode == VK_RSHIFT) { unset_modifier_mask(MASK_SHIFT_R); } @@ -271,7 +312,7 @@ static void process_key_released(KBDLLHOOKSTRUCT *kbhook) { else if (kbhook->vkCode == VK_SCROLL) { unset_modifier_mask(MASK_SCROLL_LOCK); } // Populate key pressed event. - event.time = kbhook->time; + event.time = timestamp; event.reserved = 0x00; event.type = EVENT_KEY_RELEASED; @@ -321,7 +362,11 @@ LRESULT CALLBACK keyboard_hook_event_proc(int nCode, WPARAM wParam, LPARAM lPara static void process_button_pressed(MSLLHOOKSTRUCT *mshook, uint16_t button) { - DWORD timestamp = mshook->time; + #ifdef USE_EPOCH_TIME + uint64_t timestamp = get_unix_timestamp(); + #else + uint64_t timestamp = mshook->time; + #endif // Track the number of clicks, the button must match the previous button. if (button == click_button && (long int) (timestamp - click_time) <= hook_get_multi_click_time()) { @@ -368,8 +413,14 @@ static void process_button_pressed(MSLLHOOKSTRUCT *mshook, uint16_t button) { } static void process_button_released(MSLLHOOKSTRUCT *mshook, uint16_t button) { + #ifdef USE_EPOCH_TIME + uint64_t timestamp = get_unix_timestamp(); + #else + uint64_t timestamp = mshook->time; + #endif + // Populate mouse released event. - event.time = mshook->time; + event.time = timestamp; event.reserved = 0x00; event.type = EVENT_MOUSE_RELEASED; @@ -392,7 +443,7 @@ static void process_button_released(MSLLHOOKSTRUCT *mshook, uint16_t button) { // If the pressed event was not consumed... if (event.reserved ^ 0x01 && last_click.x == mshook->pt.x && last_click.y == mshook->pt.y) { // Populate mouse clicked event. - event.time = mshook->time; + event.time = timestamp; event.reserved = 0x00; event.type = EVENT_MOUSE_CLICKED; @@ -412,14 +463,18 @@ static void process_button_released(MSLLHOOKSTRUCT *mshook, uint16_t button) { } // Reset the number of clicks. - if (button == click_button && (long int) (event.time - click_time) > hook_get_multi_click_time()) { + if (button == click_button && (long int) (timestamp - click_time) > hook_get_multi_click_time()) { // Reset the click count. click_count = 0; } } static void process_mouse_moved(MSLLHOOKSTRUCT *mshook) { + #ifdef USE_EPOCH_TIME + uint64_t timestamp = get_unix_timestamp(); + #else uint64_t timestamp = mshook->time; + #endif // We received a mouse move event with the mouse actually moving. // This verifies that the mouse was moved after being depressed. @@ -460,13 +515,19 @@ static void process_mouse_moved(MSLLHOOKSTRUCT *mshook) { } static void process_mouse_wheel(MSLLHOOKSTRUCT *mshook, uint8_t direction) { + #ifdef USE_EPOCH_TIME + uint64_t timestamp = get_unix_timestamp(); + #else + uint64_t timestamp = mshook->time; + #endif + // Track the number of clicks. // Reset the click count and previous button. click_count = 1; click_button = MOUSE_NOBUTTON; // Populate mouse wheel event. - event.time = mshook->time; + event.time = timestamp; event.reserved = 0x00; event.type = EVENT_MOUSE_WHEEL; diff --git a/src/x11/input_hook.c b/src/x11/input_hook.c index 3d009906..cbd01c82 100644 --- a/src/x11/input_hook.c +++ b/src/x11/input_hook.c @@ -96,6 +96,11 @@ typedef union { static struct xkb_state *state = NULL; #endif +#ifdef USE_EPOCH_TIME +// Structure for the current Unix epoch in milliseconds. +static struct timeval system_time; +#endif + // Virtual event pointer. static uiohook_event event; @@ -192,7 +197,7 @@ static void initialize_modifiers() { char keymap[32]; XQueryKeymap(hook->ctrl.display, keymap); - Window unused_win; + Window unused_win; int unused_int; unsigned int mask; if (XQueryPointer(hook->ctrl.display, DefaultRootWindow(hook->ctrl.display), &unused_win, &unused_win, &unused_int, &unused_int, &unused_int, &unused_int, &mask)) { @@ -251,8 +256,24 @@ static void initialize_modifiers() { initialize_locks(); } +#ifdef USE_EPOCH_TIME +static inline uint64_t get_unix_timestamp() { + // Get the local system time in UTC. + gettimeofday(&system_time, NULL); + + // Convert the local system time to a Unix epoch in MS. + uint64_t timestamp = (system_time.tv_sec * 1000) + (system_time.tv_usec / 1000); + + return timestamp; +} +#endif + void hook_event_proc(XPointer closeure, XRecordInterceptData *recorded_data) { + #ifdef USE_EPOCH_TIME + uint64_t timestamp = get_unix_timestamp(); + #else uint64_t timestamp = (uint64_t) recorded_data->server_time; + #endif if (recorded_data->category == XRecordStartOfData) { // Initialize native input helper functions. From f931f5de6a53610ed2807f2aec5e946affbb91ef Mon Sep 17 00:00:00 2001 From: Alexander Barker Date: Tue, 8 Mar 2022 08:18:48 -0800 Subject: [PATCH 02/12] =?UTF-8?q?input=5Fhook.c:262:9:=20warning:=20implic?= =?UTF-8?q?it=20declaration=20of=20function=20=E2=80=98gettimeofday?= =?UTF-8?q?=E2=80=99=20#118?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/x11/input_hook.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/x11/input_hook.c b/src/x11/input_hook.c index cbd01c82..7718e151 100644 --- a/src/x11/input_hook.c +++ b/src/x11/input_hook.c @@ -26,6 +26,10 @@ #include #include +#ifdef USE_EPOCH_TIME +#include +#endif + #include #include From 108fc28acc4f12ed5dcc27f19c6b0e3c18b7b340 Mon Sep 17 00:00:00 2001 From: Alexander Barker Date: Thu, 17 Mar 2022 11:47:54 -0700 Subject: [PATCH 03/12] Adding fix for click counter when using epoch time. --- src/darwin/input_hook.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/darwin/input_hook.c b/src/darwin/input_hook.c index 23289464..7eac992f 100644 --- a/src/darwin/input_hook.c +++ b/src/darwin/input_hook.c @@ -32,6 +32,11 @@ #include "input_helper.h" #include "logger.h" +#ifdef USE_EPOCH_TIME +#define TIMER_RESOLUTION_MS 1 +#else +#define TIMER_RESOLUTION_MS 1000000 +#endif typedef struct _event_runloop_info { CFMachPortRef port; @@ -703,7 +708,7 @@ static inline void process_system_key(uint64_t timestamp, CGEventRef event_ref) static inline void process_button_pressed(uint64_t timestamp, CGEventRef event_ref, uint16_t button) { // Track the number of clicks. - if (button == click_button && (long int) (timestamp - click_time) / 1000000 <= hook_get_multi_click_time()) { + if (button == click_button && (long int) (timestamp - click_time) / TIMER_RESOLUTION_MS <= hook_get_multi_click_time()) { if (click_count < USHRT_MAX) { click_count++; } @@ -789,7 +794,7 @@ static inline void process_button_released(uint64_t timestamp, CGEventRef event_ } // Reset the number of clicks. - if ((long int) (timestamp - click_time) / 1000000 > hook_get_multi_click_time()) { + if ((long int) (timestamp - click_time) / TIMER_RESOLUTION_MS > hook_get_multi_click_time()) { // Reset the click count. click_count = 0; } @@ -797,7 +802,7 @@ static inline void process_button_released(uint64_t timestamp, CGEventRef event_ static inline void process_mouse_moved(uint64_t timestamp, CGEventRef event_ref) { // Reset the click count. - if (click_count != 0 && (long int) (timestamp - click_time) / 1000000 > hook_get_multi_click_time()) { + if (click_count != 0 && (long int) (timestamp - click_time) / TIMER_RESOLUTION_MS > hook_get_multi_click_time()) { click_count = 0; } From 34306e9c9f36450250505244dccaf26bae96d5c3 Mon Sep 17 00:00:00 2001 From: Tolik Pylypchuk Date: Mon, 14 Mar 2022 20:07:57 +0200 Subject: [PATCH 04/12] Add an alternative logger callback which accepts va_list --- include/uiohook.h | 6 +++++- src/logger.c | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/uiohook.h b/include/uiohook.h index b132c935..57547363 100644 --- a/include/uiohook.h +++ b/include/uiohook.h @@ -60,6 +60,7 @@ typedef enum _log_level { // Logger callback function prototype. typedef bool (*logger_t)(unsigned int, const char *, ...); +typedef bool (*va_logger_t)(unsigned int, const char *, va_list); /* End Log Levels and Function Prototype */ /* Begin Virtual Event Types and Data Structures */ @@ -414,9 +415,12 @@ typedef void (*dispatcher_t)(uiohook_event *const); extern "C" { #endif - // Set the logger callback functions. + // Set the logger callback function. UIOHOOK_API void hook_set_logger_proc(logger_t logger_proc); + // Set the logger callback function. + UIOHOOK_API void hook_set_va_logger_proc(va_logger_t logger_proc); + // Send a virtual event back to the system. UIOHOOK_API void hook_post_event(uiohook_event * const event); diff --git a/src/logger.c b/src/logger.c index 28dc9a98..5007c7d4 100644 --- a/src/logger.c +++ b/src/logger.c @@ -28,6 +28,18 @@ static bool default_logger(unsigned int level, const char *format, ...) { return false; } +static va_logger_t va_logger; + +static bool va_logger_wrapper(unsigned int level, const char *format, ...) { + va_list args; + + va_start(args, format); + bool result = va_logger(level, format, args); + va_end(args); + + return result; +} + // Current logger function pointer, should never be null. logger_t logger = &default_logger; @@ -38,3 +50,12 @@ UIOHOOK_API void hook_set_logger_proc(logger_t logger_proc) { logger = logger_proc; } } + +UIOHOOK_API void hook_set_va_logger_proc(va_logger_t logger_proc) { + if (logger_proc == NULL) { + logger = &default_logger; + } else { + va_logger = logger_proc; + logger = &va_logger_wrapper; + } +} From 6af0a9d5a5149388f0771abde21dbee5c6ce64dc Mon Sep 17 00:00:00 2001 From: Alexander Barker Date: Sat, 19 Mar 2022 01:06:54 -0700 Subject: [PATCH 05/12] Request for void* argument in hook_set_logger_proc and hook_set_dispatch_proc. Fixes #86 --- demo/demo_hook.c | 62 ++++++++++++++--------------- demo/demo_hook_async.c | 69 ++++++++++++++++----------------- demo/demo_post.c | 23 ++++++----- demo/demo_properties.c | 56 +++++++++++++------------- include/uiohook.h | 12 ++---- src/darwin/input_helper.c | 2 +- src/darwin/input_hook.c | 20 +++++----- src/darwin/post_event.c | 6 +-- src/logger.c | 40 ++++++------------- src/logger.h | 3 +- src/windows/input_helper.c | 27 +++++-------- src/windows/input_hook.c | 14 ++++--- src/windows/post_event.c | 6 +-- src/windows/system_properties.c | 12 +++--- src/x11/input_helper.c | 6 +-- src/x11/input_hook.c | 18 +++++---- src/x11/post_event.c | 12 +++--- src/x11/system_properties.c | 14 +++---- 18 files changed, 187 insertions(+), 215 deletions(-) diff --git a/demo/demo_hook.c b/demo/demo_hook.c index ae2722bb..cdb190ac 100644 --- a/demo/demo_hook.c +++ b/demo/demo_hook.c @@ -28,26 +28,26 @@ #include #include -bool logger_proc(unsigned int level, const char *format, ...) { - bool status = false; - - va_list args; + +static void logger_proc(unsigned int level, void *user_data, const char *format, va_list args) { switch (level) { case LOG_LEVEL_INFO: - va_start(args, format); - status = vfprintf(stdout, format, args) >= 0; - va_end(args); + vfprintf(stdout, format, args); break; case LOG_LEVEL_WARN: case LOG_LEVEL_ERROR: - va_start(args, format); - status = vfprintf(stderr, format, args) >= 0; - va_end(args); + vfprintf(stderr, format, args); break; } - - return status; +} + +static void logger(unsigned int level, const char *format, ...) { + va_list args; + + va_start(args, format); + logger_proc(level, NULL, format, args); + va_end(args); } // NOTE: The following callback executes on the same thread that hook_run() is called @@ -56,7 +56,7 @@ bool logger_proc(unsigned int level, const char *format, ...) { // Furthermore, some operating systems may choose to disable your hook if it // takes too long to process. If you need to do any extended processing, please // do so by copying the event to your own queued dispatch thread. -void dispatch_proc(uiohook_event * const event) { +void dispatch_proc(uiohook_event * const event, void *user_data) { char buffer[256] = { 0 }; size_t length = snprintf(buffer, sizeof(buffer), "id=%i,when=%" PRIu64 ",mask=0x%X", @@ -74,18 +74,18 @@ void dispatch_proc(uiohook_event * const event) { // System level errors. case UIOHOOK_ERROR_OUT_OF_MEMORY: - logger_proc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status); break; case UIOHOOK_ERROR_X_RECORD_GET_CONTEXT: // NOTE This is the only platform specific error that occurs on hook_stop(). - logger_proc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status); break; // Default error. case UIOHOOK_FAILURE: default: - logger_proc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status); + logger(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status); break; } } @@ -129,10 +129,10 @@ void dispatch_proc(uiohook_event * const event) { int main() { // Set the logger callback for library output. - hook_set_logger_proc(&logger_proc); + hook_set_logger_proc(&logger_proc, NULL); // Set the event callback for uiohook events. - hook_set_dispatch_proc(&dispatch_proc); + hook_set_dispatch_proc(&dispatch_proc, NULL); // Start the hook and block. // NOTE If EVENT_HOOK_ENABLED was delivered, the status will always succeed. @@ -144,63 +144,63 @@ int main() { // System level errors. case UIOHOOK_ERROR_OUT_OF_MEMORY: - logger_proc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status); break; // X11 specific errors. case UIOHOOK_ERROR_X_OPEN_DISPLAY: - logger_proc(LOG_LEVEL_ERROR, "Failed to open X11 display. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Failed to open X11 display. (%#X)", status); break; case UIOHOOK_ERROR_X_RECORD_NOT_FOUND: - logger_proc(LOG_LEVEL_ERROR, "Unable to locate XRecord extension. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Unable to locate XRecord extension. (%#X)", status); break; case UIOHOOK_ERROR_X_RECORD_ALLOC_RANGE: - logger_proc(LOG_LEVEL_ERROR, "Unable to allocate XRecord range. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Unable to allocate XRecord range. (%#X)", status); break; case UIOHOOK_ERROR_X_RECORD_CREATE_CONTEXT: - logger_proc(LOG_LEVEL_ERROR, "Unable to allocate XRecord context. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Unable to allocate XRecord context. (%#X)", status); break; case UIOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT: - logger_proc(LOG_LEVEL_ERROR, "Failed to enable XRecord context. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Failed to enable XRecord context. (%#X)", status); break; // Windows specific errors. case UIOHOOK_ERROR_SET_WINDOWS_HOOK_EX: - logger_proc(LOG_LEVEL_ERROR, "Failed to register low level windows hook. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Failed to register low level windows hook. (%#X)", status); break; // Darwin specific errors. case UIOHOOK_ERROR_AXAPI_DISABLED: - logger_proc(LOG_LEVEL_ERROR, "Failed to enable access for assistive devices. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Failed to enable access for assistive devices. (%#X)", status); break; case UIOHOOK_ERROR_CREATE_EVENT_PORT: - logger_proc(LOG_LEVEL_ERROR, "Failed to create apple event port. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Failed to create apple event port. (%#X)", status); break; case UIOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE: - logger_proc(LOG_LEVEL_ERROR, "Failed to create apple run loop source. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Failed to create apple run loop source. (%#X)", status); break; case UIOHOOK_ERROR_GET_RUNLOOP: - logger_proc(LOG_LEVEL_ERROR, "Failed to acquire apple run loop. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Failed to acquire apple run loop. (%#X)", status); break; case UIOHOOK_ERROR_CREATE_OBSERVER: - logger_proc(LOG_LEVEL_ERROR, "Failed to create apple run loop observer. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Failed to create apple run loop observer. (%#X)", status); break; // Default error. case UIOHOOK_FAILURE: default: - logger_proc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status); + logger(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status); break; } diff --git a/demo/demo_hook_async.c b/demo/demo_hook_async.c index b443cb55..096aad78 100644 --- a/demo/demo_hook_async.c +++ b/demo/demo_hook_async.c @@ -58,26 +58,25 @@ static pthread_cond_t hook_control_cond; #endif -bool logger_proc(unsigned int level, const char *format, ...) { - bool status = false; - - va_list args; +static void logger_proc(unsigned int level, void *user_data, const char *format, va_list args) { switch (level) { case LOG_LEVEL_INFO: - va_start(args, format); - status = vfprintf(stdout, format, args) >= 0; - va_end(args); + vfprintf(stdout, format, args); break; case LOG_LEVEL_WARN: case LOG_LEVEL_ERROR: - va_start(args, format); - status = vfprintf(stderr, format, args) >= 0; - va_end(args); + vfprintf(stderr, format, args); break; } - - return status; +} + +static void logger(unsigned int level, const char *format, ...) { + va_list args; + + va_start(args, format); + logger_proc(level, NULL, format, args); + va_end(args); } // NOTE: The following callback executes on the same thread that hook_run() is called @@ -86,7 +85,7 @@ bool logger_proc(unsigned int level, const char *format, ...) { // Furthermore, some operating systems may choose to disable your hook if it // takes too long to process. If you need to do any extended processing, please // do so by copying the event to your own queued dispatch thread. -void dispatch_proc(uiohook_event * const event) { +void dispatch_proc(uiohook_event * const event, void *user_data) { char buffer[256] = { 0 }; size_t length = snprintf(buffer, sizeof(buffer), "id=%i,when=%" PRIu64 ",mask=0x%X", @@ -143,18 +142,18 @@ void dispatch_proc(uiohook_event * const event) { // System level errors. case UIOHOOK_ERROR_OUT_OF_MEMORY: - logger_proc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status); break; case UIOHOOK_ERROR_X_RECORD_GET_CONTEXT: // NOTE This is the only platform specific error that occurs on hook_stop(). - logger_proc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status); + logger(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status); break; // Default error. case UIOHOOK_FAILURE: default: - logger_proc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status); + logger(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status); break; } } @@ -263,7 +262,7 @@ int hook_enable() { #if defined(_WIN32) // Attempt to set the thread priority to time critical. if (SetThreadPriority(hook_thread, THREAD_PRIORITY_TIME_CRITICAL) == 0) { - logger_proc(LOG_LEVEL_WARN, "%s [%u]: Could not set thread priority %li for thread %#p! (%#lX)\n", + logger(LOG_LEVEL_WARN, "%s [%u]: Could not set thread priority %li for thread %#p! (%#lX)\n", __FUNCTION__, __LINE__, (long) THREAD_PRIORITY_TIME_CRITICAL, hook_thread , (unsigned long) GetLastError()); } @@ -272,13 +271,13 @@ int hook_enable() { // use pthread_setschedparam instead. struct sched_param param = { .sched_priority = priority }; if (pthread_setschedparam(hook_thread, SCHED_OTHER, ¶m) != 0) { - logger_proc(LOG_LEVEL_WARN, "%s [%u]: Could not set thread priority %i for thread 0x%lX!\n", + logger(LOG_LEVEL_WARN, "%s [%u]: Could not set thread priority %i for thread 0x%lX!\n", __FUNCTION__, __LINE__, priority, (unsigned long) hook_thread); } #else // Raise the thread priority using glibc pthread_setschedprio. if (pthread_setschedprio(hook_thread, priority) != 0) { - logger_proc(LOG_LEVEL_WARN, "%s [%u]: Could not set thread priority %i for thread 0x%lX!\n", + logger(LOG_LEVEL_WARN, "%s [%u]: Could not set thread priority %i for thread 0x%lX!\n", __FUNCTION__, __LINE__, priority, (unsigned long) hook_thread); } #endif @@ -319,7 +318,7 @@ int hook_enable() { free(hook_thread_status); - logger_proc(LOG_LEVEL_DEBUG, "%s [%u]: Thread Result: (%#X).\n", + logger(LOG_LEVEL_DEBUG, "%s [%u]: Thread Result: (%#X).\n", __FUNCTION__, __LINE__, status); } else { @@ -352,10 +351,10 @@ int main() { #endif // Set the logger callback for library output. - hook_set_logger_proc(&logger_proc); + hook_set_logger_proc(&logger_proc, NULL); // Set the event callback for uiohook events. - hook_set_dispatch_proc(&dispatch_proc); + hook_set_dispatch_proc(&dispatch_proc, NULL); // Start the hook and block. // NOTE If EVENT_HOOK_ENABLED was delivered, the status will always succeed. @@ -377,63 +376,63 @@ int main() { // System level errors. case UIOHOOK_ERROR_OUT_OF_MEMORY: - logger_proc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)\n", status); + logger(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)\n", status); break; // X11 specific errors. case UIOHOOK_ERROR_X_OPEN_DISPLAY: - logger_proc(LOG_LEVEL_ERROR, "Failed to open X11 display. (%#X)\n", status); + logger(LOG_LEVEL_ERROR, "Failed to open X11 display. (%#X)\n", status); break; case UIOHOOK_ERROR_X_RECORD_NOT_FOUND: - logger_proc(LOG_LEVEL_ERROR, "Unable to locate XRecord extension. (%#X)\n", status); + logger(LOG_LEVEL_ERROR, "Unable to locate XRecord extension. (%#X)\n", status); break; case UIOHOOK_ERROR_X_RECORD_ALLOC_RANGE: - logger_proc(LOG_LEVEL_ERROR, "Unable to allocate XRecord range. (%#X)\n", status); + logger(LOG_LEVEL_ERROR, "Unable to allocate XRecord range. (%#X)\n", status); break; case UIOHOOK_ERROR_X_RECORD_CREATE_CONTEXT: - logger_proc(LOG_LEVEL_ERROR, "Unable to allocate XRecord context. (%#X)\n", status); + logger(LOG_LEVEL_ERROR, "Unable to allocate XRecord context. (%#X)\n", status); break; case UIOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT: - logger_proc(LOG_LEVEL_ERROR, "Failed to enable XRecord context. (%#X)\n", status); + logger(LOG_LEVEL_ERROR, "Failed to enable XRecord context. (%#X)\n", status); break; // Windows specific errors. case UIOHOOK_ERROR_SET_WINDOWS_HOOK_EX: - logger_proc(LOG_LEVEL_ERROR, "Failed to register low level windows hook. (%#X)\n", status); + logger(LOG_LEVEL_ERROR, "Failed to register low level windows hook. (%#X)\n", status); break; // Darwin specific errors. case UIOHOOK_ERROR_AXAPI_DISABLED: - logger_proc(LOG_LEVEL_ERROR, "Failed to enable access for assistive devices. (%#X)\n", status); + logger(LOG_LEVEL_ERROR, "Failed to enable access for assistive devices. (%#X)\n", status); break; case UIOHOOK_ERROR_CREATE_EVENT_PORT: - logger_proc(LOG_LEVEL_ERROR, "Failed to create apple event port. (%#X)\n", status); + logger(LOG_LEVEL_ERROR, "Failed to create apple event port. (%#X)\n", status); break; case UIOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE: - logger_proc(LOG_LEVEL_ERROR, "Failed to create apple run loop source. (%#X)\n", status); + logger(LOG_LEVEL_ERROR, "Failed to create apple run loop source. (%#X)\n", status); break; case UIOHOOK_ERROR_GET_RUNLOOP: - logger_proc(LOG_LEVEL_ERROR, "Failed to acquire apple run loop. (%#X)\n", status); + logger(LOG_LEVEL_ERROR, "Failed to acquire apple run loop. (%#X)\n", status); break; case UIOHOOK_ERROR_CREATE_OBSERVER: - logger_proc(LOG_LEVEL_ERROR, "Failed to create apple run loop observer. (%#X)\n", status); + logger(LOG_LEVEL_ERROR, "Failed to create apple run loop observer. (%#X)\n", status); break; // Default error. case UIOHOOK_FAILURE: default: - logger_proc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)\n", status); + logger(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)\n", status); break; } diff --git a/demo/demo_post.c b/demo/demo_post.c index 579741aa..32d05e55 100644 --- a/demo/demo_post.c +++ b/demo/demo_post.c @@ -35,33 +35,32 @@ static uiohook_event *event = NULL; -bool logger_proc(unsigned int level, const char *format, ...) { - bool status = false; - - va_list args; +static void logger_proc(unsigned int level, void *user_data, const char *format, va_list args) { switch (level) { case LOG_LEVEL_INFO: - va_start(args, format); - status = vfprintf(stdout, format, args) >= 0; - va_end(args); + vfprintf(stdout, format, args); break; case LOG_LEVEL_WARN: case LOG_LEVEL_ERROR: - va_start(args, format); - status = vfprintf(stderr, format, args) >= 0; - va_end(args); + vfprintf(stderr, format, args); break; } +} + +static void logger(unsigned int level, const char *format, ...) { + va_list args; - return status; + va_start(args, format); + logger_proc(level, NULL, format, args); + va_end(args); } // TODO Implement CLI options. //int main(int argc, char *argv[]) { int main() { // Set the logger callback for library output. - hook_set_logger_proc(&logger_proc); + hook_set_logger_proc(&logger_proc, NULL); // Allocate memory for the virtual events only once. event = (uiohook_event *) malloc(sizeof(uiohook_event)); diff --git a/demo/demo_properties.c b/demo/demo_properties.c index e40ce94b..c6acc066 100644 --- a/demo/demo_properties.c +++ b/demo/demo_properties.c @@ -25,90 +25,90 @@ #include #include -static bool logger_proc(unsigned int level, const char *format, ...) { - bool status = false; - - va_list args; + +static void logger_proc(unsigned int level, void *user_data, const char *format, va_list args) { switch (level) { case LOG_LEVEL_INFO: - va_start(args, format); - status = vfprintf(stdout, format, args) >= 0; - va_end(args); + vfprintf(stdout, format, args); break; case LOG_LEVEL_WARN: case LOG_LEVEL_ERROR: - va_start(args, format); - status = vfprintf(stderr, format, args) >= 0; - va_end(args); + vfprintf(stderr, format, args); break; } - - return status; +} + +static void logger(unsigned int level, const char *format, ...) { + va_list args; + + va_start(args, format); + logger_proc(level, NULL, format, args); + va_end(args); } int main() { // Disable the logger. - hook_set_logger_proc(&logger_proc); + hook_set_logger_proc(&logger_proc, NULL); // Retrieves current monitor layout and size. unsigned char count; screen_data* monitors = hook_create_screen_info(&count); - logger_proc(LOG_LEVEL_INFO, "Monitors Found:\t%u\n", count); + logger(LOG_LEVEL_INFO, "Monitors Found:\t%u\n", count); for (int i = 0; i < count; i++) { - logger_proc(LOG_LEVEL_INFO, "\t%3u) %4u x %-4u (%5d, %-5d)\n", + logger(LOG_LEVEL_INFO, "\t%3u) %4u x %-4u (%5d, %-5d)\n", monitors[i].number, monitors[i].width, monitors[i].height, monitors[i].x, monitors[i].y); } - logger_proc(LOG_LEVEL_INFO, "\n"); + logger(LOG_LEVEL_INFO, "\n"); // Retrieves the keyboard auto repeat rate. long int repeat_rate = hook_get_auto_repeat_rate(); if (repeat_rate >= 0) { - logger_proc(LOG_LEVEL_INFO, "Auto Repeat Rate:\t%ld\n", repeat_rate); + logger(LOG_LEVEL_INFO, "Auto Repeat Rate:\t%ld\n", repeat_rate); } else { - logger_proc(LOG_LEVEL_WARN, "Failed to acquire keyboard auto repeat rate!\n"); + logger(LOG_LEVEL_WARN, "Failed to acquire keyboard auto repeat rate!\n"); } // Retrieves the keyboard auto repeat delay. long int repeat_delay = hook_get_auto_repeat_delay(); if (repeat_delay >= 0) { - logger_proc(LOG_LEVEL_INFO, "Auto Repeat Delay:\t%ld\n", repeat_delay); + logger(LOG_LEVEL_INFO, "Auto Repeat Delay:\t%ld\n", repeat_delay); } else { - logger_proc(LOG_LEVEL_WARN, "Failed to acquire keyboard auto repeat delay!\n"); + logger(LOG_LEVEL_WARN, "Failed to acquire keyboard auto repeat delay!\n"); } // Retrieves the mouse acceleration multiplier. long int acceleration_multiplier = hook_get_pointer_acceleration_multiplier(); if (acceleration_multiplier >= 0) { - logger_proc(LOG_LEVEL_INFO, "Mouse Acceleration Multiplier:\t%ld\n", acceleration_multiplier); + logger(LOG_LEVEL_INFO, "Mouse Acceleration Multiplier:\t%ld\n", acceleration_multiplier); } else { - logger_proc(LOG_LEVEL_WARN, "Failed to acquire mouse acceleration multiplier!\n"); + logger(LOG_LEVEL_WARN, "Failed to acquire mouse acceleration multiplier!\n"); } // Retrieves the mouse acceleration threshold. long int acceleration_threshold = hook_get_pointer_acceleration_threshold(); if (acceleration_threshold >= 0) { - logger_proc(LOG_LEVEL_INFO, "Mouse Acceleration Threshold:\t%ld\n", acceleration_threshold); + logger(LOG_LEVEL_INFO, "Mouse Acceleration Threshold:\t%ld\n", acceleration_threshold); } else { - logger_proc(LOG_LEVEL_WARN, "Failed to acquire mouse acceleration threshold!\n"); + logger(LOG_LEVEL_WARN, "Failed to acquire mouse acceleration threshold!\n"); } // Retrieves the mouse sensitivity. long int sensitivity = hook_get_pointer_sensitivity(); if (sensitivity >= 0) { - logger_proc(LOG_LEVEL_INFO, "Mouse Sensitivity:\t%ld\n", sensitivity); + logger(LOG_LEVEL_INFO, "Mouse Sensitivity:\t%ld\n", sensitivity); } else { - logger_proc(LOG_LEVEL_WARN, "Failed to acquire mouse sensitivity value!\n"); + logger(LOG_LEVEL_WARN, "Failed to acquire mouse sensitivity value!\n"); } // Retrieves the double/triple click interval. long int click_time = hook_get_multi_click_time(); if (click_time >= 0) { - logger_proc(LOG_LEVEL_INFO, "Multi-Click Time:\t%ld\n", click_time); + logger(LOG_LEVEL_INFO, "Multi-Click Time:\t%ld\n", click_time); } else { - logger_proc(LOG_LEVEL_WARN, "Failed to acquire mouse multi-click time!\n"); + logger(LOG_LEVEL_WARN, "Failed to acquire mouse multi-click time!\n"); } return EXIT_SUCCESS; diff --git a/include/uiohook.h b/include/uiohook.h index 57547363..1520edfc 100644 --- a/include/uiohook.h +++ b/include/uiohook.h @@ -59,8 +59,7 @@ typedef enum _log_level { } log_level; // Logger callback function prototype. -typedef bool (*logger_t)(unsigned int, const char *, ...); -typedef bool (*va_logger_t)(unsigned int, const char *, va_list); +typedef void (*logger_t)(unsigned int, void *, const char *, va_list); /* End Log Levels and Function Prototype */ /* Begin Virtual Event Types and Data Structures */ @@ -127,7 +126,7 @@ typedef struct _uiohook_event { } data; } uiohook_event; -typedef void (*dispatcher_t)(uiohook_event *const); +typedef void (*dispatcher_t)(uiohook_event * const, void *); /* End Virtual Event Types and Data Structures */ @@ -416,16 +415,13 @@ extern "C" { #endif // Set the logger callback function. - UIOHOOK_API void hook_set_logger_proc(logger_t logger_proc); - - // Set the logger callback function. - UIOHOOK_API void hook_set_va_logger_proc(va_logger_t logger_proc); + UIOHOOK_API void hook_set_logger_proc(logger_t logger_proc, void *user_data); // Send a virtual event back to the system. UIOHOOK_API void hook_post_event(uiohook_event * const event); // Set the event callback function. - UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc); + UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc, void *user_data); // Insert the event hook. UIOHOOK_API int hook_run(); diff --git a/src/darwin/input_helper.c b/src/darwin/input_helper.c index df9c0b93..3871d476 100644 --- a/src/darwin/input_helper.c +++ b/src/darwin/input_helper.c @@ -78,7 +78,7 @@ bool is_accessibility_enabled() { } logger(LOG_LEVEL_DEBUG, "%s [%u]: AXIsProcessTrustedWithOptions not found.\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); logger(LOG_LEVEL_DEBUG, "%s [%u]: Falling back to AXAPIEnabled().\n", __FUNCTION__, __LINE__); diff --git a/src/darwin/input_hook.c b/src/darwin/input_hook.c index 7eac992f..1648d865 100644 --- a/src/darwin/input_hook.c +++ b/src/darwin/input_hook.c @@ -107,22 +107,24 @@ static struct timeval system_time; static uiohook_event event; // Event dispatch callback. -static dispatcher_t dispatcher = NULL; +static dispatcher_t dispatch = NULL; +static void *dispatch_data = NULL; -UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc) { +UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc, void *user_data) { logger(LOG_LEVEL_DEBUG, "%s [%u]: Setting new dispatch callback to %#p.\n", __FUNCTION__, __LINE__, dispatch_proc); - dispatcher = dispatch_proc; + dispatch = dispatch_proc; + dispatch_data = user_data; } // Send out an event if a dispatcher was set. -static inline void dispatch_event(uiohook_event *const event) { - if (dispatcher != NULL) { +static void dispatch_event(uiohook_event *const event) { + if (dispatch != NULL) { logger(LOG_LEVEL_DEBUG, "%s [%u]: Dispatching event type %u.\n", __FUNCTION__, __LINE__, event->type); - dispatcher(event); + dispatch(event, dispatch_data); } else { logger(LOG_LEVEL_WARN, "%s [%u]: No dispatch callback set!\n", __FUNCTION__, __LINE__); @@ -214,7 +216,7 @@ static void keycode_to_lookup(void *info) { } #ifdef USE_EPOCH_TIME -static inline uint64_t get_unix_timestamp() { +static uint64_t get_unix_timestamp() { // Get the local system time in UTC. gettimeofday(&system_time, NULL); @@ -1316,7 +1318,7 @@ UIOHOOK_API int hook_run() { tis_keycode_message = (TISKeycodeMessage *) calloc(1, sizeof(TISKeycodeMessage)); if (tis_keycode_message == NULL) { logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for TIS message structure!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); return UIOHOOK_ERROR_OUT_OF_MEMORY; } @@ -1325,7 +1327,7 @@ UIOHOOK_API int hook_run() { tis_event_message = (TISEventMessage *) calloc(1, sizeof(TISEventMessage)); if (tis_event_message == NULL) { logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for TIS event structure!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); return UIOHOOK_ERROR_OUT_OF_MEMORY; } diff --git a/src/darwin/post_event.c b/src/darwin/post_event.c index 8bf8508c..22c36846 100644 --- a/src/darwin/post_event.c +++ b/src/darwin/post_event.c @@ -81,7 +81,7 @@ static int post_key_event(uiohook_event * const event, CGEventSourceRef src) { } } else { logger(LOG_LEVEL_DEBUG, "%s [%u]: Invalid event for keyboard post event: %#X.\n", - __FUNCTION__, __LINE__, event->type); + __FUNCTION__, __LINE__, event->type); return UIOHOOK_FAILURE; } @@ -197,7 +197,7 @@ static int post_mouse_event(uiohook_event * const event, CGEventSourceRef src) { default: logger(LOG_LEVEL_DEBUG, "%s [%u]: Invalid mouse event: %#X.\n", - __FUNCTION__, __LINE__, event->type); + __FUNCTION__, __LINE__, event->type); return UIOHOOK_FAILURE; } @@ -296,7 +296,7 @@ UIOHOOK_API void hook_post_event(uiohook_event * const event) { default: logger(LOG_LEVEL_DEBUG, "%s [%u]: Ignoring post event: %#X.\n", - __FUNCTION__, __LINE__, event->type); + __FUNCTION__, __LINE__, event->type); } CFRelease(src); diff --git a/src/logger.c b/src/logger.c index 5007c7d4..34c06009 100644 --- a/src/logger.c +++ b/src/logger.c @@ -24,38 +24,20 @@ #include "logger.h" -static bool default_logger(unsigned int level, const char *format, ...) { - return false; -} - -static va_logger_t va_logger; +static logger_t callback = NULL; +static void *callback_data = NULL; -static bool va_logger_wrapper(unsigned int level, const char *format, ...) { - va_list args; +void logger(unsigned int level, const char *format, ...) { + if (callback != NULL) { + va_list args; - va_start(args, format); - bool result = va_logger(level, format, args); - va_end(args); - - return result; -} - -// Current logger function pointer, should never be null. -logger_t logger = &default_logger; - -UIOHOOK_API void hook_set_logger_proc(logger_t logger_proc) { - if (logger_proc == NULL) { - logger = &default_logger; - } else { - logger = logger_proc; + va_start(args, format); + callback(level, callback_data, format, args); + va_end(args); } } -UIOHOOK_API void hook_set_va_logger_proc(va_logger_t logger_proc) { - if (logger_proc == NULL) { - logger = &default_logger; - } else { - va_logger = logger_proc; - logger = &va_logger_wrapper; - } +UIOHOOK_API void hook_set_logger_proc(logger_t logger_proc, void *user_data) { + callback = logger_proc; + callback_data = user_data; } diff --git a/src/logger.h b/src/logger.h index 8a64c467..1e1f5201 100644 --- a/src/logger.h +++ b/src/logger.h @@ -26,7 +26,6 @@ #define __FUNCTION__ __func__ #endif -// logger(level, message) -extern logger_t logger; +extern void logger(unsigned int level, const char *format, ...); #endif diff --git a/src/windows/input_helper.c b/src/windows/input_helper.c index 82429165..928f29ce 100644 --- a/src/windows/input_helper.c +++ b/src/windows/input_helper.c @@ -479,8 +479,7 @@ static int refresh_locale_list() { int new_size = GetKeyboardLayoutList(hkl_size, hkl_list); if (new_size > 0) { if (new_size != hkl_size) { - logger(LOG_LEVEL_WARN, "%s [%u]: Locale size mismatch! " - "Expected %i, received %i!\n", + logger(LOG_LEVEL_WARN, "%s [%u]: Locale size mismatch! Expected %i, received %i!\n", __FUNCTION__, __LINE__, hkl_size, new_size); } else { logger(LOG_LEVEL_DEBUG, "%s [%u]: Received %i locales.\n", @@ -612,8 +611,7 @@ static int refresh_locale_list() { count++; } else { - logger(LOG_LEVEL_ERROR, - "%s [%u]: GetProcAddress() failed for KbdLayerDescriptor!\n", + logger(LOG_LEVEL_ERROR, "%s [%u]: GetProcAddress() failed for KbdLayerDescriptor!\n", __FUNCTION__, __LINE__); FreeLibrary(locale_item->library); @@ -621,20 +619,17 @@ static int refresh_locale_list() { locale_item = NULL; } } else { - logger(LOG_LEVEL_ERROR, - "%s [%u]: GetSystemDirectory() failed!\n", + logger(LOG_LEVEL_ERROR, "%s [%u]: GetSystemDirectory() failed!\n", __FUNCTION__, __LINE__); } } else { - logger(LOG_LEVEL_ERROR, - "%s [%u]: Could not find keyboard map for locale %#p!\n", + logger(LOG_LEVEL_ERROR, "%s [%u]: Could not find keyboard map for locale %#p!\n", __FUNCTION__, __LINE__, hkl_list[i]); } } } } else { - logger(LOG_LEVEL_ERROR, - "%s [%u]: GetKeyboardLayoutList() failed!\n", + logger(LOG_LEVEL_ERROR, "%s [%u]: GetKeyboardLayoutList() failed!\n", __FUNCTION__, __LINE__); // TODO Try and recover by using the current layout. @@ -670,8 +665,7 @@ SIZE_T keycode_to_unicode(DWORD keycode, PWCHAR buffer, SIZE_T size) { // You may already be a winner! if (locale_item != NULL && locale_item->id == locale_id) { - logger(LOG_LEVEL_DEBUG, - "%s [%u]: Activating keyboard layout %#p.\n", + logger(LOG_LEVEL_DEBUG, "%s [%u]: Activating keyboard layout %#p.\n", __FUNCTION__, __LINE__, locale_item->id); // Switch the current locale. @@ -682,8 +676,7 @@ SIZE_T keycode_to_unicode(DWORD keycode, PWCHAR buffer, SIZE_T size) { // This is consistent with the way Windows handles locale changes. deadChar = WCH_NONE; } else { - logger(LOG_LEVEL_DEBUG, - "%s [%u]: Refreshing locale cache.\n", + logger(LOG_LEVEL_DEBUG, "%s [%u]: Refreshing locale cache.\n", __FUNCTION__, __LINE__); refresh_locale_list(); @@ -696,8 +689,7 @@ SIZE_T keycode_to_unicode(DWORD keycode, PWCHAR buffer, SIZE_T size) { // Check and make sure the Unicode helper was loaded. if (locale_current != NULL) { - logger(LOG_LEVEL_DEBUG, - "%s [%u]: Using keyboard layout %#p.\n", + logger(LOG_LEVEL_DEBUG, "%s [%u]: Using keyboard layout %#p.\n", __FUNCTION__, __LINE__, locale_current->id); int mod = 0; @@ -855,8 +847,7 @@ int load_input_helper() { count = refresh_locale_list(); - logger(LOG_LEVEL_DEBUG, - "%s [%u]: refresh_locale_list() found %i locale(s).\n", + logger(LOG_LEVEL_DEBUG, "%s [%u]: refresh_locale_list() found %i locale(s).\n", __FUNCTION__, __LINE__, count); return count; diff --git a/src/windows/input_hook.c b/src/windows/input_hook.c index f28d1f80..c05f2e3f 100644 --- a/src/windows/input_hook.c +++ b/src/windows/input_hook.c @@ -49,22 +49,24 @@ static POINT last_click; static uiohook_event event; // Event dispatch callback. -static dispatcher_t dispatcher = NULL; +static dispatcher_t dispatch = NULL; +static void *dispatch_data = NULL; -UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc) { +UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc, void *user_data) { logger(LOG_LEVEL_DEBUG, "%s [%u]: Setting new dispatch callback to %#p.\n", __FUNCTION__, __LINE__, dispatch_proc); - dispatcher = dispatch_proc; + dispatch = dispatch_proc; + dispatch_data = user_data; } // Send out an event if a dispatcher was set. -static inline void dispatch_event(uiohook_event *const event) { - if (dispatcher != NULL) { +static void dispatch_event(uiohook_event *const event) { + if (dispatch != NULL) { logger(LOG_LEVEL_DEBUG, "%s [%u]: Dispatching event type %u.\n", __FUNCTION__, __LINE__, event->type); - dispatcher(event); + dispatch(event, dispatch_data); } else { logger(LOG_LEVEL_WARN, "%s [%u]: No dispatch callback set!\n", __FUNCTION__, __LINE__); diff --git a/src/windows/post_event.c b/src/windows/post_event.c index 275b67af..f5ddd3c1 100644 --- a/src/windows/post_event.c +++ b/src/windows/post_event.c @@ -87,7 +87,7 @@ static int map_keyboard_event(uiohook_event * const event, INPUT * const input) default: logger(LOG_LEVEL_DEBUG, "%s [%u]: Invalid event for keyboard event mapping: %#X.\n", - __FUNCTION__, __LINE__, event->type); + __FUNCTION__, __LINE__, event->type); return UIOHOOK_FAILURE; } @@ -195,7 +195,7 @@ static int map_mouse_event(uiohook_event * const event, INPUT * const input) { default: logger(LOG_LEVEL_DEBUG, "%s [%u]: Invalid event for mouse event mapping: %#X.\n", - __FUNCTION__, __LINE__, event->type); + __FUNCTION__, __LINE__, event->type); return UIOHOOK_FAILURE; } @@ -235,7 +235,7 @@ UIOHOOK_API void hook_post_event(uiohook_event * const event) { default: logger(LOG_LEVEL_DEBUG, "%s [%u]: Ignoring post event: %#X.\n", - __FUNCTION__, __LINE__, event->type); + __FUNCTION__, __LINE__, event->type); } diff --git a/src/windows/system_properties.c b/src/windows/system_properties.c index fd351ce7..dd3d53cd 100644 --- a/src/windows/system_properties.c +++ b/src/windows/system_properties.c @@ -126,7 +126,7 @@ UIOHOOK_API long int hook_get_auto_repeat_rate() { if (SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &rate, 0)) { logger(LOG_LEVEL_DEBUG, "%s [%u]: SPI_GETKEYBOARDSPEED: %li.\n", - __FUNCTION__, __LINE__, rate); + __FUNCTION__, __LINE__, rate); value = rate; } @@ -140,7 +140,7 @@ UIOHOOK_API long int hook_get_auto_repeat_delay() { if (SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &delay, 0)) { logger(LOG_LEVEL_DEBUG, "%s [%u]: SPI_GETKEYBOARDDELAY: %li.\n", - __FUNCTION__, __LINE__, delay); + __FUNCTION__, __LINE__, delay); value = delay; } @@ -154,7 +154,7 @@ UIOHOOK_API long int hook_get_pointer_acceleration_multiplier() { if (SystemParametersInfo(SPI_GETMOUSE, 0, &mouse, 0)) { logger(LOG_LEVEL_DEBUG, "%s [%u]: SPI_GETMOUSE[2]: %i.\n", - __FUNCTION__, __LINE__, mouse[2]); + __FUNCTION__, __LINE__, mouse[2]); value = mouse[2]; } @@ -168,9 +168,9 @@ UIOHOOK_API long int hook_get_pointer_acceleration_threshold() { if (SystemParametersInfo(SPI_GETMOUSE, 0, &mouse, 0)) { logger(LOG_LEVEL_DEBUG, "%s [%u]: SPI_GETMOUSE[0]: %i.\n", - __FUNCTION__, __LINE__, mouse[0]); + __FUNCTION__, __LINE__, mouse[0]); logger(LOG_LEVEL_DEBUG, "%s [%u]: SPI_GETMOUSE[1]: %i.\n", - __FUNCTION__, __LINE__, mouse[1]); + __FUNCTION__, __LINE__, mouse[1]); // Average the x and y thresholds. value = (mouse[0] + mouse[1]) / 2; @@ -185,7 +185,7 @@ UIOHOOK_API long int hook_get_pointer_sensitivity() { if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &sensitivity, 0)) { logger(LOG_LEVEL_DEBUG, "%s [%u]: SPI_GETMOUSESPEED: %i.\n", - __FUNCTION__, __LINE__, sensitivity); + __FUNCTION__, __LINE__, sensitivity); value = sensitivity; } diff --git a/src/x11/input_helper.c b/src/x11/input_helper.c index a947029a..d076e629 100644 --- a/src/x11/input_helper.c +++ b/src/x11/input_helper.c @@ -1768,11 +1768,11 @@ unsigned int button_map_lookup(unsigned int button) { } } else { logger(LOG_LEVEL_WARN, "%s [%u]: Mouse button map memory is unavailable!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); } } else { logger(LOG_LEVEL_WARN, "%s [%u]: XDisplay helper_disp is unavailable!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); } // X11 numbers buttons 2 & 3 backwards from other platforms so we normalize them. @@ -1787,7 +1787,7 @@ void load_input_helper() { mouse_button_map = malloc(sizeof(unsigned char) * BUTTON_MAP_MAX); if (mouse_button_map == NULL) { logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for mouse button map!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); //return UIOHOOK_ERROR_OUT_OF_MEMORY; } diff --git a/src/x11/input_hook.c b/src/x11/input_hook.c index 7718e151..6a627493 100644 --- a/src/x11/input_hook.c +++ b/src/x11/input_hook.c @@ -109,22 +109,24 @@ static struct timeval system_time; static uiohook_event event; // Event dispatch callback. -static dispatcher_t dispatcher = NULL; +static dispatcher_t dispatch = NULL; +static void *dispatch_data = NULL; -UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc) { +UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc, void *user_data) { logger(LOG_LEVEL_DEBUG, "%s [%u]: Setting new dispatch callback to %#p.\n", __FUNCTION__, __LINE__, dispatch_proc); - dispatcher = dispatch_proc; + dispatch = dispatch_proc; + dispatch_data = user_data; } // Send out an event if a dispatcher was set. -static inline void dispatch_event(uiohook_event *const event) { - if (dispatcher != NULL) { +static void dispatch_event(uiohook_event *const event) { + if (dispatch != NULL) { logger(LOG_LEVEL_DEBUG, "%s [%u]: Dispatching event type %u.\n", __FUNCTION__, __LINE__, event->type); - dispatcher(event); + dispatch(event, dispatch_data); } else { logger(LOG_LEVEL_WARN, "%s [%u]: No dispatch callback set!\n", __FUNCTION__, __LINE__); @@ -891,7 +893,7 @@ static inline int xrecord_block() { #endif else { logger(LOG_LEVEL_ERROR, "%s [%u]: XRecordEnableContext failure!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); #ifdef USE_XRECORD_ASYNC // Reset the running state. @@ -1068,7 +1070,7 @@ UIOHOOK_API int hook_run() { hook = malloc(sizeof(hook_info)); if (hook == NULL) { logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for hook structure!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); return UIOHOOK_ERROR_OUT_OF_MEMORY; } diff --git a/src/x11/post_event.c b/src/x11/post_event.c index 70e12b61..e060986a 100644 --- a/src/x11/post_event.c +++ b/src/x11/post_event.c @@ -131,21 +131,21 @@ static int post_key_event(uiohook_event * const event) { #endif } else { logger(LOG_LEVEL_DEBUG, "%s [%u]: Invalid event for keyboard post event: %#X.\n", - __FUNCTION__, __LINE__, event->type); + __FUNCTION__, __LINE__, event->type); return UIOHOOK_FAILURE; } #ifdef USE_XTEST if (XTestFakeKeyEvent(helper_disp, keycode, is_pressed, 0) != Success) { logger(LOG_LEVEL_ERROR, "%s [%u]: XTestFakeKeyEvent() failed!\n", - __FUNCTION__, __LINE__, event->type); + __FUNCTION__, __LINE__, event->type); return UIOHOOK_FAILURE; } #else XSelectInput(helper_disp, key_event.window, KeyPressMask | KeyReleaseMask); if (XSendEvent(helper_disp, key_event.window, False, event_mask, (XEvent *) &key_event) == 0) { logger(LOG_LEVEL_ERROR, "%s [%u]: XSendEvent() failed!\n", - __FUNCTION__, __LINE__, event->type); + __FUNCTION__, __LINE__, event->type); return UIOHOOK_FAILURE; } #endif @@ -271,7 +271,7 @@ static int post_mouse_button_event(uiohook_event * const event) { default: logger(LOG_LEVEL_DEBUG, "%s [%u]: Invalid mouse button event: %#X.\n", - __FUNCTION__, __LINE__, event->type); + __FUNCTION__, __LINE__, event->type); return UIOHOOK_FAILURE; } @@ -383,7 +383,7 @@ static void post_mouse_motion_event(uiohook_event * const event) { UIOHOOK_API void hook_post_event(uiohook_event * const event) { if (helper_disp == NULL) { logger(LOG_LEVEL_ERROR, "%s [%u]: XDisplay helper_disp is unavailable!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); return; // UIOHOOK_ERROR_X_OPEN_DISPLAY } @@ -417,7 +417,7 @@ UIOHOOK_API void hook_post_event(uiohook_event * const event) { default: logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n", - __FUNCTION__, __LINE__, event->type); + __FUNCTION__, __LINE__, event->type); break; } diff --git a/src/x11/system_properties.c b/src/x11/system_properties.c index b821b8a1..e9044a7b 100644 --- a/src/x11/system_properties.c +++ b/src/x11/system_properties.c @@ -214,7 +214,7 @@ UIOHOOK_API screen_data* hook_create_screen_info(unsigned char *count) { #endif } else { logger(LOG_LEVEL_WARN, "%s [%u]: XDisplay helper_disp is unavailable!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); } return screens; @@ -253,7 +253,7 @@ UIOHOOK_API long int hook_get_auto_repeat_rate() { #endif } else { logger(LOG_LEVEL_WARN, "%s [%u]: XDisplay helper_disp is unavailable!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); } if (successful) { @@ -296,7 +296,7 @@ UIOHOOK_API long int hook_get_auto_repeat_delay() { #endif } else { logger(LOG_LEVEL_WARN, "%s [%u]: XDisplay helper_disp is unavailable!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); } if (successful) { @@ -321,7 +321,7 @@ UIOHOOK_API long int hook_get_pointer_acceleration_multiplier() { } } else { logger(LOG_LEVEL_WARN, "%s [%u]: XDisplay helper_disp is unavailable!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); } return value; @@ -342,7 +342,7 @@ UIOHOOK_API long int hook_get_pointer_acceleration_threshold() { } } else { logger(LOG_LEVEL_WARN, "%s [%u]: XDisplay helper_disp is unavailable!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); } return value; @@ -363,7 +363,7 @@ UIOHOOK_API long int hook_get_pointer_sensitivity() { } } else { logger(LOG_LEVEL_WARN, "%s [%u]: XDisplay helper_disp is unavailable!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); } return value; @@ -418,7 +418,7 @@ UIOHOOK_API long int hook_get_multi_click_time() { } } else { logger(LOG_LEVEL_WARN, "%s [%u]: XDisplay helper_disp is unavailable!\n", - __FUNCTION__, __LINE__); + __FUNCTION__, __LINE__); } if (successful) { From bf7edd59efe78c2603b1d24d5f1a243535b4119f Mon Sep 17 00:00:00 2001 From: Alexander Barker Date: Sat, 19 Mar 2022 13:14:08 -0700 Subject: [PATCH 06/12] Remove USE_CARBON_LEGACY build flag. Fixes #127 --- CMakeLists.txt | 10 ------ README.md | 1 - src/darwin/input_helper.c | 25 +++----------- src/darwin/input_hook.c | 10 +++--- src/darwin/system_properties.c | 62 ++-------------------------------- 5 files changed, 13 insertions(+), 95 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 939b6897..a880277a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -264,16 +264,6 @@ elseif(APPLE) target_include_directories(uiohook PRIVATE "${APPKIT}") target_link_libraries(uiohook "${APPKIT}") endif() - - option(USE_CARBON_LEGACY "Legacy Carbon framework functionality (default: OFF)" OFF) - if(USE_CARBON_LEGACY) - message(DEPRECATION "Legacy Carbon functionality has been deprecated.") - add_compile_definitions(USE_CARBON_LEGACY) - - if(USE_CARBON_LEGACY AND CMAKE_SIZEOF_VOID_P EQUAL 8) - message(WARNING "Legacy Carbon functionality should not be used with 64-bit targets.") - endif() - endif() elseif(WIN32) target_link_libraries(uiohook Advapi32) endif() diff --git a/README.md b/README.md index 5b49e4a0..46f38077 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,6 @@ $ cmake --build . --parallel 2 --target install | __OSX__ | USE_APPLICATION_SERVICES:BOOL | framework | ON | | | USE_IOKIT:BOOL | framework | ON | | | USE_OBJC:BOOL | obj-c api | ON | -| | USE_CARBON_LEGACY:BOOL | legacy framework | OFF | | __Win32__ | | | | | __Linux__ | USE_EVDEV:BOOL | generic input driver | ON | | __*nix__ | USE_XF86MISC:BOOL | xfree86-misc extension | OFF | diff --git a/src/darwin/input_helper.c b/src/darwin/input_helper.c index 3871d476..1bbd417f 100644 --- a/src/darwin/input_helper.c +++ b/src/darwin/input_helper.c @@ -28,15 +28,11 @@ #include "input_helper.h" #include "logger.h" +#ifdef USE_APPLICATION_SERVICES // Current dead key state. -#if defined(USE_CARBON_LEGACY) || defined(USE_APPLICATION_SERVICES) static UInt32 deadkey_state; -#endif // Input source data for the keyboard. -#if defined(USE_CARBON_LEGACY) -static KeyboardLayoutRef prev_keyboard_layout = NULL; -#elif defined(USE_APPLICATION_SERVICES) static TISInputSourceRef prev_keyboard_layout = NULL; #endif @@ -104,14 +100,7 @@ UniCharCount keycode_to_unicode(CGEventRef event_ref, UniChar *buffer, UniCharCo UniCharCount count = 0; CFDataRef inputData = NULL; - #if defined(USE_CARBON_LEGACY) - KeyboardLayoutRef curr_keyboard_layout; - if (KLGetCurrentKeyboardLayout(&curr_keyboard_layout) == noErr) { - if (KLGetKeyboardLayoutProperty(curr_keyboard_layout, kKLuchrData, (const void **) &inputData) != noErr) { - inputData = NULL; - } - } - #elif defined(USE_APPLICATION_SERVICES) + #ifdef USE_APPLICATION_SERVICES // TODO Try https://developer.apple.com/documentation/coregraphics/1456120-cgeventkeyboardgetunicodestring?language=objc if (CFEqual(CFRunLoopGetCurrent(), CFRunLoopGetMain())) { // NOTE The following block must execute on the main runloop, @@ -144,13 +133,9 @@ UniCharCount keycode_to_unicode(CGEventRef event_ref, UniChar *buffer, UniCharCo } #endif - #if defined(USE_CARBON_LEGACY) || defined(USE_APPLICATION_SERVICES) + #ifdef USE_APPLICATION_SERVICES if (inputData != NULL) { - #ifdef USE_CARBON_LEGACY - const UCKeyboardLayout *keyboard_layout = (const UCKeyboardLayout *) inputData; - #else const UCKeyboardLayout *keyboard_layout = (const UCKeyboardLayout*) CFDataGetBytePtr(inputData); - #endif if (keyboard_layout != NULL) { //Extract keycode and modifier information. @@ -518,14 +503,14 @@ UInt64 scancode_to_keycode(uint16_t scancode) { } void load_input_helper() { - #if defined(USE_CARBON_LEGACY) || defined(USE_APPLICATION_SERVICES) + #ifdef USE_APPLICATION_SERVICES // Start with a fresh dead key state. //curr_deadkey_state = 0; #endif } void unload_input_helper() { - #if defined(USE_CARBON_LEGACY) || defined(USE_APPLICATION_SERVICES) + #ifdef USE_APPLICATION_SERVICES if (prev_keyboard_layout != NULL) { // Cleanup tracking of the previous layout. CFRelease(prev_keyboard_layout); diff --git a/src/darwin/input_hook.c b/src/darwin/input_hook.c index 1648d865..33934d52 100644 --- a/src/darwin/input_hook.c +++ b/src/darwin/input_hook.c @@ -80,7 +80,7 @@ typedef void* dispatch_queue_t; static struct dispatch_queue_s *dispatch_main_queue_s; static void (*dispatch_sync_f_f)(dispatch_queue_t, void *, void (*function)(void *)); -#if !defined(USE_CARBON_LEGACY) && defined(USE_APPLICATION_SERVICES) +#if defined(USE_APPLICATION_SERVICES) typedef struct _main_runloop_info { CFRunLoopSourceRef source; CFRunLoopObserverRef observer; @@ -303,7 +303,7 @@ static inline void process_key_pressed(uint64_t timestamp, CGEventRef event_ref) __FUNCTION__, __LINE__); (*dispatch_sync_f_f)(dispatch_main_queue_s, tis_keycode_message, &keycode_to_lookup); } - #if !defined(USE_CARBON_LEGACY) && defined(USE_APPLICATION_SERVICES) + #ifdef USE_APPLICATION_SERVICES else if (!is_runloop_main) { logger(LOG_LEVEL_DEBUG, "%s [%u]: Using CFRunLoopWakeUp for key typed events.\n", __FUNCTION__, __LINE__); @@ -1037,7 +1037,7 @@ CGEventRef hook_event_proc(CGEventTapProxy tap_proxy, CGEventType type, CGEventR } -#if !defined(USE_CARBON_LEGACY) && defined(USE_APPLICATION_SERVICES) +#ifdef USE_APPLICATION_SERVICES void main_runloop_status_proc(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { switch (activity) { case kCFRunLoopExit: @@ -1357,7 +1357,7 @@ UIOHOOK_API int hook_run() { logger(LOG_LEVEL_DEBUG, "%s [%u]: Failed to locate dispatch_sync_f() or dispatch_get_main_queue()!\n", __FUNCTION__, __LINE__); - #if !defined(USE_CARBON_LEGACY) && defined(USE_APPLICATION_SERVICES) + #ifdef USE_APPLICATION_SERVICES logger(LOG_LEVEL_DEBUG, "%s [%u]: Falling back to runloop signaling.\n", __FUNCTION__, __LINE__); @@ -1404,7 +1404,7 @@ UIOHOOK_API int hook_run() { eventWithoutCGEvent(auto_release_pool, sel_registerName("release")); #endif - #if !defined(USE_CARBON_LEGACY) && defined(USE_APPLICATION_SERVICES) + #ifdef USE_APPLICATION_SERVICES pthread_mutex_lock(&main_runloop_mutex); if (!CFEqual(event_loop, CFRunLoopGetMain())) { if (dispatch_sync_f_f == NULL || dispatch_main_queue_s == NULL) { diff --git a/src/darwin/system_properties.c b/src/darwin/system_properties.c index 74a1fe0d..57b87c12 100644 --- a/src/darwin/system_properties.c +++ b/src/darwin/system_properties.c @@ -16,10 +16,6 @@ * along with this program. If not, see . */ -#ifdef USE_CARBON_LEGACY -#include -#endif - #if defined(USE_APPLICATION_SERVICES) || defined(USE_IOKIT) #include #endif @@ -147,7 +143,7 @@ UIOHOOK_API screen_data* hook_create_screen_info(unsigned char *count) { * CharSec = 66 / (MS / 15) */ UIOHOOK_API long int hook_get_auto_repeat_rate() { - #if defined(USE_APPLICATION_SERVICES) || defined(USE_IOKIT) || defined(USE_CARBON_LEGACY) + #if defined(USE_APPLICATION_SERVICES) || defined(USE_IOKIT) bool successful = false; SInt64 rate; #endif @@ -207,29 +203,11 @@ UIOHOOK_API long int hook_get_auto_repeat_rate() { } #endif - #ifdef USE_CARBON_LEGACY - if (!successful) { - // Apple documentation states that value is in 'ticks'. I am not sure - // what that means, but it looks a lot like the arbitrary slider value. - rate = LMGetKeyRepThresh(); - if (rate > -1) { - /* This is the slider value, we must multiply by 15 to convert to - * milliseconds. - */ - value = (long) rate * 15; - successful = true; - - logger(LOG_LEVEL_DEBUG, "%s [%u]: LMGetKeyRepThresh: %li.\n", - __FUNCTION__, __LINE__, value); - } - } - #endif - return value; } UIOHOOK_API long int hook_get_auto_repeat_delay() { - #if defined(USE_APPLICATION_SERVICES) || defined(USE_IOKIT) || defined(USE_CARBON_LEGACY) + #if defined(USE_APPLICATION_SERVICES) || defined(USE_IOKIT) bool successful = false; SInt64 delay; #endif @@ -288,23 +266,6 @@ UIOHOOK_API long int hook_get_auto_repeat_delay() { } #endif - #ifdef USE_CARBON_LEGACY - if (!successful) { - // Apple documentation states that value is in 'ticks'. I am not sure - // what that means, but it looks a lot like the arbitrary slider value. - delay = LMGetKeyThresh(); - if (delay > -1) { - // This is the slider value, we must multiply by 15 to convert to - // milliseconds. - value = (long) delay * 15; - successful = true; - - logger(LOG_LEVEL_DEBUG, "%s [%u]: LMGetKeyThresh: %li.\n", - __FUNCTION__, __LINE__, value); - } - } - #endif - return value; } @@ -378,7 +339,7 @@ UIOHOOK_API long int hook_get_pointer_sensitivity() { } UIOHOOK_API long int hook_get_multi_click_time() { - #if defined(USE_APPLICATION_SERVICES) || defined(USE_IOKIT) || defined(USE_CARBON_LEGACY) + #if defined(USE_APPLICATION_SERVICES) || defined(USE_IOKIT) bool successful = false; Float64 time; #endif @@ -425,23 +386,6 @@ UIOHOOK_API long int hook_get_multi_click_time() { } #endif - #ifdef USE_CARBON_LEGACY - if (!successful) { - // Apple documentation states that value is in 'ticks'. I am not sure - // what that means, but it looks a lot like the arbitrary slider value. - time = GetDblTime(); - if (time > -1) { - // This is the slider value, we must multiply by 15 to convert to - // milliseconds. - value = (long) time * 15; - successful = true; - - logger(LOG_LEVEL_DEBUG, "%s [%u]: GetDblTime: %li.\n", - __FUNCTION__, __LINE__, value); - } - } - #endif - return value; } From 5dfab6bf1b2e4688b86e6a8fa04d5663787d0a78 Mon Sep 17 00:00:00 2001 From: Alexander Barker Date: Sat, 19 Mar 2022 17:48:59 -0700 Subject: [PATCH 07/12] hook_post_event should return an int for status UIOHOOK_SUCCESS or otherwise. Fixes #117 --- include/uiohook.h | 6 +- src/darwin/post_event.c | 14 ++--- src/windows/post_event.c | 15 ++--- src/x11/post_event.c | 120 +++++++++++++++++++++++++-------------- 4 files changed, 93 insertions(+), 62 deletions(-) diff --git a/include/uiohook.h b/include/uiohook.h index 1520edfc..0af32d55 100644 --- a/include/uiohook.h +++ b/include/uiohook.h @@ -417,12 +417,12 @@ extern "C" { // Set the logger callback function. UIOHOOK_API void hook_set_logger_proc(logger_t logger_proc, void *user_data); - // Send a virtual event back to the system. - UIOHOOK_API void hook_post_event(uiohook_event * const event); - // Set the event callback function. UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc, void *user_data); + // Send a virtual event back to the system. + UIOHOOK_API int hook_post_event(uiohook_event * const event); + // Insert the event hook. UIOHOOK_API int hook_run(); diff --git a/src/darwin/post_event.c b/src/darwin/post_event.c index 22c36846..59b34ebd 100644 --- a/src/darwin/post_event.c +++ b/src/darwin/post_event.c @@ -126,7 +126,6 @@ static int post_key_event(uiohook_event * const event, CGEventSourceRef src) { if (cg_event == NULL) { logger(LOG_LEVEL_ERROR, "%s [%u]: CGEventCreateKeyboardEvent failed!\n", __FUNCTION__, __LINE__); - return UIOHOOK_ERROR_OUT_OF_MEMORY; } @@ -257,25 +256,21 @@ static int post_mouse_wheel_event(uiohook_event * const event, CGEventSourceRef return UIOHOOK_SUCCESS; } - -// TODO This should return a status code, UIOHOOK_SUCCESS or otherwise. -UIOHOOK_API void hook_post_event(uiohook_event * const event) { - int status = UIOHOOK_FAILURE; - +UIOHOOK_API int hook_post_event(uiohook_event * const event) { CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); if (src == NULL) { logger(LOG_LEVEL_ERROR, "%s [%u]: CGEventSourceCreate failed!\n", __FUNCTION__, __LINE__); - return; // UIOHOOK_ERROR_OUT_OF_MEMORY + return UIOHOOK_ERROR_OUT_OF_MEMORY; } + int status = UIOHOOK_FAILURE; switch (event->type) { case EVENT_KEY_PRESSED: case EVENT_KEY_RELEASED: status = post_key_event(event, src); break; - case EVENT_MOUSE_PRESSED: case EVENT_MOUSE_RELEASED: @@ -297,7 +292,10 @@ UIOHOOK_API void hook_post_event(uiohook_event * const event) { default: logger(LOG_LEVEL_DEBUG, "%s [%u]: Ignoring post event: %#X.\n", __FUNCTION__, __LINE__, event->type); + status = UIOHOOK_FAILURE; } CFRelease(src); + + return status; } diff --git a/src/windows/post_event.c b/src/windows/post_event.c index f5ddd3c1..a6ddbe51 100644 --- a/src/windows/post_event.c +++ b/src/windows/post_event.c @@ -202,17 +202,15 @@ static int map_mouse_event(uiohook_event * const event, INPUT * const input) { return UIOHOOK_SUCCESS; } -// TODO This should return a status code, UIOHOOK_SUCCESS or otherwise. -UIOHOOK_API void hook_post_event(uiohook_event * const event) { - int status = UIOHOOK_FAILURE; - +UIOHOOK_API int hook_post_event(uiohook_event * const event) { INPUT *input = (INPUT *) calloc(1, sizeof(INPUT)) ; if (input == NULL) { logger(LOG_LEVEL_ERROR, "%s [%u]: failed to allocate memory: calloc!\n", __FUNCTION__, __LINE__); - return; // UIOHOOK_ERROR_OUT_OF_MEMORY + return UIOHOOK_ERROR_OUT_OF_MEMORY; } + int status = UIOHOOK_FAILURE; switch (event->type) { case EVENT_KEY_PRESSED: case EVENT_KEY_RELEASED: @@ -236,13 +234,16 @@ UIOHOOK_API void hook_post_event(uiohook_event * const event) { default: logger(LOG_LEVEL_DEBUG, "%s [%u]: Ignoring post event: %#X.\n", __FUNCTION__, __LINE__, event->type); - + status = UIOHOOK_FAILURE; } - if (status != UIOHOOK_FAILURE && !SendInput(1, input, sizeof(INPUT))) { + if (status == UIOHOOK_SUCCESS && !SendInput(1, input, sizeof(INPUT))) { logger(LOG_LEVEL_ERROR, "%s [%u]: SendInput() failed! (%#lX)\n", __FUNCTION__, __LINE__, (unsigned long) GetLastError()); + status = UIOHOOK_FAILURE; } free(input); + + return status; } diff --git a/src/x11/post_event.c b/src/x11/post_event.c index e060986a..43b2fbee 100644 --- a/src/x11/post_event.c +++ b/src/x11/post_event.c @@ -159,19 +159,19 @@ static int post_mouse_button_event(uiohook_event * const event) { .send_event = False, .display = helper_disp, - .window = None, /* “event” window it is reported relative to */ - .root = None, /* root window that the event occurred on */ - .subwindow = XDefaultRootWindow(helper_disp), /* child window */ + .window = None, /* “event” window it is reported relative to */ + .root = None, /* root window that the event occurred on */ + .subwindow = XDefaultRootWindow(helper_disp), /* child window */ .time = CurrentTime, - .x = event->data.mouse.x, /* pointer x, y coordinates in event window */ + .x = event->data.mouse.x, /* pointer x, y coordinates in event window */ .y = event->data.mouse.y, - .x_root = 0, /* coordinates relative to root */ + .x_root = 0, /* coordinates relative to root */ .y_root = 0, - .state = 0x00, /* key or button mask */ + .state = 0x00, /* key or button mask */ .same_screen = True }; @@ -202,6 +202,7 @@ static int post_mouse_button_event(uiohook_event * const event) { } #endif + int status = UIOHOOK_FAILURE; switch (event->type) { case EVENT_MOUSE_PRESSED: #ifdef USE_XTEST @@ -211,7 +212,9 @@ static int post_mouse_button_event(uiohook_event * const event) { return UIOHOOK_FAILURE; } - XTestFakeButtonEvent(helper_disp, event->data.mouse.button, True, 0); + if (XTestFakeButtonEvent(helper_disp, event->data.mouse.button, True, 0) != 0) { + status = UIOHOOK_SUCCESS; + } #else if (event->data.mouse.button == MOUSE_BUTTON1) { current_modifier_mask |= Button1MotionMask; @@ -232,7 +235,9 @@ static int post_mouse_button_event(uiohook_event * const event) { btn_event.type = ButtonPress; btn_event.button = event->data.mouse.button; btn_event.state = current_modifier_mask; - XSendEvent(helper_disp, btn_event.window, False, ButtonPressMask, (XEvent *) &btn_event); + if (XSendEvent(helper_disp, btn_event.window, False, ButtonPressMask, (XEvent *) &btn_event) != 0) { + status = UIOHOOK_SUCCESS; + } #endif break; @@ -244,7 +249,9 @@ static int post_mouse_button_event(uiohook_event * const event) { return UIOHOOK_FAILURE; } - XTestFakeButtonEvent(helper_disp, event->data.mouse.button, False, 0); + if (XTestFakeButtonEvent(helper_disp, event->data.mouse.button, False, 0) != 0) { + status = UIOHOOK_SUCCESS; + } #else if (event->data.mouse.button == MOUSE_BUTTON1) { current_modifier_mask &= ~Button1MotionMask; @@ -265,38 +272,42 @@ static int post_mouse_button_event(uiohook_event * const event) { btn_event.type = ButtonRelease; btn_event.button = event->data.mouse.button; btn_event.state = current_modifier_mask; - XSendEvent(helper_disp, btn_event.window, False, ButtonReleaseMask, (XEvent *) &btn_event); + if (XSendEvent(helper_disp, btn_event.window, False, ButtonReleaseMask, (XEvent *) &btn_event) != 0) { + status = UIOHOOK_SUCCESS; + } #endif break; default: logger(LOG_LEVEL_DEBUG, "%s [%u]: Invalid mouse button event: %#X.\n", __FUNCTION__, __LINE__, event->type); - return UIOHOOK_FAILURE; + status = UIOHOOK_FAILURE; } - return UIOHOOK_SUCCESS; + return status; } static int post_mouse_wheel_event(uiohook_event * const event) { + int status = UIOHOOK_FAILURE; + XButtonEvent btn_event = { .serial = 0, .send_event = False, .display = helper_disp, - .window = None, /* “event” window it is reported relative to */ - .root = None, /* root window that the event occurred on */ - .subwindow = XDefaultRootWindow(helper_disp), /* child window */ + .window = None, /* “event” window it is reported relative to */ + .root = None, /* root window that the event occurred on */ + .subwindow = XDefaultRootWindow(helper_disp), /* child window */ .time = CurrentTime, - .x = event->data.wheel.x, /* pointer x, y coordinates in event window */ + .x = event->data.wheel.x, /* pointer x, y coordinates in event window */ .y = event->data.wheel.y, - .x_root = 0, /* coordinates relative to root */ + .x_root = 0, /* coordinates relative to root */ .y_root = 0, - .state = 0x00, /* key or button mask */ + .state = 0x00, /* key or button mask */ .same_screen = True }; @@ -324,29 +335,43 @@ static int post_mouse_wheel_event(uiohook_event * const event) { unsigned int button = button_map_lookup(event->data.wheel.rotation < 0 ? WheelUp : WheelDown); #ifdef USE_XTEST - XTestFakeButtonEvent(helper_disp, button, True, 0); + if (XTestFakeButtonEvent(helper_disp, button, True, 0) != 0) { + status = UIOHOOK_SUCCESS; + } #else btn_event.type = ButtonPress; btn_event.button = button; btn_event.state = current_modifier_mask; - XSendEvent(helper_disp, btn_event.window, False, ButtonPressMask, (XEvent *) &btn_event); + if (XSendEvent(helper_disp, btn_event.window, False, ButtonPressMask, (XEvent *) &btn_event) != 0) { + status = UIOHOOK_SUCCESS; + } #endif - #ifdef USE_XTEST - XTestFakeButtonEvent(helper_disp, button, False, 0); - #else - btn_event.type = ButtonRelease; - btn_event.button = button; - btn_event.state = current_modifier_mask; - XSendEvent(helper_disp, btn_event.window, False, ButtonReleaseMask, (XEvent *) &btn_event); - #endif + if (status == UIOHOOK_SUCCESS) { + #ifdef USE_XTEST + if (XTestFakeButtonEvent(helper_disp, button, False, 0) == 0) { + status = UIOHOOK_FAILURE; + } + #else + btn_event.type = ButtonRelease; + btn_event.button = button; + btn_event.state = current_modifier_mask; + if (XSendEvent(helper_disp, btn_event.window, False, ButtonReleaseMask, (XEvent *) &btn_event) == 0) { + status = UIOHOOK_FAILURE; + } + #endif + } return UIOHOOK_SUCCESS; } -static void post_mouse_motion_event(uiohook_event * const event) { +static int post_mouse_motion_event(uiohook_event * const event) { + int status = UIOHOOK_FAILURE; + #ifdef USE_XTEST - XTestFakeMotionEvent(helper_disp, -1, event->data.mouse.x, event->data.mouse.y, 0); + if (XTestFakeMotionEvent(helper_disp, -1, event->data.mouse.x, event->data.mouse.y, 0) != 0) { + status = UIOHOOK_SUCCESS; + } #else XMotionEvent mov_event = { .type = MotionNotify, @@ -354,19 +379,19 @@ static void post_mouse_motion_event(uiohook_event * const event) { .send_event = False, .display = helper_disp, - .window = None, /* “event” window it is reported relative to */ - .root = XDefaultRootWindow(helper_disp), /* root window that the event occurred on */ - .subwindow = None, /* child window */ + .window = None, /* “event” window it is reported relative to */ + .root = XDefaultRootWindow(helper_disp), /* root window that the event occurred on */ + .subwindow = None, /* child window */ .time = CurrentTime, - .x = event->data.mouse.x, /* pointer x, y coordinates in event window */ + .x = event->data.mouse.x, /* pointer x, y coordinates in event window */ .y = event->data.mouse.y, - .x_root = event->data.mouse.x, /* coordinates relative to root */ + .x_root = event->data.mouse.x, /* coordinates relative to root */ .y_root = event->data.mouse.x, - .state = current_modifier_mask|MotionNotify, /* key or button mask */ + .state = current_modifier_mask | MotionNotify, /* key or button mask */ .is_hint = NotifyNormal, .same_screen = True @@ -375,38 +400,43 @@ static void post_mouse_motion_event(uiohook_event * const event) { int revert; XGetInputFocus(helper_disp, &(mov_event.window), &revert); - XSendEvent(helper_disp, mov_event.window, False, mov_event.state, (XEvent *) &mov_event); + if (XSendEvent(helper_disp, mov_event.window, False, mov_event.state, (XEvent *) &mov_event) != 0) { + status = UIOHOOK_SUCCESS; + } #endif + + return status; } // TODO This should return a status code, UIOHOOK_SUCCESS or otherwise. -UIOHOOK_API void hook_post_event(uiohook_event * const event) { +UIOHOOK_API int hook_post_event(uiohook_event * const event) { if (helper_disp == NULL) { logger(LOG_LEVEL_ERROR, "%s [%u]: XDisplay helper_disp is unavailable!\n", __FUNCTION__, __LINE__); - return; // UIOHOOK_ERROR_X_OPEN_DISPLAY + return UIOHOOK_ERROR_X_OPEN_DISPLAY; } XLockDisplay(helper_disp); + int status = UIOHOOK_FAILURE; switch (event->type) { case EVENT_KEY_PRESSED: case EVENT_KEY_RELEASED: - post_key_event(event); + status = post_key_event(event); break; case EVENT_MOUSE_PRESSED: case EVENT_MOUSE_RELEASED: - post_mouse_button_event(event); + status = post_mouse_button_event(event); break; case EVENT_MOUSE_WHEEL: - post_mouse_wheel_event(event); + status = post_mouse_wheel_event(event); break; case EVENT_MOUSE_MOVED: case EVENT_MOUSE_DRAGGED: - post_mouse_motion_event(event); + status = post_mouse_motion_event(event); break; case EVENT_KEY_TYPED: @@ -418,10 +448,12 @@ UIOHOOK_API void hook_post_event(uiohook_event * const event) { default: logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n", __FUNCTION__, __LINE__, event->type); - break; + status = UIOHOOK_FAILURE; } // Don't forget to flush! XSync(helper_disp, True); XUnlockDisplay(helper_disp); + + return status; } From f7dd9078c89658192e6104bfcfe3923b2f44256d Mon Sep 17 00:00:00 2001 From: Alexander Barker Date: Sat, 19 Mar 2022 20:44:03 -0700 Subject: [PATCH 08/12] Change USE_OBJC flag to USE_APPKIT. Fixes #133 --- CMakeLists.txt | 12 ++---------- README.md | 2 +- src/darwin/input_hook.c | 18 +++++++++--------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a880277a..45a65ca0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,16 +249,8 @@ elseif(APPLE) target_link_libraries(uiohook "${IOKIT}") endif() - # FIXME Change USE_OBJC flag to USE_APPKIT - #option(USE_APPKIT "AppKit framework (default: ON)" ON) - option(USE_OBJC "Objective-C API (default: ON)" ON) - if(USE_OBJC) - # FIXME Drop USE_OBJC as it is included in AppKit - find_library(OBJC objc REQUIRED) - add_compile_definitions(USE_OBJC) - target_include_directories(uiohook PRIVATE "${OBJC}") - target_link_libraries(uiohook "${OBJC}") - + option(USE_APPKIT "AppKit framework (default: ON)" ON) + if(USE_APPKIT) find_library(APPKIT AppKit REQUIRED) add_compile_definitions(USE_APPKIT) target_include_directories(uiohook PRIVATE "${APPKIT}") diff --git a/README.md b/README.md index 46f38077..601895d7 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ $ cmake --build . --parallel 2 --target install | | USE_EPOCH_TIME:BOOL | unix epch event times | OFF | | __OSX__ | USE_APPLICATION_SERVICES:BOOL | framework | ON | | | USE_IOKIT:BOOL | framework | ON | -| | USE_OBJC:BOOL | obj-c api | ON | +| | USE_APPKIT:BOOL | obj-c api | ON | | __Win32__ | | | | | __Linux__ | USE_EVDEV:BOOL | generic input driver | ON | | __*nix__ | USE_XF86MISC:BOOL | xfree86-misc extension | OFF | diff --git a/src/darwin/input_hook.c b/src/darwin/input_hook.c index 33934d52..d2a4a54f 100644 --- a/src/darwin/input_hook.c +++ b/src/darwin/input_hook.c @@ -19,7 +19,7 @@ #include #include -#ifdef USE_OBJC +#ifdef USE_APPKIT #include #include #endif @@ -44,7 +44,7 @@ typedef struct _event_runloop_info { CFRunLoopObserverRef observer; } event_runloop_info; -#ifdef USE_OBJC +#ifdef USE_APPKIT static id auto_release_pool; typedef struct { @@ -504,7 +504,7 @@ static inline void process_modifier_changed(uint64_t timestamp, CGEventRef event } } -#ifdef USE_OBJC +#ifdef USE_APPKIT static void obcj_message(void *info) { TISEventMessage *data = (TISEventMessage *) info; @@ -527,7 +527,7 @@ static inline void process_system_key(uint64_t timestamp, CGEventRef event_ref) UInt32 subtype = 0; UInt32 data1 = 0; - #ifdef USE_OBJC + #ifdef USE_APPKIT bool is_runloop_main = CFEqual(event_loop, CFRunLoopGetMain()); tis_event_message->event = event_ref; tis_event_message->subtype = 0; @@ -580,7 +580,7 @@ static inline void process_system_key(uint64_t timestamp, CGEventRef event_ref) free(buffer); CFRelease(data_ref); - #ifdef USE_OBJC + #ifdef USE_APPKIT } #endif @@ -1323,7 +1323,7 @@ UIOHOOK_API int hook_run() { return UIOHOOK_ERROR_OUT_OF_MEMORY; } - #ifdef USE_OBJC + #ifdef USE_APPKIT tis_event_message = (TISEventMessage *) calloc(1, sizeof(TISEventMessage)); if (tis_event_message == NULL) { logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for TIS event structure!\n", @@ -1384,7 +1384,7 @@ UIOHOOK_API int hook_run() { } } - #ifdef USE_OBJC + #ifdef USE_APPKIT // Contributed by Alex // Create a garbage collector to handle Cocoa events correctly. Class NSAutoreleasePool_class = (Class) objc_getClass("NSAutoreleasePool"); @@ -1399,7 +1399,7 @@ UIOHOOK_API int hook_run() { CFRunLoopRun(); - #ifdef USE_OBJC + #ifdef USE_APPKIT // Contributed by Alex eventWithoutCGEvent(auto_release_pool, sel_registerName("release")); #endif @@ -1414,7 +1414,7 @@ UIOHOOK_API int hook_run() { pthread_mutex_unlock(&main_runloop_mutex); #endif - #ifdef USE_OBJC + #ifdef USE_APPKIT free(tis_event_message); #endif free(tis_keycode_message); From de3f68346781b1f3347b44ce8e370a5f0a603f89 Mon Sep 17 00:00:00 2001 From: Alexander Barker Date: Sun, 20 Mar 2022 01:58:08 -0700 Subject: [PATCH 09/12] Require XTest extension and remove support for XSendEvent. Fixes #123 --- CMakeLists.txt | 6 -- README.md | 1 - src/x11/post_event.c | 247 +------------------------------------------ 3 files changed, 2 insertions(+), 252 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 45a65ca0..a13390e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,12 +210,6 @@ if(UNIX AND NOT APPLE) add_compile_definitions(uiohook PRIVATE USE_XRECORD_ASYNC) endif() - option(USE_XTEST "XTest API (default: ON)" ON) - if(USE_XTEST) - # XTest API is provided by Xtst - add_compile_definitions(uiohook PRIVATE USE_XTEST) - endif() - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") option(USE_EVDEV "Generic Linux input driver (default: ON)" ON) if(USE_EVDEV) diff --git a/README.md b/README.md index 601895d7..12d578f9 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,6 @@ $ cmake --build . --parallel 2 --target install | | USE_XRANDR:BOOL | xrandt extension | OFF | | | USE_XRECORD_ASYNC:BOOL | xrecord async api | OFF | | | USE_XT:BOOL | x toolkit extension | ON | -| | USE_XTEST:BOOL | xtest extension | ON | ## Usage * [Hook Demo](demo/demo_hook.c) diff --git a/src/x11/post_event.c b/src/x11/post_event.c index 43b2fbee..92aa2fa0 100644 --- a/src/x11/post_event.c +++ b/src/x11/post_event.c @@ -22,16 +22,11 @@ #include #include #include -#ifdef USE_XTEST #include -#endif #include "input_helper.h" #include "logger.h" -#ifndef USE_XTEST -static long current_modifier_mask = NoEventMask; -#endif static int post_key_event(uiohook_event * const event) { KeyCode keycode = scancode_to_keycode(event->data.keyboard.keycode); @@ -41,114 +36,22 @@ static int post_key_event(uiohook_event * const event) { return UIOHOOK_FAILURE; } - #ifdef USE_XTEST Bool is_pressed; - #else - long event_mask; - - XKeyEvent key_event = { - .serial = 0x00, - .time = CurrentTime, - .same_screen = True, - .send_event = False, - .display = helper_disp, - - .root = XDefaultRootWindow(helper_disp), - .window = None, - .subwindow = None, - - .x_root = 0, - .y_root = 0, - .x = 0, - .y = 0, - - .state = current_modifier_mask, - .keycode = keycode - }; - - int revert; - XGetInputFocus(helper_disp, &(key_event.window), &revert); - #endif - if (event->type == EVENT_KEY_PRESSED) { - #ifdef USE_XTEST is_pressed = True; - #else - key_event.type = KeyPress; - event_mask = KeyPressMask; - - switch (event->data.keyboard.keycode) { - case VC_SHIFT_L: - case VC_SHIFT_R: - current_modifier_mask |= ShiftMask; - break; - - case VC_CONTROL_L: - case VC_CONTROL_R: - current_modifier_mask |= ControlMask; - break; - - case VC_META_L: - case VC_META_R: - current_modifier_mask |= Mod4Mask; - break; - - case VC_ALT_L: - case VC_ALT_R: - current_modifier_mask |= Mod1Mask; - break; - } - #endif - } else if (event->type == EVENT_KEY_RELEASED) { - #ifdef USE_XTEST is_pressed = False; - #else - key_event.type = KeyRelease; - event_mask = KeyReleaseMask; - - switch (event->data.keyboard.keycode) { - case VC_SHIFT_L: - case VC_SHIFT_R: - current_modifier_mask &= ~ShiftMask; - break; - - case VC_CONTROL_L: - case VC_CONTROL_R: - current_modifier_mask &= ~ControlMask; - break; - - case VC_META_L: - case VC_META_R: - current_modifier_mask &= ~Mod4Mask; - break; - - case VC_ALT_L: - case VC_ALT_R: - current_modifier_mask &= ~Mod1Mask; - break; - } - #endif } else { logger(LOG_LEVEL_DEBUG, "%s [%u]: Invalid event for keyboard post event: %#X.\n", __FUNCTION__, __LINE__, event->type); return UIOHOOK_FAILURE; } - #ifdef USE_XTEST if (XTestFakeKeyEvent(helper_disp, keycode, is_pressed, 0) != Success) { logger(LOG_LEVEL_ERROR, "%s [%u]: XTestFakeKeyEvent() failed!\n", __FUNCTION__, __LINE__, event->type); return UIOHOOK_FAILURE; } - #else - XSelectInput(helper_disp, key_event.window, KeyPressMask | KeyReleaseMask); - if (XSendEvent(helper_disp, key_event.window, False, event_mask, (XEvent *) &key_event) == 0) { - logger(LOG_LEVEL_ERROR, "%s [%u]: XSendEvent() failed!\n", - __FUNCTION__, __LINE__, event->type); - return UIOHOOK_FAILURE; - } - #endif return UIOHOOK_SUCCESS; } @@ -176,36 +79,11 @@ static int post_mouse_button_event(uiohook_event * const event) { }; // Move the pointer to the specified position. - #ifdef USE_XTEST XTestFakeMotionEvent(btn_event.display, -1, btn_event.x, btn_event.y, 0); - #else - XWarpPointer(btn_event.display, None, btn_event.subwindow, 0, 0, 0, 0, btn_event.x, btn_event.y); - XFlush(btn_event.display); - #endif - - #ifndef USE_XTEST - // FIXME This is still not working correctly, clicking on other windows does not yield focus. - while (btn_event.subwindow != None) - { - btn_event.window = btn_event.subwindow; - XQueryPointer ( - btn_event.display, - btn_event.window, - &btn_event.root, - &btn_event.subwindow, - &btn_event.x_root, - &btn_event.y_root, - &btn_event.x, - &btn_event.y, - &btn_event.state - ); - } - #endif int status = UIOHOOK_FAILURE; switch (event->type) { case EVENT_MOUSE_PRESSED: - #ifdef USE_XTEST if (event->data.mouse.button < MOUSE_BUTTON1 || event->data.mouse.button > MOUSE_BUTTON5) { logger(LOG_LEVEL_WARN, "%s [%u]: Invalid button specified for mouse pressed event! (%u)\n", __FUNCTION__, __LINE__, event->data.mouse.button); @@ -215,34 +93,9 @@ static int post_mouse_button_event(uiohook_event * const event) { if (XTestFakeButtonEvent(helper_disp, event->data.mouse.button, True, 0) != 0) { status = UIOHOOK_SUCCESS; } - #else - if (event->data.mouse.button == MOUSE_BUTTON1) { - current_modifier_mask |= Button1MotionMask; - } else if (event->data.mouse.button == MOUSE_BUTTON2) { - current_modifier_mask |= Button2MotionMask; - } else if (event->data.mouse.button == MOUSE_BUTTON3) { - current_modifier_mask |= Button3MotionMask; - } else if (event->data.mouse.button == MOUSE_BUTTON4) { - current_modifier_mask |= Button4MotionMask; - } else if (event->data.mouse.button == MOUSE_BUTTON5) { - current_modifier_mask |= Button5MotionMask; - } else { - logger(LOG_LEVEL_WARN, "%s [%u]: Invalid button specified for mouse pressed event! (%u)\n", - __FUNCTION__, __LINE__, event->data.mouse.button); - return UIOHOOK_FAILURE; - } - - btn_event.type = ButtonPress; - btn_event.button = event->data.mouse.button; - btn_event.state = current_modifier_mask; - if (XSendEvent(helper_disp, btn_event.window, False, ButtonPressMask, (XEvent *) &btn_event) != 0) { - status = UIOHOOK_SUCCESS; - } - #endif break; case EVENT_MOUSE_RELEASED: - #ifdef USE_XTEST if (event->data.mouse.button < MOUSE_BUTTON1 || event->data.mouse.button > MOUSE_BUTTON5) { logger(LOG_LEVEL_WARN, "%s [%u]: Invalid button specified for mouse released event! (%u)\n", __FUNCTION__, __LINE__, event->data.mouse.button); @@ -252,30 +105,6 @@ static int post_mouse_button_event(uiohook_event * const event) { if (XTestFakeButtonEvent(helper_disp, event->data.mouse.button, False, 0) != 0) { status = UIOHOOK_SUCCESS; } - #else - if (event->data.mouse.button == MOUSE_BUTTON1) { - current_modifier_mask &= ~Button1MotionMask; - } else if (event->data.mouse.button == MOUSE_BUTTON2) { - current_modifier_mask &= ~Button2MotionMask; - } else if (event->data.mouse.button == MOUSE_BUTTON3) { - current_modifier_mask &= ~Button3MotionMask; - } else if (event->data.mouse.button == MOUSE_BUTTON4) { - current_modifier_mask &= ~Button4MotionMask; - } else if (event->data.mouse.button == MOUSE_BUTTON5) { - current_modifier_mask &= ~Button5MotionMask; - } else { - logger(LOG_LEVEL_WARN, "%s [%u]: Invalid button specified for mouse released event! (%u)\n", - __FUNCTION__, __LINE__, event->data.mouse.button); - return UIOHOOK_FAILURE; - } - - btn_event.type = ButtonRelease; - btn_event.button = event->data.mouse.button; - btn_event.state = current_modifier_mask; - if (XSendEvent(helper_disp, btn_event.window, False, ButtonReleaseMask, (XEvent *) &btn_event) != 0) { - status = UIOHOOK_SUCCESS; - } - #endif break; default: @@ -311,55 +140,16 @@ static int post_mouse_wheel_event(uiohook_event * const event) { .same_screen = True }; - #ifndef USE_XTEST - // FIXME This is still not working correctly, clicking on other windows does not yield focus. - while (btn_event.subwindow != None) - { - btn_event.window = btn_event.subwindow; - XQueryPointer ( - btn_event.display, - btn_event.window, - &btn_event.root, - &btn_event.subwindow, - &btn_event.x_root, - &btn_event.y_root, - &btn_event.x, - &btn_event.y, - &btn_event.state - ); - } - #endif - // Wheel events should be the same as click events on X11. // type, amount and rotation unsigned int button = button_map_lookup(event->data.wheel.rotation < 0 ? WheelUp : WheelDown); - #ifdef USE_XTEST if (XTestFakeButtonEvent(helper_disp, button, True, 0) != 0) { status = UIOHOOK_SUCCESS; } - #else - btn_event.type = ButtonPress; - btn_event.button = button; - btn_event.state = current_modifier_mask; - if (XSendEvent(helper_disp, btn_event.window, False, ButtonPressMask, (XEvent *) &btn_event) != 0) { - status = UIOHOOK_SUCCESS; - } - #endif - if (status == UIOHOOK_SUCCESS) { - #ifdef USE_XTEST - if (XTestFakeButtonEvent(helper_disp, button, False, 0) == 0) { - status = UIOHOOK_FAILURE; - } - #else - btn_event.type = ButtonRelease; - btn_event.button = button; - btn_event.state = current_modifier_mask; - if (XSendEvent(helper_disp, btn_event.window, False, ButtonReleaseMask, (XEvent *) &btn_event) == 0) { - status = UIOHOOK_FAILURE; - } - #endif + if (status == UIOHOOK_SUCCESS && XTestFakeButtonEvent(helper_disp, button, False, 0) == 0) { + status = UIOHOOK_FAILURE; } return UIOHOOK_SUCCESS; @@ -368,42 +158,9 @@ static int post_mouse_wheel_event(uiohook_event * const event) { static int post_mouse_motion_event(uiohook_event * const event) { int status = UIOHOOK_FAILURE; - #ifdef USE_XTEST if (XTestFakeMotionEvent(helper_disp, -1, event->data.mouse.x, event->data.mouse.y, 0) != 0) { status = UIOHOOK_SUCCESS; } - #else - XMotionEvent mov_event = { - .type = MotionNotify, - .serial = 0, - .send_event = False, - .display = helper_disp, - - .window = None, /* “event” window it is reported relative to */ - .root = XDefaultRootWindow(helper_disp), /* root window that the event occurred on */ - .subwindow = None, /* child window */ - - .time = CurrentTime, - - .x = event->data.mouse.x, /* pointer x, y coordinates in event window */ - .y = event->data.mouse.y, - - .x_root = event->data.mouse.x, /* coordinates relative to root */ - .y_root = event->data.mouse.x, - - .state = current_modifier_mask | MotionNotify, /* key or button mask */ - - .is_hint = NotifyNormal, - .same_screen = True - }; - - int revert; - XGetInputFocus(helper_disp, &(mov_event.window), &revert); - - if (XSendEvent(helper_disp, mov_event.window, False, mov_event.state, (XEvent *) &mov_event) != 0) { - status = UIOHOOK_SUCCESS; - } - #endif return status; } From d54317b0a8408f1d22ba524b33c1e459ba47aa29 Mon Sep 17 00:00:00 2001 From: FaithBeam Date: Fri, 10 Feb 2023 18:20:42 -0500 Subject: [PATCH 10/12] Fix multimonitor/multi dpi mouse movement for windows --- src/windows/post_event.c | 88 ++++++++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 17 deletions(-) diff --git a/src/windows/post_event.c b/src/windows/post_event.c index a6ddbe51..851e9b92 100644 --- a/src/windows/post_event.c +++ b/src/windows/post_event.c @@ -64,11 +64,58 @@ static const uint16_t extend_key_table[10] = { VK_DELETE }; +typedef struct { + LONG x; + LONG y; +} normalized_coordinate; + +typedef struct { + LONG left; + LONG top; +} largest_negative_coordinates; + +static LONG get_absolute_coordinate(LONG coordinate, int screen_size) { + return MulDiv((int) coordinate, MAX_WINDOWS_COORD_VALUE, screen_size); +} + +static normalized_coordinate normalize_coordinates(LONG x, LONG y, int screen_width, int screen_height, largest_negative_coordinates lnc) { + x += abs(lnc.left); + y += abs(lnc.top); -static LONG convert_to_relative_position(int coordinate, int screen_size) { - // See https://stackoverflow.com/a/4555214 and its comments - int offset = (coordinate > 0 ? 1 : -1); // Negative coordinates appear when using multiple monitors - return ((coordinate * MAX_WINDOWS_COORD_VALUE) / screen_size) + offset; + // Prevent clicking 0 coordinates to prevent monitor flicker + if (x == 0) + { + x++; + } + if (y == 0) + { + y++; + } + + normalized_coordinate nc = { + .x = get_absolute_coordinate(x, screen_width), + .y = get_absolute_coordinate(y, screen_height) + }; + + return nc; +} + +static BOOL CALLBACK get_largest_negative_coordinates_monitor_proc(HMONITOR h_monitor, HDC hdc, LPRECT lp_rect, LPARAM dwData) { + MONITORINFO MonitorInfo = {0}; + MonitorInfo.cbSize = sizeof(MonitorInfo); + largest_negative_coordinates* lnc = (largest_negative_coordinates*)dwData; + if (GetMonitorInfo(h_monitor, &MonitorInfo)) + { + if (MonitorInfo.rcMonitor.left < lnc->left) + { + lnc->left = MonitorInfo.rcMonitor.left; + } + if (MonitorInfo.rcMonitor.top < lnc->top) + { + lnc->top = MonitorInfo.rcMonitor.top; + } + } + return TRUE; } static int map_keyboard_event(uiohook_event * const event, INPUT * const input) { @@ -110,17 +157,24 @@ static int map_keyboard_event(uiohook_event * const event, INPUT * const input) } static int map_mouse_event(uiohook_event * const event, INPUT * const input) { - // FIXME implement multiple monitor support - uint16_t screen_width = GetSystemMetrics(SM_CXSCREEN); - uint16_t screen_height = GetSystemMetrics(SM_CYSCREEN); + uint16_t screen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN); + uint16_t screen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN); input->type = INPUT_MOUSE; input->mi.mouseData = 0; input->mi.dwExtraInfo = 0; input->mi.time = 0; // GetSystemTime(); - input->mi.dx = convert_to_relative_position(event->data.mouse.x, screen_width); - input->mi.dy = convert_to_relative_position(event->data.mouse.y, screen_height); + largest_negative_coordinates lnc = { + .left = 0, + .top = 0 + }; + EnumDisplayMonitors(NULL, NULL, get_largest_negative_coordinates_monitor_proc, (LPARAM) &lnc); + + normalized_coordinate nc = normalize_coordinates(event->data.mouse.x, event->data.mouse.y, screen_width, screen_height, lnc); + + input->mi.dx = nc.x; + input->mi.dy = nc.y; switch (event->type) { case EVENT_MOUSE_PRESSED: @@ -129,13 +183,13 @@ static int map_mouse_event(uiohook_event * const event, INPUT * const input) { __FUNCTION__, __LINE__); return UIOHOOK_FAILURE; } else if (event->data.mouse.button == MOUSE_BUTTON1) { - input->mi.dwFlags = MOUSEEVENTF_LEFTDOWN; + input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_LEFTDOWN; } else if (event->data.mouse.button == MOUSE_BUTTON2) { - input->mi.dwFlags = MOUSEEVENTF_RIGHTDOWN; + input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_RIGHTDOWN; } else if (event->data.mouse.button == MOUSE_BUTTON3) { - input->mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN; + input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_MIDDLEDOWN; } else { - input->mi.dwFlags = MOUSEEVENTF_XDOWN; + input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_XDOWN; if (event->data.mouse.button == MOUSE_BUTTON4) { input->mi.mouseData = XBUTTON1; } else if (event->data.mouse.button == MOUSE_BUTTON5) { @@ -158,13 +212,13 @@ static int map_mouse_event(uiohook_event * const event, INPUT * const input) { __FUNCTION__, __LINE__); return UIOHOOK_FAILURE; } else if (event->data.mouse.button == MOUSE_BUTTON1) { - input->mi.dwFlags = MOUSEEVENTF_LEFTUP; + input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_LEFTUP; } else if (event->data.mouse.button == MOUSE_BUTTON2) { - input->mi.dwFlags = MOUSEEVENTF_RIGHTUP; + input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_RIGHTUP; } else if (event->data.mouse.button == MOUSE_BUTTON3) { - input->mi.dwFlags = MOUSEEVENTF_MIDDLEUP; + input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_MIDDLEUP; } else { - input->mi.dwFlags = MOUSEEVENTF_XUP; + input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_XUP; if (event->data.mouse.button == MOUSE_BUTTON4) { input->mi.mouseData = XBUTTON1; } else if (event->data.mouse.button == MOUSE_BUTTON5) { From 060b3c148f11e82db0afef7e63102b230398c93b Mon Sep 17 00:00:00 2001 From: FaithBeam Date: Sun, 19 Feb 2023 10:06:01 -0500 Subject: [PATCH 11/12] Only enumerate monitors on WM_DISPLAYCHANGE --- CMakeLists.txt | 25 ++++++++++---- include/uiohook.h | 1 + src/windows/input_hook.c | 65 ++++++++++++++++++++++++++++++++++++ src/windows/monitor_helper.c | 37 ++++++++++++++++++++ src/windows/monitor_helper.h | 10 ++++++ src/windows/post_event.c | 32 ++---------------- 6 files changed, 134 insertions(+), 36 deletions(-) create mode 100644 src/windows/monitor_helper.c create mode 100644 src/windows/monitor_helper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a13390e0..73aa913f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,13 +28,24 @@ else() set(UIOHOOK_SOURCE_DIR "x11") endif() -add_library(uiohook - "src/logger.c" - "src/${UIOHOOK_SOURCE_DIR}/input_helper.c" - "src/${UIOHOOK_SOURCE_DIR}/input_hook.c" - "src/${UIOHOOK_SOURCE_DIR}/post_event.c" - "src/${UIOHOOK_SOURCE_DIR}/system_properties.c" -) +if (WIN32 OR WIN64) + add_library(uiohook + "src/logger.c" + "src/${UIOHOOK_SOURCE_DIR}/input_helper.c" + "src/${UIOHOOK_SOURCE_DIR}/input_hook.c" + "src/${UIOHOOK_SOURCE_DIR}/post_event.c" + "src/${UIOHOOK_SOURCE_DIR}/system_properties.c" + "src/${UIOHOOK_SOURCE_DIR}/monitor_helper.c" + ) +else() + add_library(uiohook + "src/logger.c" + "src/${UIOHOOK_SOURCE_DIR}/input_helper.c" + "src/${UIOHOOK_SOURCE_DIR}/input_hook.c" + "src/${UIOHOOK_SOURCE_DIR}/post_event.c" + "src/${UIOHOOK_SOURCE_DIR}/system_properties.c" + ) +endif () set_target_properties(uiohook PROPERTIES C_STANDARD 99 diff --git a/include/uiohook.h b/include/uiohook.h index 0af32d55..909acb9f 100644 --- a/include/uiohook.h +++ b/include/uiohook.h @@ -41,6 +41,7 @@ // Windows specific errors. #define UIOHOOK_ERROR_SET_WINDOWS_HOOK_EX 0x30 #define UIOHOOK_ERROR_GET_MODULE_HANDLE 0x31 +#define UIOHOOK_ERROR_CREATE_INVISIBLE_WINDOW 0x32 // Darwin specific errors. #define UIOHOOK_ERROR_AXAPI_DISABLED 0x40 diff --git a/src/windows/input_hook.c b/src/windows/input_hook.c index c05f2e3f..8e8725aa 100644 --- a/src/windows/input_hook.c +++ b/src/windows/input_hook.c @@ -22,11 +22,13 @@ #include "input_helper.h" #include "logger.h" +#include "monitor_helper.h" // Thread and hook handles. static DWORD hook_thread_id = 0; static HHOOK keyboard_event_hhook = NULL, mouse_event_hhook = NULL; static HWINEVENTHOOK win_event_hhook = NULL; +static HWND hWnd = NULL; // The handle to the DLL module pulled in DllMain on DLL_PROCESS_ATTACH. extern HINSTANCE hInst; @@ -721,6 +723,60 @@ void CALLBACK win_hook_event_proc(HWINEVENTHOOK hook, DWORD event, HWND hWnd, LO } } +static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_DISPLAYCHANGE: + enumerate_displays(); + break; + default: + return DefWindowProc(hwnd, message, wParam, lParam); + } + return 0; +} + +static int create_invisible_window() +{ + WNDCLASSEX wcex = { 0 }; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = WS_EX_NOACTIVATE; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInst; + wcex.hIcon = NULL; + wcex.hCursor = NULL; + wcex.hbrBackground = NULL; + wcex.lpszMenuName = NULL; + wcex.lpszClassName = "Empty"; + wcex.hIconSm = NULL; + + if (!RegisterClassEx(&wcex)) return 0; + + hWnd = CreateWindowEx( + WS_EX_NOACTIVATE, + "Empty", + "Empty", + WS_DISABLED, + 0, + 0, + 1, + 1, + NULL, + NULL, + hInst, + NULL + ); + if (!hWnd) return 0; + + ShowWindow(hWnd, SW_HIDE); + + return 1; +} UIOHOOK_API int hook_run() { int status = UIOHOOK_FAILURE; @@ -746,6 +802,15 @@ UIOHOOK_API int hook_run() { } } + // Create invisible window to receive monitor change events + if(!create_invisible_window() || hWnd == NULL) + { + logger(LOG_LEVEL_ERROR, "%s [%u]: Create invisible window failed! (%#lX)\n", + __FUNCTION__, __LINE__, (unsigned long) GetLastError()); + + status = UIOHOOK_ERROR_CREATE_INVISIBLE_WINDOW; + } + // Create the native hooks. keyboard_event_hhook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_hook_event_proc, hInst, 0); mouse_event_hhook = SetWindowsHookEx(WH_MOUSE_LL, mouse_hook_event_proc, hInst, 0); diff --git a/src/windows/monitor_helper.c b/src/windows/monitor_helper.c new file mode 100644 index 00000000..e18486a2 --- /dev/null +++ b/src/windows/monitor_helper.c @@ -0,0 +1,37 @@ +#include "monitor_helper.h" + +static LONG left = 0; +static LONG top = 0; + +static BOOL CALLBACK enum_monitor_proc(HMONITOR h_monitor, HDC hdc, LPRECT lp_rect, LPARAM dwData) { + MONITORINFO MonitorInfo = {0}; + MonitorInfo.cbSize = sizeof(MonitorInfo); + if (GetMonitorInfo(h_monitor, &MonitorInfo)) { + if (MonitorInfo.rcMonitor.left < left) { + left = MonitorInfo.rcMonitor.left; + } + if (MonitorInfo.rcMonitor.top < top) { + top = MonitorInfo.rcMonitor.top; + } + } + return TRUE; +} + +void enumerate_displays() +{ + // Reset coordinates because if a negative monitor moves to positive space, + // it will still look like there is some monitor in negative space. + left = 0; + top = 0; + + EnumDisplayMonitors(NULL, NULL, enum_monitor_proc, 0); +} + +LARGESTNEGATIVECOORDINATES get_largest_negative_coordinates() +{ + LARGESTNEGATIVECOORDINATES lnc = { + .left = left, + .top = top + }; + return lnc; +} \ No newline at end of file diff --git a/src/windows/monitor_helper.h b/src/windows/monitor_helper.h new file mode 100644 index 00000000..7c6d01a4 --- /dev/null +++ b/src/windows/monitor_helper.h @@ -0,0 +1,10 @@ +#include + +typedef struct { + LONG left; + LONG top; +} LARGESTNEGATIVECOORDINATES; + +extern void enumerate_displays(); + +extern LARGESTNEGATIVECOORDINATES get_largest_negative_coordinates(); diff --git a/src/windows/post_event.c b/src/windows/post_event.c index 851e9b92..767b3b6d 100644 --- a/src/windows/post_event.c +++ b/src/windows/post_event.c @@ -22,6 +22,7 @@ #include "input_helper.h" #include "logger.h" +#include "monitor_helper.h" // Some buggy versions of MinGW and MSys do not include these constants in winuser.h. #ifndef MAPVK_VK_TO_VSC @@ -69,16 +70,11 @@ typedef struct { LONG y; } normalized_coordinate; -typedef struct { - LONG left; - LONG top; -} largest_negative_coordinates; - static LONG get_absolute_coordinate(LONG coordinate, int screen_size) { return MulDiv((int) coordinate, MAX_WINDOWS_COORD_VALUE, screen_size); } -static normalized_coordinate normalize_coordinates(LONG x, LONG y, int screen_width, int screen_height, largest_negative_coordinates lnc) { +static normalized_coordinate normalize_coordinates(LONG x, LONG y, int screen_width, int screen_height, LARGESTNEGATIVECOORDINATES lnc) { x += abs(lnc.left); y += abs(lnc.top); @@ -100,24 +96,6 @@ static normalized_coordinate normalize_coordinates(LONG x, LONG y, int screen_wi return nc; } -static BOOL CALLBACK get_largest_negative_coordinates_monitor_proc(HMONITOR h_monitor, HDC hdc, LPRECT lp_rect, LPARAM dwData) { - MONITORINFO MonitorInfo = {0}; - MonitorInfo.cbSize = sizeof(MonitorInfo); - largest_negative_coordinates* lnc = (largest_negative_coordinates*)dwData; - if (GetMonitorInfo(h_monitor, &MonitorInfo)) - { - if (MonitorInfo.rcMonitor.left < lnc->left) - { - lnc->left = MonitorInfo.rcMonitor.left; - } - if (MonitorInfo.rcMonitor.top < lnc->top) - { - lnc->top = MonitorInfo.rcMonitor.top; - } - } - return TRUE; -} - static int map_keyboard_event(uiohook_event * const event, INPUT * const input) { input->type = INPUT_KEYBOARD; // | KEYEVENTF_SCANCODE //input->ki.wScan = event->data.keyboard.rawcode; @@ -165,11 +143,7 @@ static int map_mouse_event(uiohook_event * const event, INPUT * const input) { input->mi.dwExtraInfo = 0; input->mi.time = 0; // GetSystemTime(); - largest_negative_coordinates lnc = { - .left = 0, - .top = 0 - }; - EnumDisplayMonitors(NULL, NULL, get_largest_negative_coordinates_monitor_proc, (LPARAM) &lnc); + LARGESTNEGATIVECOORDINATES lnc = get_largest_negative_coordinates(); normalized_coordinate nc = normalize_coordinates(event->data.mouse.x, event->data.mouse.y, screen_width, screen_height, lnc); From 0ef227c30e66f062067dd62b24471dda80626285 Mon Sep 17 00:00:00 2001 From: FaithBeam Date: Tue, 28 Feb 2023 17:10:51 -0500 Subject: [PATCH 12/12] Remove unnecessary mouse flags --- src/windows/post_event.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/windows/post_event.c b/src/windows/post_event.c index 767b3b6d..d00117bb 100644 --- a/src/windows/post_event.c +++ b/src/windows/post_event.c @@ -157,13 +157,13 @@ static int map_mouse_event(uiohook_event * const event, INPUT * const input) { __FUNCTION__, __LINE__); return UIOHOOK_FAILURE; } else if (event->data.mouse.button == MOUSE_BUTTON1) { - input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_LEFTDOWN; + input->mi.dwFlags = MOUSEEVENTF_LEFTDOWN; } else if (event->data.mouse.button == MOUSE_BUTTON2) { - input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_RIGHTDOWN; + input->mi.dwFlags = MOUSEEVENTF_RIGHTDOWN; } else if (event->data.mouse.button == MOUSE_BUTTON3) { - input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_MIDDLEDOWN; + input->mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN; } else { - input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_XDOWN; + input->mi.dwFlags = MOUSEEVENTF_XDOWN; if (event->data.mouse.button == MOUSE_BUTTON4) { input->mi.mouseData = XBUTTON1; } else if (event->data.mouse.button == MOUSE_BUTTON5) { @@ -186,13 +186,13 @@ static int map_mouse_event(uiohook_event * const event, INPUT * const input) { __FUNCTION__, __LINE__); return UIOHOOK_FAILURE; } else if (event->data.mouse.button == MOUSE_BUTTON1) { - input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_LEFTUP; + input->mi.dwFlags = MOUSEEVENTF_LEFTUP; } else if (event->data.mouse.button == MOUSE_BUTTON2) { - input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_RIGHTUP; + input->mi.dwFlags = MOUSEEVENTF_RIGHTUP; } else if (event->data.mouse.button == MOUSE_BUTTON3) { - input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_MIDDLEUP; + input->mi.dwFlags = MOUSEEVENTF_MIDDLEUP; } else { - input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_XUP; + input->mi.dwFlags = MOUSEEVENTF_XUP; if (event->data.mouse.button == MOUSE_BUTTON4) { input->mi.mouseData = XBUTTON1; } else if (event->data.mouse.button == MOUSE_BUTTON5) {