use crate::error::{Error, Result};
use crate::event::{Button, Event, EventType};
use crate::keycode::Key;
use std::os::raw::c_int;
use std::ptr::null;
use x11::xlib;
use x11::xtest;
use crate::platform::linux::keycodes::key_to_keycode;
const TRUE: c_int = 1;
const FALSE: c_int = 0;
pub fn mouse_position() -> Result<(f64, f64)> {
let display = open_display()?;
let screen = unsafe { xlib::XDefaultScreen(display) };
let root = unsafe { xlib::XRootWindow(display, screen) };
let mut root_return = 0u64;
let mut child_return = 0u64;
let mut root_x: c_int = 0;
let mut root_y: c_int = 0;
let mut win_x: c_int = 0;
let mut win_y: c_int = 0;
let mut mask: u32 = 0;
let result = unsafe {
xlib::XQueryPointer(
display,
root,
&mut root_return,
&mut child_return,
&mut root_x,
&mut root_y,
&mut win_x,
&mut win_y,
&mut mask,
)
};
unsafe { xlib::XCloseDisplay(display) };
if result == FALSE {
Err(Error::SimulateFailed("XQueryPointer failed".into()))
} else {
Ok((root_x as f64, root_y as f64))
}
}
fn open_display() -> Result<*mut xlib::Display> {
let display = unsafe { xlib::XOpenDisplay(null()) };
if display.is_null() {
Err(Error::SimulateFailed("Failed to open X display".into()))
} else {
Ok(display)
}
}
pub fn simulate(event: &Event) -> Result<()> {
match event.event_type {
EventType::KeyPressed => {
if let Some(kb) = &event.keyboard {
key_press(kb.key)?;
}
}
EventType::KeyReleased => {
if let Some(kb) = &event.keyboard {
key_release(kb.key)?;
}
}
EventType::MousePressed => {
if let Some(mouse) = &event.mouse
&& let Some(button) = mouse.button
{
mouse_press(button)?;
}
}
EventType::MouseReleased => {
if let Some(mouse) = &event.mouse
&& let Some(button) = mouse.button
{
mouse_release(button)?;
}
}
EventType::MouseMoved | EventType::MouseDragged => {
if let Some(mouse) = &event.mouse {
mouse_move(mouse.x, mouse.y)?;
}
}
EventType::MouseWheel => {
if let Some(wheel) = &event.wheel {
mouse_scroll(wheel.delta as i32, 0)?;
}
}
_ => {}
}
Ok(())
}
pub fn key_press(key: Key) -> Result<()> {
let keycode = key_to_keycode(key)
.ok_or_else(|| Error::SimulateFailed(format!("Unsupported key: {:?}", key)))?;
let display = open_display()?;
let result = unsafe { xtest::XTestFakeKeyEvent(display, keycode, TRUE, 0) };
unsafe {
xlib::XFlush(display);
xlib::XSync(display, 0);
xlib::XCloseDisplay(display);
}
if result == 0 {
Err(Error::SimulateFailed("XTestFakeKeyEvent failed".into()))
} else {
Ok(())
}
}
pub fn key_release(key: Key) -> Result<()> {
let keycode = key_to_keycode(key)
.ok_or_else(|| Error::SimulateFailed(format!("Unsupported key: {:?}", key)))?;
let display = open_display()?;
let result = unsafe { xtest::XTestFakeKeyEvent(display, keycode, FALSE, 0) };
unsafe {
xlib::XFlush(display);
xlib::XSync(display, 0);
xlib::XCloseDisplay(display);
}
if result == 0 {
Err(Error::SimulateFailed("XTestFakeKeyEvent failed".into()))
} else {
Ok(())
}
}
pub fn key_tap(key: Key) -> Result<()> {
key_press(key)?;
key_release(key)?;
Ok(())
}
fn button_to_code(button: Button) -> u32 {
match button {
Button::Left => 1,
Button::Middle => 2,
Button::Right => 3,
Button::Button4 => 8,
Button::Button5 => 9,
Button::Unknown(code) => code as u32,
}
}
pub fn mouse_press(button: Button) -> Result<()> {
let code = button_to_code(button);
let display = open_display()?;
let result = unsafe { xtest::XTestFakeButtonEvent(display, code, TRUE, 0) };
unsafe {
xlib::XFlush(display);
xlib::XSync(display, 0);
xlib::XCloseDisplay(display);
}
if result == 0 {
Err(Error::SimulateFailed("XTestFakeButtonEvent failed".into()))
} else {
Ok(())
}
}
pub fn mouse_release(button: Button) -> Result<()> {
let code = button_to_code(button);
let display = open_display()?;
let result = unsafe { xtest::XTestFakeButtonEvent(display, code, FALSE, 0) };
unsafe {
xlib::XFlush(display);
xlib::XSync(display, 0);
xlib::XCloseDisplay(display);
}
if result == 0 {
Err(Error::SimulateFailed("XTestFakeButtonEvent failed".into()))
} else {
Ok(())
}
}
pub fn mouse_click(button: Button) -> Result<()> {
mouse_press(button)?;
mouse_release(button)?;
Ok(())
}
pub fn mouse_move(x: f64, y: f64) -> Result<()> {
let display = open_display()?;
let x_int = if x.is_finite() {
x.clamp(c_int::MIN as f64, c_int::MAX as f64).round() as c_int
} else {
0
};
let y_int = if y.is_finite() {
y.clamp(c_int::MIN as f64, c_int::MAX as f64).round() as c_int
} else {
0
};
let result = unsafe { xtest::XTestFakeMotionEvent(display, 0, x_int, y_int, 0) };
unsafe {
xlib::XFlush(display);
xlib::XSync(display, 0);
xlib::XCloseDisplay(display);
}
if result == 0 {
Err(Error::SimulateFailed("XTestFakeMotionEvent failed".into()))
} else {
Ok(())
}
}
pub fn mouse_scroll(delta_y: i32, delta_x: i32) -> Result<()> {
let display = open_display()?;
let mut success = true;
unsafe {
if delta_y != 0 {
let button = if delta_y > 0 { 4 } else { 5 }; for _ in 0..delta_y.abs() {
let r1 = xtest::XTestFakeButtonEvent(display, button, TRUE, 0);
let r2 = xtest::XTestFakeButtonEvent(display, button, FALSE, 0);
if r1 == 0 || r2 == 0 {
success = false;
}
}
}
if delta_x != 0 {
let button = if delta_x > 0 { 7 } else { 6 }; for _ in 0..delta_x.abs() {
let r1 = xtest::XTestFakeButtonEvent(display, button, TRUE, 0);
let r2 = xtest::XTestFakeButtonEvent(display, button, FALSE, 0);
if r1 == 0 || r2 == 0 {
success = false;
}
}
}
xlib::XFlush(display);
xlib::XSync(display, 0);
xlib::XCloseDisplay(display);
}
if success {
Ok(())
} else {
Err(Error::SimulateFailed("XTestFakeButtonEvent failed".into()))
}
}