Update event

This commit is contained in:
vCaesar 2016-10-30 21:22:06 +08:00
parent 92c57da678
commit 852ece669f
20 changed files with 9883 additions and 2 deletions

View File

@ -0,0 +1,301 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "../base/os.h"
#if defined(IS_MACOSX)
#include "hook/darwin/input_helper_c.h"
#include "hook/darwin/input_hook_c.h"
#include "hook/darwin/post_event_c.h"
#include "hook/darwin/system_properties_c.h"
#elif defined(USE_X11)
//#define USE_XKBCOMMON 0
#include "hook/x11/input_helper_c.h"
#include "hook/x11/input_hook_c.h"
#include "hook/x11/post_event_c.h"
#include "hook/x11/system_properties_c.h"
#elif defined(IS_WINDOWS)
#include "hook/windows/input_helper_c.h"
#include "hook/windows/input_hook_c.h"
#include "hook/windows/post_event_c.h"
#include "hook/windows/system_properties_c.h"
#endif
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
// #include <uiohook.h>
#include "hook/uiohook.h"
int aStop();
int aEvent(char *aevent);
bool logger_proc(unsigned int level, const char *format, ...) {
bool status = false;
va_list args;
switch (level) {
#ifdef USE_DEBUG
case LOG_LEVEL_DEBUG:
case LOG_LEVEL_INFO:
va_start(args, format);
status = vfprintf(stdout, format, args) >= 0;
va_end(args);
break;
#endif
case LOG_LEVEL_WARN:
case LOG_LEVEL_ERROR:
va_start(args, format);
status = vfprintf(stderr, format, args) >= 0;
va_end(args);
break;
}
return status;
}
// NOTE: The following callback executes on the same thread that hook_run() is called
// from. This is important because hook_run() attaches to the operating systems
// event dispatcher and may delay event delivery to the target application.
// Furthermore, some operating systems may choose to disable your hook if it
// takes to long to process. If you need to do any extended processing, please
// do so by copying the event to your own queued dispatch thread.
struct _MEvent {
uint8_t id;
size_t mask;
uint16_t keychar;
// char *keychar;
size_t x;
uint8_t y;
uint8_t bytesPerPixel;
};
typedef struct _MEvent MEvent;
// typedef MMBitmap *MMBitmapRef;
char *cevent;
// uint16_t *cevent;
int cstatus=1;
MEvent mEvent;
void dispatch_proc(uiohook_event * const event) {
char buffer[256] = { 0 };
size_t length = snprintf(buffer, sizeof(buffer),
"id=%i,when=%" PRIu64 ",mask=0x%X",
event->type, event->time, event->mask);
switch (event->type) {
case EVENT_KEY_PRESSED:
// If the escape key is pressed, naturally terminate the program.
if (event->data.keyboard.keycode == VC_ESCAPE) {
int status = hook_stop();
switch (status) {
// System level errors.
case UIOHOOK_ERROR_OUT_OF_MEMORY:
logger_proc(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);
break;
// Default error.
case UIOHOOK_FAILURE:
default:
logger_proc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status);
break;
}
}
case EVENT_KEY_RELEASED:
snprintf(buffer + length, sizeof(buffer) - length,
",keycode=%u,rawcode=0x%X",
event->data.keyboard.keycode, event->data.keyboard.rawcode);
break;
case EVENT_KEY_TYPED:
snprintf(buffer + length, sizeof(buffer) - length,
",keychar=%lc,rawcode=%u",
(uint16_t) event->data.keyboard.keychar,//wint_t
event->data.keyboard.rawcode);
#ifdef WE_REALLY_WANT_A_POINTER
char *buf = malloc (6);
#else
char buf[6];
#endif
sprintf(buf, "%lc",(uint16_t) event->data.keyboard.keychar);
#ifdef WE_REALLY_WANT_A_POINTER
free (buf);
#endif
if (strcmp(buf, cevent) == 0){
int astop=aStop();
printf("%d\n",astop);
cstatus=0;
}
// return (char*) event->data.keyboard.keychar;
break;
case EVENT_MOUSE_PRESSED:
case EVENT_MOUSE_RELEASED:
case EVENT_MOUSE_CLICKED:
case EVENT_MOUSE_MOVED:
case EVENT_MOUSE_DRAGGED:
snprintf(buffer + length, sizeof(buffer) - length,
",x=%i,y=%i,button=%i,clicks=%i",
event->data.mouse.x, event->data.mouse.y,
event->data.mouse.button, event->data.mouse.clicks);
int abutton=event->data.mouse.button;
int aclicks=event->data.mouse.clicks;
int amouse;
if (strcmp(cevent,"mleft") == 0){
amouse=1;
}
if (strcmp(cevent,"mright") == 0){
amouse=2;
}
if (strcmp(cevent,"wheelDown") == 0){
amouse=4;
}
if (strcmp(cevent,"wheelUp") == 0){
amouse=5;
}
if (strcmp(cevent,"wheelLeft") == 0){
amouse=6;
}
if (strcmp(cevent,"wheelRight") == 0){
amouse=7;
}
if (abutton==amouse && aclicks==1){
int astop=aStop();
cstatus=0;
}
break;
case EVENT_MOUSE_WHEEL:
snprintf(buffer + length, sizeof(buffer) - length,
",type=%i,amount=%i,rotation=%i",
event->data.wheel.type, event->data.wheel.amount,
event->data.wheel.rotation);
break;
default:
break;
}
// fprintf(stdout, "----%s\n", buffer);
}
int aEvent(char *aevent) {
// (uint16_t *)
cevent=aevent;
// Set the logger callback for library output.
hook_set_logger_proc(&logger_proc);
// Set the event callback for uiohook events.
hook_set_dispatch_proc(&dispatch_proc);
// Start the hook and block.
// NOTE If EVENT_HOOK_ENABLED was delivered, the status will always succeed.
int status = hook_run();
switch (status) {
case UIOHOOK_SUCCESS:
// Everything is ok.
break;
// System level errors.
case UIOHOOK_ERROR_OUT_OF_MEMORY:
logger_proc(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);
break;
case UIOHOOK_ERROR_X_RECORD_NOT_FOUND:
logger_proc(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);
break;
case UIOHOOK_ERROR_X_RECORD_CREATE_CONTEXT:
logger_proc(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);
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);
break;
// Darwin specific errors.
case UIOHOOK_ERROR_AXAPI_DISABLED:
logger_proc(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);
break;
case UIOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE:
logger_proc(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);
break;
case UIOHOOK_ERROR_CREATE_OBSERVER:
logger_proc(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);
break;
}
// return status;
printf("%d\n",status);
return cstatus;
}
int aStop(){
int status = hook_stop();
switch (status) {
// System level errors.
case UIOHOOK_ERROR_OUT_OF_MEMORY:
logger_proc(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);
break;
// Default error.
case UIOHOOK_FAILURE:
default:
// logger_proc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status);
break;
}
return status;
}

View File

@ -0,0 +1,103 @@
#ifndef _included_input_helper
#define _included_input_helper
#include <ApplicationServices/ApplicationServices.h>
#include <Carbon/Carbon.h> // For HIToolbox kVK_ keycodes and TIS funcitons.
#ifdef USE_IOKIT
#include <IOKit/hidsystem/ev_keymap.h>
#endif
#include <stdbool.h>
#ifndef USE_IOKIT
// Some of the system key codes that are needed from IOKit.
// #define NX_KEYTYPE_SOUND_UP 0x00
// #define NX_KEYTYPE_SOUND_DOWN 0x01
// #define NX_KEYTYPE_MUTE 0x07
/* Display controls...
#define NX_KEYTYPE_BRIGHTNESS_UP 0x02
#define NX_KEYTYPE_BRIGHTNESS_DOWN 0x03
#define NX_KEYTYPE_CONTRAST_UP 0x0B
#define NX_KEYTYPE_CONTRAST_DOWN 0x0C
#define NX_KEYTYPE_ILLUMINATION_UP 0x15
#define NX_KEYTYPE_ILLUMINATION_DOWN 0x16
#define NX_KEYTYPE_ILLUMINATION_TOGGLE 0x17
*/
// #define NX_KEYTYPE_CAPS_LOCK 0x04
//#define NX_KEYTYPE_HELP 0x05
// #define NX_POWER_KEY 0x06
// #define NX_KEYTYPE_EJECT 0x0E
// #define NX_KEYTYPE_PLAY 0x10
// #define NX_KEYTYPE_NEXT 0x12
// #define NX_KEYTYPE_PREVIOUS 0x13
/* There is no official fast-forward or rewind scan code support.*/
// #define NX_KEYTYPE_FAST 0x14
// #define NX_KEYTYPE_REWIND 0x15
#endif
// These virtual key codes do not appear to be defined anywhere by Apple.
#define kVK_NX_Power 0xE0 | NX_POWER_KEY /* 0xE6 */
#define kVK_NX_Eject 0xE0 | NX_KEYTYPE_EJECT /* 0xEE */
#define kVK_MEDIA_Play 0xE0 | NX_KEYTYPE_PLAY /* 0xF0 */
#define kVK_MEDIA_Next 0xE0 | NX_KEYTYPE_NEXT /* 0xF1 */
#define kVK_MEDIA_Previous 0xE0 | NX_KEYTYPE_PREVIOUS /* 0xF2 */
#define kVK_RightCommand 0x36
#define kVK_ContextMenu 0x6E // AKA kMenuPowerGlyph
#define kVK_Undefined 0xFF
// These button codes do not appear to be defined anywhere by Apple.
#define kVK_LBUTTON kCGMouseButtonLeft
#define kVK_RBUTTON kCGMouseButtonRight
#define kVK_MBUTTON kCGMouseButtonCenter
#define kVK_XBUTTON1 3
#define kVK_XBUTTON2 4
// These button masks do not appear to be defined anywhere by Apple.
#define kCGEventFlagMaskButtonLeft 1 << 0
#define kCGEventFlagMaskButtonRight 1 << 1
#define kCGEventFlagMaskButtonCenter 1 << 2
#define kCGEventFlagMaskXButton1 1 << 3
#define kCGEventFlagMaskXButton2 1 << 4
/* Check for access to Apples accessibility API.
*/
extern bool is_accessibility_enabled();
/* Converts an OSX key code and event mask to the appropriate Unicode character
* representation.
*/
extern UniCharCount keycode_to_unicode(CGEventRef event_ref, UniChar *buffer, UniCharCount size);
/* Converts an OSX keycode to the appropriate UIOHook scancode constant.
*/
extern uint16_t keycode_to_scancode(UInt64 keycode);
/* Converts a UIOHook scancode constant to the appropriate OSX keycode.
*/
extern UInt64 scancode_to_keycode(uint16_t keycode);
/* Initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
* functionality. This method is called by OnLibraryLoad() and may need to be
* called in combination with UnloadInputHelper() if the native keyboard layout
* is changed.
*/
extern void load_input_helper();
/* De-initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
* functionality. This method is called by OnLibraryUnload() and may need to be
* called in combination with LoadInputHelper() if the native keyboard layout
* is changed.
*/
extern void unload_input_helper();
#endif

View File

