diff --git a/Cargo.lock b/Cargo.lock index efdd8bc..87da707 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,7 +233,12 @@ checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" name = "lan-mouse" version = "0.1.0" dependencies = [ + "memmap", + "serde", + "serde_derive", "tempfile", + "threadpool", + "toml", "trust-dns-resolver", "wayland-client", "wayland-protocols", @@ -315,6 +320,16 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "memoffset" version = "0.6.5" @@ -520,6 +535,23 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "serde" +version = "1.0.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" + +[[package]] +name = "serde_derive" +version = "1.0.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "slab" version = "0.4.7" @@ -590,6 +622,15 @@ dependencies = [ "syn", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -623,6 +664,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + [[package]] name = "tracing" version = "0.1.36" diff --git a/Cargo.toml b/Cargo.toml index c40b94a..c7140a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,4 +12,8 @@ wayland-protocols-wlr = { git="https://github.com/Smithay/wayland-rs.git", featu wayland-protocols-misc = { git="https://github.com/Smithay/wayland-rs.git", features=["client", "server"] } tempfile = "3.2" trust-dns-resolver = "0.22" - +memmap = "0.7" +toml = "0.5" +serde = "1.0" +serde_derive = "1.0" +threadpool = "1.8" diff --git a/README.md b/README.md index b0121ef..bde94a0 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ -# Lan Mouse [WIP] -Goal of this project is to be an open-source replacement for tools like [Synergy](https://symless.com/synergy) or [Share Mouse](https://www.sharemouse.com/de/) on wayland compositors. +# Lan Mouse Share +Goal of this project is to be an open-source replacement for tools like [Synergy](https://symless.com/synergy) or [Share Mouse](https://www.sharemouse.com/de/). +Currently on wayland is supported but I will take a look at xorg, windows & MacOS in the future. ## Very much alpha state -The protocol used for the virtual mouse driver is currently unstable and only supported by wlroots: -[wlr-virtual-pointer-unstable-v1](wlr-virtual-pointer-unstable-v1) +The protocols used for the virtual mouse and virtual keyboard drivers are currently unstable and only supported by wlroots: +[zwlr\_virtual\_pointer\_manager\_v1](wlr-virtual-pointer-unstable-v1) +[virtual-keyboard-unstable-v1](https://wayland.app/protocols/virtual-keyboard-unstable-v1) Currently the mouse moves in a circle when receiving a(ny) udp packet on port 42069. @@ -12,13 +14,35 @@ Currently the mouse moves in a circle when receiving a(ny) udp packet on port 42 - [x] Mouse grabbing - [ ] Window with absolute position (wlr\_layer\_shell?) - [x] DNS resolving -- [ ] Keyboard support -- [ ] Scrollwheel support +- [ ] Multiple IP addresses -> check which one is reachable +- [ ] [WIP] Keyboard support +- [ ] [WIP] Scrollwheel support - [x] Button support -- [ ] Merge server and client +- [ ] Merge server and client -> Both client and server can send and receive events depending on what mouse is used where +- [ ] Liveness tracking (automatically ungrab mouse when client unreachable) - [ ] Clipboard support - [ ] Graphical frontend (gtk?) -- [ ] *Encrytion* +- [ ] *Encrytion* -> likely DTLS +- [ ] + +## Protocol considerations +Currently UDP is used exclusively for all events sent and / or received. +The most bandwidth is taken up by mouse events. A typical office mouse has a polling rate of 125Hz +while gaming mice typically have a much higher polling rate of 1000Hz. +A mouse Event consists of 21 Bytes: +- 1 Byte for the event type enum, +- 4 Bytes (u32) for the timestamp, +- 8 Bytes (f64) for dx, +- 8 Bytes (f64) for dy. +Additionally the IP header with 20 Bytes and the udp header with 8 Bytes take up another 28 Byte. +So in total there is 49 * 1000 Bytes/s for a 1000Hz gaming mouse. +This makes for a bandwidth requirement of 392 kbit/s in total _even_ for a high end gaming mouse. +So bandwidth is a non-issue. + +In the future to support clipboard contents the easiest solution to not block +mouse events while receiving e.g. a large file is probably to send these via tcp simultaneously. +This way we dont need to implement any congestion control and leave this up to tcp. + ## Security Sending key and mouse event data over the local network might not be the biggest security concern but in any public network it's QUITE a problem to basically broadcast your keystrokes. diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..4862d2f --- /dev/null +++ b/config.toml @@ -0,0 +1,9 @@ +[client.left] +host_name = "iridium" +ip = "192.168.178.141" +port = 42069 + +[client.right] +host_name = "Osmium" +ip = "192.168.2.182" +port = 42069 diff --git a/src/bin/client.rs b/src/bin/client.rs index 440ec71..ed4ee91 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -1,5 +1,5 @@ -use std::net::UdpSocket; -use lan_mouse::protocol; +use std::{os::unix::prelude::AsRawFd, io::{Write, BufWriter}}; +use lan_mouse::{protocol::{self, DataRequest}, config::Config}; use wayland_protocols_wlr::virtual_pointer::v1::client::{ zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1 as VpManager, @@ -12,10 +12,12 @@ use wayland_protocols_misc::zwp_virtual_keyboard_v1::client::{ }; use wayland_client::{ - protocol::{wl_registry, wl_seat, wl_keyboard::KeyState}, + protocol::{wl_registry, wl_seat, wl_keyboard}, Connection, Dispatch, EventQueue, QueueHandle, }; +use tempfile; + // App State, implements Dispatch event handlers struct App { vpm: Option, @@ -41,6 +43,9 @@ impl Dispatch for App { { // println!("[{}] {} (v{})", name, interface, version); match &interface[..] { + "wl_keyboard" => { + registry.bind::(name, 1, qh, ()); + } "wl_seat" => { app.seat = Some(registry.bind::(name, 1, qh, ())); } @@ -58,6 +63,7 @@ impl Dispatch for App { // The main function of our program fn main() { + let config = Config::new("config.toml").unwrap(); // establish connection via environment-provided configuration. let conn = Connection::connect_to_env().unwrap(); @@ -80,47 +86,43 @@ fn main() { // use roundtrip to process this event synchronously event_queue.roundtrip(&mut app).unwrap(); - let vpm = app.vpm.unwrap(); - let vkm = app.vkm.unwrap(); - let seat = app.seat.unwrap(); + + let vpm = app.vpm.as_ref().unwrap(); + let vkm = app.vkm.as_ref().unwrap(); + let seat = app.seat.as_ref().unwrap(); let pointer: Vp = vpm.create_virtual_pointer(None, &qh, ()); let keyboard: Vk = vkm.create_virtual_keyboard(&seat, &qh, ()); - udp_loop(&pointer, &keyboard, event_queue).unwrap(); - println!(); + let connection = protocol::Connection::new(config); + let data = loop { + match connection.receive_data(DataRequest::KeyMap) { + Some(data) => { break data } + None => {} + } + }; + // TODO use shm_open + let f = tempfile::tempfile().unwrap(); + let mut buf = BufWriter::new(&f); + buf.write_all(&data[..]).unwrap(); + buf.flush().unwrap(); + keyboard.keymap(1, f.as_raw_fd(), data.len() as u32); + event_queue.roundtrip(&mut app).unwrap(); + udp_loop(&connection, &pointer, &keyboard, event_queue).unwrap(); } /// main loop handling udp packets -fn udp_loop(pointer: &Vp, keyboard: &Vk, q: EventQueue) -> std::io::Result<()> { - let socket = UdpSocket::bind("0.0.0.0:42069")?; - // we don't care about possible dropped packets for now - - let mut buf = [0u8; 21]; +fn udp_loop(connection: &protocol::Connection, pointer: &Vp, keyboard: &Vk, q: EventQueue) -> std::io::Result<()> { loop { - let (_amt, _src) = socket.recv_from(&mut buf)?; - - match protocol::Event::decode(buf) { - protocol::Event::Mouse { t, x, y } => { - pointer.motion(t, x, y); + if let Some(event) = connection.receive_event() { + match event { + protocol::Event::Mouse { t, x, y } => { pointer.motion(t, x, y); } + protocol::Event::Button { t, b, s } => { pointer.button(t, b, s); } + protocol::Event::Axis { t, a, v } => { pointer.axis(t, a, v); } + protocol::Event::Key { t, k, s } => { keyboard.key(t, k, u32::from(s)); }, + protocol::Event::KeyModifier { mods_depressed, mods_latched, mods_locked, group } => { + keyboard.modifiers(mods_depressed, mods_latched, mods_locked, group); + }, } - protocol::Event::Button { t, b, s } => { - pointer.button(t, b, s); - } - protocol::Event::Axis { t, a, v } => { - pointer.axis(t, a, v); - } - protocol::Event::Key { t, k, s } => { - // TODO send keymap fist - // keyboard.key(t, k, match s { - // KeyState::Released => 0, - // KeyState::Pressed => 1, - // _ => 1, - // }); - }, - protocol::Event::KeyModifier { mods_depressed, mods_latched, mods_locked, group } => { - // keyboard.modifiers(mods_depressed, mods_latched, mods_locked, group); - }, } - q.flush().unwrap(); } } @@ -151,6 +153,19 @@ impl Dispatch for App { } } +impl Dispatch for App { + fn event( + _: &mut Self, + _: &wl_keyboard::WlKeyboard, + _: ::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + // + } +} + impl Dispatch for App { fn event( _: &mut Self, diff --git a/src/bin/server.rs b/src/bin/server.rs index da42104..eda4b85 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -1,11 +1,10 @@ use lan_mouse::protocol; +use memmap::Mmap; + use std::{ - env, fs::File, io::{BufWriter, Write}, - net::{IpAddr, SocketAddr, UdpSocket}, - str::FromStr, - os::unix::prelude::AsRawFd, + os::unix::prelude::{AsRawFd, FromRawFd}, }; use wayland_protocols::{ @@ -25,7 +24,6 @@ use wayland_client::{ }; use tempfile; -use trust_dns_resolver::Resolver; struct App { running: bool, @@ -35,15 +33,16 @@ struct App { surface: Option, top_level: Option, xdg_surface: Option, - socket: UdpSocket, pointer_constraints: Option, rel_pointer_manager: Option, pointer_lock: Option, rel_pointer: Option, - ip: Option, + connection: protocol::Connection, } fn main() { + let config = lan_mouse::config::Config::new("config.toml").unwrap(); + let connection = protocol::Connection::new(config); // establish connection via environment-provided configuration. let conn = Connection::connect_to_env().unwrap(); @@ -65,26 +64,13 @@ fn main() { surface: None, top_level: None, xdg_surface: None, - socket: UdpSocket::bind("0.0.0.0:42070").expect("couldn't bind to address"), pointer_constraints: None, rel_pointer_manager: None, pointer_lock: None, rel_pointer: None, - ip: None, + connection, }; - let args: Vec = env::args().collect(); - let arg = match args.get(1) { - Some(s) => s.as_str(), - None => "localhost", - }; - if let Ok(ip) = IpAddr::from_str(arg) { - app.ip = Some(ip); - println!("{} ", ip); - } else { - app.resolve_host(arg); - } - // use roundtrip to process this event synchronously event_queue.roundtrip(&mut app).unwrap(); @@ -97,7 +83,7 @@ fn main() { app.top_level .as_ref() .unwrap() - .set_title("LAN Mouse".into()); + .set_title("LAN Mouse Share".into()); app.surface.as_ref().unwrap().commit(); while app.running { @@ -120,37 +106,20 @@ fn draw(f: &mut File, (width, height): (u32, u32)) { } impl App { - fn resolve_host(&mut self, host: &str) { - let resolver = Resolver::from_system_conf().unwrap(); - let response = resolver - .lookup_ip(host) - .expect(format!("couldn't resolve {}", host).as_str()); - self.ip = response.iter().next(); - if let None = self.ip { - panic!("couldn't resolve host: {}!", host) - } - println!("Client: {} {}", host, self.ip.unwrap()); - } - fn send_event(&self, e: &protocol::Event) { - let buf = e.encode(); - self.socket - .send_to(&buf, SocketAddr::new(self.ip.unwrap(), 42069)) - .unwrap(); - } fn send_motion_event(&self, time: u32, x: f64, y: f64) { let e = protocol::Event::Mouse { t: (time), x: (x), y: (y) }; - self.send_event(&e); + self.connection.send_event(&e); } fn send_button_event(&self, t: u32, b: u32, s: ButtonState) { let e = protocol::Event::Button { t, b, s }; - self.send_event(&e); + self.connection.send_event(&e); } fn send_axis_event(&self, t: u32, a: Axis, v: f64) { let e = protocol::Event::Axis { t, a, v }; - self.send_event(&e); + self.connection.send_event(&e); } } @@ -425,20 +394,30 @@ impl Dispatch for App { _: &Connection, _: &QueueHandle, ) { - if let wl_keyboard::Event::Key { serial: _, time, key, state } = event { - if key == 1 { - // ESC key - if let Some(pointer_lock) = app.pointer_lock.as_ref() { - pointer_lock.destroy(); - app.pointer_lock = None; + match event { + wl_keyboard::Event::Key { serial: _, time, key, state } => { + if key == 1 { + // ESC key + if let Some(pointer_lock) = app.pointer_lock.as_ref() { + pointer_lock.destroy(); + app.pointer_lock = None; + } + if let Some(rel_pointer) = app.rel_pointer.as_ref() { + rel_pointer.destroy(); + app.rel_pointer = None; + } + } else { + app.connection.send_event(&protocol::Event::Key{ t: (time), k: (key), s: (state.into_result().unwrap()) }); } - if let Some(rel_pointer) = app.rel_pointer.as_ref() { - rel_pointer.destroy(); - app.rel_pointer = None; - } - } else { - app.send_event(&protocol::Event::Key{ t: (time), k: (key), s: (state.into_result().unwrap()) }); } + wl_keyboard::Event::Modifiers { serial: _, mods_depressed, mods_latched, mods_locked, group } => { + app.connection.send_event(&protocol::Event::KeyModifier{ mods_depressed, mods_latched, mods_locked, group }); + } + wl_keyboard::Event::Keymap { format:_ , fd, size:_ } => { + let mmap = unsafe { Mmap::map(&File::from_raw_fd(fd.as_raw_fd())).unwrap() }; + app.connection.offer_data(protocol::DataRequest::KeyMap, mmap); + } + _ => (), } } } diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..5c75ddf --- /dev/null +++ b/src/config.rs @@ -0,0 +1,32 @@ +use toml; +use std::net::IpAddr; +use std::{fs, error::Error}; +use serde_derive::{Serialize, Deserialize}; + +#[derive(Serialize,Deserialize,Debug)] +pub struct Config { + pub client: Clients, + pub port: Option, +} + +#[derive(Serialize,Deserialize,Debug)] +pub struct Clients { + pub left: Option, + pub right: Option, + pub top: Option, + pub bottom: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Client { + pub host_name: Option, + pub ip: Option, + pub port: Option, +} + +impl Config { + pub fn new(path: &str) -> Result> { + let config = fs::read_to_string(path)?; + Ok(toml::from_str::<_>(&config).unwrap()) + } +} diff --git a/src/dns.rs b/src/dns.rs new file mode 100644 index 0000000..6fa7841 --- /dev/null +++ b/src/dns.rs @@ -0,0 +1,40 @@ +use std::{net::IpAddr, error::Error, fmt::Display}; + +use trust_dns_resolver::Resolver; + +#[derive(Debug, Clone)] +struct InvalidConfigError; + +#[derive(Debug, Clone)] +struct DnsError{ + host: String, +} + + +impl Error for InvalidConfigError {} + +impl Display for InvalidConfigError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "No hostname specified!") + } +} + +impl Error for DnsError {} + +impl Display for DnsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "couldn't resolve host \"{}\"", self.host) + } +} + +pub fn resolve(host: &Option) -> Result> { + let host = match host { + Some(host) => host, + None => return Err(InvalidConfigError.into()), + }; + let response = Resolver::from_system_conf()?.lookup_ip(host)?; + match response.iter().next() { + Some(ip) => Ok(ip), + None => Err(DnsError{host: host.clone()}.into()), + } +} diff --git a/src/lib.rs b/src/lib.rs index 1b800ec..d0df885 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,3 @@ +pub mod dns; +pub mod config; pub mod protocol; diff --git a/src/protocol.rs b/src/protocol.rs index 5b2146a..aabbdc3 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,8 +1,46 @@ +use memmap::Mmap; +use crate::config::{self, Config}; +use std::{io::prelude::*, net::TcpListener, thread, sync::{Arc, RwLock}, collections::HashMap}; +use crate::dns; + use wayland_client::protocol::{ wl_pointer::{Axis, ButtonState}, wl_keyboard::KeyState, }; +use std::net::{SocketAddr, UdpSocket, TcpStream}; + +trait Resolve { + fn resolve(&self) -> Option; +} + +impl Resolve for Option { + fn resolve(&self) -> Option { + let client = match self { + Some(client) => client, + None => return None, + }; + let ip = match client.ip { + Some(ip) => ip, + None => dns::resolve(&client.host_name).unwrap() + }; + Some(SocketAddr::new(ip, client.port.unwrap_or(42069))) + } +} + +struct ClientAddrs { + left: Option, + right: Option, + _top: Option, + _bottom: Option, +} + +pub struct Connection { + udp_socket: UdpSocket, + client: ClientAddrs, + offer_data: Arc>>, +} + pub enum Event { Mouse{t: u32, x: f64, y: f64}, Button{t: u32, b: u32, s: ButtonState}, @@ -11,66 +49,8 @@ pub enum Event { KeyModifier{mods_depressed: u32, mods_latched: u32, mods_locked: u32, group: u32}, } -impl Event { - pub fn encode(&self) -> Vec { - match self { - Event::Mouse { t, x, y } => { - let mut buf = Vec::new(); - buf.push(0u8); - buf.extend_from_slice(t.to_ne_bytes().as_ref()); - buf.extend_from_slice(x.to_ne_bytes().as_ref()); - buf.extend_from_slice(y.to_ne_bytes().as_ref()); - buf - } - Event::Button { t, b, s } => { - let mut buf = Vec::new(); - buf.push(1u8); - buf.extend_from_slice(t.to_ne_bytes().as_ref()); - buf.extend_from_slice(b.to_ne_bytes().as_ref()); - buf.push(match s { - ButtonState::Released => 0u8, - ButtonState::Pressed => 1u8, - _ => todo!() - }); - buf - } - Event::Axis{t, a, v} => { - let mut buf = Vec::new(); - buf.push(2u8); - buf.extend_from_slice(t.to_ne_bytes().as_ref()); - buf.push(match a { - Axis::VerticalScroll => 0, - Axis::HorizontalScroll => 1, - _ => todo!() - }); - buf.extend_from_slice(v.to_ne_bytes().as_ref()); - buf - } - Event::Key{t, k, s } => { - let mut buf = Vec::new(); - buf.push(3u8); - buf.extend_from_slice(t.to_ne_bytes().as_ref()); - buf.extend_from_slice(k.to_ne_bytes().as_ref()); - buf.push(match s { - KeyState::Released => 0, - KeyState::Pressed => 1, - _ => todo!(), - }); - buf - } - Event::KeyModifier{ mods_depressed, mods_latched, mods_locked, group } => { - let mut buf = Vec::new(); - buf.push(4u8); - buf.extend_from_slice(mods_depressed.to_ne_bytes().as_ref()); - buf.extend_from_slice(mods_latched.to_ne_bytes().as_ref()); - buf.extend_from_slice(mods_locked.to_ne_bytes().as_ref()); - buf.extend_from_slice(group.to_ne_bytes().as_ref()); - buf - } - } - } - - pub fn decode(buf: [u8; 21]) -> Event { +impl From> for Event { + fn from(buf: Vec) -> Self { match buf[0] { 0 => Self::Mouse { t: u32::from_ne_bytes(buf[1..5].try_into().unwrap()), @@ -80,29 +60,17 @@ impl Event { 1 => Self::Button { t: (u32::from_ne_bytes(buf[1..5].try_into().unwrap())), b: (u32::from_ne_bytes(buf[5..9].try_into().unwrap())), - s: (match buf[9] { - 0 => ButtonState::Released, - 1 => ButtonState::Pressed, - _ => panic!("protocol violation") - }) + s: (ButtonState::try_from(buf[9] as u32).unwrap()) }, 2 => Self::Axis { t: (u32::from_ne_bytes(buf[1..5].try_into().unwrap())), - a: (match buf[5] { - 0 => Axis::VerticalScroll, - 1 => Axis::HorizontalScroll, - _ => todo!() - }), + a: (Axis::try_from(buf[5] as u32).unwrap()), v: (f64::from_ne_bytes(buf[6..14].try_into().unwrap())), }, 3 => Self::Key { t: u32::from_ne_bytes(buf[1..5].try_into().unwrap()), k: u32::from_ne_bytes(buf[5..9].try_into().unwrap()), - s: match buf[9] { - 0 => KeyState::Released, - 1 => KeyState::Pressed, - _ => todo!(), - } + s: KeyState::try_from(buf[9] as u32).unwrap(), }, 4 => Self::KeyModifier { mods_depressed: u32::from_ne_bytes(buf[1..5].try_into().unwrap()), @@ -114,3 +82,157 @@ impl Event { } } } + +impl From<&Event> for Vec { + fn from(e: &Event) -> Self { + let mut buf = Vec::new(); + match e { + Event::Mouse { t, x, y } => { + buf.push(0u8); + buf.extend_from_slice(t.to_ne_bytes().as_ref()); + buf.extend_from_slice(x.to_ne_bytes().as_ref()); + buf.extend_from_slice(y.to_ne_bytes().as_ref()); + } + Event::Button { t, b, s } => { + buf.push(1u8); + buf.extend_from_slice(t.to_ne_bytes().as_ref()); + buf.extend_from_slice(b.to_ne_bytes().as_ref()); + buf.push(u32::from(*s) as u8); + } + Event::Axis{t, a, v} => { + buf.push(2u8); + buf.extend_from_slice(t.to_ne_bytes().as_ref()); + buf.push(u32::from(*a) as u8); + buf.extend_from_slice(v.to_ne_bytes().as_ref()); + } + Event::Key{t, k, s } => { + buf.push(3u8); + buf.extend_from_slice(t.to_ne_bytes().as_ref()); + buf.extend_from_slice(k.to_ne_bytes().as_ref()); + buf.push(u32::from(*s) as u8); + } + Event::KeyModifier{ mods_depressed, mods_latched, mods_locked, group } => { + buf.push(4u8); + buf.extend_from_slice(mods_depressed.to_ne_bytes().as_ref()); + buf.extend_from_slice(mods_latched.to_ne_bytes().as_ref()); + buf.extend_from_slice(mods_locked.to_ne_bytes().as_ref()); + buf.extend_from_slice(group.to_ne_bytes().as_ref()); + } + } + buf + } +} + +#[derive(PartialEq, Eq, Hash)] +pub enum DataRequest { + KeyMap, +} + +impl From for DataRequest { + fn from(idx: u32) -> Self { + match idx { + 0 => Self::KeyMap, + _ => panic!("invalid enum value"), + } + } +} + +impl From for u32 { + fn from(d: DataRequest) -> Self { + match d { + DataRequest::KeyMap => 0, + } + } +} + +impl From<[u8;4]> for DataRequest { + fn from(buf: [u8;4]) -> Self { + DataRequest::from(u32::from_ne_bytes(buf)) + } +} + +fn handle_request(data: &Arc>>, mut stream: TcpStream) { + let mut buf = [0u8; 4]; + stream.read_exact(&mut buf).unwrap(); + match DataRequest::from(buf) { + DataRequest::KeyMap => { + let data = data.read().unwrap(); + let buf = data.get(&DataRequest::KeyMap); + match buf { + None => { + stream.write(&0u32.to_ne_bytes()).unwrap(); + } + Some(buf) => { + stream.write(&buf[..].len().to_ne_bytes()).unwrap(); + stream.write(&buf[..]).unwrap(); + } + } + stream.flush().unwrap(); + } + } +} + +impl Connection { + pub fn new(config: Config) -> Connection { + let clients = ClientAddrs { + left: config.client.left.resolve(), + right: config.client.right.resolve(), + _top: config.client.top.resolve(), + _bottom: config.client.bottom.resolve(), + }; + let data: Arc>> = Arc::new(RwLock::new(HashMap::new())); + let thread_data = data.clone(); + let port = config.port.unwrap_or(42069); + let listen_addr = SocketAddr::new("0.0.0.0".parse().unwrap(), port); + thread::spawn(move || { + let sock = TcpListener::bind(listen_addr).unwrap(); + for stream in sock.incoming() { + if let Ok(stream) = stream { + handle_request(&thread_data, stream); + } + } + }); + let c = Connection { + udp_socket: UdpSocket::bind(listen_addr).unwrap(), + client: clients, + offer_data: data, + }; + c + } + + pub fn offer_data(&self, req: DataRequest, d: Mmap) { + self.offer_data.write().unwrap().insert(req, d); + } + + pub fn receive_data(&self, req: DataRequest) -> Option> { + let mut sock = TcpStream::connect(self.client.left.unwrap()).unwrap(); + sock.write(&u32::from(req).to_ne_bytes()).unwrap(); + sock.flush().unwrap(); + let mut buf = [0u8;8]; + sock.read_exact(&mut buf[..]).unwrap(); + let len = usize::from_ne_bytes(buf); + if len == 0 { + return None; + } + let mut data: Vec = vec![0u8; len]; + sock.read_exact(&mut data[..]).unwrap(); + Some(data) + } + + pub fn send_event(&self, e: &Event) { + // TODO check which client + if let Some(addr) = self.client.right { + let buf: Vec = e.into(); + self.udp_socket.send_to(&buf, addr).unwrap(); + } + } + + pub fn receive_event(&self) -> Option { + let mut buf = vec![0u8; 21]; + if let Ok((_amt, _src)) = self.udp_socket.recv_from(&mut buf) { + Some(Event::from(buf)) + } else { + None + } + } +}