mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-20 19:50:55 +03:00
Background service (#43)
better handling of background-service: lan-mouse can now be run without a gui by specifying --daemon as an argument. Otherwise the servic will be run as a child process and correctly terminate when the window is closed / frontend exits. Closes #38
This commit is contained in:
committed by
GitHub
parent
9b242f6138
commit
56e5f7a30d
128
src/frontend.rs
128
src/frontend.rs
@@ -1,5 +1,5 @@
|
||||
use std::io::Result;
|
||||
use std::str;
|
||||
use anyhow::{Result, anyhow};
|
||||
use std::{str, io::ErrorKind, time::Duration, cmp::min};
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::{env, path::{Path, PathBuf}};
|
||||
@@ -11,6 +11,7 @@ use tokio::io::ReadHalf;
|
||||
use tokio::net::UnixStream;
|
||||
#[cfg(unix)]
|
||||
use tokio::net::UnixListener;
|
||||
|
||||
#[cfg(windows)]
|
||||
use tokio::net::TcpStream;
|
||||
#[cfg(windows)]
|
||||
@@ -18,7 +19,7 @@ use tokio::net::TcpListener;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use crate::client::{Position, ClientHandle, Client};
|
||||
use crate::{client::{Position, ClientHandle, Client}, config::{Config, Frontend}};
|
||||
|
||||
/// cli frontend
|
||||
pub mod cli;
|
||||
@@ -27,6 +28,52 @@ pub mod cli;
|
||||
#[cfg(all(unix, feature = "gtk"))]
|
||||
pub mod gtk;
|
||||
|
||||
|
||||
pub fn run_frontend(config: &Config) -> Result<()> {
|
||||
match config.frontend {
|
||||
#[cfg(all(unix, feature = "gtk"))]
|
||||
Frontend::Gtk => { gtk::run(); }
|
||||
#[cfg(any(not(feature = "gtk"), not(unix)))]
|
||||
Frontend::Gtk => panic!("gtk frontend requested but feature not enabled!"),
|
||||
Frontend::Cli => { cli::run()?; }
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exponential_back_off(duration: &mut Duration) -> &Duration {
|
||||
let new = duration.saturating_mul(2);
|
||||
*duration = min(new, Duration::from_secs(1));
|
||||
duration
|
||||
}
|
||||
|
||||
/// wait for the lan-mouse socket to come online
|
||||
#[cfg(unix)]
|
||||
pub fn wait_for_service() -> Result<std::os::unix::net::UnixStream> {
|
||||
let socket_path = FrontendListener::socket_path()?;
|
||||
let mut duration = Duration::from_millis(1);
|
||||
loop {
|
||||
use std::os::unix::net::UnixStream;
|
||||
if let Ok(stream) = UnixStream::connect(&socket_path) {
|
||||
break Ok(stream)
|
||||
}
|
||||
// a signaling mechanism or inotify could be used to
|
||||
// improve this
|
||||
std::thread::sleep(*exponential_back_off(&mut duration));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn wait_for_service() -> Result<std::net::TcpStream> {
|
||||
let mut duration = Duration::from_millis(1);
|
||||
loop {
|
||||
use std::net::TcpStream;
|
||||
if let Ok(stream) = TcpStream::connect("127.0.0.1:5252") {
|
||||
break Ok(stream)
|
||||
}
|
||||
std::thread::sleep(*exponential_back_off(&mut duration));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub enum FrontendEvent {
|
||||
/// add a new client
|
||||
@@ -70,20 +117,54 @@ pub struct FrontendListener {
|
||||
}
|
||||
|
||||
impl FrontendListener {
|
||||
pub async fn new() -> std::result::Result<Self, Box<dyn std::error::Error>> {
|
||||
#[cfg(unix)]
|
||||
pub fn socket_path() -> Result<PathBuf> {
|
||||
let xdg_runtime_dir = match env::var("XDG_RUNTIME_DIR") {
|
||||
Ok(d) => d,
|
||||
Err(e) => return Err(anyhow!("could not find XDG_RUNTIME_DIR: {e}")),
|
||||
};
|
||||
let xdg_runtime_dir = Path::new(xdg_runtime_dir.as_str());
|
||||
Ok(xdg_runtime_dir.join("lan-mouse-socket.sock"))
|
||||
}
|
||||
|
||||
pub async fn new() -> Option<Result<Self>> {
|
||||
#[cfg(unix)]
|
||||
let socket_path = Path::new(env::var("XDG_RUNTIME_DIR")?.as_str()).join("lan-mouse-socket.sock");
|
||||
#[cfg(unix)]
|
||||
log::debug!("remove socket: {:?}", socket_path);
|
||||
#[cfg(unix)]
|
||||
if socket_path.exists() {
|
||||
std::fs::remove_file(&socket_path).unwrap();
|
||||
}
|
||||
#[cfg(unix)]
|
||||
let listener = UnixListener::bind(&socket_path)?;
|
||||
let (socket_path, listener) = {
|
||||
let socket_path = match Self::socket_path() {
|
||||
Ok(path) => path,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
|
||||
log::debug!("remove socket: {:?}", socket_path);
|
||||
if socket_path.exists() {
|
||||
// try to connect to see if some other instance
|
||||
// of lan-mouse is already running
|
||||
match UnixStream::connect(&socket_path).await {
|
||||
// connected -> lan-mouse is already running
|
||||
Ok(_) => return None,
|
||||
// lan-mouse is not running but a socket was left behind
|
||||
Err(e) => {
|
||||
log::debug!("{socket_path:?}: {e} - removing left behind socket");
|
||||
let _ = std::fs::remove_file(&socket_path);
|
||||
},
|
||||
}
|
||||
}
|
||||
let listener = match UnixListener::bind(&socket_path) {
|
||||
Ok(ls) => ls,
|
||||
// some other lan-mouse instance has bound the socket in the meantime
|
||||
Err(e) if e.kind() == ErrorKind::AddrInUse => return None,
|
||||
Err(e) => return Some(Err(anyhow!("failed to bind lan-mouse-socket: {e}"))),
|
||||
};
|
||||
(socket_path, listener)
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
let listener = TcpListener::bind("127.0.0.1:5252").await?; // abuse tcp
|
||||
let listener = match TcpListener::bind("127.0.0.1:5252").await {
|
||||
Ok(ls) => ls,
|
||||
// some other lan-mouse instance has bound the socket in the meantime
|
||||
Err(e) if e.kind() == ErrorKind::AddrInUse => return None,
|
||||
Err(e) => return Some(Err(anyhow!("failed to bind lan-mouse-socket: {e}"))),
|
||||
};
|
||||
|
||||
let adapter = Self {
|
||||
listener,
|
||||
@@ -92,7 +173,7 @@ impl FrontendListener {
|
||||
tx_streams: vec![],
|
||||
};
|
||||
|
||||
Ok(adapter)
|
||||
Some(Ok(adapter))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -121,12 +202,25 @@ impl FrontendListener {
|
||||
let len = payload.len().to_be_bytes();
|
||||
log::debug!("json: {json}, len: {}", payload.len());
|
||||
|
||||
let mut keep = vec![];
|
||||
|
||||
// TODO do simultaneously
|
||||
for tx in self.tx_streams.iter_mut() {
|
||||
// write len + payload
|
||||
tx.write(&len).await?;
|
||||
tx.write(payload).await?;
|
||||
if let Err(_) = tx.write(&len).await {
|
||||
keep.push(false);
|
||||
continue;
|
||||
}
|
||||
if let Err(_) = tx.write(payload).await {
|
||||
keep.push(false);
|
||||
continue;
|
||||
}
|
||||
keep.push(true);
|
||||
}
|
||||
|
||||
// could not find a better solution because async
|
||||
let mut keep = keep.into_iter();
|
||||
self.tx_streams.retain(|_| keep.next().unwrap());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user