@ -0,0 +1,543 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef USE_COREFOUNDATION
#include <CoreFoundation/CoreFoundation.h>
#endif
#ifndef USE_WEAK_IMPORT
#include <dlfcn.h>
#endif
#include <stdbool.h>
// #include <uiohook.h>
#include "../uiohook.h"
#include "input_helper.h"
#include "../logger_c.h"
// Current dead key state.
#if defined(USE_CARBON_LEGACY) || defined(USE_COREFOUNDATION)
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_COREFOUNDATION)
static TISInputSourceRef prev_keyboard_layout = NULL;
#endif
#ifdef USE_WEAK_IMPORT
// Required to dynamically check for AXIsProcessTrustedWithOptions availability.
extern Boolean AXIsProcessTrustedWithOptions(CFDictionaryRef options) __attribute__((weak_import));
extern CFStringRef kAXTrustedCheckOptionPrompt __attribute__((weak_import));
#else
static Boolean (*AXIsProcessTrustedWithOptions_t)(CFDictionaryRef);
#endif
bool is_accessibility_enabled() {
bool is_enabled = false;
#ifdef USE_WEAK_IMPORT
// Check and make sure assistive devices is enabled.
if (AXIsProcessTrustedWithOptions != NULL) {
// New accessibility API 10.9 and later.
const void * keys[] = { kAXTrustedCheckOptionPrompt };
const void * values[] = { kCFBooleanTrue };
CFDictionaryRef options = CFDictionaryCreate(
kCFAllocatorDefault,
keys,
values,
sizeof(keys) / sizeof(*keys),
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
is_enabled = AXIsProcessTrustedWithOptions(options);
}
#else
// Dynamically load the application services framework for examination.
*(void **) (&AXIsProcessTrustedWithOptions_t) = dlsym(RTLD_DEFAULT, "AXIsProcessTrustedWithOptions");
const char *dlError = dlerror();
if (AXIsProcessTrustedWithOptions_t != NULL && dlError == NULL) {
// Check for property CFStringRef kAXTrustedCheckOptionPrompt
void ** kAXTrustedCheckOptionPrompt_t = dlsym(RTLD_DEFAULT, "kAXTrustedCheckOptionPrompt");
dlError = dlerror();
if (kAXTrustedCheckOptionPrompt_t != NULL && dlError == NULL) {
// New accessibility API 10.9 and later.
const void * keys[] = { *kAXTrustedCheckOptionPrompt_t };
const void * values[] = { kCFBooleanTrue };
CFDictionaryRef options = CFDictionaryCreate(
kCFAllocatorDefault,
keys,
values,
sizeof(keys) / sizeof(*keys),
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
is_enabled = (*AXIsProcessTrustedWithOptions_t)(options);
}
}
#endif
else {
#ifndef USE_WEAK_IMPORT
if (dlError != NULL) {
// Could not load the AXIsProcessTrustedWithOptions function!
logger(LOG_LEVEL_DEBUG, "%s [%u]: %s.\n",
__FUNCTION__, __LINE__, dlError);
}
#endif
logger(LOG_LEVEL_DEBUG, "%s [%u]: Weak import AXIsProcessTrustedWithOptions not found.\n",
__FUNCTION__, __LINE__, dlError);
logger(LOG_LEVEL_DEBUG, "%s [%u]: Falling back to AXAPIEnabled().\n",
__FUNCTION__, __LINE__, dlError);
// Old accessibility check 10.8 and older.
is_enabled = AXAPIEnabled();
}
return is_enabled;
}
UniCharCount keycode_to_unicode(CGEventRef event_ref, UniChar *buffer, UniCharCount size) {
UniCharCount count = 0;
#if defined(USE_CARBON_LEGACY)
KeyboardLayoutRef curr_keyboard_layout;
void *inputData = NULL;
if (KLGetCurrentKeyboardLayout(&curr_keyboard_layout) == noErr) {
if (KLGetKeyboardLayoutProperty(curr_keyboard_layout, kKLuchrData, (const void **) &inputData) != noErr) {
inputData = NULL;
}
}
#elif defined(USE_COREFOUNDATION)
CFDataRef inputData = NULL;
if (CFEqual(CFRunLoopGetCurrent(), CFRunLoopGetMain())) {
// NOTE The following block must execute on the main runloop,
// Ex: CFEqual(CFRunLoopGetCurrent(), CFRunLoopGetMain()) to avoid
// Exception detected while handling key input and TSMProcessRawKeyCode failed
// (-192) errors.
TISInputSourceRef curr_keyboard_layout = TISCopyCurrentKeyboardLayoutInputSource();
if (curr_keyboard_layout != NULL && CFGetTypeID(curr_keyboard_layout) == TISInputSourceGetTypeID()) {
CFDataRef data = (CFDataRef) TISGetInputSourceProperty(curr_keyboard_layout, kTISPropertyUnicodeKeyLayoutData);
if (data != NULL && CFGetTypeID(data) == CFDataGetTypeID() && CFDataGetLength(data) > 0) {
inputData = (CFDataRef) data;
}
}
// Check if the keyboard layout has changed to see if the dead key state needs to be discarded.
if (prev_keyboard_layout != NULL && curr_keyboard_layout != NULL && CFEqual(curr_keyboard_layout, prev_keyboard_layout) == false) {
deadkey_state = 0x00;
}
// Release the previous keyboard layout.
if (prev_keyboard_layout != NULL) {
CFRelease(prev_keyboard_layout);
prev_keyboard_layout = NULL;
}
// Set the previous keyboard layout to the current layout.
if (curr_keyboard_layout != NULL) {
prev_keyboard_layout = curr_keyboard_layout;
}
}
#endif
#if defined(USE_CARBON_LEGACY) || defined(USE_COREFOUNDATION)
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.
CGKeyCode keycode = CGEventGetIntegerValueField(event_ref, kCGKeyboardEventKeycode);
CGEventFlags modifiers = CGEventGetFlags(event_ref);
// Disable all command modifiers for translation. This is required
// so UCKeyTranslate will provide a keysym for the separate event.
static const CGEventFlags cmd_modifiers = kCGEventFlagMaskCommand |
kCGEventFlagMaskControl | kCGEventFlagMaskAlternate;
modifiers &= ~cmd_modifiers;
// I don't know why but UCKeyTranslate does not process the
// kCGEventFlagMaskAlphaShift (A.K.A. Caps Lock Mask) correctly.
// We need to basically turn off the mask and process the capital
// letters after UCKeyTranslate().
bool is_caps_lock = modifiers & kCGEventFlagMaskAlphaShift;
modifiers &= ~kCGEventFlagMaskAlphaShift;
// Run the translation with the saved deadkey_state.
OSStatus status = UCKeyTranslate(
keyboard_layout,
keycode,
kUCKeyActionDown, //kUCKeyActionDisplay,
(modifiers >> 16) & 0xFF, //(modifiers >> 16) & 0xFF, || (modifiers >> 8) & 0xFF,
LMGetKbdType(),
kNilOptions, //kNilOptions, //kUCKeyTranslateNoDeadKeysMask
&deadkey_state,
size,
&count,
buffer);
if (status == noErr && count > 0) {
if (is_caps_lock) {
// We *had* a caps lock mask so we need to convert to uppercase.
CFMutableStringRef keytxt = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault, buffer, count, size, kCFAllocatorNull);
if (keytxt != NULL) {
CFLocaleRef locale = CFLocaleCopyCurrent();
CFStringUppercase(keytxt, locale);
CFRelease(locale);
CFRelease(keytxt);
}
else {
// There was an problem creating the CFMutableStringRef.
count = 0;
}
}
}
else {
// Make sure the buffer count is zero if an error occurred.
count = 0;
}
}
}
#else
CGEventKeyboardGetUnicodeString(event_ref, size, &count, buffer);
#endif
// The following codes should not be processed because they are invalid.
if (count == 1) {
switch (buffer[0]) {
case 0x01: // Home
case 0x04: // End
case 0x05: // Help Key
case 0x10: // Function Keys
case 0x0B: // Page Up
case 0x0C: // Page Down
case 0x1F: // Volume Up
count = 0;
}
}
return count;
}
static const uint16_t keycode_scancode_table[][2] = {
/* idx { keycode, scancode }, */
/* 0 */ { VC_A, kVK_Undefined }, // 0x00
/* 1 */ { VC_S, kVK_Escape }, // 0x01
/* 2 */ { VC_D, kVK_ANSI_1 }, // 0x02
/* 3 */ { VC_F, kVK_ANSI_2 }, // 0x03
/* 4 */ { VC_H, kVK_ANSI_3 }, // 0x04
/* 5 */ { VC_G, kVK_ANSI_4 }, // 0x05
/* 6 */ { VC_Z, kVK_ANSI_5 }, // 0x07
/* 7 */ { VC_X, kVK_ANSI_6 }, // 0x08
/* 8 */ { VC_C, kVK_ANSI_7 }, // 0x09
/* 9 */ { VC_V, kVK_ANSI_8 }, // 0x0A
/* 10 */ { VC_UNDEFINED, kVK_ANSI_9 }, // 0x0B
/* 11 */ { VC_B, kVK_ANSI_0 }, // 0x0C
/* 12 */ { VC_Q, kVK_ANSI_Minus }, // 0x0D
/* 13 */ { VC_W, kVK_ANSI_Equal }, // 0x0E
/* 14 */ { VC_E, kVK_Delete }, // 0x0F
/* 15 */ { VC_R, kVK_Tab }, // 0x10
/* 16 */ { VC_Y, kVK_ANSI_Q }, // 0x11
/* 17 */ { VC_T, kVK_ANSI_W }, // 0x12
/* 18 */ { VC_1, kVK_ANSI_E }, // 0x13
/* 19 */ { VC_2, kVK_ANSI_R }, // 0x14
/* 20 */ { VC_3, kVK_ANSI_T }, // 0x15
/* 21 */ { VC_4, kVK_ANSI_Y }, // 0x16
/* 22 */ { VC_6, kVK_ANSI_U }, // 0x17
/* 23 */ { VC_5, kVK_ANSI_I }, // 0x18
/* 24 */ { VC_EQUALS, kVK_ANSI_O }, // 0x19
/* 25 */ { VC_9, kVK_ANSI_P }, // 0x19
/* 26 */ { VC_7, kVK_ANSI_LeftBracket }, // 0x1A
/* 27 */ { VC_MINUS, kVK_ANSI_RightBracket }, // 0x1B
/* 28 */ { VC_8, kVK_Return }, // 0x1C
/* 29 */ { VC_0, kVK_Control }, // 0x1D
/* 30 */ { VC_CLOSE_BRACKET, kVK_ANSI_A }, // 0x1E
/* 31 */ { VC_O, kVK_ANSI_S }, // 0x1F
/* 32 */ { VC_U, kVK_ANSI_D }, // 0x20
/* 33 */ { VC_OPEN_BRACKET, kVK_ANSI_F }, // 0x21
/* 34 */ { VC_I, kVK_ANSI_G }, // 0x22
/* 35 */ { VC_P, kVK_ANSI_H }, // 0x23
/* 36 */ { VC_ENTER, kVK_ANSI_J }, // 0x24
/* 37 */ { VC_L, kVK_ANSI_K }, // 0x25
/* 38 */ { VC_J, kVK_ANSI_L }, // 0x26
/* 39 */ { VC_QUOTE, kVK_ANSI_Semicolon }, // 0x27
/* 40 */ { VC_K, kVK_ANSI_Quote }, // 0x28
/* 41 */ { VC_SEMICOLON, kVK_ANSI_Grave }, // 0x29
/* 42 */ { VC_BACK_SLASH, kVK_Shift }, // 0x2A
/* 43 */ { VC_COMMA, kVK_ANSI_Backslash }, // 0x2B
/* 44 */ { VC_SLASH, kVK_ANSI_Z }, // 0x2C
/* 45 */ { VC_N, kVK_ANSI_X }, // 0x2D
/* 46 */ { VC_M, kVK_ANSI_C }, // 0x2E
/* 47 */ { VC_PERIOD, kVK_ANSI_V }, // 0x2F
/* 48 */ { VC_TAB, kVK_ANSI_B }, // 0x30
/* 49 */ { VC_SPACE, kVK_ANSI_N }, // 0x31
/* 50 */ { VC_BACKQUOTE, kVK_ANSI_M }, // 0x32
/* 51 */ { VC_BACKSPACE, kVK_ANSI_Comma }, // 0x33
/* 52 */ { VC_UNDEFINED, kVK_ANSI_Period }, // 0x34
/* 53 */ { VC_ESCAPE, kVK_ANSI_Slash }, // 0x35
/* 54 */ { VC_META_R, kVK_RightShift }, // 0x36
/* 55 */ { VC_META_L, kVK_ANSI_KeypadMultiply }, // 0x37
/* 56 */ { VC_SHIFT_L, kVK_Option }, // 0x38
/* 57 */ { VC_CAPS_LOCK, kVK_Space }, // 0x39
/* 58 */ { VC_ALT_L, kVK_CapsLock }, // 0x3A
/* 59 */ { VC_CONTROL_L, kVK_F1 }, // 0x3B
/* 60 */ { VC_SHIFT_R, kVK_F2 }, // 0x3C
/* 61 */ { VC_ALT_R, kVK_F3 }, // 0x3D
/* 62 */ { VC_CONTROL_R, kVK_F4 }, // 0x3E
/* 63 */ { VC_UNDEFINED, kVK_F5 }, // 0x3F
/* 64 */ { VC_F17, kVK_F6 }, // 0x40
/* 65 */ { VC_KP_SEPARATOR, kVK_F7 }, // 0x41
/* 66 */ { VC_UNDEFINED, kVK_F8 }, // 0x42
/* 67 */ { VC_KP_MULTIPLY, kVK_F9 }, // 0x43
/* 68 */ { VC_UNDEFINED, kVK_F10 }, // 0x44
/* 69 */ { VC_KP_ADD, kVK_ANSI_KeypadClear }, // 0x45
/* 70 */ { VC_UNDEFINED, kVK_Undefined }, // 0x46
/* 71 */ { VC_NUM_LOCK, kVK_ANSI_Keypad7 }, // 0x47
/* 72 */ { VC_VOLUME_UP, kVK_ANSI_Keypad8 }, // 0x48
/* 73 */ { VC_VOLUME_DOWN, kVK_ANSI_Keypad9 }, // 0x49
/* 74 */ { VC_VOLUME_MUTE, kVK_ANSI_KeypadMinus }, // 0x4A
/* 75 */ { VC_KP_DIVIDE, kVK_ANSI_Keypad4 }, // 0x4B
/* 76 */ { VC_KP_ENTER, kVK_ANSI_Keypad5 }, // 0x4C
/* 77 */ { VC_UNDEFINED, kVK_ANSI_Keypad6 }, // 0x4D
/* 78 */ { VC_KP_SUBTRACT, kVK_ANSI_KeypadPlus }, // 0x4E
/* 79 */ { VC_F18, kVK_ANSI_Keypad1 }, // 0x4F
/* 80 */ { VC_F19, kVK_ANSI_Keypad2 }, // 0x50
/* 81 */ { VC_KP_EQUALS, kVK_ANSI_Keypad3 }, // 0x51
/* 82 */ { VC_KP_0, kVK_ANSI_Keypad0 }, // 0x52
/* 83 */ { VC_KP_1, kVK_ANSI_KeypadDecimal }, // 0x53
/* 84 */ { VC_KP_2, kVK_Undefined }, // 0x54
/* 85 */ { VC_KP_3, kVK_Undefined }, // 0x55
/* 86 */ { VC_KP_4, kVK_Undefined }, // 0x56
/* 87 */ { VC_KP_5, kVK_F11 }, // 0x57
/* 88 */ { VC_KP_6, kVK_F12 }, // 0x58
/* 89 */ { VC_KP_7, kVK_Undefined }, // 0x59
/* 90 */ { VC_F20, kVK_Undefined }, // 0x5A
/* 91 */ { VC_KP_8, kVK_F13 }, // 0x5B
/* 92 */ { VC_KP_9, kVK_F14 }, // 0x5C
/* 93 */ { VC_YEN, kVK_F15 }, // 0x5D
/* 94 */ { VC_UNDERSCORE, kVK_Undefined }, // 0x5E
/* 95 */ { VC_KP_COMMA, kVK_Undefined }, // 0x5F
/* 96 */ { VC_F5, kVK_Undefined }, // 0x60
/* 97 */ { VC_F6, kVK_Undefined }, // 0x61
/* 98 */ { VC_F7, kVK_Undefined }, // 0x62
/* 99 */ { VC_F3, kVK_F16 }, // 0x63
/* 100 */ { VC_F8, kVK_F17 }, // 0x64
/* 101 */ { VC_F9, kVK_F18 }, // 0x65
/* 102 */ { VC_UNDEFINED, kVK_F19 }, // 0x66
/* 103 */ { VC_F11, kVK_F20 }, // 0x67
/* 104 */ { VC_KATAKANA, kVK_Undefined }, // 0x68
/* 105 */ { VC_F13, kVK_Undefined }, // 0x69
/* 106 */ { VC_F16, kVK_Undefined }, // 0x6A
/* 107 */ { VC_F14, kVK_Undefined }, // 0x6B
/* 108 */ { VC_UNDEFINED, kVK_Undefined }, // 0x6C FIXME kVK_JIS_Eisu same as Caps Lock ?
/* 109 */ { VC_F10, kVK_Undefined }, // 0x6D
/* 110 */ { VC_UNDEFINED, kVK_Undefined }, // 0x6E
/* 111 */ { VC_F12, kVK_Undefined }, // 0x6F
/* 112 */ { VC_UNDEFINED, kVK_JIS_Kana }, // 0x70
/* 113 */ { VC_F15, kVK_Undefined }, // 0x71
/* 114 */ { VC_INSERT, kVK_Undefined }, // 0x72
/* 115 */ { VC_HOME, kVK_JIS_Underscore }, // 0x73
/* 116 */ { VC_PAGE_UP, kVK_Undefined }, // 0x74
/* 117 */ { VC_DELETE, kVK_Undefined }, // 0x75
/* 118 */ { VC_F4, kVK_Undefined }, // 0x76
/* 119 */ { VC_END, kVK_Undefined }, // 0x77
/* 120 */ { VC_F2, kVK_Undefined }, // 0x78
/* 121 */ { VC_PAGE_DOWN, kVK_Undefined }, // 0x79
/* 122 */ { VC_F1, kVK_Undefined }, // 0x7A
/* 123 */ { VC_LEFT, kVK_Undefined }, // 0x7B
/* 124 */ { VC_RIGHT, kVK_Undefined }, // 0x7C
/* 125 */ { VC_DOWN, kVK_JIS_Yen }, // 0x7D
/* 126 */ { VC_UP, kVK_JIS_KeypadComma }, // 0x7E
/* 127 */ { VC_UNDEFINED, kVK_Undefined }, // 0x7F
// No Offset Offset (i & 0x007F) + 128
/* 128 */ { VC_UNDEFINED, kVK_Undefined }, // 0x80
/* 129 */ { VC_UNDEFINED, kVK_Undefined }, // 0x81
/* 130 */ { VC_UNDEFINED, kVK_Undefined }, // 0x82
/* 131 */ { VC_UNDEFINED, kVK_Undefined }, // 0x83
/* 132 */ { VC_UNDEFINED, kVK_Undefined }, // 0x84
/* 133 */ { VC_UNDEFINED, kVK_Undefined }, // 0x85
/* 134 */ { VC_UNDEFINED, kVK_Undefined }, // 0x86
/* 135 */ { VC_UNDEFINED, kVK_Undefined }, // 0x87
/* 136 */ { VC_UNDEFINED, kVK_Undefined }, // 0x88
/* 137 */ { VC_UNDEFINED, kVK_Undefined }, // 0x89
/* 138 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8A
/* 139 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8B
/* 140 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8C
/* 141 */ { VC_UNDEFINED, kVK_ANSI_KeypadEquals }, // 0x8D
/* 142 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8E
/* 143 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8F
/* 144 */ { VC_UNDEFINED, kVK_MEDIA_Previous }, // 0x90
/* 145 */ { VC_UNDEFINED, kVK_Undefined }, // 0x91
/* 146 */ { VC_UNDEFINED, kVK_Undefined }, // 0x92
/* 147 */ { VC_UNDEFINED, kVK_Undefined }, // 0x93
/* 148 */ { VC_UNDEFINED, kVK_Undefined }, // 0x94
/* 149 */ { VC_UNDEFINED, kVK_Undefined }, // 0x95
/* 150 */ { VC_UNDEFINED, kVK_Undefined }, // 0x96
/* 151 */ { VC_UNDEFINED, kVK_Undefined }, // 0x97
/* 152 */ { VC_UNDEFINED, kVK_Undefined }, // 0x98
/* 153 */ { VC_UNDEFINED, kVK_MEDIA_Next }, // 0x99
/* 154 */ { VC_UNDEFINED, kVK_Undefined }, // 0x9A
/* 155 */ { VC_UNDEFINED, kVK_Undefined }, // 0x9B
/* 156 */ { VC_UNDEFINED, kVK_ANSI_KeypadEnter }, // 0x9C
/* 157 */ { VC_UNDEFINED, kVK_RightControl }, // 0x9D
/* 158 */ { VC_UNDEFINED, kVK_Undefined }, // 0x9E
/* 159 */ { VC_UNDEFINED, kVK_Undefined }, // 0x9F
/* 160 */ { VC_UNDEFINED, kVK_Mute }, // 0xA0
/* 161 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA1
/* 162 */ { VC_UNDEFINED, kVK_MEDIA_Play }, // 0xA2
/* 163 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA3
/* 164 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA4
/* 165 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA5
/* 166 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA6
/* 167 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA7
/* 168 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA8
/* 169 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA9
/* 170 */ { VC_UNDEFINED, kVK_Undefined }, // 0xAA
/* 171 */ { VC_UNDEFINED, kVK_Undefined }, // 0xAB
/* 172 */ { VC_UNDEFINED, kVK_NX_Eject }, // 0xAC
/* 173 */ { VC_UNDEFINED, kVK_Undefined }, // 0xAD
/* 174 */ { VC_UNDEFINED, kVK_VolumeDown }, // 0xAE
/* 175 */ { VC_UNDEFINED, kVK_Undefined }, // 0xAF
/* 176 */ { VC_UNDEFINED, kVK_VolumeUp }, // 0xB0
/* 177 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB1
/* 178 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB2
/* 179 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB3
/* 180 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB4
/* 181 */ { VC_UNDEFINED, kVK_ANSI_KeypadDivide }, // 0xB5
/* 182 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB6
/* 183 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB7
/* 184 */ { VC_UNDEFINED, kVK_RightOption }, // 0xB8
/* 185 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB9
/* 186 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBA
/* 187 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBB
/* 188 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBC
/* 189 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBD
/* 190 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBE
/* 191 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBF
/* 192 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC0
/* 193 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC1
/* 194 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC2
/* 195 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC3
/* 196 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC4
/* 197 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC5
/* 198 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC6
/* 199 */ { VC_UNDEFINED, kVK_Home }, // 0xC7
/* 200 */ { VC_UNDEFINED, kVK_UpArrow }, // 0xC8
/* 201 */ { VC_UNDEFINED, kVK_PageUp }, // 0xC9
/* 202 */ { VC_UNDEFINED, kVK_Undefined }, // 0xCA
/* 203 */ { VC_UNDEFINED, kVK_LeftArrow }, // 0xCB
/* 204 */ { VC_UNDEFINED, kVK_Undefined }, // 0xCC
/* 205 */ { VC_UNDEFINED, kVK_RightArrow }, // 0xCD
/* 206 */ { VC_UNDEFINED, kVK_Undefined }, // 0xCE
/* 207 */ { VC_UNDEFINED, kVK_End }, // 0xCF
/* 208 */ { VC_UNDEFINED, kVK_DownArrow }, // 0xD0
/* 209 */ { VC_UNDEFINED, kVK_PageDown }, // 0xD1
/* 210 */ { VC_UNDEFINED, kVK_Help }, // 0xD2
/* 211 */ { VC_UNDEFINED, kVK_ForwardDelete }, // 0xD3
/* 212 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD4
/* 213 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD5
/* 214 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD6
/* 215 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD7
/* 216 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD8
/* 217 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD9
/* 218 */ { VC_UNDEFINED, kVK_Undefined }, // 0xDA
/* 219 */ { VC_UNDEFINED, kVK_Command }, // 0xDB
/* 220 */ { VC_UNDEFINED, kVK_RightCommand }, // 0xDC
/* 221 */ { VC_UNDEFINED, kVK_Undefined }, // 0xDD
/* 222 */ { VC_UNDEFINED, kVK_NX_Power }, // 0xDE
/* 223 */ { VC_UNDEFINED, kVK_Undefined }, // 0xDF
/* 224 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE0
/* 225 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE1
/* 226 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE2
/* 227 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE3
/* 228 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE4
/* 229 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE5
/* 230 */ { VC_POWER, kVK_Undefined }, // 0xE6
/* 231 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE7
/* 232 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE8
/* 233 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE9
/* 234 */ { VC_UNDEFINED, kVK_Undefined }, // 0xEA
/* 235 */ { VC_UNDEFINED, kVK_Undefined }, // 0xEB
/* 236 */ { VC_UNDEFINED, kVK_Undefined }, // 0xEC
/* 237 */ { VC_UNDEFINED, kVK_Undefined }, // 0xED
/* 238 */ { VC_MEDIA_EJECT, kVK_Undefined }, // 0xEE
/* 239 */ { VC_UNDEFINED, kVK_Undefined }, // 0xEF
/* 240 */ { VC_MEDIA_PLAY, kVK_Undefined }, // 0xF0
/* 241 */ { VC_MEDIA_NEXT, kVK_Undefined }, // 0xF1
/* 242 */ { VC_MEDIA_PREVIOUS, kVK_Undefined }, // 0xF2
/* 243 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF3
/* 244 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF4
/* 245 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF5
/* 246 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF6
/* 247 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF7
/* 248 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF8
/* 249 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF9
/* 250 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFA
/* 251 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFB
/* 252 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFC
/* 253 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFD
/* 254 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFE
/* 255 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFF
};
uint16_t keycode_to_scancode(UInt64 keycode) {
uint16_t scancode = VC_UNDEFINED;
// Bound check 0 <= keycode < 256
if (keycode < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[0])) {
scancode = keycode_scancode_table[keycode][0];
}
return scancode;
}
UInt64 scancode_to_keycode(uint16_t scancode) {
UInt64 keycode = kVK_Undefined;
// Bound check 0 <= keycode < 128
if (scancode < 128) {
keycode = keycode_scancode_table[scancode][1];
}
else {
// Calculate the upper offset.
unsigned short i = (scancode & 0x007F) | 0x80;
if (i < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[1])) {
keycode = keycode_scancode_table[i][1];
}
}
return keycode;
}
void load_input_helper() {
#if defined(USE_CARBON_LEGACY) || defined(USE_COREFOUNDATION)
// Start with a fresh dead key state.
//curr_deadkey_state = 0;
#endif
}
void unload_input_helper() {
#if defined(USE_CARBON_LEGACY) || defined(USE_COREFOUNDATION)
if (prev_keyboard_layout != NULL) {
// Cleanup tracking of the previous layout.
CFRelease(prev_keyboard_layout);
prev_keyboard_layout = NULL;
}
#endif
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,236 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ApplicationServices/ApplicationServices.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
// #include <uiohook.h>
#include "../uiohook.h"
#include "input_helper.h"
// #include "../logger_c.h"
// TODO Possibly relocate to input helper.
static inline CGEventFlags get_key_event_mask(uiohook_event * const event) {
CGEventFlags native_mask = 0x00;
if (event->mask & (MASK_SHIFT)) { native_mask |= kCGEventFlagMaskShift; }
if (event->mask & (MASK_CTRL)) { native_mask |= kCGEventFlagMaskControl; }
if (event->mask & (MASK_META)) { native_mask |= kCGEventFlagMaskControl; }
if (event->mask & (MASK_ALT)) { native_mask |= kCGEventFlagMaskAlternate; }
if (event->type == EVENT_KEY_PRESSED || event->type == EVENT_KEY_RELEASED || event->type == EVENT_KEY_TYPED) {
switch (event->data.keyboard.keycode) {
case VC_KP_0:
case VC_KP_1:
case VC_KP_2:
case VC_KP_3:
case VC_KP_4:
case VC_KP_5:
case VC_KP_6:
case VC_KP_7:
case VC_KP_8:
case VC_KP_9:
case VC_NUM_LOCK:
case VC_KP_ENTER:
case VC_KP_MULTIPLY:
case VC_KP_ADD:
case VC_KP_SEPARATOR:
case VC_KP_SUBTRACT:
case VC_KP_DIVIDE:
case VC_KP_COMMA:
native_mask |= kCGEventFlagMaskNumericPad;
break;
}
}
return native_mask;
}
static inline void post_key_event(uiohook_event * const event) {
bool is_pressed = event->type == EVENT_KEY_PRESSED;
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef cg_event = CGEventCreateKeyboardEvent(src,
(CGKeyCode) scancode_to_keycode(event->data.keyboard.keycode),
is_pressed);
CGEventSetFlags(cg_event, get_key_event_mask(event));
CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works.
CFRelease(cg_event);
CFRelease(src);
}
static inline void post_mouse_button_event(uiohook_event * const event, bool is_pressed) {
CGMouseButton mouse_button;
CGEventType mouse_type;
if (event->data.mouse.button == MOUSE_BUTTON1) {
if (is_pressed) {
mouse_type = kCGEventLeftMouseDown;
}
else {
mouse_type = kCGEventLeftMouseUp;
}
mouse_button = kCGMouseButtonLeft;
}
else if (event->data.mouse.button == MOUSE_BUTTON2) {
if (is_pressed) {
mouse_type = kCGEventRightMouseDown;
}
else {
mouse_type = kCGEventRightMouseUp;
}
mouse_button = kCGMouseButtonRight;
}
else {
if (is_pressed) {
mouse_type = kCGEventOtherMouseDown;
}
else {
mouse_type = kCGEventOtherMouseUp;
}
mouse_button = event->data.mouse.button - 1;
}
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef cg_event = CGEventCreateMouseEvent(src,
mouse_type,
CGPointMake(
(CGFloat) event->data.mouse.x,
(CGFloat) event->data.mouse.y
),
mouse_button
);
CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works.
CFRelease(cg_event);
CFRelease(src);
}
static inline void post_mouse_wheel_event(uiohook_event * const event) {
// FIXME Should I create a source event with the coords?
// It seems to automagically use the current location of the cursor.
// Two options: Query the mouse, move it to x/y, scroll, then move back
// OR disable x/y for scroll events on Windows & X11.
CGScrollEventUnit scroll_unit;
if (event->data.wheel.type == WHEEL_BLOCK_SCROLL) {
// Scrolling data is line-based.
scroll_unit = kCGScrollEventUnitLine;
}
else {
// Scrolling data is pixel-based.
scroll_unit = kCGScrollEventUnitPixel;
}
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef cg_event = CGEventCreateScrollWheelEvent(src,
kCGScrollEventUnitLine,
// TODO Currently only support 1 wheel axis.
(CGWheelCount) 1, // 1 for Y-only, 2 for Y-X, 3 for Y-X-Z
event->data.wheel.amount * event->data.wheel.rotation);
CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works.
CFRelease(cg_event);
CFRelease(src);
}
static inline void post_mouse_motion_event(uiohook_event * const event) {
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef cg_event;
if (event->mask >> 8 == 0x00) {
// No mouse flags.
cg_event = CGEventCreateMouseEvent(src,
kCGEventMouseMoved,
CGPointMake(
(CGFloat) event->data.mouse.x,
(CGFloat) event->data.mouse.y
),
0
);
}
else if (event->mask & MASK_BUTTON1) {
cg_event = CGEventCreateMouseEvent(src,
kCGEventLeftMouseDragged,
CGPointMake(
(CGFloat) event->data.mouse.x,
(CGFloat) event->data.mouse.y
),
kCGMouseButtonLeft
);
}
else if (event->mask & MASK_BUTTON2) {
cg_event = CGEventCreateMouseEvent(src,
kCGEventRightMouseDragged,
CGPointMake(
(CGFloat) event->data.mouse.x,
(CGFloat) event->data.mouse.y
),
kCGMouseButtonRight
);
}
else {
cg_event = CGEventCreateMouseEvent(src,
kCGEventOtherMouseDragged,
CGPointMake(
(CGFloat) event->data.mouse.x,
(CGFloat) event->data.mouse.y
),
(event->mask >> 8) - 1
);
}
// kCGSessionEventTap also works.
CGEventPost(kCGHIDEventTap, cg_event);
CFRelease(cg_event);
CFRelease(src);
}
UIOHOOK_API void hook_post_event(uiohook_event * const event) {
switch (event->type) {
case EVENT_KEY_PRESSED:
case EVENT_KEY_RELEASED:
post_key_event(event);
break;
case EVENT_MOUSE_PRESSED:
post_mouse_button_event(event, true);
break;
case EVENT_MOUSE_RELEASED:
post_mouse_button_event(event, false);
break;
case EVENT_MOUSE_CLICKED:
post_mouse_button_event(event, true);
post_mouse_button_event(event, false);
break;
case EVENT_MOUSE_WHEEL:
post_mouse_wheel_event(event);
break;
case EVENT_MOUSE_MOVED:
case EVENT_MOUSE_DRAGGED:
post_mouse_motion_event(event);
break;
case EVENT_KEY_TYPED:
// FIXME Ignoreing EVENT_KEY_TYPED events.
case EVENT_HOOK_ENABLED:
case EVENT_HOOK_DISABLED:
// Ignore hook enabled / disabled events.
default:
// Ignore any other garbage.
logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n",
__FUNCTION__, __LINE__, event->type);
break;
}
}

View File

@ -0,0 +1,522 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef USE_CARBON_LEGACY
#include <Carbon/Carbon.h>
#endif
#ifdef USE_COREFOUNDATION
#include <CoreFoundation/CoreFoundation.h>
#endif
#ifdef USE_IOKIT
#include <IOKit/hidsystem/IOHIDLib.h>
#include <IOKit/hidsystem/IOHIDParameter.h>
#endif
#include <stdbool.h>
// #include <uiohook.h>
#include "../uiohook.h"
// #include "../logger_c.h"
#include "input_helper.h"
UIOHOOK_API screen_data* hook_create_screen_info(unsigned char *count) {
CGError status = kCGErrorFailure;
screen_data* screens = NULL;
// Initialize count to zero.
*count = 0;
// Allocate memory to hold each display id. We will just allocate our MAX
// because its only about 1K of memory.
// TODO This can probably be realistically cut to something like 16 or 32....
// If you have more than 32 monitors, send me a picture and make a donation ;)
CGDirectDisplayID *display_ids = malloc(sizeof(CGDirectDisplayID) * UCHAR_MAX);
if (display_ids != NULL) {
// NOTE Pass UCHAR_MAX to make sure uint32_t doesn't overflow uint8_t.
// TOOD Test/Check whether CGGetOnlineDisplayList is more suitable...
status = CGGetActiveDisplayList(UCHAR_MAX, display_ids, (uint32_t *) count);
// If there is no error and at least one monitor.
if (status == kCGErrorSuccess && *count > 0) {
logger(LOG_LEVEL_INFO, "%s [%u]: CGGetActiveDisplayList: %li.\n",
__FUNCTION__, __LINE__, *count);
// Allocate memory for the number of screens found.
screens = malloc(sizeof(screen_data) * (*count));
if (screens != NULL) {
for (uint8_t i = 0; i < *count; i++) {
//size_t width = CGDisplayPixelsWide(display_ids[i]);
//size_t height = CGDisplayPixelsHigh(display_ids[i]);
CGRect boundsDisp = CGDisplayBounds(display_ids[i]);
if (boundsDisp.size.width > 0 && boundsDisp.size.height > 0) {
screens[i] = (screen_data) {
.number = i + 1,
//TODO: make sure we follow the same convention for the origin
//in all other platform implementations (upper-left)
//TODO: document the approach with examples in order to show different
//cases -> different resolutions (secondary monitors origin might be
//negative)
.x = boundsDisp.origin.x,
.y = boundsDisp.origin.y,
.width = boundsDisp.size.width,
.height = boundsDisp.size.height
};
}
}
}
}
else {
logger(LOG_LEVEL_INFO, "%s [%u]: multiple_get_screen_info failed: %ld. Fallback.\n",
__FUNCTION__, __LINE__, status);
size_t width = CGDisplayPixelsWide(CGMainDisplayID());
size_t height = CGDisplayPixelsHigh(CGMainDisplayID());
if (width > 0 && height > 0) {
screens = malloc(sizeof(screen_data));
if (screens != NULL) {
*count = 1;
screens[0] = (screen_data) {
.number = 1,
.x = 0,
.y = 0,
.width = width,
.height = height
};
}
}
}
// Free the id's after we are done.
free(display_ids);
}
return screens;
}
/*
* Apple's documentation is not very good. I was finally able to find this
* information after many hours of googling. Value is the slider value in the
* system preferences. That value * 15 is the rate in MS. 66 / the value is the
* chars per second rate.
*
* Value MS Char/Sec
*
* 1 15 66 * Out of standard range *
*
* 2 30 33
* 6 90 11
* 12 180 5.5
* 30 450 2.2
* 60 900 1.1
* 90 1350 0.73
* 120 1800 0.55
*
* V = MS / 15
* V = 66 / CharSec
*
* MS = V * 15
* MS = (66 / CharSec) * 15
*
* CharSec = 66 / V
* CharSec = 66 / (MS / 15)
*/
UIOHOOK_API long int hook_get_auto_repeat_rate() {
#if defined USE_IOKIT || defined USE_COREFOUNDATION || defined USE_CARBON_LEGACY
bool successful = false;
SInt64 rate;
#endif
long int value = -1;
#ifdef USE_IOKIT
if (!successful) {
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
if (service) {
kern_return_t kren_ret = kIOReturnError;
io_connect_t connection;
kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
if (kren_ret == kIOReturnSuccess) {
IOByteCount size = sizeof(rate);
kren_ret = IOHIDGetParameter(connection, CFSTR(kIOHIDKeyRepeatKey), (IOByteCount) sizeof(rate), &rate, &size);
if (kren_ret == kIOReturnSuccess) {
/* This is in some undefined unit of time that if we happen
* to multiply by 900 gives us the time in milliseconds. We
* add 0.5 to the result so that when we cast to long we
* actually get a rounded result. Saves the math.h depend.
*
* 33,333,333.0 / 1000.0 / 1000.0 / 1000.0 == 0.033333333 * Fast *
* 100,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.1
* 200,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.2
* 500,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.5
* 1,000,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 1
* 1,500,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 1.5
* 2,000,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 2 * Slow *
*/
value = (long) (900.0 * ((double) rate) / 1000.0 / 1000.0 / 1000.0 + 0.5);
successful = true;
logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetParameter: %li.\n",
__FUNCTION__, __LINE__, value);
}
}
}
}
#endif
#ifdef USE_COREFOUNDATION
if (!successful) {
CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("KeyRepeat"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberSInt32Type, &rate)) {
// This is the slider value, we must multiply by 15 to convert to milliseconds.
value = (long) rate * 15;
successful = true;
logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
__FUNCTION__, __LINE__, value);
}
}
}
#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_INFO, "%s [%u]: LMGetKeyRepThresh: %li.\n",
__FUNCTION__, __LINE__, value);
}
}
#endif
return value;
}
UIOHOOK_API long int hook_get_auto_repeat_delay() {
#if defined USE_IOKIT || defined USE_COREFOUNDATION || defined USE_CARBON_LEGACY
bool successful = false;
SInt64 delay;
#endif
long int value = -1;
#ifdef USE_IOKIT
if (!successful) {
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
if (service) {
kern_return_t kren_ret = kIOReturnError;
io_connect_t connection;
kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
if (kren_ret == kIOReturnSuccess) {
IOByteCount size = sizeof(delay);
kren_ret = IOHIDGetParameter(connection, CFSTR(kIOHIDInitialKeyRepeatKey), (IOByteCount) sizeof(delay), &delay, &size);
if (kren_ret == kIOReturnSuccess) {
/* This is in some undefined unit of time that if we happen
* to multiply by 900 gives us the time in milliseconds. We
* add 0.5 to the result so that when we cast to long we
* actually get a rounded result. Saves the math.h depend.
*
* 33,333,333.0 / 1000.0 / 1000.0 / 1000.0 == 0.033333333 * Fast *
* 100,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.1
* 200,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.2
* 500,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.5
* 1,000,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 1
* 1,500,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 1.5
* 2,000,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 2 * Slow *
*/
value = (long) (900.0 * ((double) delay) / 1000.0 / 1000.0 / 1000.0 + 0.5);
successful = true;
logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetParameter: %li.\n",
__FUNCTION__, __LINE__, value);
}
}
}
}
#endif
#ifdef USE_COREFOUNDATION
if (!successful) {
CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("InitialKeyRepeat"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberSInt32Type, &delay)) {
// This is the slider value, we must multiply by 15 to convert to
// milliseconds.
value = (long) delay * 15;
successful = true;
logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
__FUNCTION__, __LINE__, value);
}
}
}
#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_INFO, "%s [%u]: LMGetKeyThresh: %li.\n",
__FUNCTION__, __LINE__, value);
}
}
#endif
return value;
}
UIOHOOK_API long int hook_get_pointer_acceleration_multiplier() {
#if defined USE_IOKIT || defined USE_COREFOUNDATION
bool successful = false;
double multiplier;
#endif
long int value = -1;
#ifdef USE_IOKIT
if (!successful) {
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
if (service) {
kern_return_t kren_ret = kIOReturnError;
io_connect_t connection;
kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
if (kren_ret == kIOReturnSuccess) {
// IOByteCount size = sizeof(multiplier);
kren_ret = IOHIDGetAccelerationWithKey(connection, CFSTR(kIOHIDMouseAccelerationType), &multiplier);
if (kren_ret == kIOReturnSuccess) {
// Calculate the greatest common factor.
unsigned long denominator = 1000000, d = denominator;
unsigned long numerator = multiplier * denominator, gcf = numerator;
while (d != 0) {
unsigned long i = gcf % d;
gcf = d;
d = i;
}
value = denominator / gcf;
successful = true;
logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetAccelerationWithKey: %li.\n",
__FUNCTION__, __LINE__, value);
}
}
}
}
#endif
#ifdef USE_COREFOUNDATION
if (!successful) {
CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("com.apple.mouse.scaling"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberSInt32Type, &multiplier)) {
value = (long) multiplier;
logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
__FUNCTION__, __LINE__, value);
}
}
}
#endif
return value;
}
UIOHOOK_API long int hook_get_pointer_acceleration_threshold() {
#if defined USE_COREFOUNDATION
bool successful = false;
SInt32 threshold;
#endif
long int value = -1;
#ifdef USE_COREFOUNDATION
if (!successful) {
CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("mouseDriverMaxSpeed"), CFSTR("com.apple.universalaccess"), kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberSInt32Type, &threshold)) {
value = (long) threshold;
logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
__FUNCTION__, __LINE__, value);
}
}
}
#endif
return value;
}
UIOHOOK_API long int hook_get_pointer_sensitivity() {
#ifdef USE_IOKIT
bool successful = false;
double sensitivity;
#endif
long int value = -1;
#ifdef USE_IOKIT
if (!successful) {
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
if (service) {
kern_return_t kren_ret = kIOReturnError;
io_connect_t connection;
kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
if (kren_ret == kIOReturnSuccess) {
// IOByteCount size = sizeof(multiplier);
kren_ret = IOHIDGetAccelerationWithKey(connection, CFSTR(kIOHIDMouseAccelerationType), &sensitivity);
if (kren_ret == kIOReturnSuccess) {
// Calculate the greatest common factor.
unsigned long denominator = 1000000, d = denominator;
unsigned long numerator = sensitivity * denominator, gcf = numerator;
while (d != 0) {
unsigned long i = gcf % d;
gcf = d;
d = i;
}
value = numerator / gcf;
successful = true;
logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetAccelerationWithKey: %li.\n",
__FUNCTION__, __LINE__, value);
}
}
}
}
#endif
return value;
}
UIOHOOK_API long int hook_get_multi_click_time() {
#if defined USE_IOKIT || defined USE_COREFOUNDATION || defined USE_CARBON_LEGACY
bool successful = false;
#if defined USE_IOKIT || defined USE_CARBON_LEGACY
// This needs to be defined only if we have USE_IOKIT or USE_CARBON_LEGACY.
SInt64 time;
#endif
#endif
long int value = -1;
#ifdef USE_IOKIT
if (!successful) {
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
if (service) {
kern_return_t kren_ret = kIOReturnError;
io_connect_t connection;
kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
if (kren_ret == kIOReturnSuccess) {
IOByteCount size = sizeof(time);
kren_ret = IOHIDGetParameter(connection, CFSTR(kIOHIDClickTimeKey), (IOByteCount) sizeof(time), &time, &size);
if (kren_ret == kIOReturnSuccess) {
/* This is in some undefined unit of time that if we happen
* to multiply by 900 gives us the time in milliseconds. We
* add 0.5 to the result so that when we cast to long we
* actually get a rounded result. Saves the math.h depend.
*/
value = (long) (900.0 * ((double) time) / 1000.0 / 1000.0 / 1000.0 + 0.5);
successful = true;
logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetParameter: %li.\n",
__FUNCTION__, __LINE__, value);
}
}
}
}
#endif
#ifdef USE_COREFOUNDATION
if (!successful) {
Float32 clicktime;
CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("com.apple.mouse.doubleClickThreshold"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberFloat32Type, &clicktime)) {
/* This is in some undefined unit of time that if we happen
* to multiply by 900 gives us the time in milliseconds. It is
* completely possible that this value is in seconds and should be
* multiplied by 1000 but because IOKit values are undocumented and
* I have no idea what a Carbon 'tick' is so there really is no way
* to confirm this.
*/
value = (long) (clicktime * 900);
logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
__FUNCTION__, __LINE__, value);
}
}
}
#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_INFO, "%s [%u]: GetDblTime: %li.\n",
__FUNCTION__, __LINE__, value);
}
}
#endif
return value;
}
// Create a shared object constructor.
__attribute__ ((constructor))
void on_library_load() {
// Initialize Native Input Functions.
load_input_helper();
}
// Create a shared object destructor.
__attribute__ ((destructor))
void on_library_unload() {
// Disable the event hook.
//hook_stop();
// Cleanup native input functions.
unload_input_helper();
}

