mod buttons;
mod capture;
mod inject;
mod keymap;
use tokio::sync::mpsc;
use x11rb::connection::{Connection, RequestConnection};
use x11rb::protocol::xproto::{ConnectionExt as _, Window};
use x11rb::rust_connection::RustConnection;
use super::{
Bounds, CAPTURE_CONTROL_CAPACITY, CAPTURE_EVENT_CAPACITY, Capture, INJECT_COMMAND_CAPACITY,
INJECT_POINTER_CAPACITY, Injector, InputError,
};
struct Session {
conn: RustConnection,
root: Window,
bounds: Bounds,
}
impl Session {
fn connect() -> Result<Self, InputError> {
let (conn, screen_num) = x11rb::connect(None)?;
let screen = &conn.setup().roots[screen_num];
let bounds = Bounds {
x: 0,
y: 0,
width: u32::from(screen.width_in_pixels),
height: u32::from(screen.height_in_pixels),
};
let root = screen.root;
Ok(Self { conn, root, bounds })
}
fn require_extension(&self, name: &'static str) -> Result<(), InputError> {
match self.conn.extension_information(name)? {
Some(_) => Ok(()),
None => Err(InputError::MissingX11Extension(name)),
}
}
fn pointer_position(&self) -> Result<(i32, i32), x11rb::errors::ReplyError> {
let reply = self.conn.query_pointer(self.root)?.reply()?;
Ok((i32::from(reply.root_x), i32::from(reply.root_y)))
}
}
pub(super) fn start() -> Result<(Capture, Injector), InputError> {
let capture_session = Session::connect()?;
let inject_session = Session::connect()?;
let capture_monitors = monitors(&capture_session.conn, capture_session.root)
.unwrap_or_else(|| vec![capture_session.bounds]);
let inject_monitors = monitors(&inject_session.conn, inject_session.root)
.unwrap_or_else(|| vec![inject_session.bounds]);
let capture_thread = capture::CaptureThread::new(capture_session)?;
let inject_thread = inject::InjectThread::new(inject_session)?;
let capture_bounds = capture_thread.bounds();
let inject_bounds = inject_thread.bounds();
let (events_tx, events_rx) = mpsc::channel(CAPTURE_EVENT_CAPACITY);
let (control_tx, control_rx) = mpsc::channel(CAPTURE_CONTROL_CAPACITY);
let (commands_tx, commands_rx) = mpsc::channel(INJECT_COMMAND_CAPACITY);
let (pointer_tx, pointer_rx) = mpsc::channel(INJECT_POINTER_CAPACITY);
capture_thread.spawn(events_tx, control_rx)?;
inject_thread.spawn(commands_rx, pointer_tx)?;
let capture = Capture {
bounds: capture_bounds,
monitors: capture_monitors.into(),
events: events_rx,
control: control_tx,
};
let injector = Injector {
bounds: inject_bounds,
monitors: inject_monitors.into(),
commands: commands_tx,
pointer: pointer_rx,
};
Ok((capture, injector))
}
pub(crate) fn monitors(connection: &RustConnection, root: Window) -> Option<Vec<Bounds>> {
use x11rb::protocol::randr::ConnectionExt as _;
let reply = connection
.randr_get_monitors(root, true)
.ok()?
.reply()
.ok()?;
let monitors: Vec<_> = reply
.monitors
.iter()
.filter(|m| m.width > 0 && m.height > 0)
.map(|m| Bounds {
x: i32::from(m.x),
y: i32::from(m.y),
width: u32::from(m.width),
height: u32::from(m.height),
})
.collect();
(!monitors.is_empty()).then_some(monitors)
}