Merge pull request #1 from ferdinandschober/conditional-compilation-of-backends

enable conditional compilation for all backends
This commit is contained in:
Ferdinand Schober
2023-02-17 14:14:52 +01:00
committed by GitHub
22 changed files with 310 additions and 197 deletions

View File

@@ -15,12 +15,25 @@ serde_derive = "1.0"
threadpool = "1.8" threadpool = "1.8"
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
wayland-client = { version="0.30.0" } wayland-client = { version="0.30.0", optional = true }
wayland-protocols = { version="0.30.0", features=["client", "staging", "unstable"] } wayland-protocols = { version="0.30.0", features=["client", "staging", "unstable"], optional = true }
wayland-protocols-wlr = { version="0.1.0", features=["client"] } wayland-protocols-wlr = { version="0.1.0", features=["client"], optional = true }
wayland-protocols-misc = { version="0.1.0", features=["client"] } wayland-protocols-misc = { version="0.1.0", features=["client"], optional = true }
wayland-protocols-plasma = { version="0.1.0", features=["client"] } wayland-protocols-plasma = { version="0.1.0", features=["client"], optional = true }
x11 = { version = "2.21.0", features = ["xlib", "xtest"] } x11 = { version = "2.21.0", features = ["xlib", "xtest"], optional = true }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", features = ["winuser"] } 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 = []

View File

@@ -1,4 +1,5 @@
port = 42069 port = 42069
backend = "wlroots"
# [client.right] # [client.right]
# host_name = "localhost" # host_name = "localhost"

View File

@@ -1,14 +1,2 @@
#[cfg(windows)] pub mod consumer;
pub mod windows; pub mod producer;
#[cfg(unix)]
pub mod wayland;
#[cfg(unix)]
pub mod x11;
#[derive(Clone, Copy, Debug)]
pub enum Backend {
X11,
WAYLAND,
}

14
src/backend/consumer.rs Normal file
View File

@@ -0,0 +1,14 @@
#[cfg(windows)]
pub mod windows;
#[cfg(all(unix, feature="x11"))]
pub mod x11;
#[cfg(all(unix, feature = "wayland"))]
pub mod wlroots;
#[cfg(all(unix, feature = "xdg_desktop_portal"))]
pub mod xdg_desktop_portal;
#[cfg(all(unix, feature = "libei"))]
pub mod libei;

View File

@@ -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<Client>) {
todo!()
}

View File