16
event/hook/logger.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef _included_logger
#define _included_logger
// #include <uiohook.h>
#include "uiohook.h"
#include <stdbool.h>
#ifndef __FUNCTION__
#define __FUNCTION__ __func__
#endif
// logger(level, message)
extern logger_t logger;
#endif

54
event/hook/logger_c.h Normal file
View File

@ -0,0 +1,54 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
// #include <uiohook.h>
#include "uiohook.h"
#include "logger.h"
static bool default_logger(unsigned int level, const char *format, ...) {
bool status = false;
#ifndef USE_QUIET
va_list args;
switch (level) {
#ifdef USE_DEBUG
case LOG_LEVEL_DEBUG:
#endif
case LOG_LEVEL_INFO:
va_start(args, format);
status = vfprintf(stdout, format, args) >= 0;
va_end(args);
break;
case LOG_LEVEL_WARN:
case LOG_LEVEL_ERROR:
va_start(args, format);
status = vfprintf(stderr, format, args) >= 0;
va_end(args);
break;
}
#endif
return status;
}
// Current logger function pointer, this should never be null.
// FIXME This should be static and wrapped with a public facing function.
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;
}
}

442
event/hook/uiohook.h Normal file
View File

@ -0,0 +1,442 @@
#ifndef __UIOHOOK_H
#define __UIOHOOK_H
// #include "../../base/os.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
/* Begin Error Codes */
#define UIOHOOK_SUCCESS 0x00
#define UIOHOOK_FAILURE 0x01
// System level errors.
#define UIOHOOK_ERROR_OUT_OF_MEMORY 0x02
// Unix specific errors.
#define UIOHOOK_ERROR_X_OPEN_DISPLAY 0x20
#define UIOHOOK_ERROR_X_RECORD_NOT_FOUND 0x21
#define UIOHOOK_ERROR_X_RECORD_ALLOC_RANGE 0x22
#define UIOHOOK_ERROR_X_RECORD_CREATE_CONTEXT 0x23
#define UIOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT 0x24
#define UIOHOOK_ERROR_X_RECORD_GET_CONTEXT 0x25
// Windows specific errors.
#define UIOHOOK_ERROR_SET_WINDOWS_HOOK_EX 0x30
#define UIOHOOK_ERROR_GET_MODULE_HANDLE 0x31
// Darwin specific errors.
#define UIOHOOK_ERROR_AXAPI_DISABLED 0x40
#define UIOHOOK_ERROR_CREATE_EVENT_PORT 0x41
#define UIOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE 0x42
#define UIOHOOK_ERROR_GET_RUNLOOP 0x43
#define UIOHOOK_ERROR_CREATE_OBSERVER 0x44
/* End Error Codes */
/* Begin Log Levels and Function Prototype */
typedef enum _log_level {
LOG_LEVEL_DEBUG = 1,
LOG_LEVEL_INFO,
LOG_LEVEL_WARN,
LOG_LEVEL_ERROR
} log_level;
// Logger callback function prototype.
typedef bool (*logger_t)(unsigned int, const char *, ...);
/* End Log Levels and Function Prototype */
/* Begin Virtual Event Types and Data Structures */
typedef enum _event_type {
EVENT_HOOK_ENABLED = 1,
EVENT_HOOK_DISABLED,
EVENT_KEY_TYPED,
EVENT_KEY_PRESSED,
EVENT_KEY_RELEASED,
EVENT_MOUSE_CLICKED,
EVENT_MOUSE_PRESSED,
EVENT_MOUSE_RELEASED,
EVENT_MOUSE_MOVED,
EVENT_MOUSE_DRAGGED,
EVENT_MOUSE_WHEEL
} event_type;
typedef struct _screen_data {
uint8_t number;
int16_t x;
int16_t y;
uint16_t width;
uint16_t height;
} screen_data;
typedef struct _keyboard_event_data {
uint16_t keycode;
uint16_t rawcode;
uint16_t keychar;
// char *keychar;
} keyboard_event_data,
key_pressed_event_data,
key_released_event_data,
key_typed_event_data;
typedef struct _mouse_event_data {
uint16_t button;
uint16_t clicks;
int16_t x;
int16_t y;
} mouse_event_data,
mouse_pressed_event_data,
mouse_released_event_data,
mouse_clicked_event_data;
typedef struct _mouse_wheel_event_data {
uint16_t clicks;
int16_t x;
int16_t y;
uint8_t type;
uint16_t amount;
int16_t rotation;
uint8_t direction;
} mouse_wheel_event_data;
typedef struct _uiohook_event {
event_type type;
uint64_t time;
uint16_t mask;
uint16_t reserved;
union {
keyboard_event_data keyboard;
mouse_event_data mouse;
mouse_wheel_event_data wheel;
} data;
} uiohook_event;
typedef void (*dispatcher_t)(uiohook_event *const);
/* End Virtual Event Types and Data Structures */
/* Begin Virtual Key Codes */
#define VC_ESCAPE 0x0001
// Begin Function Keys
#define VC_F1 0x003B
#define VC_F2 0x003C
#define VC_F3 0x003D
#define VC_F4 0x003E
#define VC_F5 0x003F
#define VC_F6 0x0040
#define VC_F7 0x0041
#define VC_F8 0x0042
#define VC_F9 0x0043
#define VC_F10 0x0044
#define VC_F11 0x0057
#define VC_F12 0x0058
#define VC_F13 0x005B
#define VC_F14 0x005C
#define VC_F15 0x005D
#define VC_F16 0x0063
#define VC_F17 0x0064
#define VC_F18 0x0065
#define VC_F19 0x0066
#define VC_F20 0x0067
#define VC_F21 0x0068
#define VC_F22 0x0069
#define VC_F23 0x006A
#define VC_F24 0x006B
// End Function Keys
// Begin Alphanumeric Zone
#define VC_BACKQUOTE 0x0029
#define VC_1 0x0002
#define VC_2 0x0003
#define VC_3 0x0004
#define VC_4 0x0005
#define VC_5 0x0006
#define VC_6 0x0007
#define VC_7 0x0008
#define VC_8 0x0009
#define VC_9 0x000A
#define VC_0 0x000B
#define VC_MINUS 0x000C // '-'
#define VC_EQUALS 0x000D // '='
#define VC_BACKSPACE 0x000E
#define VC_TAB 0x000F
#define VC_CAPS_LOCK 0x003A
#define VC_A 0x001E
#define VC_B 0x0030
#define VC_C 0x002E
#define VC_D 0x0020
#define VC_E 0x0012
#define VC_F 0x0021
#define VC_G 0x0022
#define VC_H 0x0023
#define VC_I 0x0017
#define VC_J 0x0024
#define VC_K 0x0025
#define VC_L 0x0026
#define VC_M 0x0032
#define VC_N 0x0031
#define VC_O 0x0018
#define VC_P 0x0019
#define VC_Q 0x0010
#define VC_R 0x0013
#define VC_S 0x001F
#define VC_T 0x0014
#define VC_U 0x0016
#define VC_V 0x002F
#define VC_W 0x0011
#define VC_X 0x002D
#define VC_Y 0x0015
#define VC_Z 0x002C
#define VC_OPEN_BRACKET 0x001A // '['
#define VC_CLOSE_BRACKET 0x001B // ']'
#define VC_BACK_SLASH 0x002B // '\'
#define VC_SEMICOLON 0x0027 // ';'
#define VC_QUOTE 0x0028
#define VC_ENTER 0x001C
#define VC_COMMA 0x0033 // ','
#define VC_PERIOD 0x0034 // '.'
#define VC_SLASH 0x0035 // '/'
#define VC_SPACE 0x0039
// End Alphanumeric Zone
#define VC_PRINTSCREEN 0x0E37
#define VC_SCROLL_LOCK 0x0046
#define VC_PAUSE 0x0E45
// Begin Edit Key Zone
#define VC_INSERT 0x0E52
#define VC_DELETE 0x0E53
#define VC_HOME 0x0E47
#define VC_END 0x0E4F
#define VC_PAGE_UP 0x0E49
#define VC_PAGE_DOWN 0x0E51
// End Edit Key Zone
// Begin Cursor Key Zone
#define VC_UP 0xE048
#define VC_LEFT 0xE04B
#define VC_CLEAR 0xE04C
#define VC_RIGHT 0xE04D
#define VC_DOWN 0xE050
// End Cursor Key Zone
// Begin Numeric Zone
#define VC_NUM_LOCK 0x0045
#define VC_KP_DIVIDE 0x0E35
#define VC_KP_MULTIPLY 0x0037
#define VC_KP_SUBTRACT 0x004A
#define VC_KP_EQUALS 0x0E0D
#define VC_KP_ADD 0x004E
#define VC_KP_ENTER 0x0E1C
#define VC_KP_SEPARATOR 0x0053
#define VC_KP_1 0x004F
#define VC_KP_2 0x0050
#define VC_KP_3 0x0051
#define VC_KP_4 0x004B
#define VC_KP_5 0x004C
#define VC_KP_6 0x004D
#define VC_KP_7 0x0047
#define VC_KP_8 0x0048
#define VC_KP_9 0x0049
#define VC_KP_0 0x0052
#define VC_KP_END 0xEE00 | VC_KP_1
#define VC_KP_DOWN 0xEE00 | VC_KP_2
#define VC_KP_PAGE_DOWN 0xEE00 | VC_KP_3
#define VC_KP_LEFT 0xEE00 | VC_KP_4
#define VC_KP_CLEAR 0xEE00 | VC_KP_5
#define VC_KP_RIGHT 0xEE00 | VC_KP_6
#define VC_KP_HOME 0xEE00 | VC_KP_7
#define VC_KP_UP 0xEE00 | VC_KP_8
#define VC_KP_PAGE_UP 0xEE00 | VC_KP_9
#define VC_KP_INSERT 0xEE00 | VC_KP_0
#define VC_KP_DELETE 0xEE00 | VC_KP_SEPARATOR
// End Numeric Zone
// Begin Modifier and Control Keys
#define VC_SHIFT_L 0x002A
#define VC_SHIFT_R 0x0036
#define VC_CONTROL_L 0x001D
#define VC_CONTROL_R 0x0E1D
#define VC_ALT_L 0x0038 // Option or Alt Key
#define VC_ALT_R 0x0E38 // Option or Alt Key
#define VC_META_L 0x0E5B // Windows or Command Key
#define VC_META_R 0x0E5C // Windows or Command Key
#define VC_CONTEXT_MENU 0x0E5D
// End Modifier and Control Keys
// Begin Media Control Keys
#define VC_POWER 0xE05E
#define VC_SLEEP 0xE05F
#define VC_WAKE 0xE063
#define VC_MEDIA_PLAY 0xE022
#define VC_MEDIA_STOP 0xE024
#define VC_MEDIA_PREVIOUS 0xE010
#define VC_MEDIA_NEXT 0xE019
#define VC_MEDIA_SELECT 0xE06D
#define VC_MEDIA_EJECT 0xE02C
#define VC_VOLUME_MUTE 0xE020
#define VC_VOLUME_UP 0xE030
#define VC_VOLUME_DOWN 0xE02E
#define VC_APP_MAIL 0xE06C
#define VC_APP_CALCULATOR 0xE021
#define VC_APP_MUSIC 0xE03C
#define VC_APP_PICTURES 0xE064
#define VC_BROWSER_SEARCH 0xE065
#define VC_BROWSER_HOME 0xE032
#define VC_BROWSER_BACK 0xE06A
#define VC_BROWSER_FORWARD 0xE069
#define VC_BROWSER_STOP 0xE068
#define VC_BROWSER_REFRESH 0xE067
#define VC_BROWSER_FAVORITES 0xE066
// End Media Control Keys
// Begin Japanese Language Keys
#define VC_KATAKANA 0x0070
#define VC_UNDERSCORE 0x0073
#define VC_FURIGANA 0x0077
#define VC_KANJI 0x0079
#define VC_HIRAGANA 0x007B
#define VC_YEN 0x007D
#define VC_KP_COMMA 0x007E
// End Japanese Language Keys
// Begin Sun keyboards
#define VC_SUN_HELP 0xFF75
#define VC_SUN_STOP 0xFF78
#define VC_SUN_PROPS 0xFF76
#define VC_SUN_FRONT 0xFF77
#define VC_SUN_OPEN 0xFF74
#define VC_SUN_FIND 0xFF7E
#define VC_SUN_AGAIN 0xFF79
#define VC_SUN_UNDO 0xFF7A
#define VC_SUN_COPY 0xFF7C
#define VC_SUN_INSERT 0xFF7D
#define VC_SUN_CUT 0xFF7B
// End Sun keyboards
#define VC_UNDEFINED 0x0000 // KeyCode Unknown
#define CHAR_UNDEFINED 0xFFFF // CharCode Unknown
/* End Virtual Key Codes */
/* Begin Virtual Modifier Masks */
#define MASK_SHIFT_L 1 << 0
#define MASK_CTRL_L 1 << 1
#define MASK_META_L 1 << 2
#define MASK_ALT_L 1 << 3
#define MASK_SHIFT_R 1 << 4
#define MASK_CTRL_R 1 << 5
#define MASK_META_R 1 << 6
#define MASK_ALT_R 1 << 7
#define MASK_SHIFT MASK_SHIFT_L | MASK_SHIFT_R
#define MASK_CTRL MASK_CTRL_L | MASK_CTRL_R
#define MASK_META MASK_META_L | MASK_META_R
#define MASK_ALT MASK_ALT_L | MASK_ALT_R
#define MASK_BUTTON1 1 << 8
#define MASK_BUTTON2 1 << 9
#define MASK_BUTTON3 1 << 10
#define MASK_BUTTON4 1 << 11
#define MASK_BUTTON5 1 << 12
#define MASK_NUM_LOCK 1 << 13
#define MASK_CAPS_LOCK 1 << 14
#define MASK_SCROLL_LOCK 1 << 15
/* End Virtual Modifier Masks */
/* Begin Virtual Mouse Buttons */
#define MOUSE_NOBUTTON 0 // Any Button
#define MOUSE_BUTTON1 1 // Left Button
#define MOUSE_BUTTON2 2 // Right Button
#define MOUSE_BUTTON3 3 // Middle Button
#define MOUSE_BUTTON4 4 // Extra Mouse Button
#define MOUSE_BUTTON5 5 // Extra Mouse Button
#define WHEEL_UNIT_SCROLL 1
#define WHEEL_BLOCK_SCROLL 2
#define WHEEL_VERTICAL_DIRECTION 3
#define WHEEL_HORIZONTAL_DIRECTION 4
/* End Virtual Mouse Buttons */
#ifdef _WIN32
#define UIOHOOK_API __declspec(dllexport)
#else
#define UIOHOOK_API
#endif
#ifdef __cplusplus
extern "C" {
#endif
// Set the logger callback functions.
UIOHOOK_API void hook_set_logger_proc(logger_t logger_proc);
// 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);
// Insert the event hook.
UIOHOOK_API int hook_run();
// Withdraw the event hook.
UIOHOOK_API int hook_stop();
// Retrieves an array of screen data for each available monitor.
UIOHOOK_API screen_data* hook_create_screen_info(unsigned char *count);
// Retrieves the keyboard auto repeat rate.
UIOHOOK_API long int hook_get_auto_repeat_rate();
// Retrieves the keyboard auto repeat delay.
UIOHOOK_API long int hook_get_auto_repeat_delay();
// Retrieves the mouse acceleration multiplier.
UIOHOOK_API long int hook_get_pointer_acceleration_multiplier();
// Retrieves the mouse acceleration threshold.
UIOHOOK_API long int hook_get_pointer_acceleration_threshold();
// Retrieves the mouse sensitivity.
UIOHOOK_API long int hook_get_pointer_sensitivity();
// Retrieves the double/triple click interval.
UIOHOOK_API long int hook_get_multi_click_time();
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,104 @@
/***********************************************************************
***********************************************************************/
#ifndef _included_input_helper
#define _included_input_helper
#include <limits.h>
#include <windows.h>
#ifndef LPFN_ISWOW64PROCESS
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
#endif
typedef void* (CALLBACK *KbdLayerDescriptor) (VOID);
#define CAPLOK 0x01
#define WCH_NONE 0xF000
#define WCH_DEAD 0xF001
#ifndef WM_MOUSEHWHEEL
#define WM_MOUSEHWHEEL 0x020E
#endif
typedef struct _VK_TO_WCHARS {
BYTE VirtualKey;
BYTE Attributes;
WCHAR wch[];
} VK_TO_WCHARS, *PVK_TO_WCHARS;
typedef struct _LIGATURE {
BYTE VirtualKey;
WORD ModificationNumber;
WCHAR wch[];
} LIGATURE, *PLIGATURE;
typedef struct _VK_TO_BIT {
BYTE Vk;
BYTE ModBits;
} VK_TO_BIT, *PVK_TO_BIT;
typedef struct _MODIFIERS {
PVK_TO_BIT pVkToBit; // __ptr64
WORD wMaxModBits;
BYTE ModNumber[];
} MODIFIERS, *PMODIFIERS;
typedef struct _VSC_VK {
BYTE Vsc;
USHORT Vk;
} VSC_VK, *PVSC_VK;
typedef struct _VK_TO_WCHAR_TABLE {
PVK_TO_WCHARS pVkToWchars; // __ptr64
BYTE nModifications;
BYTE cbSize;
} VK_TO_WCHAR_TABLE, *PVK_TO_WCHAR_TABLE;
typedef struct _DEADKEY {
DWORD dwBoth;
WCHAR wchComposed;
USHORT uFlags;
} DEADKEY, *PDEADKEY;
typedef struct _VSC_LPWSTR {
BYTE vsc;
WCHAR *pwsz; // __ptr64
} VSC_LPWSTR, *PVSC_LPWSTR;
typedef struct tagKbdLayer {
PMODIFIERS pCharModifiers; // __ptr64
PVK_TO_WCHAR_TABLE pVkToWcharTable; // __ptr64
PDEADKEY pDeadKey; // __ptr64
PVSC_LPWSTR pKeyNames; // __ptr64
PVSC_LPWSTR pKeyNamesExt; // __ptr64
WCHAR **pKeyNamesDead; // __ptr64
USHORT *pusVSCtoVK; // __ptr64
BYTE bMaxVSCtoVK;
PVSC_VK pVSCtoVK_E0; // __ptr64
PVSC_VK pVSCtoVK_E1; // __ptr64
DWORD fLocaleFlags;
BYTE nLgMax;
BYTE cbLgEntry;
PLIGATURE pLigature; // __ptr64
DWORD dwType;
DWORD dwSubType;
} KBDTABLES, *PKBDTABLES; // __ptr64
extern SIZE_T keycode_to_unicode(DWORD keycode, PWCHAR buffer, SIZE_T size);
//extern DWORD unicode_to_keycode(wchar_t unicode);
extern unsigned short keycode_to_scancode(DWORD vk_code, DWORD flags);
extern DWORD scancode_to_keycode(unsigned short scancode);
// Initialize the locale list and wow64 pointer size.
extern int load_input_helper();
// Cleanup the initialized locales.
extern int unload_input_helper();
#endif

View File

@ -0,0 +1,833 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
// #include <uiohook.h>
#include "../uiohook.h"
#include <windows.h>
#include "../logger_c.h"
#include "input_helper.h"
static const uint16_t keycode_scancode_table[][2] = {
/* idx { vk_code, scancode }, */
/* 0 */ { VC_UNDEFINED, 0x0000 }, // 0x00
/* 1 */ { MOUSE_BUTTON1, VK_ESCAPE }, // 0x01
/* 2 */ { MOUSE_BUTTON2, 0x0031 }, // 0x02
/* 3 */ { VC_UNDEFINED, 0x0032 }, // 0x03 VK_CANCEL
/* 4 */ { MOUSE_BUTTON3, 0x0033 }, // 0x04
/* 5 */ { MOUSE_BUTTON4, 0x0034 }, // 0x05
/* 6 */ { MOUSE_BUTTON5, 0x0035 }, // 0x06
/* 7 */ { VC_UNDEFINED, 0x0036 }, // 0x07 Undefined
/* 8 */ { VC_BACKSPACE, 0x0037 }, // 0x08 VK_BACK
/* 9 */ { VC_TAB, 0x0038 }, // 0x09 VK_TAB
/* 10 */ { VC_UNDEFINED, 0x0039 }, // 0x0A Reserved
/* 11 */ { VC_UNDEFINED, 0x0030 }, // 0x0B Reserved
/* 12 */ { VC_CLEAR, VK_OEM_MINUS }, // 0x0C VK_CLEAR
/* 13 */ { VC_ENTER, VK_OEM_PLUS }, // 0x0D VK_RETURN
/* 14 */ { VC_UNDEFINED, VK_BACK }, // 0x0E Undefined
/* 15 */ { VC_UNDEFINED, VK_TAB }, // 0x0F Undefined
/* 16 */ { VC_SHIFT_L, 0x0051 }, // 0x10 VK_SHIFT
/* 17 */ { VC_CONTROL_L, 0x0057 }, // 0x11 VK_CONTROL
/* 18 */ { VC_ALT_L, 0x0045 }, // 0x12 VK_MENU ALT key
/* 19 */ { VC_PAUSE, 0x0052 }, // 0x13 VK_PAUSE
/* 20 */ { VC_CAPS_LOCK, 0x0054 }, // 0x14 VK_CAPITAL CAPS LOCK key
/* 21 */ { VC_KATAKANA, 0x0059 }, // 0x15 VK_KANA IME Kana mode
/* 22 */ { VC_UNDEFINED, 0x0055 }, // 0x16 Undefined
/* 23 */ { VC_UNDEFINED, 0x0049 }, // 0x17 VK_JUNJA IME Junja mode
/* 24 */ { VC_UNDEFINED, 0x004F }, // 0x18 VK_FINAL
/* 25 */ { VC_KANJI, 0x0050 }, // 0x19 VK_KANJI / VK_HANJA IME Kanji / Hanja mode
/* 26 */ { VC_UNDEFINED, 0x00DB }, // 0x1A Undefined
/* 27 */ { VC_ESCAPE, 0x00DD }, // 0x1B VK_ESCAPE ESC key
/* 28 */ { VC_UNDEFINED, VK_RETURN }, // 0x1C VK_CONVERT IME convert// 0x1C
/* 29 */ { VC_UNDEFINED, VK_LCONTROL }, // 0x1D VK_NONCONVERT IME nonconvert
/* 30 */ { VC_UNDEFINED, 0x0041 }, // 0x1E VK_ACCEPT IME accept
/* 31 */ { VC_UNDEFINED, 0x0053 }, // 0x1F VK_MODECHANGE IME mode change request
/* 32 */ { VC_SPACE, 0x0044 }, // 0x20 VK_SPACE SPACEBAR
/* 33 */ { VC_PAGE_UP, 0x0046 }, // 0x21 VK_PRIOR PAGE UP key
/* 34 */ { VC_PAGE_DOWN, 0x0047 }, // 0x22 VK_NEXT PAGE DOWN key
/* 35 */ { VC_END, 0x0048 }, // 0x23 VK_END END key
/* 36 */ { VC_HOME, 0x004A }, // 0x24 VK_HOME HOME key
/* 37 */ { VC_LEFT, 0x004B }, // 0x25 VK_LEFT LEFT ARROW key
/* 38 */ { VC_UP, 0x004C }, // 0x26 VK_UP UP ARROW key
/* 39 */ { VC_RIGHT, VK_OEM_1 }, // 0x27 VK_RIGHT RIGHT ARROW key
/* 40 */ { VC_DOWN, VK_OEM_7 }, // 0x28 VK_DOWN DOWN ARROW key
/* 41 */ { VC_UNDEFINED, VK_OEM_3 }, // 0x29 VK_SELECT SELECT key
/* 42 */ { VC_UNDEFINED, VK_LSHIFT }, // 0x2A VK_PRINT PRINT key
/* 43 */ { VC_UNDEFINED, VK_OEM_5 }, // 0x2B VK_EXECUTE EXECUTE key
/* 44 */ { VC_PRINTSCREEN, 0x005A }, // 0x2C VK_SNAPSHOT PRINT SCREEN key
/* 45 */ { VC_INSERT, 0x0058 }, // 0x2D VK_INSERT INS key
/* 46 */ { VC_DELETE, 0x0043 }, // 0x2E VK_DELETE DEL key
/* 47 */ { VC_UNDEFINED, 0x0056 }, // 0x2F VK_HELP HELP key
/* 48 */ { VC_0, 0x0042 }, // 0x30 0 key
/* 49 */ { VC_1, 0x004E }, // 0x31 1 key
/* 50 */ { VC_2, 0x004D }, // 0x32 2 key
/* 51 */ { VC_3, VK_OEM_COMMA }, // 0x33 3 key
/* 52 */ { VC_4, VK_OEM_PERIOD }, // 0x34 4 key
/* 53 */ { VC_5, VK_OEM_2 }, // 0x35 5 key
/* 54 */ { VC_6, VK_RSHIFT }, // 0x36 6 key
/* 55 */ { VC_7, VK_MULTIPLY }, // 0x37 7 key
/* 56 */ { VC_8, VK_LMENU }, // 0x38 8 key
/* 57 */ { VC_9, VK_SPACE }, // 0x39 9 key
/* 58 */ { VC_UNDEFINED, VK_CAPITAL }, // 0x3A Undefined
/* 59 */ { VC_UNDEFINED, VK_F1 }, // 0x3B Undefined
/* 60 */ { VC_UNDEFINED, VK_F2 }, // 0x3C Undefined
/* 61 */ { VC_UNDEFINED, VK_F3 }, // 0x3D Undefined
/* 62 */ { VC_UNDEFINED, VK_F4 }, // 0x3E Undefined
/* 63 */ { VC_UNDEFINED, VK_F5 }, // 0x3F Undefined
/* 64 */ { VC_UNDEFINED, VK_F6 }, // 0x40 Undefined
/* 65 */ { VC_A, VK_F7 }, // 0x41 A key
/* 66 */ { VC_B, VK_F8 }, // 0x42 B key
/* 67 */ { VC_C, VK_F9 }, // 0x43 C key
/* 68 */ { VC_D, VK_F10 }, // 0x44 D key
/* 69 */ { VC_E, VK_NUMLOCK }, // 0x45 E key
/* 70 */ { VC_F, VK_SCROLL }, // 0x46 F key
/* 71 */ { VC_G, VK_NUMPAD7 }, // 0x47 G key
/* 72 */ { VC_H, VK_NUMPAD8 }, // 0x48 H key
/* 73 */ { VC_I, VK_NUMPAD9 }, // 0x49 I key
/* 74 */ { VC_J, VK_SUBTRACT }, // 0x4A J key
/* 75 */ { VC_K, VK_NUMPAD4 }, // 0x4B K key
/* 76 */ { VC_L, VK_NUMPAD5 }, // 0x4C L key
/* 77 */ { VC_M, VK_NUMPAD6 }, // 0x4D M key
/* 78 */ { VC_N, VK_ADD }, // 0x4E N key
/* 79 */ { VC_O, VK_NUMPAD1 }, // 0x4F O key
/* 80 */ { VC_P, VK_NUMPAD2 }, // 0x50 P key
/* 81 */ { VC_Q, VK_NUMPAD3 }, // 0x51 Q key
/* 82 */ { VC_R, VK_NUMPAD0 }, // 0x52 R key
/* 83 */ { VC_S, VK_DECIMAL }, // 0x53 S key
/* 84 */ { VC_T, 0x0000 }, // 0x54 T key
/* 85 */ { VC_U, 0x0000 }, // 0x55 U key
/* 86 */ { VC_V, 0x0000 }, // 0x56 V key
/* 87 */ { VC_W, VK_F11 }, // 0x57 W key
/* 88 */ { VC_X, VK_F12 }, // 0x58 X key
/* 89 */ { VC_Y, 0x0000 }, // 0x59 Y key
/* 90 */ { VC_Z, 0x0000 }, // 0x5A Z key
/* 91 */ { VC_META_L, VK_F13 }, // 0x5B VK_LWIN Left Windows key (Natural keyboard)
/* 92 */ { VC_META_R, VK_F14 }, // 0x5C VK_RWIN Right Windows key (Natural keyboard)
/* 93 */ { VC_CONTEXT_MENU, VK_F15 }, // 0x5D VK_APPS Applications key (Natural keyboard)
/* 94 */ { VC_UNDEFINED, 0x0000 }, // 0x5E Reserved
/* 95 */ { VC_SLEEP, 0x0000 }, // 0x5F VK_SLEEP Computer Sleep key
/* 96 */ { VC_KP_0, 0x0000 }, // 0x60 VK_NUMPAD0 Numeric keypad 0 key
/* 97 */ { VC_KP_1, 0x0000 }, // 0x61 VK_NUMPAD1 Numeric keypad 1 key
/* 98 */ { VC_KP_2, 0x0000 }, // 0x62 VK_NUMPAD2 Numeric keypad 2 key
/* 99 */ { VC_KP_3, VK_F16 }, // 0x63 VK_NUMPAD3 Numeric keypad 3 key
/* 100 */ { VC_KP_4, VK_F17 }, // 0x64 VK_NUMPAD4 Numeric keypad 4 key
/* 101 */ { VC_KP_5, VK_F18 }, // 0x65 VK_NUMPAD5 Numeric keypad 5 key
/* 102 */ { VC_KP_6, VK_F19 }, // 0x66 VK_NUMPAD6 Numeric keypad 6 key
/* 103 */ { VC_KP_7, VK_F20 }, // 0x67 VK_NUMPAD7 Numeric keypad 7 key
/* 104 */ { VC_KP_8, VK_F21 }, // 0x68 VK_NUMPAD8 Numeric keypad 8 key
/* 105 */ { VC_KP_9, VK_F22 }, // 0x69 VK_NUMPAD9 Numeric keypad 9 key
/* 106 */ { VC_KP_MULTIPLY, VK_F23 }, // 0x6A VK_MULTIPLY Multiply key
/* 107 */ { VC_KP_ADD, VK_F24 }, // 0x6B VK_ADD Add key
/* 108 */ { VC_UNDEFINED, 0x0000 }, // 0x6C VK_SEPARATOR Separator key
/* 109 */ { VC_KP_SUBTRACT, 0x0000 }, // 0x6D VK_SUBTRACT Subtract key
/* 110 */ { VC_KP_SEPARATOR, 0x0000 }, // 0x6E VK_DECIMAL Decimal key
/* 111 */ { VC_KP_DIVIDE, 0x0000 }, // 0x6F VK_DIVIDE Divide key
/* 112 */ { VC_F1, VK_KANA }, // 0x70 VK_F1 F1 key
/* 113 */ { VC_F2, 0x0000 }, // 0x71 VK_F2 F2 key
/* 114 */ { VC_F3, 0x0000 }, // 0x72 VK_F3 F3 key
/* 115 */ { VC_F4, 0x0000 }, // 0x73 VK_F4 F4 key
/* 116 */ { VC_F5, 0x0000 }, // 0x74 VK_F5 F5 key
/* 117 */ { VC_F6, 0x0000 }, // 0x75 VK_F6 F6 key
/* 118 */ { VC_F7, 0x0000 }, // 0x76 VK_F7 F7 key
/* 119 */ { VC_F8, 0x0000 }, // 0x77 VK_F8 F8 key
/* 120 */ { VC_F9, 0x0000 }, // 0x78 VK_F9 F9 key
/* 121 */ { VC_F10, VK_KANJI }, // 0x79 VK_F10 F10 key
/* 122 */ { VC_F11, 0x0000 }, // 0x7A VK_F11 F11 key
/* 123 */ { VC_F12, 0x0000 }, // 0x7B VK_F12 F12 key
/* 124 */ { VC_F13, 0x0000 }, // 0x7C VK_F13 F13 key
/* 125 */ { VC_F14, VK_OEM_8 }, // 0x7D VK_F14 F14 key
/* 126 */ { VC_F15, 0x0000 }, // 0x7E VK_F15 F15 key
/* 127 */ { VC_F16, 0x0000 }, // 0x7F VK_F16 F16 key
// No Offset Offset (i & 0x007F) | 0x80
/* 128 */ { VC_F17, 0x0000 }, // 0x80 VK_F17 F17 key
/* 129 */ { VC_F18, 0x0000 }, // 0x81 VK_F18 F18 key
/* 130 */ { VC_F19, 0x0000 }, // 0x82 VK_F19 F19 key
/* 131 */ { VC_F20, 0x0000 }, // 0x83 VK_F20 F20 key
/* 132 */ { VC_F21, 0x0000 }, // 0x84 VK_F21 F21 key
/* 133 */ { VC_F22, 0x0000 }, // 0x85 VK_F22 F22 key
/* 134 */ { VC_F23, 0x0000 }, // 0x86 VK_F23 F23 key
/* 135 */ { VC_F24, 0x0000 }, // 0x87 VK_F24 F24 key
/* 136 */ { VC_UNDEFINED, 0x0000 }, // 0x88 Unassigned
/* 137 */ { VC_UNDEFINED, 0x0000 }, // 0x89 Unassigned
/* 138 */ { VC_UNDEFINED, 0x0000 }, // 0x8A Unassigned
/* 139 */ { VC_UNDEFINED, 0x0000 }, // 0x8B Unassigned
/* 140 */ { VC_UNDEFINED, 0x0000 }, // 0x8C Unassigned
/* 141 */ { VC_UNDEFINED, 0x0000 }, // 0x8D Unassigned
/* 142 */ { VC_UNDEFINED, 0x0000 }, // 0x8E Unassigned
/* 143 */ { VC_UNDEFINED, 0x0000 }, // 0x8F Unassigned
/* 144 */ { VC_NUM_LOCK, VK_MEDIA_PREV_TRACK }, // 0x90 VK_NUMLOCK NUM LOCK key
/* 145 */ { VC_SCROLL_LOCK, 0x0000 }, // 0x91 VK_SCROLL SCROLL LOCK key
/* 146 */ { VC_UNDEFINED, 0x0000 }, // 0x92 OEM specific
/* 147 */ { VC_UNDEFINED, 0x0000 }, // 0x93 OEM specific
/* 148 */ { VC_UNDEFINED, 0x0000 }, // 0x94 OEM specific
/* 149 */ { VC_UNDEFINED, 0x0000 }, // 0x95 OEM specific
/* 150 */ { VC_UNDEFINED, 0x0000 }, // 0x96 OEM specific
/* 151 */ { VC_UNDEFINED, 0x0000 }, // 0x97 Unassigned
/* 152 */ { VC_UNDEFINED, 0x0000 }, // 0x98 Unassigned
/* 153 */ { VC_UNDEFINED, VK_MEDIA_NEXT_TRACK }, // 0x99 Unassigned
/* 154 */ { VC_UNDEFINED, 0x0000 }, // 0x9A Unassigned
/* 155 */ { VC_UNDEFINED, 0x0000 }, // 0x9B Unassigned
/* 156 */ { VC_UNDEFINED, 0x0000 }, // 0x9C Unassigned
/* 157 */ { VC_UNDEFINED, VK_RCONTROL }, // 0x9D Unassigned
/* 158 */ { VC_UNDEFINED, 0x0000 }, // 0x9E Unassigned
/* 159 */ { VC_UNDEFINED, 0x0000 }, // 0x9F Unassigned
/* 160 */ { VC_SHIFT_L, VK_VOLUME_MUTE }, // 0xA0 VK_LSHIFT Left SHIFT key
/* 161 */ { VC_SHIFT_R, VK_LAUNCH_APP2 }, // 0xA1 VK_RSHIFT Right SHIFT key
/* 162 */ { VC_CONTROL_L, VK_MEDIA_PLAY_PAUSE }, // 0xA2 VK_LCONTROL Left CONTROL key
/* 163 */ { VC_CONTROL_R, 0x0000 }, // 0xA3 VK_RCONTROL Right CONTROL key
/* 164 */ { VC_ALT_L, VK_MEDIA_STOP }, // 0xA4 VK_LMENU Left MENU key
/* 165 */ { VC_ALT_R, 0x0000 }, // 0xA5 VK_RMENU Right MENU key
/* 166 */ { VC_BROWSER_BACK, 0x0000 }, // 0xA6 VK_BROWSER_BACK Browser Back key
/* 167 */ { VC_BROWSER_FORWARD, 0x0000 }, // 0xA7 VK_BROWSER_FORWARD Browser Forward key
/* 168 */ { VC_BROWSER_REFRESH, 0x0000 }, // 0xA8 VK_BROWSER_REFRESH Browser Refresh key
/* 169 */ { VC_BROWSER_STOP, 0x0000 }, // 0xA9 VK_BROWSER_STOP Browser Stop key
/* 170 */ { VC_BROWSER_SEARCH, 0x0000 }, // 0xAA VK_BROWSER_SEARCH Browser Search key
/* 171 */ { VC_BROWSER_FAVORITES, 0x0000 }, // 0xAB VK_BROWSER_FAVORITES Browser Favorites key
/* 172 */ { VC_BROWSER_HOME, 0x0000 }, // 0xAC VK_BROWSER_HOME Browser Start and Home key
/* 173 */ { VC_VOLUME_MUTE, 0x0000 }, // 0xAD VK_VOLUME_MUTE Volume Mute key
/* 174 */ { VC_VOLUME_DOWN, VK_VOLUME_DOWN }, // 0xAE VK_VOLUME_DOWN Volume Down key
/* 175 */ { VC_VOLUME_UP, 0x0000 }, // 0xAF VK_VOLUME_UP Volume Up key
/* 176 */ { VC_MEDIA_NEXT, VK_VOLUME_UP }, // 0xB0 VK_MEDIA_NEXT_TRACK Next Track key
/* 177 */ { VC_MEDIA_PREVIOUS, 0x0000 }, // 0xB1 VK_MEDIA_PREV_TRACK Previous Track key
/* 178 */ { VC_MEDIA_STOP, VK_BROWSER_HOME }, // 0xB2 VK_MEDIA_STOP Stop Media key
/* 179 */ { VC_MEDIA_PLAY, 0x0000 }, // 0xB3 VK_MEDIA_PLAY_PAUSE Play/Pause Media key
/* 180 */ { VC_UNDEFINED, 0x0000 }, // 0xB4 VK_LAUNCH_MAIL Start Mail key
/* 181 */ { VC_MEDIA_SELECT, VK_DIVIDE }, // 0xB5 VK_LAUNCH_MEDIA_SELECT Select Media key
/* 182 */ { VC_APP_MAIL, 0x0000 }, // 0xB6 VK_LAUNCH_APP1 Start Application 1 key
/* 183 */ { VC_APP_CALCULATOR, VK_SNAPSHOT }, // 0xB7 VK_LAUNCH_APP2 Start Application 2 key
/* 184 */ { VC_UNDEFINED, VK_RMENU }, // 0xB8 Reserved
/* 185 */ { VC_UNDEFINED, 0x0000 }, // 0xB9 Reserved
/* 186 */ { VC_SEMICOLON, 0x0000 }, // 0xBA VK_OEM_1 Varies by keyboard. For the US standard keyboard, the ';:' key
/* 187 */ { VC_EQUALS, 0x0000 }, // 0xBB VK_OEM_PLUS For any country/region, the '+' key
/* 188 */ { VC_COMMA, 0x00E6 }, // 0xBC VK_OEM_COMMA For any country/region, the ',' key
/* 189 */ { VC_MINUS, 0x0000 }, // 0xBD VK_OEM_MINUS For any country/region, the '-' key
/* 190 */ { VC_PERIOD, 0x0000 }, // 0xBE VK_OEM_PERIOD For any country/region, the '.' key
/* 191 */ { VC_SLASH, 0x0000 }, // 0xBF VK_OEM_2 Varies by keyboard. For the US standard keyboard, the '/?' key
/* 192 */ { VC_BACKQUOTE, 0x0000 }, // 0xC0 VK_OEM_3 Varies by keyboard. For the US standard keyboard, the '`~' key
/* 193 */ { VC_UNDEFINED, 0x0000 }, // 0xC1 Reserved
/* 194 */ { VC_UNDEFINED, 0x0000 }, // 0xC2 Reserved
/* 195 */ { VC_UNDEFINED, 0x0000 }, // 0xC3 Reserved
/* 196 */ { VC_UNDEFINED, 0x0000 }, // 0xC4 Reserved
/* 197 */ { VC_UNDEFINED, VK_PAUSE }, // 0xC5 Reserved
/* 198 */ { VC_UNDEFINED, 0x0000 }, // 0xC6 Reserved
/* 199 */ { VC_UNDEFINED, VK_HOME }, // 0xC7 Reserved
/* 200 */ { VC_UNDEFINED, VK_UP }, // 0xC8 Reserved
/* 201 */ { VC_UNDEFINED, VK_PRIOR }, // 0xC9 Reserved
/* 202 */ { VC_UNDEFINED, 0x0000 }, // 0xCA Reserved
/* 203 */ { VC_UNDEFINED, VK_LEFT }, // 0xCB Reserved
/* 204 */ { VC_UNDEFINED, VK_CLEAR }, // 0xCC Reserved
/* 205 */ { VC_UNDEFINED, VK_RIGHT }, // 0xCD Reserved
/* 206 */ { VC_UNDEFINED, 0x0000 }, // 0xCE Reserved
/* 207 */ { VC_UNDEFINED, VK_END }, // 0xCF Reserved
/* 208 */ { VC_UNDEFINED, VK_DOWN }, // 0xD0 Reserved
/* 209 */ { VC_UNDEFINED, VK_NEXT }, // 0xD1 Reserved
/* 210 */ { VC_UNDEFINED, VK_INSERT }, // 0xD2 Reserved
/* 211 */ { VC_UNDEFINED, VK_DELETE }, // 0xD3 Reserved
/* 212 */ { VC_UNDEFINED, 0x0000 }, // 0xD4 Reserved
/* 213 */ { VC_UNDEFINED, 0x0000 }, // 0xD5 Reserved
/* 214 */ { VC_UNDEFINED, 0x0000 }, // 0xD6 Reserved
/* 215 */ { VC_UNDEFINED, 0x0000 }, // 0xD7 Reserved
/* 216 */ { VC_UNDEFINED, 0x0000 }, // 0xD8 Unassigned
/* 217 */ { VC_UNDEFINED, 0x0000 }, // 0xD9 Unassigned
/* 218 */ { VC_UNDEFINED, 0x0000 }, // 0xDA Unassigned
/* 219 */ { VC_OPEN_BRACKET, VK_LWIN }, // 0xDB VK_OEM_4 Varies by keyboard. For the US standard keyboard, the '[{' key
/* 220 */ { VC_BACK_SLASH, VK_RWIN }, // 0xDC VK_OEM_5 Varies by keyboard. For the US standard keyboard, the '\|' key
/* 221 */ { VC_CLOSE_BRACKET, VK_APPS }, // 0xDD VK_OEM_6 Varies by keyboard. For the US standard keyboard, the ']}' key
/* 222 */ { VC_QUOTE, 0x0000 }, // 0xDE VK_OEM_7 Varies by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key
/* 223 */ { VC_YEN, VK_SLEEP }, // 0xDF VK_OEM_8 Varies by keyboard.
/* 224 */ { VC_UNDEFINED, 0x0000 }, // 0xE0 Reserved
/* 225 */ { VC_UNDEFINED, 0x0000 }, // 0xE1 OEM specific
/* 226 */ { VC_UNDEFINED, 0x0000 }, // 0xE2 VK_OEM_102 Either the angle bracket key or the backslash key on the RT 102-key keyboard
/* 227 */ { VC_UNDEFINED, 0x0000 }, // 0xE3 OEM specific
/* 228 */ { VC_UNDEFINED, 0x00E5 }, // 0xE4 VC_APP_PICTURES OEM specific
/* 229 */ { VC_APP_PICTURES, VK_BROWSER_SEARCH }, // 0xE5 VK_PROCESSKEY IME PROCESS key
/* 230 */ { VC_APP_MUSIC, VK_BROWSER_FAVORITES }, // 0xE6 OEM specific
/* 231 */ { VC_UNDEFINED, VK_BROWSER_REFRESH }, // 0xE7 VK_PACKET Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods.
/* 232 */ { VC_UNDEFINED, VK_BROWSER_STOP }, // 0xE8 Unassigned
/* 233 */ { VC_UNDEFINED, VK_BROWSER_FORWARD }, // 0xE9 OEM specific
/* 234 */ { VC_UNDEFINED, VK_BROWSER_BACK }, // 0xEA OEM specific
/* 235 */ { VC_UNDEFINED, 0x0000 }, // 0xEB OEM specific
/* 236 */ { VC_UNDEFINED, VK_LAUNCH_APP1 }, // 0xEC OEM specific
/* 237 */ { VC_UNDEFINED, VK_LAUNCH_MEDIA_SELECT }, // 0xED OEM specific
/* 238 */ { VC_UNDEFINED, 0x0000 }, // 0xEE OEM specific
/* 239 */ { VC_UNDEFINED, 0x0000 }, // 0xEF OEM specific
/* 240 */ { VC_UNDEFINED, 0x0000 }, // 0xF0 OEM specific
/* 241 */ { VC_UNDEFINED, 0x0000 }, // 0xF1 OEM specific
/* 242 */ { VC_UNDEFINED, 0x0000 }, // 0xF2 OEM specific
/* 243 */ { VC_UNDEFINED, 0x0000 }, // 0xF3 OEM specific
/* 244 */ { VC_UNDEFINED, 0x0000 }, // 0xF4 OEM specific
/* 245 */ { VC_UNDEFINED, 0x0000 }, // 0xF5 OEM specific
/* 246 */ { VC_UNDEFINED, 0x0000 }, // 0xF6 VK_ATTN Attn key
/* 247 */ { VC_UNDEFINED, 0x0000 }, // 0xF7 VK_CRSEL CrSel key
/* 248 */ { VC_UNDEFINED, 0x0000 }, // 0xF8 VK_EXSEL ExSel key
/* 249 */ { VC_UNDEFINED, 0x0000 }, // 0xF9 VK_EREOF Erase EOF key
/* 250 */ { VC_UNDEFINED, 0x0000 }, // 0xFA VK_PLAY Play key
/* 251 */ { VC_UNDEFINED, 0x0000 }, // 0xFB VK_ZOOM Zoom key
/* 252 */ { VC_UNDEFINED, 0x0000 }, // 0xFC VK_NONAME Reserved
/* 253 */ { VC_UNDEFINED, 0x0000 }, // 0xFD
/* 254 */ { VC_CLEAR, 0x0000 }, // 0xFE VK_OEM_CLEAR Clear key
/* 255 */ { VC_UNDEFINED, 0x0000 } // 0xFE Unassigned
};
unsigned short keycode_to_scancode(DWORD vk_code, DWORD flags) {
unsigned short scancode = VC_UNDEFINED;
// Check the vk_code is in range.
// NOTE vk_code >= 0 is assumed because DWORD is unsigned.
if (vk_code < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[0])) {
scancode = keycode_scancode_table[vk_code][0];
if (flags & LLKHF_EXTENDED) {
logger(LOG_LEVEL_WARN, "%s [%u]: EXTD2, vk_code %li\n",
__FUNCTION__, __LINE__, vk_code);
switch (vk_code) {
case VK_PRIOR:
case VK_NEXT:
case VK_END:
case VK_HOME:
case VK_LEFT:
case VK_UP:
case VK_RIGHT:
case VK_DOWN:
case VK_INSERT:
case VK_DELETE:
scancode |= 0xEE00;
break;
case VK_RETURN:
scancode |= 0x0E00;
break;
}
}
else {
// logger(LOG_LEVEL_WARN, "%s [%u]: Test2, vk_code %li\n",
// __FUNCTION__, __LINE__, vk_code);
}
}
return scancode;
}
DWORD scancode_to_keycode(unsigned short scancode) {
unsigned short keycode = 0x0000;
// Check the vk_code is in range.
// NOTE vk_code >= 0 is assumed because the scancode is unsigned.
if (scancode < 128) {
keycode = keycode_scancode_table[scancode][1];
}
else {
// Calculate the upper offset based on the lower half of the scancode + 128.
unsigned short int i = (scancode & 0x007F) | 0x80;
if (i < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[1])) {
keycode = keycode_scancode_table[i][1];
}
}
return keycode;
}
/************************************************************************/
// Structure and pointers for the keyboard locale cache.
typedef struct _KeyboardLocale {
HKL id; // Locale ID
HINSTANCE library; // Keyboard DLL instance.
PVK_TO_BIT pVkToBit; // Pointers struct arrays.
PVK_TO_WCHAR_TABLE pVkToWcharTable;
PDEADKEY pDeadKey;
struct _KeyboardLocale* next;
} KeyboardLocale;
static KeyboardLocale* locale_first = NULL;
static KeyboardLocale* locale_current = NULL;
static WCHAR deadChar = WCH_NONE;
// Amount of pointer padding to apply for Wow64 instances.
static unsigned short int ptr_padding = 0;
#if defined(_WIN32) && !defined(_WIN64)
// Small function to check and see if we are executing under Wow64.
static BOOL is_wow64() {
BOOL status = FALSE;
LPFN_ISWOW64PROCESS pIsWow64Process = (LPFN_ISWOW64PROCESS)
GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process");
if (pIsWow64Process != NULL) {
HANDLE current_proc = GetCurrentProcess();
if (!pIsWow64Process(current_proc, &status)) {
status = FALSE;
logger(LOG_LEVEL_DEBUG, "%s [%u]: pIsWow64Process(%#p, %#p) failed!\n",
__FUNCTION__, __LINE__, current_proc, &status);
}
}
return status;
}
#endif
// Locate the DLL that contains the current keyboard layout.
static int get_keyboard_layout_file(char *layoutFile, DWORD bufferSize) {
int status = UIOHOOK_FAILURE;
HKEY hKey;
DWORD varType = REG_SZ;
char kbdName[KL_NAMELENGTH];
if (GetKeyboardLayoutName(kbdName)) {
char kbdKeyPath[51 + KL_NAMELENGTH];
snprintf(kbdKeyPath, 51 + KL_NAMELENGTH, "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s", kbdName);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR) kbdKeyPath, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
if (RegQueryValueEx(hKey, "Layout File", NULL, &varType, (LPBYTE) layoutFile, &bufferSize) == ERROR_SUCCESS) {
RegCloseKey(hKey);
status = UIOHOOK_SUCCESS;
}
}
}
return status;
}
static int refresh_locale_list() {
int count = 0;
// Get the number of layouts the user has activated.
int hkl_size = GetKeyboardLayoutList(0, NULL);
if (hkl_size > 0) {
logger(LOG_LEVEL_INFO, "%s [%u]: GetKeyboardLayoutList(0, NULL) found %i layouts.\n",
__FUNCTION__, __LINE__, hkl_size);
// Get the thread id that currently has focus for our default.
DWORD focus_pid = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
HKL hlk_focus = GetKeyboardLayout(focus_pid);
HKL hlk_default = GetKeyboardLayout(0);
HKL *hkl_list = malloc(sizeof(HKL) * hkl_size);
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",
__FUNCTION__, __LINE__, hkl_size, new_size);
}
else {
logger(LOG_LEVEL_INFO, "%s [%u]: Received %i locales.\n",
__FUNCTION__, __LINE__, new_size);
}
KeyboardLocale* locale_previous = NULL;
KeyboardLocale* locale_item = locale_first;
// Go though the linked list and remove KeyboardLocale's that are
// no longer loaded.
while (locale_item != NULL) {
// Check to see if the old HKL is in the new list.
bool is_loaded = false;
int i;
for (i = 0; i < new_size && !is_loaded; i++) {
if (locale_item->id == hkl_list[i]) {
// Flag and jump out of the loop.
hkl_list[i] = NULL;
is_loaded = true;
}
}
if (is_loaded) {
logger(LOG_LEVEL_DEBUG, "%s [%u]: Found locale ID %#p in the cache.\n",
__FUNCTION__, __LINE__, locale_item->id);
// Set the previous local to the current locale.
locale_previous = locale_item;
// Check and see if the locale is our current active locale.
if (locale_item->id == hlk_focus) {
locale_current = locale_item;
}
count++;
}
else {
logger(LOG_LEVEL_DEBUG, "%s [%u]: Removing locale ID %#p from the cache.\n",
__FUNCTION__, __LINE__, locale_item->id);
// If the old id is not in the new list, remove it.
locale_previous->next = locale_item->next;
// Make sure the locale_current points NULL or something valid.
if (locale_item == locale_current) {
locale_current = NULL;
}
// Free the memory used by locale_item;
free(locale_item);
// Set the item to the pervious item to guarantee a next.
locale_item = locale_previous;
}
// Iterate to the next linked list item.
locale_item = locale_item->next;
}
// Insert anything new into the linked list.
int i;
for (i = 0; i < new_size; i++) {
// Check to see if the item was already in the list.
if (hkl_list[i] != NULL) {
// Set the active keyboard layout for this thread to the HKL.
ActivateKeyboardLayout(hkl_list[i], 0x00);
// Try to pull the current keyboard layout DLL from the registry.
char layoutFile[MAX_PATH];
if (get_keyboard_layout_file(layoutFile, sizeof(layoutFile)) == UIOHOOK_SUCCESS) {
// You can't trust the %SYSPATH%, look it up manually.
char systemDirectory[MAX_PATH];
if (GetSystemDirectory(systemDirectory, MAX_PATH) != 0) {
char kbdLayoutFilePath[MAX_PATH];
snprintf(kbdLayoutFilePath, MAX_PATH, "%s\\%s", systemDirectory, layoutFile);
logger(LOG_LEVEL_DEBUG, "%s [%u]: Loading layout for %#p: %s.\n",
__FUNCTION__, __LINE__, hkl_list[i], layoutFile);
// Create the new locale item.
locale_item = malloc(sizeof(KeyboardLocale));
locale_item->id = hkl_list[i];
locale_item->library = LoadLibrary(kbdLayoutFilePath);
// Get the function pointer from the library to get the keyboard layer descriptor.
KbdLayerDescriptor pKbdLayerDescriptor = (KbdLayerDescriptor) GetProcAddress(locale_item->library, "KbdLayerDescriptor");
if (pKbdLayerDescriptor != NULL) {
PKBDTABLES pKbd = pKbdLayerDescriptor();
// Store the memory address of the following 3 structures.
BYTE *base = (BYTE *) pKbd;
// First element of each structure, no offset adjustment needed.
locale_item->pVkToBit = pKbd->pCharModifiers->pVkToBit;
// Second element of pKbd, +4 byte offset on wow64.
locale_item->pVkToWcharTable = *((PVK_TO_WCHAR_TABLE *) (base + offsetof(KBDTABLES, pVkToWcharTable) + ptr_padding));
// Third element of pKbd, +8 byte offset on wow64.
locale_item->pDeadKey = *((PDEADKEY *) (base + offsetof(KBDTABLES, pDeadKey) + (ptr_padding * 2)));
// This will always be added to the end of the list.
locale_item->next = NULL;
// Insert the item into the linked list.
if (locale_previous == NULL) {
// If nothing came before, the list is empty.
locale_first = locale_item;
}
else {
// Append the new locale to the end of the list.
locale_previous->next = locale_item;
}
// Check and see if the locale is our current active locale.
if (locale_item->id == hlk_focus) {
locale_current = locale_item;
}
// Set the pervious locale item to the new one.
locale_previous = locale_item;
count++;
}
else {
logger(LOG_LEVEL_ERROR,
"%s [%u]: GetProcAddress() failed for KbdLayerDescriptor!\n",
__FUNCTION__, __LINE__);
FreeLibrary(locale_item->library);
free(locale_item);
locale_item = NULL;
}
}
else {
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",
__FUNCTION__, __LINE__, hkl_list[i]);
}
}
}
}
else {
logger(LOG_LEVEL_ERROR,
"%s [%u]: GetKeyboardLayoutList() failed!\n",
__FUNCTION__, __LINE__);
// TODO Try and recover by using the current layout.
// Hint: Use locale_id instead of hkl_list[i] in the loop above.
}
free(hkl_list);
ActivateKeyboardLayout(hlk_default, 0x00);
}
return count;
}
SIZE_T keycode_to_unicode(DWORD keycode, PWCHAR buffer, SIZE_T size) {
// Get the thread id that currently has focus and ask for its current
// locale..
DWORD focus_pid = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
HKL locale_id = GetKeyboardLayout(focus_pid);
// If the current Locale is not the new locale, search the linked list.
if (locale_current == NULL || locale_current->id != locale_id) {
locale_current = NULL;
KeyboardLocale* locale_item = locale_first;
// Search the linked list...
while (locale_item != NULL && locale_item->id != locale_id) {
locale_item = locale_item->next;
}
// You may already be a winner!
if (locale_item != NULL && locale_item->id != locale_id) {
logger(LOG_LEVEL_INFO,
"%s [%u]: Activating keyboard layout %#p.\n",
__FUNCTION__, __LINE__, locale_item->id);
// Switch the current locale.
locale_current = locale_item;
locale_item = NULL;
// If they layout changes the dead key state needs to be reset.
// This is consistent with the way Windows handles locale changes.
deadChar = WCH_NONE;
}
else {
logger(LOG_LEVEL_DEBUG,
"%s [%u]: Refreshing locale cache.\n",
__FUNCTION__, __LINE__);
refresh_locale_list();
}
}
// Initialize to empty.
SIZE_T charCount = 0;
// buffer[i] = WCH_NONE;
// Check and make sure the Unicode helper was loaded.
if (locale_current != NULL) {
logger(LOG_LEVEL_INFO,
"%s [%u]: Using keyboard layout %#p.\n",
__FUNCTION__, __LINE__, locale_current->id);
int mod = 0;
int capsLock = (GetKeyState(VK_CAPITAL) & 0x01);
PVK_TO_BIT pVkToBit = locale_current->pVkToBit;
PVK_TO_WCHAR_TABLE pVkToWcharTable = locale_current->pVkToWcharTable;
PDEADKEY pDeadKey = locale_current->pDeadKey;
/* Loop over the modifier keys for this locale and determine what is
* currently depressed. Because this is only a structure of two
* bytes, we don't need to worry about the structure padding of __ptr64
* offsets on Wow64.
*/
bool is_shift = false, is_ctrl = false, is_alt = false;
int i;
for (i = 0; pVkToBit[i].Vk != 0; i++) {
short state = GetAsyncKeyState(pVkToBit[i].Vk);
// Check to see if the most significant bit is active.
if (state & ~SHRT_MAX) {
if (pVkToBit[i].Vk == VK_SHIFT) {
is_shift = true;
}
else if (pVkToBit[i].Vk == VK_CONTROL) {
is_ctrl = true;
}
else if (pVkToBit[i].Vk == VK_MENU) {
is_alt = true;
}
}
}
// Check the Shift modifier.
if (is_shift) {
mod = 1;
}
// Check for the AltGr modifier.
if (is_ctrl && is_alt) {
mod += 3;
}
// Default 32 bit structure size should be 6 bytes (4 for the pointer and 2
// additional byte fields) that are padded out to 8 bytes by the compiler.
unsigned short sizeVkToWcharTable = sizeof(VK_TO_WCHAR_TABLE);
#if defined(_WIN32) && !defined(_WIN64)
if (is_wow64()) {
// If we are running under Wow64 the size of the first pointer will be
// 8 bringing the total size to 10 bytes padded out to 16.
sizeVkToWcharTable = (sizeVkToWcharTable + ptr_padding + 7) & -8;
}
#endif
BYTE *ptrCurrentVkToWcharTable = (BYTE *) pVkToWcharTable;
int cbSize, n;
do {
// cbSize is used to calculate n, and n is used for the size of pVkToWchars[j].wch[n]
cbSize = *(ptrCurrentVkToWcharTable + offsetof(VK_TO_WCHAR_TABLE, cbSize) + ptr_padding);
n = (cbSize - 2) / 2;
// Same as VK_TO_WCHARS pVkToWchars[] = pVkToWcharTable[i].pVkToWchars
PVK_TO_WCHARS pVkToWchars = (PVK_TO_WCHARS) ((PVK_TO_WCHAR_TABLE) ptrCurrentVkToWcharTable)->pVkToWchars;
if (pVkToWchars != NULL && mod < n) {
// pVkToWchars[j].VirtualKey
BYTE *pCurrentVkToWchars = (BYTE *) pVkToWchars;
do {
if (((PVK_TO_WCHARS) pCurrentVkToWchars)->VirtualKey == keycode) {
if ((((PVK_TO_WCHARS) pCurrentVkToWchars)->Attributes == CAPLOK) && capsLock) {
if (is_shift && mod > 0) {
mod -= 1;
}
else {
mod += 1;
}
}
// Set the initial unicode char.
WCHAR unicode = ((PVK_TO_WCHARS) pCurrentVkToWchars)->wch[mod];
// Increment the pCurrentVkToWchars by the size of wch[n].
pCurrentVkToWchars += sizeof(VK_TO_WCHARS) + (sizeof(WCHAR) * n);
if (unicode == WCH_DEAD) {
// The current unicode char is a dead key...
if (deadChar == WCH_NONE) {
// No previous dead key was set so cache the next
// wchar so we know what to do next time its pressed.
deadChar = ((PVK_TO_WCHARS) pCurrentVkToWchars)->wch[mod];
}
else {
if (size >= 2) {
// Received a second dead key.
memset(buffer, deadChar, 2);
//buffer[0] = deadChar;
//buffer[1] = deadChar;
deadChar = WCH_NONE;
charCount = 2;
}
}
}
else if (unicode != WCH_NONE) {
// We are not WCH_NONE or WCH_DEAD
if (size >= 1) {
buffer[0] = unicode;
charCount = 1;
}
}
break;
}
else {
// Add sizeof WCHAR because we are really an array of WCHAR[n] not WCHAR[]
pCurrentVkToWchars += sizeof(VK_TO_WCHARS) + (sizeof(WCHAR) * n);
}
} while ( ((PVK_TO_WCHARS) pCurrentVkToWchars)->VirtualKey != 0 );
}
// This is effectively the same as: ptrCurrentVkToWcharTable = pVkToWcharTable[++i];
ptrCurrentVkToWcharTable += sizeVkToWcharTable;
} while (cbSize != 0);
// If the current local has a dead key set.
if (deadChar != WCH_NONE) {
// Loop over the pDeadKey lookup table for the locale.
int i;
for (i = 0; pDeadKey[i].dwBoth != 0; i++) {
WCHAR baseChar = (WCHAR) pDeadKey[i].dwBoth;
WCHAR diacritic = (WCHAR) (pDeadKey[i].dwBoth >> 16);
// If we locate an extended dead char, set it.
if (size >= 1 && baseChar == buffer[0] && diacritic == deadChar) {
deadChar = WCH_NONE;
if (charCount <= size) {
memset(buffer, (WCHAR) pDeadKey[i].wchComposed, charCount);
//buffer[i] = (WCHAR) pDeadKey[i].wchComposed;
}
}
}
}
}
return charCount;
}
int load_input_helper() {
int count = 0;
#if defined(_WIN32) && !defined(_WIN64)
if (is_wow64()) {
ptr_padding = sizeof(void *);
}
#endif
count = refresh_locale_list();
logger(LOG_LEVEL_INFO,
"%s [%u]: refresh_locale_list() found %i locale(s).\n",
__FUNCTION__, __LINE__, count);
return count;
}
// This returns the number of locales that were removed.
int unload_input_helper() {
int count = 0;
// Cleanup and free memory from the old list.
KeyboardLocale* locale_item = locale_first;
while (locale_item != NULL) {
// Remove the first item from the linked list.
FreeLibrary(locale_item->library);
locale_first = locale_item->next;
free(locale_item);
locale_item = locale_first;
count++;
}
// Reset the current local.
locale_current = NULL;
return count;
}

