use std::io::{BufRead, BufReader};
use std::net::TcpListener;
use std::sync::mpsc::Sender;
use std::thread;
use crate::core::event::{
Event, EventType, MB_LEFT_BUTTON, MB_MIDDLE_BUTTON, MB_RIGHT_BUTTON, parse_key_chord,
};
use crate::core::geometry::Point;
pub fn parse_line(line: &str) -> Vec<Event> {
let mut tokens = line.split_whitespace();
let Some(first) = tokens.next() else {
return Vec::new();
};
let button = match first.to_ascii_uppercase().as_str() {
"CLICK" | "LCLICK" => Some(MB_LEFT_BUTTON),
"RCLICK" => Some(MB_RIGHT_BUTTON),
"MCLICK" => Some(MB_MIDDLE_BUTTON),
_ => None,
};
if let Some(button) = button {
let coords: Vec<&str> = tokens.collect();
if let [xs, ys] = coords[..] {
if let (Ok(x), Ok(y)) = (xs.parse::<i16>(), ys.parse::<i16>()) {
let pos = Point::new(x, y);
return vec![
Event::mouse(EventType::MouseDown, pos, button, false),
Event::mouse(EventType::MouseUp, pos, 0, false),
];
}
}
return Vec::new(); }
line.split_whitespace()
.filter_map(parse_key_chord)
.collect()
}
pub fn spawn(port: u16, tx: Sender<Event>) -> std::io::Result<()> {
let listener = TcpListener::bind(("127.0.0.1", port))?;
log::info!("Remote key input listening on 127.0.0.1:{port}");
thread::spawn(move || {
for stream in listener.incoming() {
let stream = match stream {
Ok(s) => s,
Err(e) => {
log::warn!("Remote key input: accept failed: {e}");
continue;
}
};
let tx = tx.clone();
thread::spawn(move || {
let reader = BufReader::new(stream);
for line in reader.lines() {
let Ok(line) = line else { break };
let events = parse_line(&line);
if events.is_empty() && !line.trim().is_empty() {
log::warn!("Remote input: unparseable line {line:?}");
}
for event in events {
if tx.send(event).is_err() {
return;
}
}
}
});
}
});
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::event::{KB_ALT_X, KB_CTRL_F12};
#[test]
fn parses_click_into_down_up() {
let evs = parse_line("CLICK 28 23");
assert_eq!(evs.len(), 2);
assert_eq!(evs[0].what, EventType::MouseDown);
assert_eq!(evs[0].mouse.pos, Point::new(28, 23));
assert_eq!(evs[0].mouse.buttons, MB_LEFT_BUTTON);
assert_eq!(evs[1].what, EventType::MouseUp);
}
#[test]
fn parses_key_chords_line() {
let evs = parse_line("CTRL+F12 ALT+X");
assert_eq!(evs.len(), 2);
assert_eq!(evs[0].key_code, KB_CTRL_F12);
assert_eq!(evs[1].key_code, KB_ALT_X);
}
#[test]
fn blank_and_malformed_yield_nothing() {
assert!(parse_line("").is_empty());
assert!(parse_line(" ").is_empty());
assert!(parse_line("CLICK 1").is_empty());
assert!(parse_line("CLICK x y").is_empty());
}
}