Skip to content

Commit

Permalink
Require XTest extension and remove support for XSendEvent. Fixes #123
Browse files Browse the repository at this point in the history
  • Loading branch information
kwhat committed Mar 20, 2022
1 parent 24cff27 commit a887cde
Show file tree
Hide file tree
Showing 3 changed files with 2 additions and 252 deletions.
6 changes: 0 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -210,12 +210,6 @@ if(UNIX AND NOT APPLE)
add_compile_definitions(uiohook PRIVATE USE_XRECORD_ASYNC)
endif()

option(USE_XTEST "XTest API (default: ON)" ON)
if(USE_XTEST)
# XTest API is provided by Xtst
add_compile_definitions(uiohook PRIVATE USE_XTEST)
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
option(USE_EVDEV "Generic Linux input driver (default: ON)" ON)
if(USE_EVDEV)
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ $ cmake --build . --parallel 2 --target install
| | USE_XRANDR:BOOL | xrandt extension | OFF |
| | USE_XRECORD_ASYNC:BOOL | xrecord async api | OFF |
| | USE_XT:BOOL | x toolkit extension | ON |
| | USE_XTEST:BOOL | xtest extension | ON |

## Usage
* [Hook Demo](demo/demo_hook.c)
Expand Down
247 changes: 2 additions & 245 deletions src/x11/post_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,11 @@
#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"

#ifndef USE_XTEST
static long current_modifier_mask = NoEventMask;
#endif

static int post_key_event(uiohook_event * const event) {
KeyCode keycode = scancode_to_keycode(event->data.keyboard.keycode);
Expand All @@ -41,114 +36,22 @@ static int post_key_event(uiohook_event * const event) {
return UIOHOOK_FAILURE;
}

#ifdef USE_XTEST
Bool is_pressed;
#else
long event_mask;

XKeyEvent key_event = {
.serial = 0x00,
.time = CurrentTime,
.same_screen = True,
.send_event = False,
.display = helper_disp,

.root = XDefaultRootWindow(helper_disp),
.window = None,
.subwindow = None,

.x_root = 0,
.y_root = 0,
.x = 0,
.y = 0,

.state = current_modifier_mask,
.keycode = keycode
};

int revert;
XGetInputFocus(helper_disp, &(key_event.window), &revert);
#endif

if (event->type == EVENT_KEY_PRESSED) {
#ifdef USE_XTEST
is_pressed = True;
#else
key_event.type = KeyPress;
event_mask = KeyPressMask;

switch (event->data.keyboard.keycode) {
case VC_SHIFT_L:
case VC_SHIFT_R:
current_modifier_mask |= ShiftMask;
break;

case VC_CONTROL_L:
case VC_CONTROL_R:
current_modifier_mask |= ControlMask;
break;

case VC_META_L:
case VC_META_R:
current_modifier_mask |= Mod4Mask;
break;

case VC_ALT_L:
case VC_ALT_R:
current_modifier_mask |= Mod1Mask;
break;
}
#endif

} else if (event->type == EVENT_KEY_RELEASED) {
#ifdef USE_XTEST
is_pressed = False;
#else
key_event.type = KeyRelease;
event_mask = KeyReleaseMask;

switch (event->data.keyboard.keycode) {
case VC_SHIFT_L:
case VC_SHIFT_R:
current_modifier_mask &= ~ShiftMask;
break;

case VC_CONTROL_L:
case VC_CONTROL_R:
current_modifier_mask &= ~ControlMask;
break;

case VC_META_L:
case VC_META_R:
current_modifier_mask &= ~Mod4Mask;
break;

case VC_ALT_L:
case VC_ALT_R:
current_modifier_mask &= ~Mod1Mask;
break;
}
#endif
} else {
logger(LOG_LEVEL_DEBUG, "%s [%u]: Invalid event for keyboard post event: %#X.\n",
__FUNCTION__, __LINE__, event->type);
return UIOHOOK_FAILURE;
}

#ifdef USE_XTEST
if (XTestFakeKeyEvent(helper_disp, keycode, is_pressed, 0) != Success) {
logger(LOG_LEVEL_ERROR, "%s [%u]: XTestFakeKeyEvent() failed!\n",
__FUNCTION__, __LINE__, event->type);
return UIOHOOK_FAILURE;
}
#else
XSelectInput(helper_disp, key_event.window, KeyPressMask | KeyReleaseMask);
if (XSendEvent(helper_disp, key_event.window, False, event_mask, (XEvent *) &key_event) == 0) {
logger(LOG_LEVEL_ERROR, "%s [%u]: XSendEvent() failed!\n",
__FUNCTION__, __LINE__, event->type);
return UIOHOOK_FAILURE;
}
#endif

return UIOHOOK_SUCCESS;
}
Expand Down Expand Up @@ -176,36 +79,11 @@ static int post_mouse_button_event(uiohook_event * const event) {
};