View File

@ -0,0 +1,748 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <inttypes.h>
// #include <uiohook.h>
#include "../uiohook.h"
#include <windows.h>
#include "input_helper.h"
// #include "logger.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;
// The handle to the DLL module pulled in DllMain on DLL_PROCESS_ATTACH.
extern HINSTANCE hInst;
// Modifiers for tracking key masks.
static unsigned short int current_modifiers = 0x0000;
// Click count globals.
static unsigned short click_count = 0;
static DWORD click_time = 0;
static unsigned short int click_button = MOUSE_NOBUTTON;
static POINT last_click;
// Static event memory.
static uiohook_event event;
// Event dispatch callback.
static dispatcher_t dispatcher = NULL;
UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc) {
logger(LOG_LEVEL_DEBUG, "%s [%u]: Setting new dispatch callback to %#p.\n",
__FUNCTION__, __LINE__, dispatch_proc);
dispatcher = dispatch_proc;
}
// Send out an event if a dispatcher was set.
static inline void dispatch_event(uiohook_event *const event) {
if (dispatcher != NULL) {
logger(LOG_LEVEL_DEBUG, "%s [%u]: Dispatching event type %u.\n",
__FUNCTION__, __LINE__, event->type);
dispatcher(event);
}
else {
logger(LOG_LEVEL_WARN, "%s [%u]: No dispatch callback set!\n",
__FUNCTION__, __LINE__);
}
}
// Set the native modifier mask for future events.
static inline void set_modifier_mask(unsigned short int mask) {
current_modifiers |= mask;
}
// Unset the native modifier mask for future events.
static inline void unset_modifier_mask(unsigned short int mask) {
current_modifiers ^= mask;
}
// Get the current native modifier mask state.
static inline unsigned short int get_modifiers() {
return current_modifiers;
}
// Initialize the modifier mask to the current modifiers.
static void initialize_modifiers() {
current_modifiers = 0x0000;
// NOTE We are checking the high order bit, so it will be < 0 for a singed short.
if (GetKeyState(VK_LSHIFT) < 0) { set_modifier_mask(MASK_SHIFT_L); }
if (GetKeyState(VK_RSHIFT) < 0) { set_modifier_mask(MASK_SHIFT_R); }
if (GetKeyState(VK_LCONTROL) < 0) { set_modifier_mask(MASK_CTRL_L); }
if (GetKeyState(VK_RCONTROL) < 0) { set_modifier_mask(MASK_CTRL_R); }
if (GetKeyState(VK_LMENU) < 0) { set_modifier_mask(MASK_ALT_L); }
if (GetKeyState(VK_RMENU) < 0) { set_modifier_mask(MASK_ALT_R); }
if (GetKeyState(VK_LWIN) < 0) { set_modifier_mask(MASK_META_L); }
if (GetKeyState(VK_RWIN) < 0) { set_modifier_mask(MASK_META_R); }
if (GetKeyState(VK_LBUTTON) < 0) { set_modifier_mask(MASK_BUTTON1); }
if (GetKeyState(VK_RBUTTON) < 0) { set_modifier_mask(MASK_BUTTON2); }
if (GetKeyState(VK_MBUTTON) < 0) { set_modifier_mask(MASK_BUTTON3); }
if (GetKeyState(VK_XBUTTON1) < 0) { set_modifier_mask(MASK_BUTTON4); }
if (GetKeyState(VK_XBUTTON2) < 0) { set_modifier_mask(MASK_BUTTON5); }
if (GetKeyState(VK_NUMLOCK) < 0) { set_modifier_mask(MASK_NUM_LOCK); }
if (GetKeyState(VK_CAPITAL) < 0) { set_modifier_mask(MASK_CAPS_LOCK); }
if (GetKeyState(VK_SCROLL) < 0) { set_modifier_mask(MASK_SCROLL_LOCK); }
}
/* Retrieves the mouse wheel scroll type. This function cannot be included as
* part of the input_helper.h due to platform specific calling restrictions.
*/
static unsigned short int get_scroll_wheel_type() {
unsigned short int value;
UINT wheel_type;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheel_type, 0);
if (wheel_type == WHEEL_PAGESCROLL) {
value = WHEEL_BLOCK_SCROLL;
}
else {
value = WHEEL_UNIT_SCROLL;
}
return value;
}
/* Retrieves the mouse wheel scroll amount. This function cannot be included as
* part of the input_helper.h due to platform specific calling restrictions.
*/
static unsigned short int get_scroll_wheel_amount() {
unsigned short int value;
UINT wheel_amount;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheel_amount, 0);
if (wheel_amount == WHEEL_PAGESCROLL) {
value = 1;
}
else {
value = (unsigned short int) wheel_amount;
}
return value;
}
void unregister_running_hooks() {
// Stop the event hook and any timer still running.
if (win_event_hhook != NULL) {
UnhookWinEvent(win_event_hhook);
win_event_hhook = NULL;
}
// Destroy the native hooks.
if (keyboard_event_hhook != NULL) {
UnhookWindowsHookEx(keyboard_event_hhook);
keyboard_event_hhook = NULL;
}
if (mouse_event_hhook != NULL) {
UnhookWindowsHookEx(mouse_event_hhook);
mouse_event_hhook = NULL;
}
}
void hook_start_proc() {
// Get the local system time in UNIX epoch form.
uint64_t timestamp = GetMessageTime();
// Populate the hook start event.
event.time = timestamp;
event.reserved = 0x00;
event.type = EVENT_HOOK_ENABLED;
event.mask = 0x00;
// Fire the hook start event.
dispatch_event(&event);
}
void hook_stop_proc() {
// Get the local system time in UNIX epoch form.
uint64_t timestamp = GetMessageTime();
// Populate the hook stop event.
event.time = timestamp;
event.reserved = 0x00;
event.type = EVENT_HOOK_DISABLED;
event.mask = 0x00;
// Fire the hook stop event.
dispatch_event(&event);
}
static void process_key_pressed(KBDLLHOOKSTRUCT *kbhook) {
// 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); }
else if (kbhook->vkCode == VK_LCONTROL) { set_modifier_mask(MASK_CTRL_L); }
else if (kbhook->vkCode == VK_RCONTROL) { set_modifier_mask(MASK_CTRL_R); }
else if (kbhook->vkCode == VK_LMENU) { set_modifier_mask(MASK_ALT_L); }
else if (kbhook->vkCode == VK_RMENU) { set_modifier_mask(MASK_ALT_R); }
else if (kbhook->vkCode == VK_LWIN) { set_modifier_mask(MASK_META_L); }
else if (kbhook->vkCode == VK_RWIN) { set_modifier_mask(MASK_META_R); }
else if (kbhook->vkCode == VK_NUMLOCK) { set_modifier_mask(MASK_NUM_LOCK); }
else if (kbhook->vkCode == VK_CAPITAL) { set_modifier_mask(MASK_CAPS_LOCK); }
else if (kbhook->vkCode == VK_SCROLL) { set_modifier_mask(MASK_SCROLL_LOCK); }
// Populate key pressed event.
event.time = kbhook->time;
event.reserved = 0x00;
event.type = EVENT_KEY_PRESSED;
event.mask = get_modifiers();
event.data.keyboard.keycode = keycode_to_scancode(kbhook->vkCode, kbhook->flags);
event.data.keyboard.rawcode = kbhook->vkCode;
event.data.keyboard.keychar = CHAR_UNDEFINED;
logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X pressed. (%#X)\n",
__FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode);
// Populate key pressed event.
dispatch_event(&event);
// If the pressed event was not consumed...
if (event.reserved ^ 0x01) {
// Buffer for unicode typed chars. No more than 2 needed.
WCHAR buffer[2]; // = { WCH_NONE };
// If the pressed event was not consumed and a unicode char exists...
SIZE_T count = keycode_to_unicode(kbhook->vkCode, buffer, sizeof(buffer));
unsigned int i;
for (i = 0; i < count; i++) {
// Populate key typed event.
event.time = kbhook->time;
event.reserved = 0x00;
event.type = EVENT_KEY_TYPED;
event.mask = get_modifiers();
event.data.keyboard.keycode = VC_UNDEFINED;
event.data.keyboard.rawcode = kbhook->vkCode;
event.data.keyboard.keychar = buffer[i];
logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X typed. (%lc)\n",
__FUNCTION__, __LINE__, event.data.keyboard.keycode, (wint_t) event.data.keyboard.keychar);
// Fire key typed event.
dispatch_event(&event);
}
}
}
static void process_key_released(KBDLLHOOKSTRUCT *kbhook) {
// 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); }
else if (kbhook->vkCode == VK_LCONTROL) { unset_modifier_mask(MASK_CTRL_L); }
else if (kbhook->vkCode == VK_RCONTROL) { unset_modifier_mask(MASK_CTRL_R); }
else if (kbhook->vkCode == VK_LMENU) { unset_modifier_mask(MASK_ALT_L); }
else if (kbhook->vkCode == VK_RMENU) { unset_modifier_mask(MASK_ALT_R); }
else if (kbhook->vkCode == VK_LWIN) { unset_modifier_mask(MASK_META_L); }
else if (kbhook->vkCode == VK_RWIN) { unset_modifier_mask(MASK_META_R); }
else if (kbhook->vkCode == VK_NUMLOCK) { unset_modifier_mask(MASK_NUM_LOCK); }
else if (kbhook->vkCode == VK_CAPITAL) { unset_modifier_mask(MASK_CAPS_LOCK); }
else if (kbhook->vkCode == VK_SCROLL) { unset_modifier_mask(MASK_SCROLL_LOCK); }
// Populate key pressed event.
event.time = kbhook->time;
event.reserved = 0x00;
event.type = EVENT_KEY_RELEASED;
event.mask = get_modifiers();
event.data.keyboard.keycode = keycode_to_scancode(kbhook->vkCode, kbhook->flags);
event.data.keyboard.rawcode = kbhook->vkCode;
event.data.keyboard.keychar = CHAR_UNDEFINED;
logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X released. (%#X)\n",
__FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode);
// Fire key released event.
dispatch_event(&event);
}
LRESULT CALLBACK keyboard_hook_event_proc(int nCode, WPARAM wParam, LPARAM lParam) {
KBDLLHOOKSTRUCT *kbhook = (KBDLLHOOKSTRUCT *) lParam;
switch (wParam) {
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
process_key_pressed(kbhook);
break;
case WM_KEYUP:
case WM_SYSKEYUP:
process_key_released(kbhook);
break;
default:
// In theory this *should* never execute.
logger(LOG_LEVEL_DEBUG, "%s [%u]: Unhandled Windows keyboard event: %#X.\n",
__FUNCTION__, __LINE__, (unsigned int) wParam);
break;
}
LRESULT hook_result = -1;
if (nCode < 0 || event.reserved ^ 0x01) {
hook_result = CallNextHookEx(keyboard_event_hhook, nCode, wParam, lParam);
}
else {
logger(LOG_LEVEL_DEBUG, "%s [%u]: Consuming the current event. (%li)\n",
__FUNCTION__, __LINE__, (long) hook_result);
}
return hook_result;
}
static void process_button_pressed(MSLLHOOKSTRUCT *mshook, uint16_t button) {
uint64_t timestamp = GetMessageTime();
// 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()) {
if (click_count < USHRT_MAX) {
click_count++;
}
else {
logger(LOG_LEVEL_WARN, "%s [%u]: Click count overflow detected!\n",
__FUNCTION__, __LINE__);
}
}
else {
// Reset the click count.
click_count = 1;
// Set the previous button.
click_button = button;
}
// Save this events time to calculate the click_count.
click_time = timestamp;
// Store the last click point.
last_click.x = mshook->pt.x;
last_click.y = mshook->pt.y;
// Populate mouse pressed event.
event.time = timestamp;
event.reserved = 0x00;
event.type = EVENT_MOUSE_PRESSED;
event.mask = get_modifiers();
event.data.mouse.button = button;
event.data.mouse.clicks = click_count;
event.data.mouse.x = mshook->pt.x;
event.data.mouse.y = mshook->pt.y;
logger(LOG_LEVEL_INFO, "%s [%u]: Button %u pressed %u time(s). (%u, %u)\n",
__FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks,
event.data.mouse.x, event.data.mouse.y);
// Fire mouse pressed event.
dispatch_event(&event);
}
static void process_button_released(MSLLHOOKSTRUCT *mshook, uint16_t button) {
// Populate mouse released event.
event.time = GetMessageTime();
event.reserved = 0x00;
event.type = EVENT_MOUSE_RELEASED;
event.mask = get_modifiers();
event.data.mouse.button = button;
event.data.mouse.clicks = click_count;
event.data.mouse.x = mshook->pt.x;
event.data.mouse.y = mshook->pt.y;
logger(LOG_LEVEL_INFO, "%s [%u]: Button %u released %u time(s). (%u, %u)\n",
__FUNCTION__, __LINE__, event.data.mouse.button,
event.data.mouse.clicks,
event.data.mouse.x, event.data.mouse.y);
// Fire mouse released event.
dispatch_event(&event);
// 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 = GetMessageTime();
event.reserved = 0x00;
event.type = EVENT_MOUSE_CLICKED;
event.mask = get_modifiers();
event.data.mouse.button = button;
event.data.mouse.clicks = click_count;
event.data.mouse.x = mshook->pt.x;
event.data.mouse.y = mshook->pt.y;
logger(LOG_LEVEL_INFO, "%s [%u]: Button %u clicked %u time(s). (%u, %u)\n",
__FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks,
event.data.mouse.x, event.data.mouse.y);
// Fire mouse clicked event.
dispatch_event(&event);
}
// Reset the number of clicks.
if (button == click_button && (long int) (event.time - click_time) > hook_get_multi_click_time()) {
// Reset the click count.
click_count = 0;
}
}
static void process_mouse_moved(MSLLHOOKSTRUCT *mshook) {
uint64_t timestamp = GetMessageTime();
// We received a mouse move event with the mouse actually moving.
// This verifies that the mouse was moved after being depressed.
if (last_click.x != mshook->pt.x || last_click.y != mshook->pt.y) {
// Reset the click count.
if (click_count != 0 && (long) (timestamp - click_time) > hook_get_multi_click_time()) {
click_count = 0;
}
// Populate mouse move event.
event.time = timestamp;
event.reserved = 0x00;
event.mask = get_modifiers();
// Check the modifier mask range for MASK_BUTTON1 - 5.
bool mouse_dragged = event.mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5);
if (mouse_dragged) {
// Create Mouse Dragged event.
event.type = EVENT_MOUSE_DRAGGED;
}
else {
// Create a Mouse Moved event.
event.type = EVENT_MOUSE_MOVED;
}
event.data.mouse.button = MOUSE_NOBUTTON;
event.data.mouse.clicks = click_count;
event.data.mouse.x = mshook->pt.x;
event.data.mouse.y = mshook->pt.y;
logger(LOG_LEVEL_INFO, "%s [%u]: Mouse %s to %u, %u.\n",
__FUNCTION__, __LINE__, mouse_dragged ? "dragged" : "moved",
event.data.mouse.x, event.data.mouse.y);
// Fire mouse move event.
dispatch_event(&event);
}
}
static void process_mouse_wheel(MSLLHOOKSTRUCT *mshook, uint8_t direction) {
// 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 = GetMessageTime();
event.reserved = 0x00;
event.type = EVENT_MOUSE_WHEEL;
event.mask = get_modifiers();
event.data.wheel.clicks = click_count;
event.data.wheel.x = mshook->pt.x;
event.data.wheel.y = mshook->pt.y;
event.data.wheel.type = get_scroll_wheel_type();
event.data.wheel.amount = get_scroll_wheel_amount();
/* Delta HIWORD(mshook->mouseData)
* A positive value indicates that the wheel was rotated
* forward, away from the user; a negative value indicates that
* the wheel was rotated backward, toward the user. One wheel
* click is defined as WHEEL_DELTA, which is 120. */
event.data.wheel.rotation = ((int16_t) HIWORD(mshook->mouseData) / WHEEL_DELTA) * -1;
// Set the direction based on what event was received.
event.data.wheel.direction = direction;
logger(LOG_LEVEL_INFO, "%s [%u]: Mouse wheel type %u, rotated %i units in the %u direction at %u, %u.\n",
__FUNCTION__, __LINE__, event.data.wheel.type,
event.data.wheel.amount * event.data.wheel.rotation,
event.data.wheel.direction,
event.data.wheel.x, event.data.wheel.y);
// Fire mouse wheel event.
dispatch_event(&event);
}
LRESULT CALLBACK mouse_hook_event_proc(int nCode, WPARAM wParam, LPARAM lParam) {
MSLLHOOKSTRUCT *mshook = (MSLLHOOKSTRUCT *) lParam;
switch (wParam) {
case WM_LBUTTONDOWN:
set_modifier_mask(MASK_BUTTON1);
process_button_pressed(mshook, MOUSE_BUTTON1);
break;
case WM_RBUTTONDOWN:
set_modifier_mask(MASK_BUTTON2);
process_button_pressed(mshook, MOUSE_BUTTON2);
break;
case WM_MBUTTONDOWN:
set_modifier_mask(MASK_BUTTON3);
process_button_pressed(mshook, MOUSE_BUTTON3);
break;
case WM_XBUTTONDOWN:
case WM_NCXBUTTONDOWN:
if (HIWORD(mshook->mouseData) == XBUTTON1) {
set_modifier_mask(MASK_BUTTON4);
process_button_pressed(mshook, MOUSE_BUTTON4);
}
else if (HIWORD(mshook->mouseData) == XBUTTON2) {
set_modifier_mask(MASK_BUTTON5);
process_button_pressed(mshook, MOUSE_BUTTON5);
}
else {
// Extra mouse buttons.
uint16_t button = HIWORD(mshook->mouseData);
// Add support for mouse 4 & 5.
if (button == 4) {
set_modifier_mask(MOUSE_BUTTON4);
}
else if (button == 5) {
set_modifier_mask(MOUSE_BUTTON5);
}
process_button_pressed(mshook, button);
}
break;
case WM_LBUTTONUP:
unset_modifier_mask(MASK_BUTTON1);
process_button_released(mshook, MOUSE_BUTTON1);
break;
case WM_RBUTTONUP:
unset_modifier_mask(MASK_BUTTON2);
process_button_released(mshook, MOUSE_BUTTON2);
break;
case WM_MBUTTONUP:
unset_modifier_mask(MASK_BUTTON3);
process_button_released(mshook, MOUSE_BUTTON3);
break;
case WM_XBUTTONUP:
case WM_NCXBUTTONUP:
if (HIWORD(mshook->mouseData) == XBUTTON1) {
unset_modifier_mask(MASK_BUTTON4);
process_button_released(mshook, MOUSE_BUTTON4);
}
else if (HIWORD(mshook->mouseData) == XBUTTON2) {
unset_modifier_mask(MASK_BUTTON5);
process_button_released(mshook, MOUSE_BUTTON5);
}
else {
// Extra mouse buttons.
uint16_t button = HIWORD(mshook->mouseData);
// Add support for mouse 4 & 5.
if (button == 4) {
unset_modifier_mask(MOUSE_BUTTON4);
}
else if (button == 5) {
unset_modifier_mask(MOUSE_BUTTON5);
}
process_button_released(mshook, MOUSE_BUTTON5);
}
break;
case WM_MOUSEMOVE:
process_mouse_moved(mshook);
break;
case WM_MOUSEWHEEL:
process_mouse_wheel(mshook, WHEEL_VERTICAL_DIRECTION);
break;
/* For horizontal scroll wheel support.
* NOTE Windows >= Vista
* case 0x020E:
*/
case WM_MOUSEHWHEEL:
process_mouse_wheel(mshook, WHEEL_HORIZONTAL_DIRECTION);
break;
default:
// In theory this *should* never execute.
logger(LOG_LEVEL_DEBUG, "%s [%u]: Unhandled Windows mouse event: %#X.\n",
__FUNCTION__, __LINE__, (unsigned int) wParam);
break;
}
LRESULT hook_result = -1;
if (nCode < 0 || event.reserved ^ 0x01) {
hook_result = CallNextHookEx(mouse_event_hhook, nCode, wParam, lParam);
}
else {
logger(LOG_LEVEL_DEBUG, "%s [%u]: Consuming the current event. (%li)\n",
__FUNCTION__, __LINE__, (long) hook_result);
}
return hook_result;
}
// Callback function that handles events.
void CALLBACK win_hook_event_proc(HWINEVENTHOOK hook, DWORD event, HWND hWnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime) {
switch (event) {
case EVENT_OBJECT_NAMECHANGE:
logger(LOG_LEVEL_INFO, "%s [%u]: Restarting Windows input hook on window event: %#X.\n",
__FUNCTION__, __LINE__, event);
// Remove any keyboard or mouse hooks that are still running.
if (keyboard_event_hhook != NULL) {
UnhookWindowsHookEx(keyboard_event_hhook);
}
if (mouse_event_hhook != NULL) {
UnhookWindowsHookEx(mouse_event_hhook);
}
// Restart the event 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);
// Re-initialize modifier masks.
initialize_modifiers();
// FIXME We should compare the modifier mask before and after the restart
// to determine if we should synthesize missing events.
// Check for event hook error.
if (keyboard_event_hhook == NULL || mouse_event_hhook == NULL) {
logger(LOG_LEVEL_ERROR, "%s [%u]: SetWindowsHookEx() failed! (%#lX)\n",
__FUNCTION__, __LINE__, (unsigned long) GetLastError());
}
break;
default:
logger(LOG_LEVEL_INFO, "%s [%u]: Unhandled Windows window event: %#X.\n",
__FUNCTION__, __LINE__, event);
}
}
UIOHOOK_API int hook_run() {
int status = UIOHOOK_FAILURE;
// Set the thread id we want to signal later.
hook_thread_id = GetCurrentThreadId();
// Spot check the hInst incase the library was statically linked and DllMain
// did not receive a pointer on load.
if (hInst == NULL) {
logger(LOG_LEVEL_INFO, "%s [%u]: hInst was not set by DllMain().\n",
__FUNCTION__, __LINE__);
hInst = GetModuleHandle(NULL);
if (hInst != NULL) {
// Initialize native input helper functions.
load_input_helper();
}
else {
logger(LOG_LEVEL_ERROR, "%s [%u]: Could not determine hInst for SetWindowsHookEx()! (%#lX)\n",
__FUNCTION__, __LINE__, (unsigned long) GetLastError());
status = UIOHOOK_ERROR_GET_MODULE_HANDLE;
}
}
// 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);
// Create a window event hook to listen for capture change.
win_event_hhook = SetWinEventHook(
EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_NAMECHANGE,
NULL,
win_hook_event_proc,
0, 0,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
// If we did not encounter a problem, start processing events.
if (keyboard_event_hhook != NULL && mouse_event_hhook != NULL) {
if (win_event_hhook == NULL) {
logger(LOG_LEVEL_WARN, "%s [%u]: SetWinEventHook() failed!\n",
__FUNCTION__, __LINE__);
}
logger(LOG_LEVEL_DEBUG, "%s [%u]: SetWindowsHookEx() successful.\n",
__FUNCTION__, __LINE__);
// Check and setup modifiers.
initialize_modifiers();
// Set the exit status.
status = UIOHOOK_SUCCESS;
// Windows does not have a hook start event or callback so we need to
// manually fake it.
hook_start_proc();
// Block until the thread receives an WM_QUIT request.
MSG message;
while (GetMessage(&message, (HWND) NULL, 0, 0) > 0) {
TranslateMessage(&message);
DispatchMessage(&message);
}
}
else {
logger(LOG_LEVEL_ERROR, "%s [%u]: SetWindowsHookEx() failed! (%#lX)\n",
__FUNCTION__, __LINE__, (unsigned long) GetLastError());
status = UIOHOOK_ERROR_SET_WINDOWS_HOOK_EX;
}
// Unregister any hooks that may still be installed.
unregister_running_hooks();
// We must explicitly call the cleanup handler because Windows does not
// provide a thread cleanup method like POSIX pthread_cleanup_push/pop.
hook_stop_proc();
return status;
}
UIOHOOK_API int hook_stop() {
int status = UIOHOOK_FAILURE;
// Try to exit the thread naturally.
if (PostThreadMessage(hook_thread_id, WM_QUIT, (WPARAM) NULL, (LPARAM) NULL)) {
status = UIOHOOK_SUCCESS;
}
logger(LOG_LEVEL_DEBUG, "%s [%u]: Status: %#X.\n",
__FUNCTION__, __LINE__, status);
return status;
}

View File

@ -0,0 +1,329 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
// #include <uiohook.h>
#include "../uiohook.h"
#include <windows.h>
#include "input_helper.h"
// #include "logger.h"
// Some buggy versions of MinGW and MSys do not include these constants in winuser.h.
#ifndef MAPVK_VK_TO_VSC
#define MAPVK_VK_TO_VSC 0
#define MAPVK_VSC_TO_VK 1
#define MAPVK_VK_TO_CHAR 2
#define MAPVK_VSC_TO_VK_EX 3
#endif
// Some buggy versions of MinGW and MSys only define this value for Windows
// versions >= 0x0600 (Windows Vista) when it should be 0x0500 (Windows 2000).
#ifndef MAPVK_VK_TO_VSC_EX
#define MAPVK_VK_TO_VSC_EX 4
#endif
#ifndef KEYEVENTF_SCANCODE
#define KEYEVENTF_EXTENDEDKEY 0x0001
#define KEYEVENTF_KEYUP 0x0002
#define KEYEVENTF_UNICODE 0x0004
#define KEYEVENTF_SCANCODE 0x0008
#endif
#ifndef KEYEVENTF_KEYDOWN
#define KEYEVENTF_KEYDOWN 0x0000
#endif
#define MAX_WINDOWS_COORD_VALUE 65535
static UINT keymask_lookup[8] = {
VK_LSHIFT,
VK_LCONTROL,
VK_LWIN,
VK_LMENU,
VK_RSHIFT,
VK_RCONTROL,
VK_RWIN,
VK_RMENU
};
UIOHOOK_API void hook_post_event(uiohook_event * const event) {
//FIXME implement multiple monitor support
uint16_t screen_width = GetSystemMetrics( SM_CXSCREEN );
uint16_t screen_height = GetSystemMetrics( SM_CYSCREEN );
unsigned char events_size = 0, events_max = 28;
INPUT *events = malloc(sizeof(INPUT) * events_max);
if (event->mask & (MASK_SHIFT | MASK_CTRL | MASK_META | MASK_ALT)) {
unsigned int i;
for (i = 0; i < sizeof(keymask_lookup) / sizeof(UINT); i++) {
if (event->mask & 1 << i) {
events[events_size].type = INPUT_KEYBOARD;
events[events_size].ki.wVk = keymask_lookup[i];
events[events_size].ki.dwFlags = KEYEVENTF_KEYDOWN;
events[events_size].ki.time = 0; // Use current system time.
events_size++;
}
}
}
if (event->mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5)) {
events[events_size].type = INPUT_MOUSE;
events[events_size].mi.dx = 0; // Relative mouse movement due to
events[events_size].mi.dy = 0; // MOUSEEVENTF_ABSOLUTE not being set.
events[events_size].mi.mouseData = 0x00;
events[events_size].mi.time = 0; // Use current system time.
if (event->mask & MASK_BUTTON1) {
events[events_size].mi.mouseData |= MOUSEEVENTF_LEFTDOWN;
}
if (event->mask & MASK_BUTTON2) {
events[events_size].mi.mouseData |= MOUSEEVENTF_RIGHTDOWN;
}
if (event->mask & MASK_BUTTON3) {
events[events_size].mi.mouseData |= MOUSEEVENTF_MIDDLEDOWN;
}
if (event->mask & MASK_BUTTON4) {
events[events_size].mi.mouseData = XBUTTON1;
events[events_size].mi.mouseData |= MOUSEEVENTF_XDOWN;
}
if (event->mask & MASK_BUTTON5) {
events[events_size].mi.mouseData = XBUTTON2;
events[events_size].mi.dwFlags |= MOUSEEVENTF_XDOWN;
}
events_size++;
}
switch (event->type) {
case EVENT_KEY_PRESSED:
events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
if (events[events_size].ki.wVk != 0x0000) {
events[events_size].type = INPUT_KEYBOARD;
events[events_size].ki.dwFlags = KEYEVENTF_KEYDOWN; // |= KEYEVENTF_SCANCODE;
events[events_size].ki.wScan = 0; // event->data.keyboard.keycode;
events[events_size].ki.time = 0; // GetSystemTime()
events_size++;
}
else {
logger(LOG_LEVEL_INFO, "%s [%u]: Unable to lookup scancode: %li\n",
__FUNCTION__, __LINE__,
event->data.keyboard.keycode);
}
break;
case EVENT_KEY_RELEASED:
events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
if (events[events_size].ki.wVk != 0x0000) {
events[events_size].type = INPUT_KEYBOARD;
events[events_size].ki.dwFlags = KEYEVENTF_KEYUP; // |= KEYEVENTF_SCANCODE;
events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
events[events_size].ki.wScan = 0; // event->data.keyboard.keycode;
events[events_size].ki.time = 0; // GetSystemTime()
events_size++;
}
else {
logger(LOG_LEVEL_INFO, "%s [%u]: Unable to lookup scancode: %li\n",
__FUNCTION__, __LINE__,
event->data.keyboard.keycode);
}
break;
case EVENT_MOUSE_PRESSED:
events[events_size].type = INPUT_MOUSE;
events[events_size].mi.dwFlags = MOUSEEVENTF_XDOWN;
switch (event->data.mouse.button) {
case MOUSE_BUTTON1:
events[events_size].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
break;
case MOUSE_BUTTON2:
events[events_size].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
break;
case MOUSE_BUTTON3:
events[events_size].mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN;
break;
case MOUSE_BUTTON4:
events[events_size].mi.mouseData = XBUTTON1;
break;
case MOUSE_BUTTON5:
events[events_size].mi.mouseData = XBUTTON2;
break;
default:
// Extra buttons.
if (event->data.mouse.button > 3) {
events[events_size].mi.mouseData = event->data.mouse.button - 3;
}
}
events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
events[events_size].mi.time = 0; // GetSystemTime()
events_size++;
break;
case EVENT_MOUSE_RELEASED:
events[events_size].type = INPUT_MOUSE;
events[events_size].mi.dwFlags = MOUSEEVENTF_XUP;
switch (event->data.mouse.button) {
case MOUSE_BUTTON1:
events[events_size].mi.dwFlags = MOUSEEVENTF_LEFTUP;
break;
case MOUSE_BUTTON2:
events[events_size].mi.dwFlags = MOUSEEVENTF_RIGHTUP;
break;
case MOUSE_BUTTON3:
events[events_size].mi.dwFlags = MOUSEEVENTF_MIDDLEUP;
break;
case MOUSE_BUTTON4:
events[events_size].mi.mouseData = XBUTTON1;
break;
case MOUSE_BUTTON5:
events[events_size].mi.mouseData = XBUTTON2;
break;
default:
// Extra buttons.
if (event->data.mouse.button > 3) {
events[events_size].mi.mouseData = event->data.mouse.button - 3;
}
}
events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
events[events_size].mi.time = 0; // GetSystemTime()
events_size++;
break;
case EVENT_MOUSE_WHEEL:
events[events_size].type = INPUT_MOUSE;
events[events_size].mi.dwFlags = MOUSEEVENTF_WHEEL;
// type, amount and rotation?
events[events_size].mi.mouseData = event->data.wheel.amount * event->data.wheel.rotation * WHEEL_DELTA;
events[events_size].mi.dx = event->data.wheel.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
events[events_size].mi.dy = event->data.wheel.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
events[events_size].mi.time = 0; // GetSystemTime()
events_size++;
break;
case EVENT_MOUSE_DRAGGED:
// The button masks are all applied with the modifier masks.
case EVENT_MOUSE_MOVED:
events[events_size].type = INPUT_MOUSE;
events[events_size].mi.dwFlags = MOUSEEVENTF_MOVE;
events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
events[events_size].mi.time = 0; // GetSystemTime()
events_size++;
break;
case EVENT_MOUSE_CLICKED:
case EVENT_KEY_TYPED:
// Ignore clicked and typed events.
case EVENT_HOOK_ENABLED:
case EVENT_HOOK_DISABLED:
// Ignore hook enabled / disabled events.
default:
// Ignore any other garbage.
logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n",
__FUNCTION__, __LINE__, event->type);
break;
}
// Release the previously held modifier keys used to fake the event mask.
if (event->mask & (MASK_SHIFT | MASK_CTRL | MASK_META | MASK_ALT)) {
unsigned int i;
for (i = 0; i < sizeof(keymask_lookup) / sizeof(UINT); i++) {
if (event->mask & 1 << i) {
events[events_size].type = INPUT_KEYBOARD;
events[events_size].ki.wVk = keymask_lookup[i];
events[events_size].ki.dwFlags = KEYEVENTF_KEYUP;
events[events_size].ki.time = 0; // Use current system time.
events_size++;
}
}
}
if (event->mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5)) {
events[events_size].type = INPUT_MOUSE;
events[events_size].mi.dx = 0; // Relative mouse movement due to
events[events_size].mi.dy = 0; // MOUSEEVENTF_ABSOLUTE not being set.
events[events_size].mi.mouseData = 0x00;
events[events_size].mi.time = 0; // Use current system time.
// If dwFlags does not contain MOUSEEVENTF_WHEEL, MOUSEEVENTF_XDOWN, or MOUSEEVENTF_XUP,
// then mouseData should be zero.
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646273%28v=vs.85%29.aspx
if (event->mask & MASK_BUTTON1) {
events[events_size].mi.dwFlags |= MOUSEEVENTF_LEFTUP;
}
if (event->mask & MASK_BUTTON2) {
events[events_size].mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
}
if (event->mask & MASK_BUTTON3) {
events[events_size].mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
}
if (event->mask & MASK_BUTTON4) {
events[events_size].mi.mouseData = XBUTTON1;
events[events_size].mi.dwFlags |= MOUSEEVENTF_XUP;
}
if (event->mask & MASK_BUTTON5) {
events[events_size].mi.mouseData = XBUTTON2;
events[events_size].mi.dwFlags |= MOUSEEVENTF_XUP;
}
events_size++;
}
// Create the key release input
// memcpy(key_events + 1, key_events, sizeof(INPUT));
// key_events[1].ki.dwFlags |= KEYEVENTF_KEYUP;
if (! SendInput(events_size, events, sizeof(INPUT)) ) {
logger(LOG_LEVEL_ERROR, "%s [%u]: SendInput() failed! (%#lX)\n",
__FUNCTION__, __LINE__, (unsigned long) GetLastError());
}
free(events);
}

