use x11::{keysym, xlib};
use super::sys::OpenDisplay;
use super::utils;
use crate::{Button, Key};
pub struct Simulator {
display: OpenDisplay,
supports_xtest: bool,
}
impl Simulator {
pub fn new() -> Result<Self, super::Error> {
let display = OpenDisplay::open()?;
let supports_xtest = display.xtest_query_extension();
Ok(Self {
display,
supports_xtest,
})
}
pub fn press_key(&self, key: Key) -> Result<(), super::Error> {
let keycode = self
.display
.keysym_to_keycode(utils::key_to_x11(key))
.ok_or(super::Error::UnsupportedKey(key))?;
if self.supports_xtest {
self.display.xtest_fake_key_event(keycode as _, true, 0)?;
} else {
let window = self.display.get_input_focus()?;
self.display.send_key_event(window, keycode as _, 0, true)?;
}
self.display.flush()?;
Ok(())
}
pub fn release_key(&self, key: Key) -> Result<(), super::Error> {
let keycode = self
.display
.keysym_to_keycode(utils::key_to_x11(key))
.ok_or(super::Error::UnsupportedKey(key))?;
if self.supports_xtest {
self.display.xtest_fake_key_event(keycode as _, false, 0)?;
} else {
let window = self.display.get_input_focus()?;
self.display
.send_key_event(window, keycode as _, 0, false)?;
}
self.display.flush()?;
Ok(())
}
pub fn send_key(&self, key: Key) -> Result<(), super::Error> {
let keycode = self
.display
.keysym_to_keycode(utils::key_to_x11(key))
.ok_or(super::Error::UnsupportedKey(key))?;
if self.supports_xtest {
self.display.xtest_fake_key_event(keycode as _, true, 0)?;
self.display.xtest_fake_key_event(keycode as _, false, 0)?;
} else {
let window = self.display.get_input_focus()?;
self.display.send_key_event(window, keycode as _, 0, true)?;
self.display
.send_key_event(window, keycode as _, 0, false)?;
}
self.display.flush()?;
Ok(())
}
pub fn press_button(&self, button: Button) -> Result<(), super::Error> {
let button = utils::button_to_x11(button);
if self.supports_xtest {
self.display.xtest_fake_button_event(button, true, 0)?;
} else {
let window = self.display.get_input_focus()?;
self.display.send_button_event(window, button, true)?;
}
self.display.flush()?;
Ok(())
}
pub fn release_button(&self, button: Button) -> Result<(), super::Error> {
let button = utils::button_to_x11(button);
if self.supports_xtest {
self.display.xtest_fake_button_event(button, false, 0)?;
} else {
let window = self.display.get_input_focus()?;
self.display.send_button_event(window, button, false)?;
}
self.display.flush()?;
Ok(())
}
pub fn send_button(&self, button: Button) -> Result<(), super::Error> {
let button = utils::button_to_x11(button);
if self.supports_xtest {
self.display.xtest_fake_button_event(button, true, 0)?;
self.display.xtest_fake_button_event(button, false, 0)?;
} else {
let window = self.display.get_input_focus()?;
self.display.send_button_event(window, button, true)?;
self.display.send_button_event(window, button, false)?;
}
self.display.flush()?;
Ok(())
}
fn _send_char_xtest(&self, c: char) -> Result<(), super::Error> {
let (keysym, shift) = utils::char_to_x11(c).ok_or(super::Error::UnsupportedChar(c))?;
let keycode = self
.display
.keysym_to_keycode(keysym)
.ok_or(super::Error::UnsupportedChar(c))?;
let mut shift_keycode = 0;
if shift {
shift_keycode = self
.display
.keysym_to_keycode(keysym::XK_Shift_L as _)
.ok_or(super::Error::UnsupportedChar(c))?;
self.display
.xtest_fake_key_event(shift_keycode as _, true, 0)?;
}
self.display.xtest_fake_key_event(keycode as _, true, 0)?;
self.display.xtest_fake_key_event(keycode as _, false, 0)?;
if shift {
self.display
.xtest_fake_key_event(shift_keycode as _, false, 0)?;
}
Ok(())
}
fn _send_char(&self, window: xlib::Window, c: char) -> Result<(), super::Error> {
let (keysym, shift) = utils::char_to_x11(c).ok_or(super::Error::UnsupportedChar(c))?;
let keycode = self
.display
.keysym_to_keycode(keysym)
.ok_or(super::Error::UnsupportedChar(c))?;
let state = if shift { xlib::ShiftMask } else { 0 };
self.display
.send_key_event(window, keycode as _, state, true)?;
self.display
.send_key_event(window, keycode as _, state, false)?;
Ok(())
}
pub fn send_char(&self, c: char) -> Result<(), super::Error> {
if self.supports_xtest {
self._send_char_xtest(c)?;
} else {
let window = self.display.get_input_focus()?;
self._send_char(window, c)?;
}
self.display.flush()?;
Ok(())
}
pub fn send_chars(&self, mut it: impl Iterator<Item = char>) -> Result<(), super::Error> {
if self.supports_xtest {
it.try_for_each(|c| self._send_char_xtest(c))?;
} else {
let window = self.display.get_input_focus()?;
it.try_for_each(move |c| self._send_char(window, c))?;
}
self.display.flush()?;
Ok(())
}
pub fn send_str(&self, s: &str) -> Result<(), super::Error> {
self.send_chars(s.chars())
}
}