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:
Ferdinand Schober
2023-12-09 00:36:01 +01:00
committed by GitHub
parent 9b242f6138
commit 56e5f7a30d
13 changed files with 488 additions and 170 deletions

View File

@@ -1,15 +1,14 @@
use std::{process, error::Error};
use anyhow::Result;
use std::process::{self, Command, Child};
use env_logger::Env;
use lan_mouse::{
consumer, producer,
config::{Config, Frontend::{Cli, Gtk}}, server::Server,
frontend::{FrontendListener, cli},
config::Config, server::Server,
frontend::{FrontendListener, self},
};
#[cfg(all(unix, feature = "gtk"))]
use lan_mouse::frontend::gtk;
use tokio::task::LocalSet;
use tokio::{task::LocalSet, join};
pub fn main() {
@@ -23,10 +22,36 @@ pub fn main() {
}
}
pub fn run() -> Result<(), Box<dyn Error>> {
// parse config file
let config = Config::new()?;
pub fn start_service() -> Result<Child> {
let child = Command::new(std::env::current_exe()?)
.args(std::env::args().skip(1))
.arg("--daemon")
.spawn()?;
Ok(child)
}
pub fn run() -> Result<()> {
// parse config file + cli args
let config = Config::new()?;
log::debug!("{config:?}");
if config.daemon {
// if daemon is specified we run the service
run_service(&config)?;
} else {
// otherwise start the service as a child process and
// run a frontend
start_service()?;
frontend::run_frontend(&config)?;
}
anyhow::Ok(())
}
fn run_service(config: &Config) -> Result<()> {
// create single threaded tokio runtime
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_io()
.enable_time()
@@ -34,37 +59,33 @@ pub fn run() -> Result<(), Box<dyn Error>> {
// run async event loop
runtime.block_on(LocalSet::new().run_until(async {
// start producing and consuming events
let producer = producer::create()?;
let consumer = consumer::create().await?;
// create frontend communication adapter
let frontend_adapter = FrontendListener::new().await?;
// start frontend
match config.frontend {
#[cfg(all(unix, feature = "gtk"))]
Gtk => { gtk::start()?; }
#[cfg(any(not(feature = "gtk"), not(unix)))]
Gtk => panic!("gtk frontend requested but feature not enabled!"),
Cli => { cli::start()?; }
let frontend_adapter = match FrontendListener::new().await {
Some(Err(e)) => return Err(e),
Some(Ok(f)) => f,
None => {
// none means some other instance is already running
log::info!("service already running, exiting");
return anyhow::Ok(())
}
,
};
// start sending and receiving events
let mut event_server = Server::new(config.port, frontend_adapter, consumer, producer).await?;
log::debug!("created server");
// add clients from config
for (c,h,port,p) in config.get_clients().into_iter() {
event_server.add_client(h, c, port, p).await;
}
// create event producer and consumer
let (producer, consumer) = join!(
producer::create(),
consumer::create(),
);
let (producer, consumer) = (producer?, consumer?);
// create server
let mut event_server = Server::new(config, frontend_adapter, consumer, producer).await?;
log::info!("Press Ctrl+Alt+Shift+Super to release the mouse");
// run event loop
event_server.run().await?;
Result::<_, Box<dyn Error>>::Ok(())
log::debug!("service exiting");
anyhow::Ok(())
}))?;
log::debug!("exiting main");
Ok(())
}