View File

@ -0,0 +1,214 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
// #include <uiohook.h>
#include "../uiohook.h"
#include <windows.h>
// #include "logger.h"
#include "input_helper.h"
// The handle to the DLL module pulled in DllMain on DLL_PROCESS_ATTACH.
HINSTANCE hInst;
// input_hook.c
extern void unregister_running_hooks();
// Structure for the monitor_enum_proc() callback so we can track the count.
typedef struct _screen_info {
uint8_t count;
screen_data *data;
} screen_info;
static BOOL CALLBACK monitor_enum_proc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
int width = lprcMonitor->right - lprcMonitor->left;
int height = lprcMonitor->bottom - lprcMonitor->top;
int origin_x = lprcMonitor->left;
int origin_y = lprcMonitor->top;
if (width > 0 && height > 0) {
screen_info *screens = (screen_info *) dwData;
if (screens->data == NULL) {
screens->data = (screen_data *) malloc(sizeof(screen_data));
}
else {
screens->data = (screen_data *) realloc(screens, sizeof(screen_data) * screens->count);
}
screens->data[screens->count++] = (screen_data) {
// Should monitor count start @ zero? Currently it starts at 1.
.number = screens->count,
.x = origin_x,
.y = origin_y,
.width = width,
.height = height
};
logger(LOG_LEVEL_INFO, "%s [%u]: Monitor %d: %ldx%ld (%ld, %ld)\n",
__FUNCTION__, __LINE__, screens->count, width, height, origin_x, origin_y);
}
return TRUE;
}
UIOHOOK_API screen_data* hook_create_screen_info(unsigned char *count) {
// Initialize count to zero.
*count = 0;
// Create a simple structure to make working with monitor_enum_proc easier.
screen_info screens = {
.count = 0,
.data = NULL
};
BOOL status = EnumDisplayMonitors(NULL, NULL, monitor_enum_proc, (LPARAM) &screens);
if (!status || screens.count == 0) {
// Fallback in case EnumDisplayMonitors fails.
logger(LOG_LEVEL_INFO, "%s [%u]: EnumDisplayMonitors failed. Fallback.\n",
__FUNCTION__, __LINE__);
int width = GetSystemMetrics(SM_CXSCREEN);
int height = GetSystemMetrics(SM_CYSCREEN);
if (width > 0 && height > 0) {
screens.data = (screen_data *) malloc(sizeof(screen_data));
if (screens.data != NULL) {
*count = 1;
screens.data[0] = (screen_data) {
.number = 1,
.x = 0,
.y = 0,
.width = width,
.height = height
};
}
}
}
else {
// Populate the count.
*count = screens.count;
}
return screens.data;
}
UIOHOOK_API long int hook_get_auto_repeat_rate() {
long int value = -1;
long int rate;
if (SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &rate, 0)) {
logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETKEYBOARDSPEED: %li.\n",
__FUNCTION__, __LINE__, rate);
value = rate;
}
return value;
}
UIOHOOK_API long int hook_get_auto_repeat_delay() {
long int value = -1;
long int delay;
if (SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &delay, 0)) {
logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETKEYBOARDDELAY: %li.\n",
__FUNCTION__, __LINE__, delay);
value = delay;
}
return value;
}
UIOHOOK_API long int hook_get_pointer_acceleration_multiplier() {
long int value = -1;
int mouse[3]; // 0-Threshold X, 1-Threshold Y and 2-Speed.
if (SystemParametersInfo(SPI_GETMOUSE, 0, &mouse, 0)) {
logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSE[2]: %i.\n",
__FUNCTION__, __LINE__, mouse[2]);
value = mouse[2];
}
return value;
}
UIOHOOK_API long int hook_get_pointer_acceleration_threshold() {
long int value = -1;
int mouse[3]; // 0-Threshold X, 1-Threshold Y and 2-Speed.
if (SystemParametersInfo(SPI_GETMOUSE, 0, &mouse, 0)) {
logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSE[0]: %i.\n",
__FUNCTION__, __LINE__, mouse[0]);
logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSE[1]: %i.\n",
__FUNCTION__, __LINE__, mouse[1]);
// Average the x and y thresholds.
value = (mouse[0] + mouse[1]) / 2;
}
return value;
}
UIOHOOK_API long int hook_get_pointer_sensitivity() {
long int value = -1;
int sensitivity;
if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &sensitivity, 0)) {
logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSESPEED: %i.\n",
__FUNCTION__, __LINE__, sensitivity);
value = sensitivity;
}
return value;
}
UIOHOOK_API long int hook_get_multi_click_time() {
long int value = -1;
UINT clicktime;
clicktime = GetDoubleClickTime();
logger(LOG_LEVEL_INFO, "%s [%u]: GetDoubleClickTime: %u.\n",
__FUNCTION__, __LINE__, (unsigned int) clicktime);
value = (long int) clicktime;
return value;
}
// DLL Entry point.
BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved) {
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
// Save the DLL address.
hInst = hInstDLL;
// Initialize native input helper functions.
load_input_helper();
break;
case DLL_PROCESS_DETACH:
// Unregister any hooks that may still be installed.
unregister_running_hooks();
// Deinitialize native input helper functions.
unload_input_helper();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
// Do Nothing.
break;
}
return TRUE;
}

