diff --git a/DOC.md b/DOC.md index 3aa93b7..e38f04d 100644 --- a/DOC.md +++ b/DOC.md @@ -68,16 +68,10 @@ on the server level. ## Device State - Active and Inactive To solve this problem, each device can be in exactly two states: -Events can only be sent to active clients. -Events can only be received from inactive clients. +Either events are sent or received. -Active denotes that a particular device is controlled by the local pc. +This ensures that +- a) Events can never result in a feedback loop. +- b) As soon as a virtual input enters another client, lan-mouse will stop receiving events, +which ensures clients can only be controlled directly and not indirectly through other clients. -Any event received from an active device is ignored unless it is a state change request. -In this case the device is marked as inactive and no further events are sent to the device. - -The received events are then processed until a further state change to active -is requested (when the corresponding layer surface is entered). - -**In short:** The invariance "each client either sends or receives events" must -always be true. diff --git a/config.toml b/config.toml index 94b36cf..912da89 100644 --- a/config.toml +++ b/config.toml @@ -1,11 +1,11 @@ port = 42069 backend = "wlroots" -# [client.right] +# [right] # host_name = "localhost" # port = 42068 -[client.left] +[left] host_name = "Osmium" ip = "192.168.178.114" port = 42069 diff --git a/src/client.rs b/src/client.rs index 022a1a3..bac18c5 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,4 +1,4 @@ -use std::net::SocketAddr; +use std::{net::SocketAddr, error::Error, fmt::Display}; use crate::{config, dns}; @@ -35,20 +35,29 @@ pub struct ClientManager { pub type ClientHandle = u32; +#[derive(Debug)] +struct ClientConfigError; + +impl Display for ClientConfigError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "neither ip nor hostname specified") + } +} + +impl Error for ClientConfigError {} + impl ClientManager { - fn add_client(&mut self, client: &config::Client, pos: Position) { + fn add_client(&mut self, client: &config::Client, pos: Position) -> Result<(), Box> { let ip = match client.ip { Some(ip) => ip, None => match &client.host_name { - Some(host_name) => match dns::resolve(host_name) { - Ok(ip) => ip, - Err(e) => panic!("{}", e), - }, - None => panic!("neither ip nor hostname specified"), + Some(host_name) => dns::resolve(host_name)?, + None => return Err(Box::new(ClientConfigError{})), }, }; let addr = SocketAddr::new(ip, client.port.unwrap_or(42069)); self.register_client(addr, pos); + Ok(()) } fn new_id(&mut self) -> ClientHandle { @@ -56,7 +65,7 @@ impl ClientManager { self.next_id } - pub fn new(config: &config::Config) -> Self { + pub fn new(config: &config::Config) -> Result> { let mut client_manager = ClientManager { next_id: 0, @@ -64,25 +73,11 @@ impl ClientManager { }; // add clients from config - for client in vec![ - &config.client.left, - &config.client.right, - &config.client.top, - &config.client.bottom, - ] { - if let Some(client) = client { - let pos = match client { - client if Some(client) == config.client.left.as_ref() => Position::Left, - client if Some(client) == config.client.right.as_ref() => Position::Right, - client if Some(client) == config.client.top.as_ref() => Position::Top, - client if Some(client) == config.client.bottom.as_ref() => Position::Bottom, - _ => panic!(), - }; - client_manager.add_client(client, pos); - } + for (client, pos) in config.clients.iter() { + client_manager.add_client(&client, *pos)?; } - client_manager + Ok(client_manager) } pub fn register_client(&mut self, addr: SocketAddr, pos: Position) { diff --git a/src/config.rs b/src/config.rs index 29ad2fc..9b54d45 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,17 +1,17 @@ use serde_derive::{Deserialize, Serialize}; +use core::fmt; use std::net::IpAddr; use std::{error::Error, fs}; + +use std::env; use toml; -#[derive(Serialize, Deserialize, Debug)] -pub struct Config { - pub client: Clients, - pub port: Option, - pub backend: Option, -} +use crate::client::Position; #[derive(Serialize, Deserialize, Debug)] -pub struct Clients { +pub struct ConfigToml { + pub port: Option, + pub backend: Option, pub left: Option, pub right: Option, pub top: Option, @@ -25,9 +25,94 @@ pub struct Client { pub port: Option, } -impl Config { - pub fn new(path: &str) -> Result> { +#[derive(Debug, Clone)] +struct MissingParameter { + arg: &'static str, +} + +impl fmt::Display for MissingParameter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Missing a parameter for argument: {}", self.arg) + } +} + +impl Error for MissingParameter {} + +impl ConfigToml { + pub fn new(path: &str) -> Result> { let config = fs::read_to_string(path)?; Ok(toml::from_str::<_>(&config)?) } } + +fn find_arg(key: &'static str) -> Result, MissingParameter> { + let args: Vec = env::args().collect(); + + for (i, arg) in args.iter().enumerate() { + if arg != key { + continue; + } + match args.get(i+1) { + None => return Err(MissingParameter { arg: key }), + Some(arg) => return Ok(Some(arg.clone())), + }; + } + Ok(None) +} + +pub struct Config { + pub backend: Option, + pub port: u16, + pub clients: Vec<(Client, Position)>, +} + +impl Config { + pub fn new() -> Result> { + let config_path = "config.toml"; + let config_toml = match ConfigToml::new(config_path) { + Err(e) => { + eprintln!("config.toml: {e}"); + eprintln!("Continuing without config file ..."); + None + }, + Ok(c) => Some(c), + }; + + let backend = match find_arg("--backend")? { + None => match &config_toml { + Some(c) => c.backend.clone(), + None => None, + }, + backend => backend, + }; + + const DEFAULT_PORT: u16 = 4242; + + let port = match find_arg("--port")? { + Some(port) => port.parse::()?, + None => match &config_toml { + Some(c) => c.port.unwrap_or(DEFAULT_PORT), + None => DEFAULT_PORT, + } + }; + + let mut clients: Vec<(Client, Position)> = vec![]; + + if let Some(config_toml) = config_toml { + if let Some(c) = config_toml.right { + clients.push((c, Position::Right)) + } + if let Some(c) = config_toml.left { + clients.push((c, Position::Left)) + } + if let Some(c) = config_toml.top { + clients.push((c, Position::Top)) + } + if let Some(c) = config_toml.bottom { + clients.push((c, Position::Bottom)) + } + } + + Ok(Config { backend, clients, port }) + } +} diff --git a/src/consumer.rs b/src/consumer.rs index 92a3ac5..9a11fe1 100644 --- a/src/consumer.rs +++ b/src/consumer.rs @@ -1,4 +1,4 @@ -use std::{thread::{JoinHandle, self}, sync::mpsc::Receiver}; +use std::{thread::{JoinHandle, self}, sync::mpsc::Receiver, error::Error}; #[cfg(unix)] use std::env; @@ -14,11 +14,11 @@ enum Backend { Libei, } -pub fn start(consume_rx: Receiver<(Event, ClientHandle)>, clients: Vec, backend: Option) -> JoinHandle<()> { +pub fn start(consume_rx: Receiver<(Event, ClientHandle)>, clients: Vec, backend: Option) -> Result, Box> { #[cfg(windows)] let _backend = backend; - thread::Builder::new() + Ok(thread::Builder::new() .name("event consumer".into()) .spawn(move || { #[cfg(windows)] @@ -72,6 +72,5 @@ pub fn start(consume_rx: Receiver<(Event, ClientHandle)>, clients: Vec, consumer::x11::run(consume_rx, clients); }, } - }) - .unwrap() + })?) } diff --git a/src/event/server.rs b/src/event/server.rs index 8cbd651..41d4713 100644 --- a/src/event/server.rs +++ b/src/event/server.rs @@ -20,13 +20,13 @@ pub struct Server { } impl Server { - pub fn new(port: u16) -> Self { - let listen_addr = SocketAddr::new("0.0.0.0".parse().unwrap(), port); + pub fn new(port: u16) -> Result> { + let listen_addr = SocketAddr::new("0.0.0.0".parse()?, port); let sending = Arc::new(AtomicBool::new(false)); - Server { + Ok(Server { listen_addr, sending, - } + }) } pub fn run( @@ -36,7 +36,7 @@ impl Server { consume_tx: SyncSender<(Event, ClientHandle)>, ) -> Result<(JoinHandle<()>, JoinHandle<()>), Box> { let udp_socket = UdpSocket::bind(self.listen_addr)?; - let rx = udp_socket.try_clone().unwrap(); + let rx = udp_socket.try_clone()?; let tx = udp_socket; let sending = self.sending.clone(); @@ -92,8 +92,7 @@ impl Server { .expect("event consumer unavailable"); } } - }) - .unwrap(); + })?; let sending = self.sending.clone(); @@ -124,8 +123,7 @@ impl Server { } } } - }) - .unwrap(); + })?; Ok((receiver, sender)) } diff --git a/src/main.rs b/src/main.rs index 04e8729..6e0a225 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use std::sync::mpsc; +use std::{sync::mpsc, process, env}; use lan_mouse::{ client::ClientManager, @@ -6,12 +6,24 @@ use lan_mouse::{ config, event, request, }; +fn usage() { + eprintln!("usage: {} [--backend ] [--port ]", + env::args().next().unwrap_or("lan-mouse".into())); +} + pub fn main() { // parse config file - let config = config::Config::new("config.toml").unwrap(); + let config = match config::Config::new() { + Err(e) => { + eprintln!("{e}"); + usage(); + process::exit(1); + } + Ok(config) => config, + }; // port or default - let port = config.port.unwrap_or(42069); + let port = config.port; // event channel for producing events let (produce_tx, produce_rx) = mpsc::sync_channel(128); @@ -20,26 +32,64 @@ pub fn main() { let (consume_tx, consume_rx) = mpsc::sync_channel(128); // create client manager - let mut client_manager = ClientManager::new(&config); + let mut client_manager = match ClientManager::new(&config) { + Err(e) => { + eprintln!("{e}"); + process::exit(1); + } + Ok(m) => m, + }; // start receiving client connection requests let (request_server, request_thread) = request::Server::listen(port).unwrap(); // start producing and consuming events - let event_producer = producer::start(produce_tx, client_manager.get_clients(), request_server); - let event_consumer = consumer::start(consume_rx, client_manager.get_clients(), config.backend); + let event_producer = match producer::start(produce_tx, client_manager.get_clients(), request_server) { + Err(e) => { + eprintln!("Could not start event producer: {e}"); + None + }, + Ok(p) => Some(p), + }; + let event_consumer = match consumer::start(consume_rx, client_manager.get_clients(), config.backend) { + Err(e) => { + eprintln!("Could not start event consumer: {e}"); + None + }, + Ok(p) => Some(p), + }; + + if event_consumer.is_none() && event_producer.is_none() { + process::exit(1); + } // start sending and receiving events - let event_server = event::server::Server::new(port); - let (receiver, sender) = event_server - .run(&mut client_manager, produce_rx, consume_tx) - .unwrap(); + let event_server = match event::server::Server::new(port) { + Ok(s) => s, + Err(e) => { + eprintln!("{e}"); + process::exit(1); + } + }; + let (receiver, sender) = match event_server.run(&mut client_manager, produce_rx, consume_tx) { + Ok((r,s)) => (r,s), + Err(e) => { + eprintln!("{e}"); + process::exit(1); + } + }; request_thread.join().unwrap(); + // stop receiving events and terminate event-consumer receiver.join().unwrap(); - sender.join().unwrap(); + if let Some(thread) = event_consumer { + thread.join().unwrap(); + } - event_producer.join().unwrap(); - event_consumer.join().unwrap(); + // stop producing events and terminate event-sender + if let Some(thread) = event_producer { + thread.join().unwrap(); + } + sender.join().unwrap(); } diff --git a/src/producer.rs b/src/producer.rs index 3c3aadd..6824110 100644 --- a/src/producer.rs +++ b/src/producer.rs @@ -1,6 +1,6 @@ #[cfg(unix)] use std::env; -use std::{thread::{JoinHandle, self}, sync::mpsc::SyncSender}; +use std::{thread::{JoinHandle, self}, sync::mpsc::SyncSender, error::Error}; use crate::{client::{Client, ClientHandle}, event::Event, request::Server}; @@ -16,8 +16,8 @@ pub fn start( produce_tx: SyncSender<(Event, ClientHandle)>, clients: Vec, request_server: Server, -) -> JoinHandle<()> { - thread::Builder::new() +) -> Result, Box> { + Ok(thread::Builder::new() .name("event producer".into()) .spawn(move || { #[cfg(windows)] @@ -48,6 +48,5 @@ pub fn start( producer::wayland::run(produce_tx, request_server, clients); } } - }) - .unwrap() + })?) } diff --git a/src/request.rs b/src/request.rs index 197f590..b4dd62f 100644 --- a/src/request.rs +++ b/src/request.rs @@ -60,11 +60,13 @@ impl Server { pub fn listen(port: u16) -> Result<(Server, JoinHandle<()>), Box> { let data: Arc>> = Arc::new(RwLock::new(HashMap::new())); - let listen_addr = SocketAddr::new("0.0.0.0".parse().unwrap(), port); + let listen_addr = SocketAddr::new("0.0.0.0".parse()?, port); let server = Server { data }; let server_copy = server.clone(); - let thread = thread::spawn(move || { - let listen_socket = TcpListener::bind(listen_addr).unwrap(); + let listen_socket = TcpListener::bind(listen_addr)?; + let thread = thread::Builder::new() + .name("tcp server".into()) + .spawn(move || { for stream in listen_socket.incoming() { match stream { Ok(stream) => { @@ -75,7 +77,7 @@ impl Server { } } } - }); + })?; Ok((server_copy, thread)) }