@@ -35,13 +35,8 @@ use tempfile;
use crate::event::{Event, KeyboardEvent, PointerEvent}; use crate::event::{Event, KeyboardEvent, PointerEvent};
enum VirtualInputManager { enum VirtualInputManager {
Wlroots { Wlroots { vpm: VpManager, vkm: VkManager },
vpm: VpManager, Kde { fake_input: OrgKdeKwinFakeInput },
vkm: VkManager,
},
Kde {
fake_input: OrgKdeKwinFakeInput,
}
} }
// App State, implements Dispatch event handlers // App State, implements Dispatch event handlers
@@ -72,16 +67,21 @@ impl App {
let virtual_input_manager = match (vpm, vkm, fake_input) { let virtual_input_manager = match (vpm, vkm, fake_input) {
(Ok(vpm), Ok(vkm), _) => VirtualInputManager::Wlroots { vpm, vkm }, (Ok(vpm), Ok(vkm), _) => VirtualInputManager::Wlroots { vpm, vkm },
(_, _, Ok(fake_input)) => { (_, _, 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 } VirtualInputManager::Kde { fake_input }
}, }
(Err(e1), Err(e2), Err(e3)) => { (Err(e1), Err(e2), Err(e3)) => {
eprintln!("zwlr_virtual_pointer_v1: {e1}"); eprintln!("zwlr_virtual_pointer_v1: {e1}");
eprintln!("zwp_virtual_keyboard_v1: {e2}"); eprintln!("zwp_virtual_keyboard_v1: {e2}");
eprintln!("org_kde_kwin_fake_input: {e3}"); eprintln!("org_kde_kwin_fake_input: {e3}");
panic!("neither wlroots nor kde input emulation protocol supported!") panic!("neither wlroots nor kde input emulation protocol supported!")
}, }
_ => { panic!() } _ => {
panic!()
}
}; };
let input_for_client: HashMap<ClientHandle, VirtualInput> = HashMap::new(); let input_for_client: HashMap<ClientHandle, VirtualInput> = HashMap::new();
@@ -152,28 +152,22 @@ impl App {
buf.flush().unwrap(); buf.flush().unwrap();
keyboard.keymap(1, f.as_raw_fd(), data.len() as u32); 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); self.input_for_client.insert(client.handle, vinput);
}
},
VirtualInputManager::Kde { fake_input } => { VirtualInputManager::Kde { fake_input } => {
let fake_input = fake_input.clone(); let fake_input = fake_input.clone();
let vinput = VirtualInput::Kde { fake_input }; let vinput = VirtualInput::Kde { fake_input };
self.input_for_client.insert(client.handle, vinput); self.input_for_client.insert(client.handle, vinput);
}, }
} }
} }
} }
enum VirtualInput { enum VirtualInput {
Wlroots { Wlroots { pointer: Vp, keyboard: Vk },
pointer: Vp, Kde { fake_input: OrgKdeKwinFakeInput },
keyboard: Vk,
},
Kde {
fake_input: OrgKdeKwinFakeInput,
}
} }
impl VirtualInput { impl VirtualInput {
@@ -185,17 +179,18 @@ impl VirtualInput {
time, time,
relative_x, relative_x,
relative_y, relative_y,
} => { } => match self {
match self { VirtualInput::Wlroots {
VirtualInput::Wlroots { pointer, keyboard:_ } => { pointer,
pointer.motion(time, relative_x, relative_y); keyboard: _,
pointer.frame(); } => {
}, pointer.motion(time, relative_x, relative_y);
VirtualInput::Kde { fake_input } => { pointer.frame();
fake_input.pointer_motion(relative_y, relative_y);
},
} }
} VirtualInput::Kde { fake_input } => {
fake_input.pointer_motion(relative_y, relative_y);
}
},
PointerEvent::Button { PointerEvent::Button {
time, time,
button, button,
@@ -203,70 +198,80 @@ impl VirtualInput {
} => { } => {
let state: ButtonState = state.try_into().unwrap(); let state: ButtonState = state.try_into().unwrap();
match self { match self {
VirtualInput::Wlroots { pointer, keyboard:_ } => { VirtualInput::Wlroots {
pointer,
keyboard: _,
} => {
pointer.button(time, button, state); pointer.button(time, button, state);
pointer.frame(); pointer.frame();
}, }
VirtualInput::Kde { fake_input } => { VirtualInput::Kde { fake_input } => {
fake_input.button(button, state as u32); fake_input.button(button, state as u32);
}, }
} }
} }
PointerEvent::Axis { time, axis, value } => { PointerEvent::Axis { time, axis, value } => {
let axis: Axis = (axis as u32).try_into().unwrap(); let axis: Axis = (axis as u32).try_into().unwrap();
match self { match self {
VirtualInput::Wlroots { pointer, keyboard:_ } => { VirtualInput::Wlroots {
pointer,
keyboard: _,
} => {
pointer.axis(time, axis, value); pointer.axis(time, axis, value);
pointer.frame(); pointer.frame();
}, }
VirtualInput::Kde { fake_input } => { VirtualInput::Kde { fake_input } => {
fake_input.axis(axis as u32, value); fake_input.axis(axis as u32, value);
}, }
} }
} }
PointerEvent::Frame {} => { PointerEvent::Frame {} => match self {
match self { VirtualInput::Wlroots {
VirtualInput::Wlroots { pointer, keyboard:_ } => { pointer,
pointer.frame(); keyboard: _,
}, } => {
VirtualInput::Kde { fake_input:_ } => { }, pointer.frame();
} }
} VirtualInput::Kde { fake_input: _ } => {}
},
}, },
Event::Keyboard(e) => match e { Event::Keyboard(e) => match e {
KeyboardEvent::Key { time, key, state } => { KeyboardEvent::Key { time, key, state } => match self {
match self { VirtualInput::Wlroots {
VirtualInput::Wlroots { pointer:_, keyboard } => { pointer: _,
keyboard.key(time, key, state as u32); keyboard,
}, } => {
VirtualInput::Kde { fake_input } => { keyboard.key(time, key, state as u32);
fake_input.keyboard_key(key, state as u32);
},
} }
} VirtualInput::Kde { fake_input } => {
fake_input.keyboard_key(key, state as u32);
}
},
KeyboardEvent::Modifiers { KeyboardEvent::Modifiers {
mods_depressed, mods_depressed,
mods_latched, mods_latched,
mods_locked, mods_locked,
group, group,
} => { } => match self {
match self { VirtualInput::Wlroots {
VirtualInput::Wlroots { pointer:_, keyboard } => { pointer: _,
keyboard.modifiers(mods_depressed, mods_latched, mods_locked, group); keyboard,
}, } => {
VirtualInput::Kde { fake_input:_ } => { }, keyboard.modifiers(mods_depressed, mods_latched, mods_locked, group);
} }
} VirtualInput::Kde { fake_input: _ } => {}
},
}, },
Event::Release() => { Event::Release() => match self {
match self { VirtualInput::Wlroots {
VirtualInput::Wlroots { pointer:_, keyboard } => { pointer: _,
keyboard.modifiers(77, 0, 0, 0); keyboard,
keyboard.modifiers(0, 0, 0, 0); } => {
}, keyboard.modifiers(77, 0, 0, 0);
VirtualInput::Kde { fake_input:_ } => {}, keyboard.modifiers(0, 0, 0, 0);
} }
} VirtualInput::Kde { fake_input: _ } => {}
},
} }
Ok(()) Ok(())
} }

