From d4b4e7c2695a6aef5c0d420be54584628428a9b0 Mon Sep 17 00:00:00 2001 From: Ferdinand Schober Date: Fri, 16 Sep 2022 23:13:32 +0200 Subject: [PATCH] transmit button events + stub for keyboard --- Cargo.lock | 53 ++++++++++++++----- Cargo.toml | 6 +-- README.md | 7 +-- src/bin/client.rs | 128 ++++++++++++++++++++++++++++++++++++---------- src/bin/server.rs | 108 ++++++++++++++++++++++++-------------- src/lib.rs | 1 + src/protocol.rs | 116 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 332 insertions(+), 87 deletions(-) create mode 100644 src/lib.rs create mode 100644 src/protocol.rs diff --git a/Cargo.lock b/Cargo.lock index d3482b7..efdd8bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -201,6 +201,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.0-rc1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f65dae1d3aa98e6877917ab4e6fdbfdfb00e95885ea7c4f4f29e3a5dfc08fdf" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "ipconfig" version = "0.3.0" @@ -227,6 +237,7 @@ dependencies = [ "trust-dns-resolver", "wayland-client", "wayland-protocols", + "wayland-protocols-misc", "wayland-protocols-wlr", ] @@ -729,11 +740,12 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wayland-backend" -version = "0.1.0-beta.9" -source = "git+https://github.com/Smithay/wayland-rs.git#fa4be85e8f3b57c8b6e8dd2476d6b4734b810e59" +version = "0.1.0-beta.10" +source = "git+https://github.com/Smithay/wayland-rs.git#2cd2b5857b476238eb7d2e7e3bb2b1c308bcd16c" dependencies = [ "cc", "downcast-rs", + "io-lifetimes", "nix", "scoped-tls", "smallvec", @@ -742,8 +754,8 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.30.0-beta.9" -source = "git+https://github.com/Smithay/wayland-rs.git#fa4be85e8f3b57c8b6e8dd2476d6b4734b810e59" +version = "0.30.0-beta.10" +source = "git+https://github.com/Smithay/wayland-rs.git#2cd2b5857b476238eb7d2e7e3bb2b1c308bcd16c" dependencies = [ "bitflags", "futures-channel", @@ -756,8 +768,8 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.30.0-beta.9" -source = "git+https://github.com/Smithay/wayland-rs.git#fa4be85e8f3b57c8b6e8dd2476d6b4734b810e59" +version = "0.30.0-beta.10" +source = "git+https://github.com/Smithay/wayland-rs.git#2cd2b5857b476238eb7d2e7e3bb2b1c308bcd16c" dependencies = [ "bitflags", "wayland-backend", @@ -766,10 +778,23 @@ dependencies = [ "wayland-server", ] +[[package]] +name = "wayland-protocols-misc" +version = "0.1.0-beta.10" +source = "git+https://github.com/Smithay/wayland-rs.git#2cd2b5857b476238eb7d2e7e3bb2b1c308bcd16c" +dependencies = [ + "bitflags", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", + "wayland-server", +] + [[package]] name = "wayland-protocols-wlr" -version = "0.1.0-beta.9" -source = "git+https://github.com/Smithay/wayland-rs.git#fa4be85e8f3b57c8b6e8dd2476d6b4734b810e59" +version = "0.1.0-beta.10" +source = "git+https://github.com/Smithay/wayland-rs.git#2cd2b5857b476238eb7d2e7e3bb2b1c308bcd16c" dependencies = [ "bitflags", "wayland-backend", @@ -781,8 +806,8 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.30.0-beta.9" -source = "git+https://github.com/Smithay/wayland-rs.git#fa4be85e8f3b57c8b6e8dd2476d6b4734b810e59" +version = "0.30.0-beta.10" +source = "git+https://github.com/Smithay/wayland-rs.git#2cd2b5857b476238eb7d2e7e3bb2b1c308bcd16c" dependencies = [ "proc-macro2", "quick-xml", @@ -792,8 +817,8 @@ dependencies = [ [[package]] name = "wayland-server" -version = "0.30.0-beta.9" -source = "git+https://github.com/Smithay/wayland-rs.git#fa4be85e8f3b57c8b6e8dd2476d6b4734b810e59" +version = "0.30.0-beta.10" +source = "git+https://github.com/Smithay/wayland-rs.git#2cd2b5857b476238eb7d2e7e3bb2b1c308bcd16c" dependencies = [ "bitflags", "downcast-rs", @@ -805,8 +830,8 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.30.0-beta.9" -source = "git+https://github.com/Smithay/wayland-rs.git#fa4be85e8f3b57c8b6e8dd2476d6b4734b810e59" +version = "0.30.0-beta.10" +source = "git+https://github.com/Smithay/wayland-rs.git#2cd2b5857b476238eb7d2e7e3bb2b1c308bcd16c" dependencies = [ "dlib", "log", diff --git a/Cargo.toml b/Cargo.toml index 8d06af1..c40b94a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,11 +9,7 @@ edition = "2021" wayland-client = { git="https://github.com/Smithay/wayland-rs.git" } wayland-protocols = { git="https://github.com/Smithay/wayland-rs.git", features=["client", "staging", "unstable"] } wayland-protocols-wlr = { git="https://github.com/Smithay/wayland-rs.git", features=["client", "server"] } +wayland-protocols-misc = { git="https://github.com/Smithay/wayland-rs.git", features=["client", "server"] } tempfile = "3.2" trust-dns-resolver = "0.22" -[[bin]] -name = "client" - -[[bin]] -name = "server" diff --git a/README.md b/README.md index ad26508..b0121ef 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,15 @@ Currently the mouse moves in a circle when receiving a(ny) udp packet on port 42 ## TODO - [x] Capture the actual mouse events on the server side via a wayland client and send them to the client - [x] Mouse grabbing -- [x] Window with absolute position (wlr\_layer\_shell?) -- [ ] DNS resolving +- [ ] Window with absolute position (wlr\_layer\_shell?) +- [x] DNS resolving - [ ] Keyboard support - [ ] Scrollwheel support -- [ ] Button support +- [x] Button support - [ ] Merge server and client - [ ] Clipboard support - [ ] Graphical frontend (gtk?) +- [ ] *Encrytion* ## 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/src/bin/client.rs b/src/bin/client.rs index 297e3d9..440ec71 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -1,27 +1,38 @@ use std::net::UdpSocket; +use lan_mouse::protocol; use wayland_protocols_wlr::virtual_pointer::v1::client::{ zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1 as VpManager, zwlr_virtual_pointer_v1::ZwlrVirtualPointerV1 as Vp, }; -use wayland_client::{protocol::wl_registry, Connection, Dispatch, EventQueue, QueueHandle}; +use wayland_protocols_misc::zwp_virtual_keyboard_v1::client::{ + zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1 as VkManager, + zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1 as Vk, +}; + +use wayland_client::{ + protocol::{wl_registry, wl_seat, wl_keyboard::KeyState}, + Connection, Dispatch, EventQueue, QueueHandle, +}; // App State, implements Dispatch event handlers -struct AppData { +struct App { vpm: Option, + vkm: Option, + seat: Option, } // Implement `Dispatch event handler // user-data = () -impl Dispatch for AppData { +impl Dispatch for App { fn event( app: &mut Self, registry: &wl_registry::WlRegistry, event: wl_registry::Event, _: &(), _: &Connection, - qh: &QueueHandle, + qh: &QueueHandle, ) { // Match global event to get globals after requesting them in main if let wl_registry::Event::Global { @@ -30,10 +41,14 @@ impl Dispatch for AppData { { // println!("[{}] {} (v{})", name, interface, version); match &interface[..] { + "wl_seat" => { + app.seat = Some(registry.bind::(name, 1, qh, ())); + } "zwlr_virtual_pointer_manager_v1" => { - // virtual pointer protocol - let vpm = registry.bind::(name, 1, qh, ()); // get the vp manager - app.vpm = Some(vpm); // save it to app state + app.vpm = Some(registry.bind::(name, 1, qh, ())); + } + "zwp_virtual_keyboard_manager_v1" => { + app.vkm = Some(registry.bind::(name, 1, qh, ())); } _ => {} } @@ -56,39 +71,61 @@ fn main() { // Create a wl_registry object by sending the wl_display.get_registry request let _registry = display.get_registry(&qh, ()); - let mut app_data = AppData { vpm: None }; + let mut app = App { + vpm: None, + vkm: None, + seat: None, + }; // use roundtrip to process this event synchronously - event_queue.roundtrip(&mut app_data).unwrap(); - if let Some(vpm) = app_data.vpm { - let pointer: Vp = vpm.create_virtual_pointer(None, &qh, ()); - udp_loop(pointer, event_queue).unwrap(); - println!(); - } else { - panic!("zwlr_virtual_pointer_manager_v1 protocol required") - }; + event_queue.roundtrip(&mut app).unwrap(); + + let vpm = app.vpm.unwrap(); + let vkm = app.vkm.unwrap(); + let seat = app.seat.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!(); } /// main loop handling udp packets -fn udp_loop(pointer: Vp, q: EventQueue) -> std::io::Result<()> { +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; 20]; + let mut buf = [0u8; 21]; loop { - let (amt, _src) = socket.recv_from(&mut buf)?; - assert!(amt == 20); + let (_amt, _src) = socket.recv_from(&mut buf)?; - let time: u32 = u32::from_ne_bytes(buf[0..4].try_into().unwrap()); - let x: f64 = f64::from_ne_bytes(buf[4..12].try_into().unwrap()); - let y: f64 = f64::from_ne_bytes(buf[12..20].try_into().unwrap()); + match protocol::Event::decode(buf) { + 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 } => { + // 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); + }, + } - pointer.motion(time, x, y); q.flush().unwrap(); } } -impl Dispatch for AppData { +impl Dispatch for App { fn event( _: &mut Self, _: &VpManager, @@ -101,7 +138,7 @@ impl Dispatch for AppData { } } -impl Dispatch for AppData { +impl Dispatch for App { fn event( _: &mut Self, _: &Vp, @@ -113,3 +150,42 @@ impl Dispatch for AppData { // no events defined for vp either } } + +impl Dispatch for App { + fn event( + _: &mut Self, + _: &VkManager, + _: ::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + // + } +} + +impl Dispatch for App { + fn event( + _: &mut Self, + _: &Vk, + _: ::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + // + } +} + +impl Dispatch for App { + fn event( + _: &mut Self, + _: &wl_seat::WlSeat, + _: ::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + // + } +} diff --git a/src/bin/server.rs b/src/bin/server.rs index 51f28e2..da42104 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -1,8 +1,10 @@ +use lan_mouse::protocol; use std::{ env, fs::File, io::{BufWriter, Write}, - net::{UdpSocket, IpAddr, SocketAddr}, + net::{IpAddr, SocketAddr, UdpSocket}, + str::FromStr, os::unix::prelude::AsRawFd, }; @@ -16,14 +18,14 @@ use wayland_protocols::{ use wayland_client::{ protocol::{ - wl_buffer, wl_compositor, wl_keyboard, wl_pointer, wl_registry, wl_seat, wl_shm, + wl_buffer, wl_compositor, wl_keyboard, wl_pointer::{self, Axis, ButtonState}, wl_registry, wl_seat, wl_shm, wl_shm_pool, wl_surface, }, Connection, Dispatch, QueueHandle, WEnum, }; -use trust_dns_resolver::Resolver; use tempfile; +use trust_dns_resolver::Resolver; struct App { running: bool, @@ -72,11 +74,16 @@ fn main() { }; let args: Vec = env::args().collect(); - let host = match args.get(1) { + let arg = match args.get(1) { Some(s) => s.as_str(), None => "localhost", }; - app.resolve_host(host); + 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(); @@ -115,7 +122,9 @@ 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()); + 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) @@ -123,15 +132,25 @@ impl App { 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 time_bytes = time.to_ne_bytes(); - let x_bytes = x.to_ne_bytes(); - let y_bytes = y.to_ne_bytes(); - let mut buf: [u8; 20] = [0u8; 20]; - buf[0..4].copy_from_slice(&time_bytes); - buf[4..12].copy_from_slice(&x_bytes); - buf[12..20].copy_from_slice(&y_bytes); - self.socket.send_to(&buf, SocketAddr::new(self.ip.unwrap(), 42069)).unwrap(); + let e = protocol::Event::Mouse { t: (time), x: (x), y: (y) }; + self.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); + } + fn send_axis_event(&self, t: u32, a: Axis, v: f64) { + let e = protocol::Event::Axis { t, a, v }; + self.send_event(&e); } } @@ -367,22 +386,31 @@ impl Dispatch for App { surface_y: _, } => { if app.pointer_lock.is_none() { - app.pointer_lock = Some(app.pointer_constraints.as_ref().unwrap().lock_pointer( - &app.surface.as_ref().unwrap(), - pointer, - None, - zwp_pointer_constraints_v1::Lifetime::Persistent, - qh, - (), - )); + app.pointer_lock = + Some(app.pointer_constraints.as_ref().unwrap().lock_pointer( + &app.surface.as_ref().unwrap(), + pointer, + None, + zwp_pointer_constraints_v1::Lifetime::Persistent, + qh, + (), + )); } if app.rel_pointer.is_none() { - app.rel_pointer = Some(app.rel_pointer_manager - .as_ref() - .unwrap() - .get_relative_pointer(pointer, qh, ())); + app.rel_pointer = Some( + app.rel_pointer_manager + .as_ref() + .unwrap() + .get_relative_pointer(pointer, qh, ()), + ); } } + wl_pointer::Event::Button { serial:_, time, button, state } => { + app.send_button_event(time, button, state.into_result().unwrap()); + } + wl_pointer::Event::Axis { time, axis, value } => { + app.send_axis_event(time, axis.into_result().unwrap(), value); + } _ => (), } } @@ -390,24 +418,26 @@ impl Dispatch for App { impl Dispatch for App { fn event( - state: &mut Self, + app: &mut Self, _: &wl_keyboard::WlKeyboard, event: wl_keyboard::Event, _: &(), _: &Connection, _: &QueueHandle, ) { - if let wl_keyboard::Event::Key { key, .. } = event { + if let wl_keyboard::Event::Key { serial: _, time, key, state } = event { if key == 1 { // ESC key - if let Some(pointer_lock) = state.pointer_lock.as_ref() { + if let Some(pointer_lock) = app.pointer_lock.as_ref() { pointer_lock.destroy(); - state.pointer_lock = None; + app.pointer_lock = None; } - if let Some(rel_pointer) = state.rel_pointer.as_ref() { + if let Some(rel_pointer) = app.rel_pointer.as_ref() { rel_pointer.destroy(); - state.rel_pointer = None; + app.rel_pointer = None; } + } else { + app.send_event(&protocol::Event::Key{ t: (time), k: (key), s: (state.into_result().unwrap()) }); } } } @@ -465,13 +495,13 @@ impl Dispatch for App { _: &QueueHandle, ) { if let zwp_relative_pointer_v1::Event::RelativeMotion { - utime_hi, - utime_lo, - dx: _, - dy: _, - dx_unaccel, - dy_unaccel, - } = event { + utime_hi, + utime_lo, + dx: _, + dy: _, + dx_unaccel, + dy_unaccel, + } = event { let time = ((utime_hi as u64) << 32 | utime_lo as u64) / 1000; app.send_motion_event(time as u32, dx_unaccel, dy_unaccel); } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..1b800ec --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod protocol; diff --git a/src/protocol.rs b/src/protocol.rs new file mode 100644 index 0000000..5b2146a --- /dev/null +++ b/src/protocol.rs @@ -0,0 +1,116 @@ +use wayland_client::protocol::{ + wl_pointer::{Axis, ButtonState}, + wl_keyboard::KeyState, +}; + +pub enum Event { + Mouse{t: u32, x: f64, y: f64}, + Button{t: u32, b: u32, s: ButtonState}, + Axis{t: u32, a: Axis, v: f64}, + Key{t: u32, k: u32, s: KeyState}, + 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 { + match buf[0] { + 0 => Self::Mouse { + t: u32::from_ne_bytes(buf[1..5].try_into().unwrap()), + x: f64::from_ne_bytes(buf[5..13].try_into().unwrap()), + y: f64::from_ne_bytes(buf[13..21].try_into().unwrap()), + }, + 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") + }) + }, + 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!() + }), + 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!(), + } + }, + 4 => Self::KeyModifier { + mods_depressed: u32::from_ne_bytes(buf[1..5].try_into().unwrap()), + mods_latched: u32::from_ne_bytes(buf[5..9].try_into().unwrap()), + mods_locked: u32::from_ne_bytes(buf[9..13].try_into().unwrap()), + group: u32::from_ne_bytes(buf[13..17].try_into().unwrap()), + }, + _ => panic!("protocol violation"), + } + } +}