use crate::builder::KeyflowBuilder;
use crate::error::*;
use crate::eventhandler::EventHandler;
use crate::hotkey::{Hotkey, HotkeyRegistry};
use crate::platform::{Backend, BackendListener, Listener, Simulation};
use crate::types::*;
use crossbeam_channel::{Sender, unbounded};
use std::sync::{Arc, OnceLock, RwLock};
static KEYFLOW: OnceLock<Keyflow> = OnceLock::new();
pub struct Keyflow {
event_tx: Sender<InternalMessage>,
hotkey_listener: Option<Box<dyn Listener>>,
}
impl Keyflow {
pub fn initialize() -> Result<()> {
Self::builder().initialize()
}
pub(crate) fn initialize_with_config(config: KeyflowBuilder) -> Result<()> {
if KEYFLOW.get().is_some() {
return Err(KeyflowError::AlreadyInitialized);
}
#[cfg(target_os = "linux")]
let backend = {
if config.screen.is_none() {
return Err(KeyflowError::PlatformError(
"Screen size not set. It is required if run on linux".into(),
));
}
Box::new(Backend::new(config.screen.unwrap())?) as Box<dyn Simulation>
};
#[cfg(target_os = "windows")]
let backend = { Box::new(Backend::new()?) as Box<dyn Simulation> };
let (event_tx, event_rx) = unbounded();
let hotkey_registry = Arc::new(RwLock::new(HotkeyRegistry::new()));
let mut handler = EventHandler::new(backend, hotkey_registry.clone(), event_rx);
std::thread::Builder::new()
.name("keyflow-event-handler".to_string())
.spawn(move || {
if let Err(e) = handler.run() {
eprintln!("EventHandler error: {}", e);
}
})
.map_err(|e| KeyflowError::PlatformError(e.to_string()))?;
let hotkey_listener = if config.enable_hotkeys {
let listener =
Box::new(BackendListener::new(hotkey_registry.clone())) as Box<dyn Listener>;
listener.start()?;
Some(listener)
} else {
None
};
KEYFLOW
.set(Self {
event_tx,
hotkey_listener,
})
.map_err(|_| KeyflowError::AlreadyInitialized)?;
Ok(())
}
pub fn press_key(key: Key) {
Self::send_event(InputEvent::KeyEvent {
key,
action: Action::Press,
})
.expect("Failed to send key press event");
}
pub fn release_key(key: Key) {
Self::send_event(InputEvent::KeyEvent {
key,
action: Action::Release,
})
.expect("Failed to send key release event");
}
pub fn click_key(key: Key) {
Self::send_event(InputEvent::KeyEvent {
key,
action: Action::Click,
})
.expect("Failed to send key click event");
}
pub fn press_button(button: Button) {
Self::send_event(InputEvent::ButtonEvent {
button,
action: Action::Press,
})
.expect("Failed to send button press event");
}
pub fn release_button(button: Button) {
Self::send_event(InputEvent::ButtonEvent {
button,
action: Action::Release,
})
.expect("Failed to send button release event");
}
pub fn click_button(button: Button) {
Self::send_event(InputEvent::ButtonEvent {
button,
action: Action::Click,
})
.expect("Failed to send button click event");
}
pub fn move_by(dx: i32, dy: i32) {
Self::send_event(InputEvent::MouseMove(Movement::Relative { dx, dy }))
.expect("Failed to send relative move event");
}
pub fn move_to(x: i32, y: i32) {
Self::send_event(InputEvent::MouseMove(Movement::Absolute { x, y }))
.expect("Failed to send absolute move event");
}
pub fn scroll_vertical(delta: i32) {
Self::send_event(InputEvent::MouseScroll(Scroll::Vertical(delta)))
.expect("Failed to send scroll event");
}
pub fn scroll_horizontal(delta: i32) {
Self::send_event(InputEvent::MouseScroll(Scroll::Horizontal(delta)))
.expect("Failed to send scroll event");
}
pub fn batch(events: Vec<InputEvent>) {
Self::send_batch_events(events).expect("Failed to send batch events");
}
pub fn register_hotkey<F>(id: impl Into<String>, hotkey: Hotkey, callback: F) -> Result<()>
where
F: Fn() + Send + Sync + 'static,
{
let state = Self::get_state()?;
if state.hotkey_listener.is_none() {
return Err(KeyflowError::HotkeysNotEnabled);
}
state
.event_tx
.send(InternalMessage::RegisterHotkey {
id: id.into(),
combo: hotkey,
callback: Box::new(callback),
})
.map_err(|_| KeyflowError::ChannelDisconnected)?;
Ok(())
}
pub fn unregister_hotkey(id: impl Into<String>) -> Result<()> {
let state = Self::get_state()?;
if state.hotkey_listener.is_none() {
return Err(KeyflowError::HotkeysNotEnabled);
}
state
.event_tx
.send(InternalMessage::UnregisterHotkey { id: id.into() })
.map_err(|_| KeyflowError::ChannelDisconnected)?;
Ok(())
}
fn send_event(event: InputEvent) -> Result<()> {
let state = Self::get_state()?;
state
.event_tx
.send(InternalMessage::SimulateEvent(event))
.map_err(|_| KeyflowError::ChannelDisconnected)
}
fn send_batch_events(events: Vec<InputEvent>) -> Result<()> {
let state = Self::get_state()?;
state
.event_tx
.send(InternalMessage::BatchEvents(events))
.map_err(|_| KeyflowError::ChannelDisconnected)
}
fn get_state() -> Result<&'static Keyflow> {
KEYFLOW.get().ok_or(KeyflowError::NotInitialized)
}
}
impl Drop for Keyflow {
fn drop(&mut self) {
if let Some(listener) = &self.hotkey_listener {
listener.stop()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(target_os = "linux")]
#[test]
fn test_screen_required() {
let result = Keyflow::initialize();
assert!(result.is_err());
}
#[test]
fn test_hotkeys_enabled() {
Keyflow::builder()
.with_hotkeys()
.with_screen(Screen::default())
.initialize()
.unwrap();
Keyflow::register_hotkey("test", Hotkey::new(Key::P).with_alt(), || {}).unwrap()
}
#[test]
fn test_hotkeys_disabled() {
Keyflow::builder()
.with_screen(Screen::default())
.initialize()
.unwrap();
let result = Keyflow::register_hotkey("test", Hotkey::new(Key::P).with_alt(), || {});
assert!(result.is_err());
}
}