mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-07 11:59:59 +03:00
Initial Hotplug support (#8)
This allows to dynamically add clients when an event is received from an unknown IP address. The user is asked to confirm any unknown connection from new clients. Currently the back-ends for event producing and consuming are not yet notified, so events will not be received and sent to the newly created clients.
This commit is contained in:
committed by
GitHub
parent
0feb1350a9
commit
225ef818a2
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -2,6 +2,12 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.57"
|
||||
@@ -233,6 +239,7 @@ checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
|
||||
name = "lan-mouse"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"memmap",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
|
||||
12
Cargo.toml
12
Cargo.toml
@@ -16,6 +16,7 @@ memmap = "0.7"
|
||||
toml = "0.5"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
anyhow = "1.0.71"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
wayland-client = { version="0.30.0", optional = true }
|
||||
@@ -30,13 +31,8 @@ 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" ]
|
||||
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 = []
|
||||
|
||||
@@ -69,7 +69,7 @@ impl App {
|
||||
(_, _, Ok(fake_input)) => {
|
||||
fake_input.authenticate(
|
||||
"lan-mouse".into(),
|
||||
"Allow remote clients to control this devices".into(),
|
||||
"Allow remote clients to control this device".into(),
|
||||
);
|
||||
VirtualInputManager::Kde { fake_input }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{net::SocketAddr, error::Error, fmt::Display};
|
||||
use std::{net::SocketAddr, error::Error, fmt::Display, sync::{Arc, atomic::{AtomicBool, Ordering, AtomicU32}, RwLock}};
|
||||
|
||||
use crate::{config, dns};
|
||||
|
||||
@@ -29,8 +29,9 @@ pub enum ClientEvent {
|
||||
}
|
||||
|
||||
pub struct ClientManager {
|
||||
next_id: u32,
|
||||
clients: Vec<Client>,
|
||||
next_id: AtomicU32,
|
||||
clients: RwLock<Vec<Client>>,
|
||||
subscribers: RwLock<Vec<Arc<AtomicBool>>>,
|
||||
}
|
||||
|
||||
pub type ClientHandle = u32;
|
||||
@@ -47,7 +48,7 @@ impl Display for ClientConfigError {
|
||||
impl Error for ClientConfigError {}
|
||||
|
||||
impl ClientManager {
|
||||
fn add_client(&mut self, client: &config::Client, pos: Position) -> Result<(), Box<dyn Error>> {
|
||||
fn add_client(&self, client: &config::Client, pos: Position) -> Result<(), Box<dyn Error>> {
|
||||
let ip = match client.ip {
|
||||
Some(ip) => ip,
|
||||
None => match &client.host_name {
|
||||
@@ -60,16 +61,24 @@ impl ClientManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new_id(&mut self) -> ClientHandle {
|
||||
self.next_id += 1;
|
||||
self.next_id
|
||||
fn notify(&self) {
|
||||
for subscriber in self.subscribers.read().unwrap().iter() {
|
||||
subscriber.store(true, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
fn new_id(&self) -> ClientHandle {
|
||||
let id = self.next_id.load(Ordering::Acquire);
|
||||
self.next_id.store(id + 1, Ordering::Release);
|
||||
id as ClientHandle
|
||||
}
|
||||
|
||||
pub fn new(config: &config::Config) -> Result<Self, Box<dyn Error>> {
|
||||
|
||||
let mut client_manager = ClientManager {
|
||||
next_id: 0,
|
||||
clients: Vec::new(),
|
||||
let client_manager = ClientManager {
|
||||
next_id: AtomicU32::new(0),
|
||||
clients: RwLock::new(Vec::new()),
|
||||
subscribers: RwLock::new(vec![]),
|
||||
};
|
||||
|
||||
// add clients from config
|
||||
@@ -80,13 +89,18 @@ impl ClientManager {
|
||||
Ok(client_manager)
|
||||
}
|
||||
|
||||
pub fn register_client(&mut self, addr: SocketAddr, pos: Position) {
|
||||
pub fn register_client(&self, addr: SocketAddr, pos: Position) {
|
||||
let handle = self.new_id();
|
||||
let client = Client { addr, pos, handle };
|
||||
self.clients.push(client);
|
||||
self.clients.write().unwrap().push(client);
|
||||
self.notify();
|
||||
}
|
||||
|
||||
pub fn get_clients(&self) -> Vec<Client> {
|
||||
self.clients.clone()
|
||||
self.clients.read().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn subscribe(&self, subscriber: Arc<AtomicBool>) {
|
||||
self.subscribers.write().unwrap().push(subscriber);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use anyhow::Result;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
error::Error,
|
||||
@@ -10,7 +12,7 @@ use std::{
|
||||
thread::{self, JoinHandle},
|
||||
};
|
||||
|
||||
use crate::client::{ClientHandle, ClientManager};
|
||||
use crate::{client::{ClientHandle, ClientManager}, ioutils::{ask_confirmation, ask_position}};
|
||||
|
||||
use super::Event;
|
||||
|
||||
@@ -30,25 +32,25 @@ impl Server {
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
self,
|
||||
client_manager: &mut ClientManager,
|
||||
&self,
|
||||
client_manager: Arc<ClientManager>,
|
||||
produce_rx: Receiver<(Event, ClientHandle)>,
|
||||
consume_tx: SyncSender<(Event, ClientHandle)>,
|
||||
) -> Result<(JoinHandle<()>, JoinHandle<()>), Box<dyn Error>> {
|
||||
) -> Result<(JoinHandle<Result<()>>, JoinHandle<Result<()>>), Box<dyn Error>> {
|
||||
let udp_socket = UdpSocket::bind(self.listen_addr)?;
|
||||
let rx = udp_socket.try_clone()?;
|
||||
let tx = udp_socket;
|
||||
|
||||
let sending = self.sending.clone();
|
||||
let clients_updated = Arc::new(AtomicBool::new(true));
|
||||
client_manager.subscribe(clients_updated.clone());
|
||||
let client_manager_clone = client_manager.clone();
|
||||
|
||||
let mut client_for_socket = HashMap::new();
|
||||
for client in client_manager.get_clients() {
|
||||
println!("{}: {}", client.handle, client.addr);
|
||||
client_for_socket.insert(client.addr, client.handle);
|
||||
}
|
||||
let receiver = thread::Builder::new()
|
||||
.name("event receiver".into())
|
||||
.spawn(move || {
|
||||
let mut client_for_socket = HashMap::new();
|
||||
|
||||
loop {
|
||||
let (event, addr) = match Server::receive_event(&rx) {
|
||||
Ok(e) => e,
|
||||
@@ -58,10 +60,30 @@ impl Server {
|
||||
}
|
||||
};
|
||||
|
||||
if let Ok(_) = clients_updated.compare_exchange(
|
||||
true,
|
||||
false,
|
||||
Ordering::SeqCst,
|
||||
Ordering::SeqCst,
|
||||
) {
|
||||
clients_updated.store(false, Ordering::SeqCst);
|
||||
client_for_socket.clear();
|
||||
println!("updating clients: ");
|
||||
for client in client_manager_clone.get_clients() {
|
||||
println!("{}: {}", client.handle, client.addr);
|
||||
client_for_socket.insert(client.addr, client.handle);
|
||||
}
|
||||
}
|
||||
|
||||
let client_handle = match client_for_socket.get(&addr) {
|
||||
Some(c) => *c,
|
||||
None => {
|
||||
println!("Allow connection from {:?}? [Y/n]", addr);
|
||||
eprint!("Allow connection from {:?}? ", addr);
|
||||
if ask_confirmation(false)? {
|
||||
client_manager_clone.register_client(addr, ask_position()?);
|
||||
} else {
|
||||
eprintln!("rejecting client: {:?}?", addr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
49
src/ioutils.rs
Normal file
49
src/ioutils.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use std::io::{self, Write};
|
||||
|
||||
use crate::client::Position;
|
||||
|
||||
|
||||
pub fn ask_confirmation(default: bool) -> Result<bool, io::Error> {
|
||||
eprint!("{}", if default {" [Y,n] "} else { " [y,N] "});
|
||||
io::stderr().flush()?;
|
||||
let answer = loop {
|
||||
let mut buffer = String::new();
|
||||
io::stdin().read_line(&mut buffer)?;
|
||||
let answer = buffer.to_lowercase();
|
||||
let answer = answer.trim();
|
||||
match answer {
|
||||
"" => break default,
|
||||
"y" => break true,
|
||||
"n" => break false,
|
||||
_ => {
|
||||
eprint!("Enter y for Yes or n for No: ");
|
||||
io::stderr().flush()?;
|
||||
continue
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(answer)
|
||||
}
|
||||
|
||||
pub fn ask_position() -> Result<Position, io::Error> {
|
||||
eprint!("Enter position - top (t) | bottom (b) | left(l) | right(r): ");
|
||||
io::stderr().flush()?;
|
||||
let pos = loop {
|
||||
let mut buffer = String::new();
|
||||
io::stdin().read_line(&mut buffer)?;
|
||||
let answer = buffer.to_lowercase();
|
||||
let answer = answer.trim();
|
||||
match answer {
|
||||
"t" | "top" => break Position::Top,
|
||||
"b" | "bottom" => break Position::Bottom,
|
||||
"l" | "left" => break Position::Right,
|
||||
"r" | "right" => break Position::Left,
|
||||
_ => {
|
||||
eprint!("Invalid position: {answer} - enter top (t) | bottom (b) | left(l) | right(r): ");
|
||||
io::stderr().flush()?;
|
||||
continue
|
||||
}
|
||||
};
|
||||
};
|
||||
Ok(pos)
|
||||
}
|
||||
@@ -8,3 +8,4 @@ pub mod consumer;
|
||||
pub mod producer;
|
||||
|
||||
pub mod backend;
|
||||
pub mod ioutils;
|
||||
|
||||
18
src/main.rs
18
src/main.rs
@@ -1,4 +1,4 @@
|
||||
use std::{sync::mpsc, process, env};
|
||||
use std::{sync::{mpsc, Arc}, process, env};
|
||||
|
||||
use lan_mouse::{
|
||||
client::ClientManager,
|
||||
@@ -32,7 +32,7 @@ pub fn main() {
|
||||
let (consume_tx, consume_rx) = mpsc::sync_channel(128);
|
||||
|
||||
// create client manager
|
||||
let mut client_manager = match ClientManager::new(&config) {
|
||||
let client_manager = match ClientManager::new(&config) {
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
process::exit(1);
|
||||
@@ -73,7 +73,7 @@ pub fn main() {
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
let (receiver, sender) = match event_server.run(&mut client_manager, produce_rx, consume_tx) {
|
||||
let (receiver, sender) = match event_server.run(Arc::new(client_manager), produce_rx, consume_tx) {
|
||||
Ok((r,s)) => (r,s),
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
@@ -84,7 +84,11 @@ pub fn main() {
|
||||
request_thread.join().unwrap();
|
||||
|
||||
// stop receiving events and terminate event-consumer
|
||||
receiver.join().unwrap();
|
||||
if let Err(e) = receiver.join().unwrap() {
|
||||
eprint!("{e}");
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
if let Some(thread) = event_consumer {
|
||||
thread.join().unwrap();
|
||||
}
|
||||
@@ -93,5 +97,9 @@ pub fn main() {
|
||||
if let Some(thread) = event_producer {
|
||||
thread.join().unwrap();
|
||||
}
|
||||
sender.join().unwrap();
|
||||
|
||||
if let Err(e) = sender.join().unwrap() {
|
||||
eprint!("{e}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user