mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-04-08 21:41:27 +03:00
Keyboard support + data requests via tcp server
This commit is contained in:
50
Cargo.lock
generated
50
Cargo.lock
generated
@@ -233,7 +233,12 @@ checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
|
|||||||
name = "lan-mouse"
|
name = "lan-mouse"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"memmap",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
"threadpool",
|
||||||
|
"toml",
|
||||||
"trust-dns-resolver",
|
"trust-dns-resolver",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
"wayland-protocols",
|
"wayland-protocols",
|
||||||
@@ -315,6 +320,16 @@ version = "2.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
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]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
version = "0.6.5"
|
version = "0.6.5"
|
||||||
@@ -520,6 +535,23 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
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]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
@@ -590,6 +622,15 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "threadpool"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
|
||||||
|
dependencies = [
|
||||||
|
"num_cpus",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
@@ -623,6 +664,15 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.5.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.36"
|
version = "0.1.36"
|
||||||
|
|||||||
@@ -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"] }
|
wayland-protocols-misc = { git="https://github.com/Smithay/wayland-rs.git", features=["client", "server"] }
|
||||||
tempfile = "3.2"
|
tempfile = "3.2"
|
||||||
trust-dns-resolver = "0.22"
|
trust-dns-resolver = "0.22"
|
||||||
|
memmap = "0.7"
|
||||||
|
toml = "0.5"
|
||||||
|
serde = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
|
threadpool = "1.8"
|
||||||
|
|||||||
40
README.md
40
README.md
@@ -1,9 +1,11 @@
|
|||||||
# Lan Mouse [WIP]
|
# 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/) on wayland compositors.
|
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
|
## Very much alpha state
|
||||||
The protocol used for the virtual mouse driver is currently unstable and only supported by wlroots:
|
The protocols used for the virtual mouse and virtual keyboard drivers are currently unstable and only supported by wlroots:
|
||||||
[wlr-virtual-pointer-unstable-v1](wlr-virtual-pointer-unstable-v1)
|
[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.
|
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
|
- [x] Mouse grabbing
|
||||||
- [ ] Window with absolute position (wlr\_layer\_shell?)
|
- [ ] Window with absolute position (wlr\_layer\_shell?)
|
||||||
- [x] DNS resolving
|
- [x] DNS resolving
|
||||||
- [ ] Keyboard support
|
- [ ] Multiple IP addresses -> check which one is reachable
|
||||||
- [ ] Scrollwheel support
|
- [ ] [WIP] Keyboard support
|
||||||
|
- [ ] [WIP] Scrollwheel support
|
||||||
- [x] Button 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
|
- [ ] Clipboard support
|
||||||
- [ ] Graphical frontend (gtk?)
|
- [ ] 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
|
## 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.
|
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.
|
||||||
|
|||||||
9
config.toml
Normal file
9
config.toml
Normal file
@@ -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
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use std::net::UdpSocket;
|
use std::{os::unix::prelude::AsRawFd, io::{Write, BufWriter}};
|
||||||
use lan_mouse::protocol;
|
use lan_mouse::{protocol::{self, DataRequest}, config::Config};
|
||||||
|
|
||||||
use wayland_protocols_wlr::virtual_pointer::v1::client::{
|
use wayland_protocols_wlr::virtual_pointer::v1::client::{
|
||||||
zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1 as VpManager,
|
zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1 as VpManager,
|
||||||
@@ -12,10 +12,12 @@ use wayland_protocols_misc::zwp_virtual_keyboard_v1::client::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use wayland_client::{
|
use wayland_client::{
|
||||||
protocol::{wl_registry, wl_seat, wl_keyboard::KeyState},
|
protocol::{wl_registry, wl_seat, wl_keyboard},
|
||||||
Connection, Dispatch, EventQueue, QueueHandle,
|
Connection, Dispatch, EventQueue, QueueHandle,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use tempfile;
|
||||||
|
|
||||||
// App State, implements Dispatch event handlers
|
// App State, implements Dispatch event handlers
|
||||||
struct App {
|
struct App {
|
||||||
vpm: Option<VpManager>,
|
vpm: Option<VpManager>,
|
||||||
@@ -41,6 +43,9 @@ impl Dispatch<wl_registry::WlRegistry, ()> for App {
|
|||||||
{
|
{
|
||||||
// println!("[{}] {} (v{})", name, interface, version);
|
// println!("[{}] {} (v{})", name, interface, version);
|
||||||
match &interface[..] {
|
match &interface[..] {
|
||||||
|
"wl_keyboard" => {
|
||||||
|
registry.bind::<wl_keyboard::WlKeyboard, _, _>(name, 1, qh, ());
|
||||||
|
}
|
||||||
"wl_seat" => {
|
"wl_seat" => {
|
||||||
app.seat = Some(registry.bind::<wl_seat::WlSeat, _, _>(name, 1, qh, ()));
|
app.seat = Some(registry.bind::<wl_seat::WlSeat, _, _>(name, 1, qh, ()));
|
||||||
}
|
}
|
||||||
@@ -58,6 +63,7 @@ impl Dispatch<wl_registry::WlRegistry, ()> for App {
|
|||||||
|
|
||||||
// The main function of our program
|
// The main function of our program
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let config = Config::new("config.toml").unwrap();
|
||||||
// establish connection via environment-provided configuration.
|
// establish connection via environment-provided configuration.
|
||||||
let conn = Connection::connect_to_env().unwrap();
|
let conn = Connection::connect_to_env().unwrap();
|
||||||
|
|
||||||
@@ -80,47 +86,43 @@ fn main() {
|
|||||||
// use roundtrip to process this event synchronously
|
// use roundtrip to process this event synchronously
|
||||||
event_queue.roundtrip(&mut app).unwrap();
|
event_queue.roundtrip(&mut app).unwrap();
|
||||||
|
|
||||||
let vpm = app.vpm.unwrap();
|
|
||||||
let vkm = app.vkm.unwrap();
|
let vpm = app.vpm.as_ref().unwrap();
|
||||||
let seat = app.seat.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 pointer: Vp = vpm.create_virtual_pointer(None, &qh, ());
|
||||||
let keyboard: Vk = vkm.create_virtual_keyboard(&seat, &qh, ());
|
let keyboard: Vk = vkm.create_virtual_keyboard(&seat, &qh, ());
|
||||||
udp_loop(&pointer, &keyboard, event_queue).unwrap();
|
let connection = protocol::Connection::new(config);
|
||||||
println!();
|
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
|
/// main loop handling udp packets
|
||||||
fn udp_loop(pointer: &Vp, keyboard: &Vk, q: EventQueue<App>) -> std::io::Result<()> {
|
fn udp_loop(connection: &protocol::Connection, pointer: &Vp, keyboard: &Vk, q: EventQueue<App>) -> 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];
|
|
||||||
loop {
|
loop {
|
||||||
let (_amt, _src) = socket.recv_from(&mut buf)?;
|
if let Some(event) = connection.receive_event() {
|
||||||
|
match event {
|
||||||
match protocol::Event::decode(buf) {
|
protocol::Event::Mouse { t, x, y } => { pointer.motion(t, x, y); }
|
||||||
protocol::Event::Mouse { t, x, y } => {
|
protocol::Event::Button { t, b, s } => { pointer.button(t, b, s); }
|
||||||
pointer.motion(t, x, y);
|
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();
|
q.flush().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,6 +153,19 @@ impl Dispatch<Vp, ()> for App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Dispatch<wl_keyboard::WlKeyboard, ()> for App {
|
||||||
|
fn event(
|
||||||
|
_: &mut Self,
|
||||||
|
_: &wl_keyboard::WlKeyboard,
|
||||||
|
_: <wl_keyboard::WlKeyboard as wayland_client::Proxy>::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Dispatch<VkManager, ()> for App {
|
impl Dispatch<VkManager, ()> for App {
|
||||||
fn event(
|
fn event(
|
||||||
_: &mut Self,
|
_: &mut Self,
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
use lan_mouse::protocol;
|
use lan_mouse::protocol;
|
||||||
|
use memmap::Mmap;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{BufWriter, Write},
|
io::{BufWriter, Write},
|
||||||
net::{IpAddr, SocketAddr, UdpSocket},
|
os::unix::prelude::{AsRawFd, FromRawFd},
|
||||||
str::FromStr,
|
|
||||||
os::unix::prelude::AsRawFd,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use wayland_protocols::{
|
use wayland_protocols::{
|
||||||
@@ -25,7 +24,6 @@ use wayland_client::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use tempfile;
|
use tempfile;
|
||||||
use trust_dns_resolver::Resolver;
|
|
||||||
|
|
||||||
struct App {
|
struct App {
|
||||||
running: bool,
|
running: bool,
|
||||||
@@ -35,15 +33,16 @@ struct App {
|
|||||||
surface: Option<wl_surface::WlSurface>,
|
surface: Option<wl_surface::WlSurface>,
|
||||||
top_level: Option<xdg_toplevel::XdgToplevel>,
|
top_level: Option<xdg_toplevel::XdgToplevel>,
|
||||||
xdg_surface: Option<xdg_surface::XdgSurface>,
|
xdg_surface: Option<xdg_surface::XdgSurface>,
|
||||||
socket: UdpSocket,
|
|
||||||
pointer_constraints: Option<zwp_pointer_constraints_v1::ZwpPointerConstraintsV1>,
|
pointer_constraints: Option<zwp_pointer_constraints_v1::ZwpPointerConstraintsV1>,
|
||||||
rel_pointer_manager: Option<zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1>,
|
rel_pointer_manager: Option<zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1>,
|
||||||
pointer_lock: Option<zwp_locked_pointer_v1::ZwpLockedPointerV1>,
|
pointer_lock: Option<zwp_locked_pointer_v1::ZwpLockedPointerV1>,
|
||||||
rel_pointer: Option<zwp_relative_pointer_v1::ZwpRelativePointerV1>,
|
rel_pointer: Option<zwp_relative_pointer_v1::ZwpRelativePointerV1>,
|
||||||
ip: Option<IpAddr>,
|
connection: protocol::Connection,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let config = lan_mouse::config::Config::new("config.toml").unwrap();
|
||||||
|
let connection = protocol::Connection::new(config);
|
||||||
// establish connection via environment-provided configuration.
|
// establish connection via environment-provided configuration.
|
||||||
let conn = Connection::connect_to_env().unwrap();
|
let conn = Connection::connect_to_env().unwrap();
|
||||||
|
|
||||||
@@ -65,26 +64,13 @@ fn main() {
|
|||||||
surface: None,
|
surface: None,
|
||||||
top_level: None,
|
top_level: None,
|
||||||
xdg_surface: None,
|
xdg_surface: None,
|
||||||
socket: UdpSocket::bind("0.0.0.0:42070").expect("couldn't bind to address"),
|
|
||||||
pointer_constraints: None,
|
pointer_constraints: None,
|
||||||
rel_pointer_manager: None,
|
rel_pointer_manager: None,
|
||||||
pointer_lock: None,
|
pointer_lock: None,
|
||||||
rel_pointer: None,
|
rel_pointer: None,
|
||||||
ip: None,
|
connection,
|
||||||
};
|
};
|
||||||
|
|
||||||
let args: Vec<String> = 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
|
// use roundtrip to process this event synchronously
|
||||||
event_queue.roundtrip(&mut app).unwrap();
|
event_queue.roundtrip(&mut app).unwrap();
|
||||||
|
|
||||||
@@ -97,7 +83,7 @@ fn main() {
|
|||||||
app.top_level
|
app.top_level
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_title("LAN Mouse".into());
|
.set_title("LAN Mouse Share".into());
|
||||||
app.surface.as_ref().unwrap().commit();
|
app.surface.as_ref().unwrap().commit();
|
||||||
|
|
||||||
while app.running {
|
while app.running {
|
||||||
@@ -120,37 +106,20 @@ fn draw(f: &mut File, (width, height): (u32, u32)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
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) {
|
fn send_motion_event(&self, time: u32, x: f64, y: f64) {
|
||||||
let e = protocol::Event::Mouse { t: (time), x: (x), y: (y) };
|
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) {
|
fn send_button_event(&self, t: u32, b: u32, s: ButtonState) {
|
||||||
let e = protocol::Event::Button { t, b, s };
|
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) {
|
fn send_axis_event(&self, t: u32, a: Axis, v: f64) {
|
||||||
let e = protocol::Event::Axis { t, a, v };
|
let e = protocol::Event::Axis { t, a, v };
|
||||||
self.send_event(&e);
|
self.connection.send_event(&e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,20 +394,30 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for App {
|
|||||||
_: &Connection,
|
_: &Connection,
|
||||||
_: &QueueHandle<Self>,
|
_: &QueueHandle<Self>,
|
||||||
) {
|
) {
|
||||||
if let wl_keyboard::Event::Key { serial: _, time, key, state } = event {
|
match event {
|
||||||
if key == 1 {
|
wl_keyboard::Event::Key { serial: _, time, key, state } => {
|
||||||
// ESC key
|
if key == 1 {
|
||||||
if let Some(pointer_lock) = app.pointer_lock.as_ref() {
|
// ESC key
|
||||||
pointer_lock.destroy();
|
if let Some(pointer_lock) = app.pointer_lock.as_ref() {
|
||||||
app.pointer_lock = None;
|
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);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
src/config.rs
Normal file
32
src/config.rs
Normal file
@@ -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<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize,Deserialize,Debug)]
|
||||||
|
pub struct Clients {
|
||||||
|
pub left: Option<Client>,
|
||||||
|
pub right: Option<Client>,
|
||||||
|
pub top: Option<Client>,
|
||||||
|
pub bottom: Option<Client>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Client {
|
||||||
|
pub host_name: Option<String>,
|
||||||
|
pub ip: Option<IpAddr>,
|
||||||
|
pub port: Option<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn new(path: &str) -> Result<Config, Box<dyn Error>> {
|
||||||
|
let config = fs::read_to_string(path)?;
|
||||||
|
Ok(toml::from_str::<_>(&config).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/dns.rs
Normal file
40
src/dns.rs
Normal file
@@ -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<String>) -> Result<IpAddr, Box<dyn Error>> {
|
||||||
|
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()),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1,3 @@
|
|||||||
|
pub mod dns;
|
||||||
|
pub mod config;
|
||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
|
|||||||
272
src/protocol.rs
272
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::{
|
use wayland_client::protocol::{
|
||||||
wl_pointer::{Axis, ButtonState},
|
wl_pointer::{Axis, ButtonState},
|
||||||
wl_keyboard::KeyState,
|
wl_keyboard::KeyState,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use std::net::{SocketAddr, UdpSocket, TcpStream};
|
||||||
|
|
||||||
|
trait Resolve {
|
||||||
|
fn resolve(&self) -> Option<SocketAddr>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolve for Option<config::Client> {
|
||||||
|
fn resolve(&self) -> Option<SocketAddr> {
|
||||||
|
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<SocketAddr>,
|
||||||
|
right: Option<SocketAddr>,
|
||||||
|
_top: Option<SocketAddr>,
|
||||||
|
_bottom: Option<SocketAddr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Connection {
|
||||||
|
udp_socket: UdpSocket,
|
||||||
|
client: ClientAddrs,
|
||||||
|
offer_data: Arc<RwLock<HashMap<DataRequest, Mmap>>>,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Mouse{t: u32, x: f64, y: f64},
|
Mouse{t: u32, x: f64, y: f64},
|
||||||
Button{t: u32, b: u32, s: ButtonState},
|
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},
|
KeyModifier{mods_depressed: u32, mods_latched: u32, mods_locked: u32, group: u32},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Event {
|
impl From<Vec<u8>> for Event {
|
||||||
pub fn encode(&self) -> Vec<u8> {
|
fn from(buf: Vec<u8>) -> Self {
|
||||||
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] {
|
match buf[0] {
|
||||||
0 => Self::Mouse {
|
0 => Self::Mouse {
|
||||||
t: u32::from_ne_bytes(buf[1..5].try_into().unwrap()),
|
t: u32::from_ne_bytes(buf[1..5].try_into().unwrap()),
|
||||||
@@ -80,29 +60,17 @@ impl Event {
|
|||||||
1 => Self::Button {
|
1 => Self::Button {
|
||||||
t: (u32::from_ne_bytes(buf[1..5].try_into().unwrap())),
|
t: (u32::from_ne_bytes(buf[1..5].try_into().unwrap())),
|
||||||
b: (u32::from_ne_bytes(buf[5..9].try_into().unwrap())),
|
b: (u32::from_ne_bytes(buf[5..9].try_into().unwrap())),
|
||||||
s: (match buf[9] {
|
s: (ButtonState::try_from(buf[9] as u32).unwrap())
|
||||||
0 => ButtonState::Released,
|
|
||||||
1 => ButtonState::Pressed,
|
|
||||||
_ => panic!("protocol violation")
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
2 => Self::Axis {
|
2 => Self::Axis {
|
||||||
t: (u32::from_ne_bytes(buf[1..5].try_into().unwrap())),
|
t: (u32::from_ne_bytes(buf[1..5].try_into().unwrap())),
|
||||||
a: (match buf[5] {
|
a: (Axis::try_from(buf[5] as u32).unwrap()),
|
||||||
0 => Axis::VerticalScroll,
|
|
||||||
1 => Axis::HorizontalScroll,
|
|
||||||
_ => todo!()
|
|
||||||
}),
|
|
||||||
v: (f64::from_ne_bytes(buf[6..14].try_into().unwrap())),
|
v: (f64::from_ne_bytes(buf[6..14].try_into().unwrap())),
|
||||||
},
|
},
|
||||||
3 => Self::Key {
|
3 => Self::Key {
|
||||||
t: u32::from_ne_bytes(buf[1..5].try_into().unwrap()),
|
t: u32::from_ne_bytes(buf[1..5].try_into().unwrap()),
|
||||||
k: u32::from_ne_bytes(buf[5..9].try_into().unwrap()),
|
k: u32::from_ne_bytes(buf[5..9].try_into().unwrap()),
|
||||||
s: match buf[9] {
|
s: KeyState::try_from(buf[9] as u32).unwrap(),
|
||||||
0 => KeyState::Released,
|
|
||||||
1 => KeyState::Pressed,
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
4 => Self::KeyModifier {
|
4 => Self::KeyModifier {
|
||||||
mods_depressed: u32::from_ne_bytes(buf[1..5].try_into().unwrap()),
|
mods_depressed: u32::from_ne_bytes(buf[1..5].try_into().unwrap()),
|
||||||
@@ -114,3 +82,157 @@ impl Event {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&Event> for Vec<u8> {
|
||||||
|
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<u32> for DataRequest {
|
||||||
|
fn from(idx: u32) -> Self {
|
||||||
|
match idx {
|
||||||
|
0 => Self::KeyMap,
|
||||||
|
_ => panic!("invalid enum value"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DataRequest> 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<RwLock<HashMap<DataRequest, Mmap>>>, 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<RwLock<HashMap<DataRequest, Mmap>>> = 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<Vec<u8>> {
|
||||||
|
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<u8> = 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<u8> = e.into();
|
||||||
|
self.udp_socket.send_to(&buf, addr).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receive_event(&self) -> Option<Event> {
|
||||||
|
let mut buf = vec![0u8; 21];
|
||||||
|
if let Ok((_amt, _src)) = self.udp_socket.recv_from(&mut buf) {
|
||||||
|
Some(Event::from(buf))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user