View File

@@ -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<Client>) {
todo!()
}

6
src/backend/producer.rs Normal file
View File

@@ -0,0 +1,6 @@
#[cfg(all(unix, feature = "wayland"))]
pub mod wayland;
#[cfg(windows)]
pub mod windows;
#[cfg(all(unix, feature = "x11"))]
pub mod x11;

View File

@@ -1,2 +0,0 @@
pub mod consumer;
pub mod producer;

View File

@@ -1,2 +0,0 @@
pub mod consumer;
pub mod producer;

View File

@@ -1,2 +0,0 @@
pub mod consumer;
pub mod producer;

View File

@@ -1,5 +1,7 @@
use std::net::SocketAddr; use std::net::SocketAddr;
use crate::{config, dns};
#[derive(Eq, Hash, PartialEq, Clone, Copy)] #[derive(Eq, Hash, PartialEq, Clone, Copy)]
pub enum Position { pub enum Position {
Left, Left,
@@ -34,19 +36,56 @@ pub struct ClientManager {
pub type ClientHandle = u32; pub type ClientHandle = u32;
impl ClientManager { 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 { fn new_id(&mut self) -> ClientHandle {
self.next_id += 1; self.next_id += 1;
self.next_id self.next_id
} }
pub fn new() -> Self { pub fn new(config: &config::Config) -> Self {
ClientManager {
let mut client_manager = ClientManager {
next_id: 0, next_id: 0,
clients: Vec::new(), 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 handle = self.new_id();
let client = Client { addr, pos, handle }; let client = Client { addr, pos, handle };
self.clients.push(client); self.clients.push(client);

View File

@@ -7,6 +7,7 @@ use toml;
pub struct Config { pub struct Config {
pub client: Clients, pub client: Clients,
pub port: Option<u16>, pub port: Option<u16>,
pub backend: Option<String>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]

70
src/consumer.rs Normal file
View File

@@ -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<Client>, backend: Option<String>) -> 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()
}

View File

@@ -4,4 +4,7 @@ pub mod dns;
pub mod event; pub mod event;
pub mod request; pub mod request;
pub mod consumer;
pub mod producer;
pub mod backend; pub mod backend;

View File

@@ -1,34 +1,11 @@
use std::{net::SocketAddr, sync::mpsc, thread}; use std::sync::mpsc;
#[cfg(unix)]
use std::env;
use lan_mouse::{ use lan_mouse::{
client::{ClientManager, Position}, client::ClientManager,
config, dns, event, request, 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() { pub fn main() {
// parse config file // parse config file
let config = config::Config::new("config.toml").unwrap(); let config = config::Config::new("config.toml").unwrap();
@@ -42,83 +19,15 @@ pub fn main() {
// event channel for consuming events // event channel for consuming events
let (consume_tx, consume_rx) = mpsc::sync_channel(128); let (consume_tx, consume_rx) = mpsc::sync_channel(128);
let mut client_manager = ClientManager::new(); // create client manager
let mut client_manager = ClientManager::new(&config);
// 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);
}
}
// start receiving client connection requests // start receiving client connection requests
let (request_server, request_thread) = request::Server::listen(port).unwrap(); 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 // start producing and consuming events
let event_producer = thread::Builder::new() let event_producer = producer::start(produce_tx, client_manager.get_clients(), request_server);
.name("event producer".into()) let event_consumer = consumer::start(consume_rx, client_manager.get_clients(), config.backend);
.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();
// start sending and receiving events // start sending and receiving events
let event_server = event::server::Server::new(port); let event_server = event::server::Server::new(port);

52
src/producer.rs Normal file
View File

@@ -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<Client>,
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()
}