Compare commits

..

5 Commits

Author SHA1 Message Date
Ferdinand Schober
d5ecc0a931 fix list command 2025-03-15 17:55:56 +01:00
Ferdinand Schober
58383646f8 better error handling 2025-03-15 16:52:13 +01:00
Ferdinand Schober
83c3319a26 update docs 2025-03-15 16:52:13 +01:00
Ferdinand Schober
5fb04176be add warning if there is no frontend available 2025-03-15 16:52:13 +01:00
Ferdinand Schober
d8e2c1ef02 rework cli frontend 2025-03-15 16:52:13 +01:00
10 changed files with 244 additions and 225 deletions

1
Cargo.lock generated
View File

@@ -2023,7 +2023,6 @@ dependencies = [
"lan-mouse-ipc", "lan-mouse-ipc",
"libadwaita", "libadwaita",
"log", "log",
"thiserror 2.0.0",
] ]
[[package]] [[package]]

View File

@@ -18,7 +18,7 @@ pub enum CliError {
Ipc(#[from] IpcError), Ipc(#[from] IpcError),
} }
#[derive(Parser, Clone, Debug, PartialEq, Eq)] #[derive(Parser, Debug, PartialEq, Eq)]
#[command(name = "lan-mouse-cli", about = "LanMouse CLI interface")] #[command(name = "lan-mouse-cli", about = "LanMouse CLI interface")]
pub struct CliArgs { pub struct CliArgs {
#[command(subcommand)] #[command(subcommand)]
@@ -37,7 +37,7 @@ struct Client {
enter_hook: Option<String>, enter_hook: Option<String>,
} }
#[derive(Clone, Subcommand, Debug, PartialEq, Eq)] #[derive(Subcommand, Debug, PartialEq, Eq)]
enum CliSubcommand { enum CliSubcommand {
/// add a new client /// add a new client
AddClient(Client), AddClient(Client),

View File

@@ -13,7 +13,6 @@ async-channel = { version = "2.1.1" }
hostname = "0.4.0" hostname = "0.4.0"
log = "0.4.20" log = "0.4.20"
lan-mouse-ipc = { path = "../lan-mouse-ipc", version = "0.2.0" } lan-mouse-ipc = { path = "../lan-mouse-ipc", version = "0.2.0" }
thiserror = "2.0.0"
[build-dependencies] [build-dependencies]
glib-build-tools = { version = "0.20.0" } glib-build-tools = { version = "0.20.0" }

View File

@@ -18,15 +18,7 @@ use gtk::{gio, glib, prelude::ApplicationExt};
use self::client_object::ClientObject; use self::client_object::ClientObject;
use self::key_object::KeyObject; use self::key_object::KeyObject;
use thiserror::Error; pub fn run() -> glib::ExitCode {
#[derive(Error, Debug)]
pub enum GtkError {
#[error("gtk frontend exited with non zero exit code: {0}")]
NonZeroExitCode(i32),
}
pub fn run() -> Result<(), GtkError> {
log::debug!("running gtk frontend"); log::debug!("running gtk frontend");
#[cfg(windows)] #[cfg(windows)]
let ret = std::thread::Builder::new() let ret = std::thread::Builder::new()
@@ -39,10 +31,13 @@ pub fn run() -> Result<(), GtkError> {
#[cfg(not(windows))] #[cfg(not(windows))]
let ret = gtk_main(); let ret = gtk_main();
match ret { if ret == glib::ExitCode::FAILURE {
glib::ExitCode::SUCCESS => Ok(()), log::error!("frontend exited with failure");
e => Err(GtkError::NonZeroExitCode(e.value())), } else {
log::info!("frontend exited successfully");
} }
ret
} }
fn gtk_main() -> glib::ExitCode { fn gtk_main() -> glib::ExitCode {

View File

@@ -126,7 +126,7 @@ impl Window {
#[strong] #[strong]
window, window,
move |row: ClientRow, hostname: String| { move |row: ClientRow, hostname: String| {
log::debug!("request-hostname-change"); log::info!("request-hostname-change");
if let Some(client) = window.client_by_idx(row.index() as u32) { if let Some(client) = window.client_by_idx(row.index() as u32) {
let hostname = Some(hostname).filter(|s| !s.is_empty()); let hostname = Some(hostname).filter(|s| !s.is_empty());
/* changed in response to FrontendEvent /* changed in response to FrontendEvent
@@ -163,7 +163,7 @@ impl Window {
window, window,
move |row: ClientRow, active: bool| { move |row: ClientRow, active: bool| {
if let Some(client) = window.client_by_idx(row.index() as u32) { if let Some(client) = window.client_by_idx(row.index() as u32) {
log::debug!( log::info!(
"request: {} client", "request: {} client",
if active { "activating" } else { "deactivating" } if active { "activating" } else { "deactivating" }
); );

View File

@@ -4,13 +4,13 @@ use futures::StreamExt;
use input_capture::{self, CaptureError, CaptureEvent, InputCapture, InputCaptureError, Position}; use input_capture::{self, CaptureError, CaptureEvent, InputCapture, InputCaptureError, Position};
use input_event::{Event, KeyboardEvent}; use input_event::{Event, KeyboardEvent};
#[derive(Args, Clone, Debug, Eq, PartialEq)] #[derive(Args, Debug, Eq, PartialEq)]
pub struct TestCaptureArgs {} pub struct TestCaptureArgs {}
pub async fn run(config: Config, _args: TestCaptureArgs) -> Result<(), InputCaptureError> { pub async fn run(config: Config, _args: TestCaptureArgs) -> Result<(), InputCaptureError> {
log::info!("running input capture test"); log::info!("running input capture test");
log::info!("creating input capture"); log::info!("creating input capture");
let backend = config.capture_backend().map(|b| b.into()); let backend = config.capture_backend.map(|b| b.into());
loop { loop {
let mut input_capture = InputCapture::new(backend).await?; let mut input_capture = InputCapture::new(backend).await?;
log::info!("creating clients"); log::info!("creating clients");

View File

@@ -24,50 +24,33 @@ use shadow_rs::shadow;
shadow!(build); shadow!(build);
const CONFIG_FILE_NAME: &str = "config.toml";
const CERT_FILE_NAME: &str = "lan-mouse.pem";
fn default_path() -> Result<PathBuf, VarError> {
#[cfg(unix)]
let default_path = {
let xdg_config_home =
env::var("XDG_CONFIG_HOME").unwrap_or(format!("{}/.config", env::var("HOME")?));
format!("{xdg_config_home}/lan-mouse/")
};
#[cfg(not(unix))]
let default_path = {
let app_data =
env::var("LOCALAPPDATA").unwrap_or(format!("{}/.config", env::var("USERPROFILE")?));
format!("{app_data}\\lan-mouse\\")
};
Ok(PathBuf::from(default_path))
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
struct ConfigToml { pub struct ConfigToml {
capture_backend: Option<CaptureBackend>, pub capture_backend: Option<CaptureBackend>,
emulation_backend: Option<EmulationBackend>, pub emulation_backend: Option<EmulationBackend>,
port: Option<u16>, pub port: Option<u16>,
release_bind: Option<Vec<scancode::Linux>>, pub frontend: Option<Frontend>,
cert_path: Option<PathBuf>, pub release_bind: Option<Vec<scancode::Linux>>,
clients: Vec<TomlClient>, pub cert_path: Option<PathBuf>,
authorized_fingerprints: Option<HashMap<String, String>>, pub left: Option<TomlClient>,
pub right: Option<TomlClient>,
pub top: Option<TomlClient>,
pub bottom: Option<TomlClient>,
pub authorized_fingerprints: Option<HashMap<String, String>>,
} }
#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)] #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct TomlClient { pub struct TomlClient {
hostname: Option<String>, pub hostname: Option<String>,
host_name: Option<String>, pub host_name: Option<String>,
ips: Option<Vec<IpAddr>>, pub ips: Option<Vec<IpAddr>>,
port: Option<u16>, pub port: Option<u16>,
pos: Option<Position>, pub activate_on_startup: Option<bool>,
activate_on_startup: Option<bool>, pub enter_hook: Option<String>,
enter_hook: Option<String>,
} }
impl ConfigToml { impl ConfigToml {
fn new(path: &Path) -> Result<ConfigToml, ConfigError> { pub fn new(path: &Path) -> Result<ConfigToml, ConfigError> {
let config = fs::read_to_string(path)?; let config = fs::read_to_string(path)?;
Ok(toml::from_str::<_>(&config)?) Ok(toml::from_str::<_>(&config)?)
} }
@@ -75,33 +58,36 @@ impl ConfigToml {
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version=build::CLAP_LONG_VERSION, about, long_about = None)] #[command(author, version=build::CLAP_LONG_VERSION, about, long_about = None)]
struct Args { pub struct Args {
/// the listen port for lan-mouse /// the listen port for lan-mouse
#[arg(short, long)] #[arg(short, long)]
port: Option<u16>, port: Option<u16>,
/// the frontend to use [cli | gtk]
#[arg(short, long)]
frontend: Option<Frontend>,
/// non-default config file location /// non-default config file location
#[arg(short, long)] #[arg(short, long)]
config: Option<PathBuf>, pub config: Option<PathBuf>,
#[command(subcommand)]
pub command: Option<Command>,
/// capture backend override /// capture backend override
#[arg(long)] #[arg(long)]
capture_backend: Option<CaptureBackend>, pub capture_backend: Option<CaptureBackend>,
/// emulation backend override /// emulation backend override
#[arg(long)] #[arg(long)]
emulation_backend: Option<EmulationBackend>, pub emulation_backend: Option<EmulationBackend>,
/// path to non-default certificate location /// path to non-default certificate location
#[arg(long)] #[arg(long)]
cert_path: Option<PathBuf>, pub cert_path: Option<PathBuf>,
/// subcommands
#[command(subcommand)]
command: Option<Command>,
} }
#[derive(Subcommand, Clone, Debug, Eq, PartialEq)] #[derive(Subcommand, Debug, Eq, PartialEq)]
pub enum Command { pub enum Command {
/// test input emulation /// test input emulation
TestEmulation(TestEmulationArgs), TestEmulation(TestEmulationArgs),
@@ -234,16 +220,48 @@ impl Display for EmulationBackend {
} }
} }
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize, ValueEnum)]
pub enum Frontend {
#[serde(rename = "gtk")]
Gtk,
#[serde(rename = "none")]
None,
}
impl Default for Frontend {
fn default() -> Self {
if cfg!(feature = "gtk") {
Self::Gtk
} else {
Self::None
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Config { pub struct Config {
/// command line arguments /// the path to the configuration file used
args: Args, pub path: PathBuf,
/// path to the certificate file used /// public key fingerprints authorized for connection
cert_path: PathBuf, pub authorized_fingerprints: HashMap<String, String>,
/// path to the config file used /// optional input-capture backend override
config_path: PathBuf, pub capture_backend: Option<CaptureBackend>,
/// the (optional) toml config and it's path /// optional input-emulation backend override
config_toml: Option<ConfigToml>, pub emulation_backend: Option<EmulationBackend>,
/// the frontend to use
pub frontend: Frontend,
/// the port to use (initially)
pub port: u16,
/// list of clients
pub clients: Vec<(TomlClient, Position)>,
/// configured release bind
pub release_bind: Vec<scancode::Linux>,
/// test capture instead of running the app
pub test_capture: bool,
/// test emulation instead of running the app
pub test_emulation: bool,
/// path to the tls certificate to use
pub cert_path: PathBuf,
} }
pub struct ConfigClient { pub struct ConfigClient {
@@ -255,25 +273,6 @@ pub struct ConfigClient {
pub enter_hook: Option<String>, pub enter_hook: Option<String>,
} }
impl From<TomlClient> for ConfigClient {
fn from(toml: TomlClient) -> Self {
let active = toml.activate_on_startup.unwrap_or(false);
let enter_hook = toml.enter_hook;
let hostname = toml.hostname;
let ips = HashSet::from_iter(toml.ips.into_iter().flatten());
let port = toml.port.unwrap_or(DEFAULT_PORT);
let pos = toml.pos.unwrap_or_default();
Self {
ips,
hostname,
port,
pos,
active,
enter_hook,
}
}
}
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum ConfigError { pub enum ConfigError {
#[error(transparent)] #[error(transparent)]
@@ -288,99 +287,133 @@ const DEFAULT_RELEASE_KEYS: [scancode::Linux; 4] =
[KeyLeftCtrl, KeyLeftShift, KeyLeftMeta, KeyLeftAlt]; [KeyLeftCtrl, KeyLeftShift, KeyLeftMeta, KeyLeftAlt];
impl Config { impl Config {
pub fn new() -> Result<Self, ConfigError> { pub fn new(args: &Args) -> Result<Self, ConfigError> {
let args = Args::parse(); const CONFIG_FILE_NAME: &str = "config.toml";
const CERT_FILE_NAME: &str = "lan-mouse.pem";
#[cfg(unix)]
let config_path = {
let xdg_config_home =
env::var("XDG_CONFIG_HOME").unwrap_or(format!("{}/.config", env::var("HOME")?));
format!("{xdg_config_home}/lan-mouse/")
};
#[cfg(not(unix))]
let config_path = {
let app_data =
env::var("LOCALAPPDATA").unwrap_or(format!("{}/.config", env::var("USERPROFILE")?));
format!("{app_data}\\lan-mouse\\")
};
let config_path = PathBuf::from(config_path);
let config_file = config_path.join(CONFIG_FILE_NAME);
// --config <file> overrules default location // --config <file> overrules default location
let config_path = args let config_file = args.config.clone().unwrap_or(config_file);
.config
.clone()
.unwrap_or(default_path()?.join(CONFIG_FILE_NAME));
let config_toml = match ConfigToml::new(&config_path) { let mut config_toml = match ConfigToml::new(&config_file) {
Err(e) => { Err(e) => {
log::warn!("{config_path:?}: {e}"); log::warn!("{config_file:?}: {e}");
log::warn!("Continuing without config file ..."); log::warn!("Continuing without config file ...");
None None
} }
Ok(c) => Some(c), Ok(c) => Some(c),
}; };
// --cert-path <file> overrules default location let frontend_arg = args.frontend;
let frontend_cfg = config_toml.as_ref().and_then(|c| c.frontend);
let frontend = frontend_arg.or(frontend_cfg).unwrap_or_default();
let port = args
.port
.or(config_toml.as_ref().and_then(|c| c.port))
.unwrap_or(DEFAULT_PORT);
log::debug!("{config_toml:?}");
let release_bind = config_toml
.as_ref()
.and_then(|c| c.release_bind.clone())
.unwrap_or(Vec::from_iter(DEFAULT_RELEASE_KEYS.iter().cloned()));
let capture_backend = args
.capture_backend
.or(config_toml.as_ref().and_then(|c| c.capture_backend));
let emulation_backend = args
.emulation_backend
.or(config_toml.as_ref().and_then(|c| c.emulation_backend));
let cert_path = args let cert_path = args
.cert_path .cert_path
.clone() .clone()
.or(config_toml.as_ref().and_then(|c| c.cert_path.clone())) .or(config_toml.as_ref().and_then(|c| c.cert_path.clone()))
.unwrap_or(default_path()?.join(CERT_FILE_NAME)); .unwrap_or(config_path.join(CERT_FILE_NAME));
let authorized_fingerprints = config_toml
.as_mut()
.and_then(|c| std::mem::take(&mut c.authorized_fingerprints))
.unwrap_or_default();
let mut clients: Vec<(TomlClient, Position)> = vec![];
if let Some(config_toml) = config_toml {
if let Some(c) = config_toml.right {
clients.push((c, Position::Right))
}
if let Some(c) = config_toml.left {
clients.push((c, Position::Left))
}
if let Some(c) = config_toml.top {
clients.push((c, Position::Top))
}
if let Some(c) = config_toml.bottom {
clients.push((c, Position::Bottom))
}
}
let test_capture = matches!(args.command, Some(Command::TestCapture(_)));
let test_emulation = matches!(args.command, Some(Command::TestEmulation(_)));
Ok(Config { Ok(Config {
args, path: config_path,
authorized_fingerprints,
capture_backend,
emulation_backend,
frontend,
clients,
port,
release_bind,
test_capture,
test_emulation,
cert_path, cert_path,
config_path,
config_toml,
}) })
} }
/// the command to run pub fn get_clients(&self) -> Vec<ConfigClient> {
pub fn command(&self) -> Option<Command> { self.clients
self.args.command.clone() .iter()
} .map(|(c, pos)| {
let port = c.port.unwrap_or(DEFAULT_PORT);
pub fn config_path(&self) -> &Path { let ips: HashSet<IpAddr> = if let Some(ips) = c.ips.as_ref() {
&self.config_path HashSet::from_iter(ips.iter().cloned())
} } else {
HashSet::new()
/// public key fingerprints authorized for connection };
pub fn authorized_fingerprints(&self) -> HashMap<String, String> { let hostname = match &c.hostname {
self.config_toml Some(h) => Some(h.clone()),
.as_ref() None => c.host_name.clone(),
.and_then(|c| c.authorized_fingerprints.clone()) };
.unwrap_or_default() let active = c.activate_on_startup.unwrap_or(false);
} let enter_hook = c.enter_hook.clone();
ConfigClient {
/// path to certificate ips,
pub fn cert_path(&self) -> &Path { hostname,
&self.cert_path port,
} pos: *pos,
active,
/// optional input-capture backend override enter_hook,
pub fn capture_backend(&self) -> Option<CaptureBackend> { }
self.args })
.capture_backend
.or(self.config_toml.as_ref().and_then(|c| c.capture_backend))
}
/// optional input-emulation backend override
pub fn emulation_backend(&self) -> Option<EmulationBackend> {
self.args
.emulation_backend
.or(self.config_toml.as_ref().and_then(|c| c.emulation_backend))
}
/// the port to use (initially)
pub fn port(&self) -> u16 {
self.args
.port
.or(self.config_toml.as_ref().and_then(|c| c.port))
.unwrap_or(DEFAULT_PORT)
}
/// list of configured clients
pub fn clients(&self) -> Vec<ConfigClient> {
self.config_toml
.as_ref()
.map(|c| c.clients.clone())
.into_iter()
.flatten()
.map(From::<TomlClient>::from)
.collect() .collect()
} }
/// release bind for returning control to the host
pub fn release_bind(&self) -> Vec<scancode::Linux> {
self.config_toml
.as_ref()
.and_then(|c| c.release_bind.clone())
.unwrap_or(Vec::from_iter(DEFAULT_RELEASE_KEYS.iter().cloned()))
}
} }

View File

@@ -8,7 +8,7 @@ use std::time::{Duration, Instant};
const FREQUENCY_HZ: f64 = 1.0; const FREQUENCY_HZ: f64 = 1.0;
const RADIUS: f64 = 100.0; const RADIUS: f64 = 100.0;
#[derive(Args, Clone, Debug, Eq, PartialEq)] #[derive(Args, Debug, Eq, PartialEq)]
pub struct TestEmulationArgs { pub struct TestEmulationArgs {
#[arg(long)] #[arg(long)]
mouse: bool, mouse: bool,
@@ -21,7 +21,7 @@ pub struct TestEmulationArgs {
pub async fn run(config: Config, _args: TestEmulationArgs) -> Result<(), InputEmulationError> { pub async fn run(config: Config, _args: TestEmulationArgs) -> Result<(), InputEmulationError> {
log::info!("running input emulation test"); log::info!("running input emulation test");
let backend = config.emulation_backend().map(|b| b.into()); let backend = config.emulation_backend.map(|b| b.into());
let mut emulation = InputEmulation::new(backend).await?; let mut emulation = InputEmulation::new(backend).await?;
emulation.create(0).await; emulation.create(0).await;

View File

@@ -1,20 +1,19 @@
use clap::Parser;
use env_logger::Env; use env_logger::Env;
use input_capture::InputCaptureError; use input_capture::InputCaptureError;
use input_emulation::InputEmulationError; use input_emulation::InputEmulationError;
use lan_mouse::{ use lan_mouse::{
capture_test, capture_test,
config::{self, Command, Config, ConfigError}, config::{self, Config, ConfigError, Frontend},
emulation_test, emulation_test,
service::{Service, ServiceError}, service::{Service, ServiceError},
}; };
use lan_mouse_cli::CliError; use lan_mouse_cli::CliError;
#[cfg(feature = "gtk")]
use lan_mouse_gtk::GtkError;
use lan_mouse_ipc::{IpcError, IpcListenerCreationError}; use lan_mouse_ipc::{IpcError, IpcListenerCreationError};
use std::{ use std::{
future::Future, future::Future,
io, io,
process::{self, Child}, process::{self, Child, Command},
}; };
use thiserror::Error; use thiserror::Error;
use tokio::task::LocalSet; use tokio::task::LocalSet;
@@ -33,9 +32,6 @@ enum LanMouseError {
Capture(#[from] InputCaptureError), Capture(#[from] InputCaptureError),
#[error(transparent)] #[error(transparent)]
Emulation(#[from] InputEmulationError), Emulation(#[from] InputEmulationError),
#[cfg(feature = "gtk")]
#[error(transparent)]
Gtk(#[from] GtkError),
#[error(transparent)] #[error(transparent)]
Cli(#[from] CliError), Cli(#[from] CliError),
} }
@@ -52,13 +48,15 @@ fn main() {
} }
fn run() -> Result<(), LanMouseError> { fn run() -> Result<(), LanMouseError> {
let config = config::Config::new()?; // parse config file + cli args
match config.command() { let args = config::Args::parse();
let config = config::Config::new(&args)?;
match args.command {
Some(command) => match command { Some(command) => match command {
Command::TestEmulation(args) => run_async(emulation_test::run(config, args))?, config::Command::TestEmulation(args) => run_async(emulation_test::run(config, args))?,
Command::TestCapture(args) => run_async(capture_test::run(config, args))?, config::Command::TestCapture(args) => run_async(capture_test::run(config, args))?,
Command::Cli(cli_args) => run_async(lan_mouse_cli::run(cli_args))?, config::Command::Cli(cli_args) => run_async(lan_mouse_cli::run(cli_args))?,
Command::Daemon => { config::Command::Daemon => {
// if daemon is specified we run the service // if daemon is specified we run the service
match run_async(run_service(config)) { match run_async(run_service(config)) {
Err(LanMouseError::Service(ServiceError::IpcListen( Err(LanMouseError::Service(ServiceError::IpcListen(
@@ -71,32 +69,18 @@ fn run() -> Result<(), LanMouseError> {
None => { None => {
// otherwise start the service as a child process and // otherwise start the service as a child process and
// run a frontend // run a frontend
#[cfg(feature = "gtk")] let mut service = start_service()?;
run_frontend(&config)?;
#[cfg(unix)]
{ {
let mut service = start_service()?; // on unix we give the service a chance to terminate gracefully
let res = lan_mouse_gtk::run(); let pid = service.id() as libc::pid_t;
#[cfg(unix)] unsafe {
{ libc::kill(pid, libc::SIGINT);
// on unix we give the service a chance to terminate gracefully
let pid = service.id() as libc::pid_t;
unsafe {
libc::kill(pid, libc::SIGINT);
}
service.wait()?;
}
service.kill()?;
res?;
}
#[cfg(not(feature = "gtk"))]
{
// run daemon if gtk is diabled
match run_async(run_service(config)) {
Err(LanMouseError::Service(ServiceError::IpcListen(
IpcListenerCreationError::AlreadyRunning,
))) => log::info!("service already running!"),
r => r?,
} }
service.wait()?;
} }
service.kill()?;
} }
} }
@@ -119,7 +103,7 @@ where
} }
fn start_service() -> Result<Child, io::Error> { fn start_service() -> Result<Child, io::Error> {
let child = process::Command::new(std::env::current_exe()?) let child = Command::new(std::env::current_exe()?)
.args(std::env::args().skip(1)) .args(std::env::args().skip(1))
.arg("daemon") .arg("daemon")
.spawn()?; .spawn()?;
@@ -127,12 +111,25 @@ fn start_service() -> Result<Child, io::Error> {
} }
async fn run_service(config: Config) -> Result<(), ServiceError> { async fn run_service(config: Config) -> Result<(), ServiceError> {
let release_bind = config.release_bind(); log::info!("using config: {:?}", config.path);
let config_path = config.config_path().to_owned(); log::info!("Press {:?} to release the mouse", config.release_bind);
let mut service = Service::new(config).await?; let mut service = Service::new(config).await?;
log::info!("using config: {config_path:?}");
log::info!("Press {release_bind:?} to release the mouse");
service.run().await?; service.run().await?;
log::info!("service exited!"); log::info!("service exited!");
Ok(()) Ok(())
} }
fn run_frontend(config: &Config) -> Result<(), IpcError> {
match config.frontend {
#[cfg(feature = "gtk")]
Frontend::Gtk => {
lan_mouse_gtk::run();
}
#[cfg(not(feature = "gtk"))]
Frontend::Gtk => panic!("gtk frontend requested but feature not enabled!"),
Frontend::None => {
log::warn!("no frontend available!");
}
};
Ok(())
}

View File

@@ -80,7 +80,7 @@ struct Incoming {
impl Service { impl Service {
pub async fn new(config: Config) -> Result<Self, ServiceError> { pub async fn new(config: Config) -> Result<Self, ServiceError> {
let client_manager = ClientManager::default(); let client_manager = ClientManager::default();
for client in config.clients() { for client in config.get_clients() {
let config = ClientConfig { let config = ClientConfig {
hostname: client.hostname, hostname: client.hostname,
fix_ips: client.ips.into_iter().collect(), fix_ips: client.ips.into_iter().collect(),
@@ -99,28 +99,28 @@ impl Service {
} }
// load certificate // load certificate
let cert = crypto::load_or_generate_key_and_cert(config.cert_path())?; let cert = crypto::load_or_generate_key_and_cert(&config.cert_path)?;
let public_key_fingerprint = crypto::certificate_fingerprint(&cert); let public_key_fingerprint = crypto::certificate_fingerprint(&cert);
// create frontend communication adapter, exit if already running // create frontend communication adapter, exit if already running
let frontend_listener = AsyncFrontendListener::new().await?; let frontend_listener = AsyncFrontendListener::new().await?;
let authorized_keys = Arc::new(RwLock::new(config.authorized_fingerprints())); let authorized_keys = Arc::new(RwLock::new(config.authorized_fingerprints.clone()));
// listener + connection // listener + connection
let listener = let listener =
LanMouseListener::new(config.port(), cert.clone(), authorized_keys.clone()).await?; LanMouseListener::new(config.port, cert.clone(), authorized_keys.clone()).await?;
let conn = LanMouseConnection::new(cert.clone(), client_manager.clone()); let conn = LanMouseConnection::new(cert.clone(), client_manager.clone());
// input capture + emulation // input capture + emulation
let capture_backend = config.capture_backend().map(|b| b.into()); let capture_backend = config.capture_backend.map(|b| b.into());
let capture = Capture::new(capture_backend, conn, config.release_bind()); let capture = Capture::new(capture_backend, conn, config.release_bind.clone());
let emulation_backend = config.emulation_backend().map(|b| b.into()); let emulation_backend = config.emulation_backend.map(|b| b.into());
let emulation = Emulation::new(emulation_backend, listener); let emulation = Emulation::new(emulation_backend, listener);
// create dns resolver // create dns resolver
let resolver = DnsResolver::new()?; let resolver = DnsResolver::new()?;
let port = config.port(); let port = config.port;
let service = Self { let service = Self {
capture, capture,
emulation, emulation,
@@ -142,15 +142,11 @@ impl Service {
} }
pub async fn run(&mut self) -> Result<(), ServiceError> { pub async fn run(&mut self) -> Result<(), ServiceError> {
let active = self.client_manager.active_clients(); for handle in self.client_manager.active_clients() {
for handle in active.iter() {
// small hack: `activate_client()` checks, if the client // small hack: `activate_client()` checks, if the client
// is already active in client_manager and does not create a // is already active in client_manager and does not create a
// capture barrier in that case so we have to deactivate it first // capture barrier in that case so we have to deactivate it first
self.client_manager.deactivate_client(*handle); self.client_manager.deactivate_client(handle);
}
for handle in active {
self.activate_client(handle); self.activate_client(handle);
} }