diff --git a/Cargo.toml b/Cargo.toml index dcb4631..d3aab63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,12 +15,25 @@ serde_derive = "1.0" threadpool = "1.8" [target.'cfg(unix)'.dependencies] -wayland-client = { version="0.30.0" } -wayland-protocols = { version="0.30.0", features=["client", "staging", "unstable"] } -wayland-protocols-wlr = { version="0.1.0", features=["client"] } -wayland-protocols-misc = { version="0.1.0", features=["client"] } -wayland-protocols-plasma = { version="0.1.0", features=["client"] } -x11 = { version = "2.21.0", features = ["xlib", "xtest"] } +wayland-client = { version="0.30.0", optional = true } +wayland-protocols = { version="0.30.0", features=["client", "staging", "unstable"], optional = true } +wayland-protocols-wlr = { version="0.1.0", features=["client"], optional = true } +wayland-protocols-misc = { version="0.1.0", features=["client"], optional = true } +wayland-protocols-plasma = { version="0.1.0", features=["client"], optional = true } +x11 = { version = "2.21.0", features = ["xlib", "xtest"], optional = true } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.9", features = ["winuser"] } + + +[features] +default = [ "wayland", "x11", "xdg_desktop_portal", "libei" ] +wayland = [ + "dep:wayland-client", + "dep:wayland-protocols", + "dep:wayland-protocols-wlr", + "dep:wayland-protocols-misc", + "dep:wayland-protocols-plasma" ] +x11 = [ "dep:x11" ] +xdg_desktop_portal = [] +libei = [] diff --git a/config.toml b/config.toml index 13be4f6..94b36cf 100644 --- a/config.toml +++ b/config.toml @@ -1,4 +1,5 @@ port = 42069 +backend = "wlroots" # [client.right] # host_name = "localhost" diff --git a/src/backend.rs b/src/backend.rs index c5d67de..934046a 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1,14 +1,2 @@ -#[cfg(windows)] -pub mod windows; - -#[cfg(unix)] -pub mod wayland; - -#[cfg(unix)] -pub mod x11; - -#[derive(Clone, Copy, Debug)] -pub enum Backend { - X11, - WAYLAND, -} +pub mod consumer; +pub mod producer; diff --git a/src/backend/consumer.rs b/src/backend/consumer.rs new file mode 100644 index 0000000..7426052 --- /dev/null +++ b/src/backend/consumer.rs @@ -0,0 +1,14 @@ +#[cfg(windows)] +pub mod windows; + +#[cfg(feature="x11")] +pub mod x11; + +#[cfg(feature = "wayland")] +pub mod wlroots; + +#[cfg(feature = "xdg_desktop_portal")] +pub mod xdg_desktop_portal; + +#[cfg(feature = "libei")] +pub mod libei; diff --git a/src/backend/consumer/libei.rs b/src/backend/consumer/libei.rs new file mode 100644 index 0000000..2bf80fb --- /dev/null +++ b/src/backend/consumer/libei.rs @@ -0,0 +1,9 @@ +use std::sync::mpsc::Receiver; + +use crate::{event::Event, client::{ClientHandle, Client}}; + + + +pub(crate) fn run(_consume_rx: Receiver<(Event, ClientHandle)>, _clients: Vec) { + todo!() +} diff --git a/src/backend/windows/consumer.rs b/src/backend/consumer/windows.rs similarity index 100% rename from src/backend/windows/consumer.rs rename to src/backend/consumer/windows.rs diff --git a/src/backend/wayland/consumer.rs b/src/backend/consumer/wlroots.rs similarity index 73% rename from src/backend/wayland/consumer.rs rename to src/backend/consumer/wlroots.rs index 076f882..e162645 100644 --- a/src/backend/wayland/consumer.rs +++ b/src/backend/consumer/wlroots.rs @@ -35,13 +35,8 @@ use tempfile; use crate::event::{Event, KeyboardEvent, PointerEvent}; enum VirtualInputManager { - Wlroots { - vpm: VpManager, - vkm: VkManager, - }, - Kde { - fake_input: OrgKdeKwinFakeInput, - } + Wlroots { vpm: VpManager, vkm: VkManager }, + Kde { fake_input: OrgKdeKwinFakeInput }, } // App State, implements Dispatch event handlers @@ -72,16 +67,21 @@ impl App { let virtual_input_manager = match (vpm, vkm, fake_input) { (Ok(vpm), Ok(vkm), _) => VirtualInputManager::Wlroots { vpm, vkm }, (_, _, Ok(fake_input)) => { - fake_input.authenticate("lan-mouse".into(), "Allow remote clients to control this devices".into()); + fake_input.authenticate( + "lan-mouse".into(), + "Allow remote clients to control this devices".into(), + ); VirtualInputManager::Kde { fake_input } - }, + } (Err(e1), Err(e2), Err(e3)) => { eprintln!("zwlr_virtual_pointer_v1: {e1}"); eprintln!("zwp_virtual_keyboard_v1: {e2}"); eprintln!("org_kde_kwin_fake_input: {e3}"); panic!("neither wlroots nor kde input emulation protocol supported!") - }, - _ => { panic!() } + } + _ => { + panic!() + } }; let input_for_client: HashMap = HashMap::new(); @@ -152,28 +152,22 @@ impl App { buf.flush().unwrap(); keyboard.keymap(1, f.as_raw_fd(), data.len() as u32); - let vinput = VirtualInput::Wlroots{ pointer, keyboard }; + let vinput = VirtualInput::Wlroots { pointer, keyboard }; self.input_for_client.insert(client.handle, vinput); - - }, + } VirtualInputManager::Kde { fake_input } => { let fake_input = fake_input.clone(); let vinput = VirtualInput::Kde { fake_input }; self.input_for_client.insert(client.handle, vinput); - }, + } } } } enum VirtualInput { - Wlroots { - pointer: Vp, - keyboard: Vk, - }, - Kde { - fake_input: OrgKdeKwinFakeInput, - } + Wlroots { pointer: Vp, keyboard: Vk }, + Kde { fake_input: OrgKdeKwinFakeInput }, } impl VirtualInput { @@ -185,17 +179,18 @@ impl VirtualInput { time, relative_x, relative_y, - } => { - match self { - VirtualInput::Wlroots { pointer, keyboard:_ } => { - pointer.motion(time, relative_x, relative_y); - pointer.frame(); - }, - VirtualInput::Kde { fake_input } => { - fake_input.pointer_motion(relative_y, relative_y); - }, + } => match self { + VirtualInput::Wlroots { + pointer, + keyboard: _, + } => { + pointer.motion(time, relative_x, relative_y); + pointer.frame(); } - } + VirtualInput::Kde { fake_input } => { + fake_input.pointer_motion(relative_y, relative_y); + } + }, PointerEvent::Button { time, button, @@ -203,70 +198,80 @@ impl VirtualInput { } => { let state: ButtonState = state.try_into().unwrap(); match self { - VirtualInput::Wlroots { pointer, keyboard:_ } => { + VirtualInput::Wlroots { + pointer, + keyboard: _, + } => { pointer.button(time, button, state); pointer.frame(); - }, + } VirtualInput::Kde { fake_input } => { fake_input.button(button, state as u32); - }, + } } } PointerEvent::Axis { time, axis, value } => { let axis: Axis = (axis as u32).try_into().unwrap(); match self { - VirtualInput::Wlroots { pointer, keyboard:_ } => { + VirtualInput::Wlroots { + pointer, + keyboard: _, + } => { pointer.axis(time, axis, value); pointer.frame(); - }, + } VirtualInput::Kde { fake_input } => { fake_input.axis(axis as u32, value); - }, + } } } - PointerEvent::Frame {} => { - match self { - VirtualInput::Wlroots { pointer, keyboard:_ } => { - pointer.frame(); - }, - VirtualInput::Kde { fake_input:_ } => { }, + PointerEvent::Frame {} => match self { + VirtualInput::Wlroots { + pointer, + keyboard: _, + } => { + pointer.frame(); } - } + VirtualInput::Kde { fake_input: _ } => {} + }, }, Event::Keyboard(e) => match e { - KeyboardEvent::Key { time, key, state } => { - match self { - VirtualInput::Wlroots { pointer:_, keyboard } => { - keyboard.key(time, key, state as u32); - }, - VirtualInput::Kde { fake_input } => { - fake_input.keyboard_key(key, state as u32); - }, + KeyboardEvent::Key { time, key, state } => match self { + VirtualInput::Wlroots { + pointer: _, + keyboard, + } => { + keyboard.key(time, key, state as u32); } - } + VirtualInput::Kde { fake_input } => { + fake_input.keyboard_key(key, state as u32); + } + }, KeyboardEvent::Modifiers { mods_depressed, mods_latched, mods_locked, group, - } => { - match self { - VirtualInput::Wlroots { pointer:_, keyboard } => { - keyboard.modifiers(mods_depressed, mods_latched, mods_locked, group); - }, - VirtualInput::Kde { fake_input:_ } => { }, + } => match self { + VirtualInput::Wlroots { + pointer: _, + keyboard, + } => { + keyboard.modifiers(mods_depressed, mods_latched, mods_locked, group); } - } + VirtualInput::Kde { fake_input: _ } => {} + }, }, - Event::Release() => { - match self { - VirtualInput::Wlroots { pointer:_, keyboard } => { - keyboard.modifiers(77, 0, 0, 0); - keyboard.modifiers(0, 0, 0, 0); - }, - VirtualInput::Kde { fake_input:_ } => {}, + Event::Release() => match self { + VirtualInput::Wlroots { + pointer: _, + keyboard, + } => { + keyboard.modifiers(77, 0, 0, 0); + keyboard.modifiers(0, 0, 0, 0); } - } + VirtualInput::Kde { fake_input: _ } => {} + }, } Ok(()) } diff --git a/src/backend/x11/consumer.rs b/src/backend/consumer/x11.rs similarity index 100% rename from src/backend/x11/consumer.rs rename to src/backend/consumer/x11.rs diff --git a/src/backend/consumer/xdg_desktop_portal.rs b/src/backend/consumer/xdg_desktop_portal.rs new file mode 100644 index 0000000..2bf80fb --- /dev/null +++ b/src/backend/consumer/xdg_desktop_portal.rs @@ -0,0 +1,9 @@ +use std::sync::mpsc::Receiver; + +use crate::{event::Event, client::{ClientHandle, Client}}; + + + +pub(crate) fn run(_consume_rx: Receiver<(Event, ClientHandle)>, _clients: Vec) { + todo!() +} diff --git a/src/backend/producer.rs b/src/backend/producer.rs new file mode 100644 index 0000000..67a401b --- /dev/null +++ b/src/backend/producer.rs @@ -0,0 +1,6 @@ +#[cfg(feature = "wayland")] +pub mod wayland; +#[cfg(windows)] +pub mod windows; +#[cfg(feature = "x11")] +pub mod x11; diff --git a/src/backend/wayland/producer.rs b/src/backend/producer/wayland.rs similarity index 100% rename from src/backend/wayland/producer.rs rename to src/backend/producer/wayland.rs diff --git a/src/backend/windows/producer.rs b/src/backend/producer/windows.rs similarity index 100% rename from src/backend/windows/producer.rs rename to src/backend/producer/windows.rs diff --git a/src/backend/x11/producer.rs b/src/backend/producer/x11.rs similarity index 100% rename from src/backend/x11/producer.rs rename to src/backend/producer/x11.rs diff --git a/src/backend/wayland.rs b/src/backend/wayland.rs deleted file mode 100644 index 934046a..0000000 --- a/src/backend/wayland.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod consumer; -pub mod producer; diff --git a/src/backend/windows.rs b/src/backend/windows.rs deleted file mode 100644 index 934046a..0000000 --- a/src/backend/windows.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod consumer; -pub mod producer; diff --git a/src/backend/x11.rs b/src/backend/x11.rs deleted file mode 100644 index 934046a..0000000 --- a/src/backend/x11.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod consumer; -pub mod producer; diff --git a/src/client.rs b/src/client.rs index 2865516..022a1a3 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,5 +1,7 @@ use std::net::SocketAddr; +use crate::{config, dns}; + #[derive(Eq, Hash, PartialEq, Clone, Copy)] pub enum Position { Left, @@ -34,19 +36,56 @@ pub struct ClientManager { pub type ClientHandle = u32; impl ClientManager { + fn add_client(&mut self, client: &config::Client, pos: Position) { + 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"), + }, + }; + let addr = SocketAddr::new(ip, client.port.unwrap_or(42069)); + self.register_client(addr, pos); + } + fn new_id(&mut self) -> ClientHandle { self.next_id += 1; self.next_id } - pub fn new() -> Self { - ClientManager { + pub fn new(config: &config::Config) -> Self { + + let mut client_manager = ClientManager { next_id: 0, clients: Vec::new(), + }; + + // 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); + } } + + client_manager } - pub fn add_client(&mut self, addr: SocketAddr, pos: Position) { + pub fn register_client(&mut self, addr: SocketAddr, pos: Position) { let handle = self.new_id(); let client = Client { addr, pos, handle }; self.clients.push(client); diff --git a/src/config.rs b/src/config.rs index 13f6f7d..29ad2fc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,6 +7,7 @@ use toml; pub struct Config { pub client: Clients, pub port: Option, + pub backend: Option, } #[derive(Serialize, Deserialize, Debug)] diff --git a/src/consumer.rs b/src/consumer.rs new file mode 100644 index 0000000..cfa5e40 --- /dev/null +++ b/src/consumer.rs @@ -0,0 +1,70 @@ +use std::{thread::{JoinHandle, self}, env, sync::mpsc::Receiver}; + +use crate::{backend::consumer, client::{Client, ClientHandle}, event::Event}; + +#[derive(Debug)] +enum Backend { + Wlroots, + X11, + RemoteDesktopPortal, + Libei, +} + +pub fn start(consume_rx: Receiver<(Event, ClientHandle)>, clients: Vec, backend: Option) -> JoinHandle<()> { + thread::Builder::new() + .name("event consumer".into()) + .spawn(move || { + #[cfg(windows)] + consumer::windows::run(consume_rx, clients); + + #[cfg(unix)] + let backend = match env::var("XDG_SESSION_TYPE") { + Ok(session_type) => match session_type.as_str() { + "x11" => Backend::X11, + "wayland" => { + match backend { + Some(backend) => match backend.as_str() { + "wlroots" => Backend::Wlroots, + "libei" => Backend::Libei, + "xdg_desktop_portal" => Backend::RemoteDesktopPortal, + backend => panic!("invalid backend: {}", backend) + } + // default to wlroots backend for now + _ => Backend::Wlroots, + } + } + _ => panic!("unknown XDG_SESSION_TYPE"), + }, + Err(_) => panic!("could not detect session type: XDG_SESSION_TYPE environment variable not set!"), + }; + + #[cfg(unix)] + match backend { + Backend::Libei => { + #[cfg(not(feature = "libei"))] + panic!("feature libei not enabled"); + #[cfg(feature = "libei")] + consumer::libei::run(consume_rx, clients); + }, + Backend::RemoteDesktopPortal => { + #[cfg(not(feature = "xdg_desktop_portal"))] + panic!("feature xdg_desktop_portal not enabled"); + #[cfg(feature = "xdg_desktop_portal")] + consumer::xdg_desktop_portal::run(consume_rx, clients); + }, + Backend::Wlroots => { + #[cfg(not(feature = "wayland"))] + panic!("feature wayland not enabled"); + #[cfg(feature = "wayland")] + consumer::wlroots::run(consume_rx, clients); + }, + Backend::X11 => { + #[cfg(not(feature = "x11"))] + panic!("feature x11 not enabled"); + #[cfg(feature = "x11")] + consumer::x11::run(consume_rx, clients); + }, + } + }) + .unwrap() +} diff --git a/src/lib.rs b/src/lib.rs index 62b258e..cb0d557 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,4 +4,7 @@ pub mod dns; pub mod event; pub mod request; +pub mod consumer; +pub mod producer; + pub mod backend; diff --git a/src/main.rs b/src/main.rs index 49ce4ae..04e8729 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,34 +1,11 @@ -use std::{net::SocketAddr, sync::mpsc, thread}; - -#[cfg(unix)] -use std::env; +use std::sync::mpsc; use lan_mouse::{ - client::{ClientManager, Position}, - config, dns, event, request, + client::ClientManager, + consumer, producer, + config, event, request, }; -#[cfg(windows)] -use lan_mouse::backend::windows; - -#[cfg(unix)] -use lan_mouse::backend::{wayland, x11, Backend}; - -fn add_client(client_manager: &mut ClientManager, client: &config::Client, pos: Position) { - 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"), - }, - }; - let addr = SocketAddr::new(ip, client.port.unwrap_or(42069)); - client_manager.add_client(addr, pos); -} - pub fn main() { // parse config file let config = config::Config::new("config.toml").unwrap(); @@ -42,83 +19,15 @@ pub fn main() { // event channel for consuming events let (consume_tx, consume_rx) = mpsc::sync_channel(128); - let mut client_manager = ClientManager::new(); - - // 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!(), - }; - add_client(&mut client_manager, client, pos); - } - } + // create client manager + let mut client_manager = ClientManager::new(&config); // start receiving client connection requests let (request_server, request_thread) = request::Server::listen(port).unwrap(); - let clients = client_manager.get_clients(); - - #[cfg(unix)] - let backend = match env::var("XDG_SESSION_TYPE") { - Ok(session_type) => match session_type.as_str() { - "x11" => Backend::X11, - "wayland" => Backend::WAYLAND, - _ => panic!("unknown XDG_SESSION_TYPE"), - }, - Err(_) => panic!("could not detect session type"), - }; - - #[cfg(windows)] - println!("using backend: windows"); - - #[cfg(unix)] - println!( - "using backend: {}", - match backend { - Backend::X11 => "x11", - Backend::WAYLAND => "wayland", - } - ); - // start producing and consuming events - let event_producer = thread::Builder::new() - .name("event producer".into()) - .spawn(move || { - #[cfg(windows)] - windows::producer::run(produce_tx, request_server, clients); - - #[cfg(unix)] - match backend { - Backend::X11 => x11::producer::run(produce_tx, request_server, clients), - Backend::WAYLAND => wayland::producer::run(produce_tx, request_server, clients), - } - }) - .unwrap(); - - let clients = client_manager.get_clients(); - let event_consumer = thread::Builder::new() - .name("event consumer".into()) - .spawn(move || { - #[cfg(windows)] - windows::consumer::run(consume_rx, clients); - - #[cfg(unix)] - match backend { - Backend::X11 => x11::consumer::run(consume_rx, clients), - Backend::WAYLAND => wayland::consumer::run(consume_rx, clients), - } - }) - .unwrap(); + 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); // start sending and receiving events let event_server = event::server::Server::new(port); diff --git a/src/producer.rs b/src/producer.rs new file mode 100644 index 0000000..a75cfbf --- /dev/null +++ b/src/producer.rs @@ -0,0 +1,52 @@ +#[cfg(unix)] +use std::env; +use std::{thread::{JoinHandle, self}, sync::mpsc::SyncSender}; + +use crate::{client::{Client, ClientHandle}, event::Event, request::Server}; + +use crate::backend::producer; + +enum Backend { + Wayland, + X11, +} + +pub fn start( + produce_tx: SyncSender<(Event, ClientHandle)>, + clients: Vec, + request_server: Server, +) -> JoinHandle<()> { + thread::Builder::new() + .name("event producer".into()) + .spawn(move || { + #[cfg(windows)] + producer::windows::run(produce_tx, request_server, clients); + + #[cfg(unix)] + let backend = match env::var("XDG_SESSION_TYPE") { + Ok(session_type) => match session_type.as_str() { + "x11" => Backend::X11, + "wayland" => Backend::Wayland, + _ => panic!("unknown XDG_SESSION_TYPE"), + }, + Err(_) => panic!("could not detect session type: XDG_SESSION_TYPE environment variable not set!"), + }; + + #[cfg(unix)] + match backend { + Backend::X11 => { + #[cfg(not(feature = "x11"))] + panic!("feature x11 not enabled"); + #[cfg(feature = "x11")] + producer::x11::run(produce_tx, request_server, clients); + } + Backend::Wayland => { + #[cfg(not(feature = "wayland"))] + panic!("feature wayland not enabled"); + #[cfg(feature = "wayland")] + producer::wayland::run(produce_tx, request_server, clients); + } + } + }) + .unwrap() +}