View File

@ -0,0 +1,86 @@
#define USE_XKBCOMMON 0
//#define _included_input_helper 0
#ifndef _included_input_helper
#define _included_input_helper
#include <stdint.h>
#include <X11/Xlib.h>
#ifdef USE_XKBCOMMON
#include <X11/Xlib-xcb.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-x11.h>
#endif
// Virtual button codes that are not defined by X11.
#define Button1 1
#define Button2 2
#define Button3 3
#define WheelUp 4
#define WheelDown 5
#define WheelLeft 6
#define WheelRight 7
#define XButton1 8
#define XButton2 9
/* Converts an X11 key symbol to a single Unicode character. No direct X11
* functionality exists to provide this information.
*/
extern size_t keysym_to_unicode(KeySym keysym, uint16_t *buffer, size_t size);
/* Convert a single Unicode character to an X11 key symbol. This function
* provides a better translation than XStringToKeysym() for Unicode characters.
*/
extern KeySym unicode_to_keysym(uint16_t unicode);
/* Converts an X11 key code to the appropriate keyboard scan code.
*/
extern uint16_t keycode_to_scancode(KeyCode keycode);
/* Converts a keyboard scan code to the appropriate X11 key code.
*/
extern KeyCode scancode_to_keycode(uint16_t scancode);
#ifdef USE_XKBCOMMON
/* Converts an X11 key code to a Unicode character sequence. libXKBCommon support
* is required for this method.
*/
extern size_t keycode_to_unicode(struct xkb_state* state, KeyCode keycode, uint16_t *buffer, size_t size);
/* Create a xkb_state structure and return a pointer to it.
*/
extern struct xkb_state * create_xkb_state(struct xkb_context *context, xcb_connection_t *connection);
/* Release xkb_state structure created by create_xkb_state().
*/
extern void destroy_xkb_state(struct xkb_state* state);
#else
/* Converts an X11 key code and event mask to the appropriate X11 key symbol.
* This functions in much the same way as XKeycodeToKeysym() but allows for a
* faster and more flexible lookup.
*/
extern KeySym keycode_to_keysym(KeyCode keycode, unsigned int modifier_mask);
#endif
/* Initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
* functionality. This method is called by OnLibraryLoad() and may need to be
* called in combination with UnloadInputHelper() if the native keyboard layout
* is changed.
*/
extern void load_input_helper();
/* De-initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
* functionality. This method is called by OnLibraryUnload() and may need to be
* called in combination with LoadInputHelper() if the native keyboard layout
* is changed.
*/
extern void unload_input_helper();
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,384 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
// #include <uiohook.h>
#include "../uiohook.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef USE_XTEST
#include <X11/extensions/XTest.h>
#endif
#include "input_helper.h"
// #include "../logger.h"
extern Display *properties_disp;
// This lookup table must be in the same order the masks are defined.
#ifdef USE_XTEST
static KeySym keymask_lookup[8] = {
XK_Shift_L,
XK_Control_L,
XK_Meta_L,
XK_Alt_L,
XK_Shift_R,
XK_Control_R,
XK_Meta_R,
XK_Alt_R
};
static unsigned int btnmask_lookup[5] = {
MASK_BUTTON1,
MASK_BUTTON2,
MASK_BUTTON3,
MASK_BUTTON4,
MASK_BUTTON5
};
#else
// TODO Possibly relocate to input helper.
static unsigned int convert_to_native_mask(unsigned int mask) {
unsigned int native_mask = 0x00;
if (mask & (MASK_SHIFT)) { native_mask |= ShiftMask; }
if (mask & (MASK_CTRL)) { native_mask |= ControlMask; }
if (mask & (MASK_META)) { native_mask |= Mod4Mask; }
if (mask & (MASK_ALT)) { native_mask |= Mod1Mask; }
if (mask & MASK_BUTTON1) { native_mask |= Button1Mask; }
if (mask & MASK_BUTTON2) { native_mask |= Button2Mask; }
if (mask & MASK_BUTTON3) { native_mask |= Button3Mask; }
if (mask & MASK_BUTTON4) { native_mask |= Button4Mask; }
if (mask & MASK_BUTTON5) { native_mask |= Button5Mask; }
return native_mask;
}
#endif
static inline void post_key_event(uiohook_event * const event) {
#ifdef USE_XTEST
// FIXME Currently ignoring EVENT_KEY_TYPED.
if (event->type == EVENT_KEY_PRESSED) {
XTestFakeKeyEvent(
properties_disp,
scancode_to_keycode(event->data.keyboard.keycode),
True,
0);
}
else if (event->type == EVENT_KEY_RELEASED) {
XTestFakeKeyEvent(
properties_disp,
scancode_to_keycode(event->data.keyboard.keycode),
False,
0);
}
#else
XKeyEvent key_event;
key_event.serial = 0x00;
key_event.send_event = False;
key_event.display = properties_disp;
key_event.time = CurrentTime;
key_event.same_screen = True;
unsigned int mask;
if (!XQueryPointer(properties_disp, DefaultRootWindow(properties_disp), &(key_event.root), &(key_event.subwindow), &(key_event.x_root), &(key_event.y_root), &(key_event.x), &(key_event.y), &mask)) {
key_event.root = DefaultRootWindow(properties_disp);
key_event.window = key_event.root;
key_event.subwindow = None;
key_event.x_root = 0;
key_event.y_root = 0;
key_event.x = 0;
key_event.y = 0;
}
key_event.state = convert_to_native_mask(event->mask);
key_event.keycode = XKeysymToKeycode(properties_disp, scancode_to_keycode(event->data.keyboard.keycode));
// FIXME Currently ignoring typed events.
if (event->type == EVENT_KEY_PRESSED) {
key_event.type = KeyPress;
XSendEvent(properties_disp, InputFocus, False, KeyPressMask, (XEvent *) &key_event);
}
else if (event->type == EVENT_KEY_RELEASED) {
key_event.type = KeyRelease;
XSendEvent(properties_disp, InputFocus, False, KeyReleaseMask, (XEvent *) &key_event);
}
#endif
}
static inline void post_mouse_button_event(uiohook_event * const event) {
#ifdef USE_XTEST
Window ret_root;
Window ret_child;
int root_x;
int root_y;
int win_x;
int win_y;
unsigned int mask;
Window win_root = XDefaultRootWindow(properties_disp);
Bool query_status = XQueryPointer(properties_disp, win_root, &ret_root, &ret_child, &root_x, &root_y, &win_x, &win_y, &mask);
if (query_status) {
if (event->data.mouse.x != root_x || event->data.mouse.y != root_y) {
// Move the pointer to the specified position.
XTestFakeMotionEvent(properties_disp, -1, event->data.mouse.x, event->data.mouse.y, 0);
}
else {
query_status = False;
}
}
if (event->type == EVENT_MOUSE_WHEEL) {
// Wheel events should be the same as click events on X11.
// type, amount and rotation
if (event->data.wheel.rotation < 0) {
XTestFakeButtonEvent(properties_disp, WheelUp, True, 0);
XTestFakeButtonEvent(properties_disp, WheelUp, False, 0);
}
else {
XTestFakeButtonEvent(properties_disp, WheelDown, True, 0);
XTestFakeButtonEvent(properties_disp, WheelDown, False, 0);
}
}
else if (event->type == EVENT_MOUSE_PRESSED) {
XTestFakeButtonEvent(properties_disp, event->data.mouse.button, True, 0);
}
else if (event->type == EVENT_MOUSE_RELEASED) {
XTestFakeButtonEvent(properties_disp, event->data.mouse.button, False, 0);
}
else if (event->type == EVENT_MOUSE_CLICKED) {
XTestFakeButtonEvent(properties_disp, event->data.mouse.button, True, 0);
XTestFakeButtonEvent(properties_disp, event->data.mouse.button, False, 0);
}
if (query_status) {
// Move the pointer back to the original position.
XTestFakeMotionEvent(properties_disp, -1, root_x, root_y, 0);
}
#else
XButtonEvent btn_event;
btn_event.serial = 0x00;
btn_event.send_event = False;
btn_event.display = properties_disp;
btn_event.time = CurrentTime;
btn_event.same_screen = True;
btn_event.root = DefaultRootWindow(properties_disp);
btn_event.window = btn_event.root;
btn_event.subwindow = None;
btn_event.type = 0x00;
btn_event.state = 0x00;
btn_event.x_root = 0;
btn_event.y_root = 0;
btn_event.x = 0;
btn_event.y = 0;
btn_event.button = 0x00;
btn_event.state = convert_to_native_mask(event->mask);
btn_event.x = event->data.mouse.x;
btn_event.y = event->data.mouse.y;
#if defined(USE_XINERAMA) || defined(USE_XRANDR)
uint8_t screen_count;
screen_data *screens = hook_create_screen_info(&screen_count);
if (screen_count > 1) {
btn_event.x += screens[0].x;
btn_event.y += screens[0].y;
}
if (screens != NULL) {
free(screens);
}
#endif
// These are the same because Window == Root Window.
btn_event.x_root = btn_event.x;
btn_event.y_root = btn_event.y;
if (event->type == EVENT_MOUSE_WHEEL) {
// type, amount and rotation
if (event->data.wheel.rotation < 0) {
btn_event.button = WheelUp;
}
else {
btn_event.button = WheelDown;
}
}
if (event->type != EVENT_MOUSE_RELEASED) {
// FIXME Where do we set event->button?
btn_event.type = ButtonPress;
XSendEvent(properties_disp, InputFocus, False, ButtonPressMask, (XEvent *) &btn_event);
}
if (event->type != EVENT_MOUSE_PRESSED) {
btn_event.type = ButtonRelease;
XSendEvent(properties_disp, InputFocus, False, ButtonReleaseMask, (XEvent *) &btn_event);
}
#endif
}
static inline void post_mouse_motion_event(uiohook_event * const event) {
#ifdef USE_XTEST
XTestFakeMotionEvent(properties_disp, -1, event->data.mouse.x, event->data.mouse.y, 0);
#else
XMotionEvent mov_event;
mov_event.serial = MotionNotify;
mov_event.send_event = False;
mov_event.display = properties_disp;
mov_event.time = CurrentTime;
mov_event.same_screen = True;
mov_event.is_hint = NotifyNormal,
mov_event.root = DefaultRootWindow(properties_disp);
mov_event.window = mov_event.root;
mov_event.subwindow = None;
mov_event.type = 0x00;
mov_event.state = 0x00;
mov_event.x_root = 0;
mov_event.y_root = 0;
mov_event.x = 0;
mov_event.y = 0;
mov_event.state = convert_to_native_mask(event->mask);
mov_event.x = event->data.mouse.x;
mov_event.y = event->data.mouse.y;
#if defined(USE_XINERAMA) || defined(USE_XRANDR)
uint8_t screen_count;
screen_data *screens = hook_create_screen_info(&screen_count);
if (screen_count > 1) {
mov_event.x += screens[0].x;
mov_event.y += screens[0].y;
}
if (screens != NULL) {
free(screens);
}
#endif
// These are the same because Window == Root Window.
mov_event.x_root = mov_event.x;
mov_event.y_root = mov_event.y;
long int event_mask = NoEventMask;
if (event->type == EVENT_MOUSE_DRAGGED) {
#if Button1Mask == Button1MotionMask && \
Button2Mask == Button2MotionMask && \
Button3Mask == Button3MotionMask && \
Button4Mask == Button4MotionMask && \
Button5Mask == Button5MotionMask
// This little trick only works if Button#MotionMasks align with
// the Button#Masks.
event_mask = mov_event.state &
(Button1MotionMask | Button2MotionMask |
Button2MotionMask | Button3MotionMask | Button5MotionMask);
#else
// Fallback to some slightly larger...
if (event->state & Button1Mask) {
event_mask |= Button1MotionMask;
}
if (event->state & Button2Mask) {
event_mask |= Button2MotionMask;
}
if (event->state & Button3Mask) {
event_mask |= Button3MotionMask;
}
if (event->state & Button4Mask) {
event_mask |= Button4MotionMask;
}
if (event->state & Button5Mask) {
event_mask |= Button5MotionMask;
}
#endif
}
// NOTE x_mask = NoEventMask.
XSendEvent(properties_disp, InputFocus, False, event_mask, (XEvent *) &mov_event);
#endif
}
UIOHOOK_API void hook_post_event(uiohook_event * const event) {
XLockDisplay(properties_disp);
#ifdef USE_XTEST
// XTest does not have modifier support, so we fake it by depressing the
// appropriate modifier keys.
for (unsigned int i = 0; i < sizeof(keymask_lookup) / sizeof(KeySym); i++) {
if (event->mask & 1 << i) {
XTestFakeKeyEvent(properties_disp, XKeysymToKeycode(properties_disp, keymask_lookup[i]), True, 0);
}
}
for (unsigned int i = 0; i < sizeof(btnmask_lookup) / sizeof(unsigned int); i++) {
if (event->mask & btnmask_lookup[i]) {
XTestFakeButtonEvent(properties_disp, i + 1, True, 0);
}
}
#endif
switch (event->type) {
case EVENT_KEY_PRESSED:
case EVENT_KEY_RELEASED:
case EVENT_KEY_TYPED:
post_key_event(event);
break;
case EVENT_MOUSE_PRESSED:
case EVENT_MOUSE_RELEASED:
case EVENT_MOUSE_WHEEL:
case EVENT_MOUSE_CLICKED:
post_mouse_button_event(event);
break;
case EVENT_MOUSE_DRAGGED:
case EVENT_MOUSE_MOVED:
post_mouse_motion_event(event);
break;
case EVENT_HOOK_ENABLED:
case EVENT_HOOK_DISABLED:
// Ignore hook enabled / disabled events.
default:
// Ignore any other garbage.
logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n",
__FUNCTION__, __LINE__, event->type);
break;
}
#ifdef USE_XTEST
// Release the previously held modifier keys used to fake the event mask.
for (unsigned int i = 0; i < sizeof(keymask_lookup) / sizeof(KeySym); i++) {
if (event->mask & 1 << i) {
XTestFakeKeyEvent(properties_disp, XKeysymToKeycode(properties_disp, keymask_lookup[i]), False, 0);
}
}
for (unsigned int i = 0; i < sizeof(btnmask_lookup) / sizeof(unsigned int); i++) {
if (event->mask & btnmask_lookup[i]) {
XTestFakeButtonEvent(properties_disp, i + 1, False, 0);
}
}
#endif
// Don't forget to flush!
XSync(properties_disp, True);
XUnlockDisplay(properties_disp);
}

