mirror of
https://github.com/go-vgo/robotgo.git
synced 2025-05-31 06:13:55 +00:00
Update event
This commit is contained in:
parent
92c57da678
commit
852ece669f
301
event/goEvent.h
301
event/goEvent.h
@ -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;
|
||||
}
|
103
event/hook/darwin/input_helper.h
Normal file
103
event/hook/darwin/input_helper.h
Normal 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
|
543
event/hook/darwin/input_helper_c.h
Normal file
543
event/hook/darwin/input_helper_c.h
Normal 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
|
||||
}
|
1363
event/hook/darwin/input_hook_c.h
Normal file
1363
event/hook/darwin/input_hook_c.h
Normal file
File diff suppressed because it is too large
Load Diff
236
event/hook/darwin/post_event_c.h
Normal file
236
event/hook/darwin/post_event_c.h
Normal 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;
|
||||
}
|
||||
}
|
522
event/hook/darwin/system_properties_c.h
Normal file
522
event/hook/darwin/system_properties_c.h
Normal 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
16
event/hook/logger.h
Normal 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
54
event/hook/logger_c.h
Normal 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
442
event/hook/uiohook.h
Normal 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
|
104
event/hook/windows/input_helper.h
Normal file
104
event/hook/windows/input_helper.h
Normal 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
|
833
event/hook/windows/input_helper_c.h
Normal file
833
event/hook/windows/input_helper_c.h
Normal 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;
|
||||
}
|
748
event/hook/windows/input_hook_c.h
Normal file
748
event/hook/windows/input_hook_c.h
Normal 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;
|
||||
}
|
329
event/hook/windows/post_event_c.h
Normal file
329
event/hook/windows/post_event_c.h
Normal 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);
|
||||
}
|
214
event/hook/windows/system_properties_c.h
Normal file
214
event/hook/windows/system_properties_c.h
Normal 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;
|
||||
}
|
86
event/hook/x11/input_helper.h
Normal file
86
event/hook/x11/input_helper.h
Normal 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
|
1969
event/hook/x11/input_helper_c.h
Normal file
1969
event/hook/x11/input_helper_c.h
Normal file
File diff suppressed because it is too large
Load Diff
1128
event/hook/x11/input_hook_c.h
Normal file
1128
event/hook/x11/input_hook_c.h
Normal file
File diff suppressed because it is too large
Load Diff
384
event/hook/x11/post_event_c.h
Normal file
384
event/hook/x11/post_event_c.h
Normal 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);
|
||||
}
|
498
event/hook/x11/system_properties_c.h
Normal file
498
event/hook/x11/system_properties_c.h
Normal 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;
|
||||
}
|
||||
}
|
12
robotgo.go
12
robotgo.go
@ -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
|
||||
}
|
||||
|
||||
/*
|
||||
____ __ ____ __ .__ __. _______ ______ ____ __ ____
|
||||
\ \ / \ / / | | | \ | | | \ / __ \ \ \ / \ / /
|
||||
|
Loading…
Reference in New Issue
Block a user