// Move the pointer to the specified position.
#ifdef USE_XTEST
XTestFakeMotionEvent(btn_event.display, -1, btn_event.x, btn_event.y, 0);
#else
XWarpPointer(btn_event.display, None, btn_event.subwindow, 0, 0, 0, 0, btn_event.x, btn_event.y);
XFlush(btn_event.display);
#endif

#ifndef USE_XTEST
// FIXME This is still not working correctly, clicking on other windows does not yield focus.
while (btn_event.subwindow != None)
{
btn_event.window = btn_event.subwindow;
XQueryPointer (
btn_event.display,
btn_event.window,
&btn_event.root,
&btn_event.subwindow,
&btn_event.x_root,
&btn_event.y_root,
&btn_event.x,
&btn_event.y,
&btn_event.state
);
}
#endif

int status = UIOHOOK_FAILURE;
switch (event->type) {
case EVENT_MOUSE_PRESSED:
#ifdef USE_XTEST
if (event->data.mouse.button < MOUSE_BUTTON1 || event->data.mouse.button > MOUSE_BUTTON5) {
logger(LOG_LEVEL_WARN, "%s [%u]: Invalid button specified for mouse pressed event! (%u)\n",
__FUNCTION__, __LINE__, event->data.mouse.button);
Expand All @@ -215,34 +93,9 @@ static int post_mouse_button_event(uiohook_event * const event) {
if (XTestFakeButtonEvent(helper_disp, event->data.mouse.button, True, 0) != 0) {
status = UIOHOOK_SUCCESS;
}
#else
if (event->data.mouse.button == MOUSE_BUTTON1) {
current_modifier_mask |= Button1MotionMask;
} else if (event->data.mouse.button == MOUSE_BUTTON2) {
current_modifier_mask |= Button2MotionMask;
} else if (event->data.mouse.button == MOUSE_BUTTON3) {
current_modifier_mask |= Button3MotionMask;
} else if (event->data.mouse.button == MOUSE_BUTTON4) {
current_modifier_mask |= Button4MotionMask;
} else if (event->data.mouse.button == MOUSE_BUTTON5) {
current_modifier_mask |= Button5MotionMask;
} else {
logger(LOG_LEVEL_WARN, "%s [%u]: Invalid button specified for mouse pressed event! (%u)\n",
__FUNCTION__, __LINE__, event->data.mouse.button);
return UIOHOOK_FAILURE;
}

btn_event.type = ButtonPress;
btn_event.button = event->data.mouse.button;
btn_event.state = current_modifier_mask;
if (XSendEvent(helper_disp, btn_event.window, False, ButtonPressMask, (XEvent *) &btn_event) != 0) {
status = UIOHOOK_SUCCESS;
}
#endif
break;

case EVENT_MOUSE_RELEASED:
#ifdef USE_XTEST
if (event->data.mouse.button < MOUSE_BUTTON1 || event->data.mouse.button > MOUSE_BUTTON5) {
logger(LOG_LEVEL_WARN, "%s [%u]: Invalid button specified for mouse released event! (%u)\n",
__FUNCTION__, __LINE__, event->data.mouse.button);
Expand All @@ -252,30 +105,6 @@ static int post_mouse_button_event(uiohook_event * const event) {
if (XTestFakeButtonEvent(helper_disp, event->data.mouse.button, False, 0) != 0) {
status = UIOHOOK_SUCCESS;
}
#else
if (event->data.mouse.button == MOUSE_BUTTON1) {
current_modifier_mask &= ~Button1MotionMask;
} else if (event->data.mouse.button == MOUSE_BUTTON2) {
current_modifier_mask &= ~Button2MotionMask;
} else if (event->data.mouse.button == MOUSE_BUTTON3) {
current_modifier_mask &= ~Button3MotionMask;
} else if (event->data.mouse.button == MOUSE_BUTTON4) {
current_modifier_mask &= ~Button4MotionMask;
} else if (event->data.mouse.button == MOUSE_BUTTON5) {
current_modifier_mask &= ~Button5MotionMask;
} else {
logger(LOG_LEVEL_WARN, "%s [%u]: Invalid button specified for mouse released event! (%u)\n",
__FUNCTION__, __LINE__, event->data.mouse.button);
return UIOHOOK_FAILURE;
}

btn_event.type = ButtonRelease;
btn_event.button = event->data.mouse.button;
btn_event.state = current_modifier_mask;
if (XSendEvent(helper_disp, btn_event.window, False, ButtonReleaseMask, (XEvent *) &btn_event) != 0) {
status = UIOHOOK_SUCCESS;
}
#endif
break;

default:
Expand Down Expand Up @@ -311,55 +140,16 @@ static int post_mouse_wheel_event(uiohook_event * const event) {
.same_screen = True
};

#ifndef USE_XTEST
// FIXME This is still not working correctly, clicking on other windows does not yield focus.
while (btn_event.subwindow != None)
{
btn_event.window = btn_event.subwindow;
XQueryPointer (
btn_event.display,
btn_event.window,
&btn_event.root,
&btn_event.subwindow,
&btn_event.x_root,
&btn_event.y_root,
&btn_event.x,
&btn_event.y,
&btn_event.state
);
}
#endif

// Wheel events should be the same as click events on X11.
// type, amount and rotation
unsigned int button = button_map_lookup(event->data.wheel.rotation < 0 ? WheelUp : WheelDown);

#ifdef USE_XTEST
if (XTestFakeButtonEvent(helper_disp, button, True, 0) != 0) {
status = UIOHOOK_SUCCESS;
}
#else
btn_event.type = ButtonPress;
btn_event.button = button;
btn_event.state = current_modifier_mask;
if (XSendEvent(helper_disp, btn_event.window, False, ButtonPressMask, (XEvent *) &btn_event) != 0) {
status = UIOHOOK_SUCCESS;
}
#endif

if (status == UIOHOOK_SUCCESS) {
#ifdef USE_XTEST
if (XTestFakeButtonEvent(helper_disp, button, False, 0) == 0) {
status = UIOHOOK_FAILURE;
}
#else
btn_event.type = ButtonRelease;
btn_event.button = button;
btn_event.state = current_modifier_mask;
if (XSendEvent(helper_disp, btn_event.window, False, ButtonReleaseMask, (XEvent *) &btn_event) == 0) {
status = UIOHOOK_FAILURE;
}
#endif
if (status == UIOHOOK_SUCCESS && XTestFakeButtonEvent(helper_disp, button, False, 0) == 0) {
status = UIOHOOK_FAILURE;
}

return UIOHOOK_SUCCESS;
Expand All @@ -368,42 +158,9 @@ static int post_mouse_wheel_event(uiohook_event * const event) {
static int post_mouse_motion_event(uiohook_event * const event) {
int status = UIOHOOK_FAILURE;

#ifdef USE_XTEST
if (XTestFakeMotionEvent(helper_disp, -1, event->data.mouse.x, event->data.mouse.y, 0) != 0) {
status = UIOHOOK_SUCCESS;
}
#else
XMotionEvent mov_event = {
.type = MotionNotify,
.serial = 0,
.send_event = False,
.display = helper_disp,

.window = None, /* “event” window it is reported relative to */
.root = XDefaultRootWindow(helper_disp), /* root window that the event occurred on */
.subwindow = None, /* child window */

.time = CurrentTime,

.x = event->data.mouse.x, /* pointer x, y coordinates in event window */
.y = event->data.mouse.y,

.x_root = event->data.mouse.x, /* coordinates relative to root */
.y_root = event->data.mouse.x,

.state = current_modifier_mask | MotionNotify, /* key or button mask */

.is_hint = NotifyNormal,
.same_screen = True
};

int revert;
XGetInputFocus(helper_disp, &(mov_event.window), &revert);

if (XSendEvent(helper_disp, mov_event.window, False, mov_event.state, (XEvent *) &mov_event) != 0) {
status = UIOHOOK_SUCCESS;
}
#endif

return status;
}
Expand Down

0 comments on commit a887cde

Please sign in to comment.