View File

@ -0,0 +1,498 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
// #include <uiohook.h>
#include "../uiohook.h"
#include <X11/Xlib.h>
#ifdef USE_XKB
#include <X11/XKBlib.h>
#endif
#ifdef USE_XF86MISC
#include <X11/extensions/xf86misc.h>
#include <X11/extensions/xf86mscstr.h>
#endif
#if defined(USE_XINERAMA) && !defined(USE_XRANDR)
#include <X11/extensions/Xinerama.h>
#elif defined(USE_XRANDR)
#include <pthread.h>
#include <X11/extensions/Xrandr.h>
#endif
#ifdef USE_XT
#include <X11/Intrinsic.h>
static XtAppContext xt_context;
static Display *xt_disp;
#endif
#include "input_helper.h"
// #include "../logger.h"
Display *properties_disp;
#ifdef USE_XRANDR
static pthread_mutex_t xrandr_mutex = PTHREAD_MUTEX_INITIALIZER;
static XRRScreenResources *xrandr_resources = NULL;
static void settings_cleanup_proc(void *arg) {
if (pthread_mutex_trylock(&xrandr_mutex) == 0) {
if (xrandr_resources != NULL) {
XRRFreeScreenResources(xrandr_resources);
xrandr_resources = NULL;
}
if (arg != NULL) {
XCloseDisplay((Display *) arg);
arg = NULL;
}
pthread_mutex_unlock(&xrandr_mutex);
}
}
static void *settings_thread_proc(void *arg) {
Display *settings_disp = XOpenDisplay(XDisplayName(NULL));;
if (settings_disp != NULL) {
logger(LOG_LEVEL_DEBUG, "%s [%u]: %s\n",
__FUNCTION__, __LINE__, "XOpenDisplay success.");
pthread_cleanup_push(settings_cleanup_proc, settings_disp);
int event_base = 0;
int error_base = 0;
if (XRRQueryExtension(settings_disp, &event_base, &error_base)) {
Window root = XDefaultRootWindow(settings_disp);
unsigned long event_mask = RRScreenChangeNotifyMask;
XRRSelectInput(settings_disp, root, event_mask);
XEvent ev;
while(settings_disp != NULL) {
XNextEvent(settings_disp, &ev);
if (ev.type == event_base + RRScreenChangeNotifyMask) {
logger(LOG_LEVEL_DEBUG, "%s [%u]: Received XRRScreenChangeNotifyEvent.\n",
__FUNCTION__, __LINE__);
pthread_mutex_lock(&xrandr_mutex);
if (xrandr_resources != NULL) {
XRRFreeScreenResources(xrandr_resources);
}
xrandr_resources = XRRGetScreenResources(settings_disp, root);
if (xrandr_resources == NULL) {
logger(LOG_LEVEL_WARN, "%s [%u]: XRandR could not get screen resources!\n",
__FUNCTION__, __LINE__);
}
pthread_mutex_unlock(&xrandr_mutex);
}
else {
logger(LOG_LEVEL_WARN, "%s [%u]: XRandR is not currently available!\n",
__FUNCTION__, __LINE__);
}
}
}
// Execute the thread cleanup handler.
pthread_cleanup_pop(1);
}
else {
logger(LOG_LEVEL_ERROR, "%s [%u]: XOpenDisplay failure!\n",
__FUNCTION__, __LINE__);
}
return NULL;
}
#endif
UIOHOOK_API screen_data* hook_create_screen_info(unsigned char *count) {
*count = 0;
screen_data *screens = NULL;
#if defined(USE_XINERAMA) && !defined(USE_XRANDR)
if (XineramaIsActive(properties_disp)) {
int xine_count = 0;
XineramaScreenInfo *xine_info = XineramaQueryScreens(properties_disp, &xine_count);
if (xine_info != NULL) {
if (xine_count > UINT8_MAX) {
*count = UINT8_MAX;
logger(LOG_LEVEL_WARN, "%s [%u]: Screen count overflow detected!\n",
__FUNCTION__, __LINE__);
}
else {
*count = (uint8_t) xine_count;
}
screens = malloc(sizeof(screen_data) * xine_count);
if (screens != NULL) {
for (int i = 0; i < xine_count; i++) {
screens[i] = (screen_data) {
.number = xine_info[i].screen_number,
.x = xine_info[i].x_org,
.y = xine_info[i].y_org,
.width = xine_info[i].width,
.height = xine_info[i].height
};
}
}
XFree(xine_info);
}
}
#elif defined(USE_XRANDR)
pthread_mutex_lock(&xrandr_mutex);
if (xrandr_resources != NULL) {
int xrandr_count = xrandr_resources->ncrtc;
if (xrandr_count > UINT8_MAX) {
*count = UINT8_MAX;
logger(LOG_LEVEL_WARN, "%s [%u]: Screen count overflow detected!\n",
__FUNCTION__, __LINE__);
}
else {
*count = (uint8_t) xrandr_count;
}
screens = malloc(sizeof(screen_data) * xrandr_count);
if (screens != NULL) {
for (int i = 0; i < xrandr_count; i++) {
XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(properties_disp, xrandr_resources, xrandr_resources->crtcs[i]);
if (crtc_info != NULL) {
screens[i] = (screen_data) {
.number = i + 1,
.x = crtc_info->x,
.y = crtc_info->y,
.width = crtc_info->width,
.height = crtc_info->height
};
XRRFreeCrtcInfo(crtc_info);
}
else {
logger(LOG_LEVEL_WARN, "%s [%u]: XRandr failed to return crtc information! (%#X)\n",
__FUNCTION__, __LINE__, xrandr_resources->crtcs[i]);
}
}
}
}
pthread_mutex_unlock(&xrandr_mutex);
#else
Screen* default_screen = DefaultScreenOfDisplay(properties_disp);
if (default_screen->width > 0 && default_screen->height > 0) {
screens = malloc(sizeof(screen_data));
if (screens != NULL) {
*count = 1;
screens[0] = (screen_data) {
.number = 1,
.x = 0,
.y = 0,
.width = default_screen->width,
.height = default_screen->height
};
}
}
#endif
return screens;
}
UIOHOOK_API long int hook_get_auto_repeat_rate() {
bool successful = false;
long int value = -1;
unsigned int delay = 0, rate = 0;
// Check and make sure we could connect to the x server.
if (properties_disp != NULL) {
#ifdef USE_XKB
// Attempt to acquire the keyboard auto repeat rate using the XKB extension.
if (!successful) {
successful = XkbGetAutoRepeatRate(properties_disp, XkbUseCoreKbd, &delay, &rate);
if (successful) {
logger(LOG_LEVEL_INFO, "%s [%u]: XkbGetAutoRepeatRate: %u.\n",
__FUNCTION__, __LINE__, rate);
}
}
#endif
#ifdef USE_XF86MISC
// Fallback to the XF86 Misc extension if available and other efforts failed.
if (!successful) {
XF86MiscKbdSettings kb_info;
successful = (bool) XF86MiscGetKbdSettings(properties_disp, &kb_info);
if (successful) {
logger(LOG_LEVEL_INFO, "%s [%u]: XF86MiscGetKbdSettings: %i.\n",
__FUNCTION__, __LINE__, kbdinfo.rate);
delay = (unsigned int) kbdinfo.delay;
rate = (unsigned int) kbdinfo.rate;
}
}
#endif
}
else {
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
}
if (successful) {
value = (long int) rate;
}
return value;
}
UIOHOOK_API long int hook_get_auto_repeat_delay() {
bool successful = false;
long int value = -1;
unsigned int delay = 0, rate = 0;
// Check and make sure we could connect to the x server.
if (properties_disp != NULL) {
#ifdef USE_XKB
// Attempt to acquire the keyboard auto repeat rate using the XKB extension.
if (!successful) {
successful = XkbGetAutoRepeatRate(properties_disp, XkbUseCoreKbd, &delay, &rate);
if (successful) {
logger(LOG_LEVEL_INFO, "%s [%u]: XkbGetAutoRepeatRate: %u.\n",
__FUNCTION__, __LINE__, delay);
}
}
#endif
#ifdef USE_XF86MISC
// Fallback to the XF86 Misc extension if available and other efforts failed.
if (!successful) {
XF86MiscKbdSettings kb_info;
successful = (bool) XF86MiscGetKbdSettings(properties_disp, &kb_info);
if (successful) {
logger(LOG_LEVEL_INFO, "%s [%u]: XF86MiscGetKbdSettings: %i.\n",
__FUNCTION__, __LINE__, kbdinfo.delay);
delay = (unsigned int) kbdinfo.delay;
rate = (unsigned int) kbdinfo.rate;
}
}
#endif
}
else {
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
}
if (successful) {
value = (long int) delay;
}
return value;
}
UIOHOOK_API long int hook_get_pointer_acceleration_multiplier() {
long int value = -1;
int accel_numerator, accel_denominator, threshold;
// Check and make sure we could connect to the x server.
if (properties_disp != NULL) {
XGetPointerControl(properties_disp, &accel_numerator, &accel_denominator, &threshold);
if (accel_denominator >= 0) {
logger(LOG_LEVEL_INFO, "%s [%u]: XGetPointerControl: %i.\n",
__FUNCTION__, __LINE__, accel_denominator);
value = (long int) accel_denominator;
}
}
else {
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
}
return value;
}
UIOHOOK_API long int hook_get_pointer_acceleration_threshold() {
long int value = -1;
int accel_numerator, accel_denominator, threshold;
// Check and make sure we could connect to the x server.
if (properties_disp != NULL) {
XGetPointerControl(properties_disp, &accel_numerator, &accel_denominator, &threshold);
if (threshold >= 0) {
logger(LOG_LEVEL_INFO, "%s [%u]: XGetPointerControl: %i.\n",
__FUNCTION__, __LINE__, threshold);
value = (long int) threshold;
}
}
else {
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
}
return value;
}
UIOHOOK_API long int hook_get_pointer_sensitivity() {
long int value = -1;
int accel_numerator, accel_denominator, threshold;
// Check and make sure we could connect to the x server.
if (properties_disp != NULL) {
XGetPointerControl(properties_disp, &accel_numerator, &accel_denominator, &threshold);
if (accel_numerator >= 0) {
logger(LOG_LEVEL_INFO, "%s [%u]: XGetPointerControl: %i.\n",
__FUNCTION__, __LINE__, accel_numerator);
value = (long int) accel_numerator;
}
}
else {
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
}
return value;
}
UIOHOOK_API long int hook_get_multi_click_time() {
long int value = 200;
int click_time;
bool successful = false;
#ifdef USE_XT
// Check and make sure we could connect to the x server.
if (xt_disp != NULL) {
// Try and use the Xt extention to get the current multi-click.
if (!successful) {
// Fall back to the X Toolkit extension if available and other efforts failed.
click_time = XtGetMultiClickTime(xt_disp);
if (click_time >= 0) {
logger(LOG_LEVEL_INFO, "%s [%u]: XtGetMultiClickTime: %i.\n",
__FUNCTION__, __LINE__, click_time);
successful = true;
}
}
}
else {
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
}
#endif
// Check and make sure we could connect to the x server.
if (properties_disp != NULL) {
// Try and acquire the multi-click time from the user defined X defaults.
if (!successful) {
char *xprop = XGetDefault(properties_disp, "*", "multiClickTime");
if (xprop != NULL && sscanf(xprop, "%4i", &click_time) != EOF) {
logger(LOG_LEVEL_INFO, "%s [%u]: X default 'multiClickTime' property: %i.\n",
__FUNCTION__, __LINE__, click_time);
successful = true;
}
}
if (!successful) {
char *xprop = XGetDefault(properties_disp, "OpenWindows", "MultiClickTimeout");
if (xprop != NULL && sscanf(xprop, "%4i", &click_time) != EOF) {
logger(LOG_LEVEL_INFO, "%s [%u]: X default 'MultiClickTimeout' property: %i.\n",
__FUNCTION__, __LINE__, click_time);
successful = true;
}
}
}
else {
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
}
if (successful) {
value = (long int) click_time;
}
return value;
}
// Create a shared object constructor.
__attribute__ ((constructor))
void on_library_load() {
// Make sure we are initialized for threading.
XInitThreads();
// Open local display.
properties_disp = XOpenDisplay(XDisplayName(NULL));
if (properties_disp == NULL) {
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
}
else {
logger(LOG_LEVEL_DEBUG, "%s [%u]: %s\n",
__FUNCTION__, __LINE__, "XOpenDisplay success.");
}
#ifdef USE_XRANDR
// Create the thread attribute.
pthread_attr_t settings_thread_attr;
pthread_attr_init(&settings_thread_attr);
pthread_t settings_thread_id;
if (pthread_create(&settings_thread_id, &settings_thread_attr, settings_thread_proc, NULL) == 0) {
logger(LOG_LEVEL_DEBUG, "%s [%u]: Successfully created settings thread.\n",
__FUNCTION__, __LINE__);
}
else {
logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to create settings thread!\n",
__FUNCTION__, __LINE__);
}
// Make sure the thread attribute is removed.
pthread_attr_destroy(&settings_thread_attr);
#endif
#ifdef USE_XT
XtToolkitInitialize();
xt_context = XtCreateApplicationContext();
int argc = 0;
char ** argv = { NULL };
xt_disp = XtOpenDisplay(xt_context, NULL, "UIOHook", "libuiohook", NULL, 0, &argc, argv);
#endif
// Initialize.
load_input_helper(properties_disp);
}
// Create a shared object destructor.
__attribute__ ((destructor))
void on_library_unload() {
// Disable the event hook.
//hook_stop();
// Cleanup.
unload_input_helper();
#ifdef USE_XT
XtCloseDisplay(xt_disp);
XtDestroyApplicationContext(xt_context);
#endif
// Destroy the native displays.
if (properties_disp != NULL) {
XCloseDisplay(properties_disp);
properties_disp = NULL;
}
}

