mirror of
https://github.com/robotn/gohook.git
synced 2024-11-23 03:56:47 +08:00
507 lines
13 KiB
C
507 lines
13 KiB
C
/* Copyright (C) 2006-2017 Alexander Barker.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published
|
|
* by the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.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 "../iohook.h"
|
|
#include "input.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
|
|
|
|
IOHOOK_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) {
|
|
int i;
|
|
for (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) {
|
|
int i;
|
|
for (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;
|
|
}
|
|
|
|
IOHOOK_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;
|
|
}
|
|
|
|
IOHOOK_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;
|
|
}
|
|
|
|
IOHOOK_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;
|
|
}
|
|
|
|
IOHOOK_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;
|
|
}
|
|
|
|
IOHOOK_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;
|
|
}
|
|
|
|
IOHOOK_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_WARN, "%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, "IOHook", "libIOhook", 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;
|
|
}
|
|
}
|