View File

@ -6,7 +6,7 @@ package robotgo
#cgo darwin LDFLAGS: -framework Cocoa -framework OpenGL -framework IOKit -framework Carbon -framework CoreFoundation -L/usr/local/opt/libpng/lib -lpng -L/usr/local/opt/zlib/lib -lz
//#elif defined(USE_X11)
#cgo linux CFLAGS:-I/usr/src
#cgo linux LDFLAGS:-L/usr/src -lpng -lz -lX11 -lXtst -lm
#cgo linux LDFLAGS:-L/usr/src -lpng -lz -lX11 -lXtst -lX11-xcb -lxcb -lxcb-xkb -lxkbcommon -lxkbcommon-x11 -lm
//#endif
#cgo windows LDFLAGS: -lgdi32 -luser32 -lpng -lz
//#include <AppKit/NSEvent.h>
@ -14,7 +14,7 @@ package robotgo
#include "mouse/goMouse.h"
#include "key/goKey.h"
#include "bitmap/goBitmap.h"
//#include "event/goEvent.h"
#include "event/goEvent.h"
//#include "window/goWindow.h"
*/
import "C"
@ -373,6 +373,14 @@ func Convert(args ...interface{}) {
*/
func LEvent(aeve string) int {
cs := C.CString(aeve)
eve := C.aEvent(cs)
// Println("event@@", eve)
geve := int(eve)
return geve
}
/*
____ __ ____ __ .__ __. _______ ______ ____ __ ____
\ \ / \ / / | | | \ | | | \ / __ \ \ \ / \ / /