Compare commits

...

1 Commits

Author SHA1 Message Date
Ferdinand Schober
3ee49753c0 simplify configuration 2025-03-15 18:22:31 +01:00
10 changed files with 226 additions and 245 deletions

1
Cargo.lock generated
View File

@@ -2023,6 +2023,7 @@ 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, Debug, PartialEq, Eq)] #[derive(Parser, Clone, 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(Subcommand, Debug, PartialEq, Eq)] #[derive(Clone, Subcommand, Debug, PartialEq, Eq)]
enum CliSubcommand { enum CliSubcommand {
/// add a new client /// add a new client
AddClient(Client), AddClient(Client),

View File

@@ -13,6 +13,7 @@ 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,7 +18,15 @@ 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;
pub fn run() -> glib::ExitCode { use thiserror::Error;
#[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()
@@ -31,13 +39,10 @@ pub fn run() -> glib::ExitCode {
#[cfg(not(windows))] #[cfg(not(windows))]
let ret = gtk_main(); let ret = gtk_main();
if ret == glib::ExitCode::FAILURE { match ret {
log::error!("frontend exited with failure"); glib::ExitCode::SUCCESS => Ok(()),
} else { e => Err(GtkError::NonZeroExitCode(e.value())),
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::info!("request-hostname-change"); log::debug!("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::info!( log::debug!(
"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, Debug, Eq, PartialEq)] #[derive(Args, Clone, 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,33 +24,50 @@ use shadow_rs::shadow;
shadow!(build); shadow!(build);
#[derive(Serialize, Deserialize, Debug)] const CONFIG_FILE_NAME: &str = "config.toml";
pub struct ConfigToml { const CERT_FILE_NAME: &str = "lan-mouse.pem";
pub capture_backend: Option<CaptureBackend>,
pub emulation_backend: Option<EmulationBackend>, fn default_path() -> Result<PathBuf, VarError> {
pub port: Option<u16>, #[cfg(unix)]
pub frontend: Option<Frontend>, let default_path = {
pub release_bind: Option<Vec<scancode::Linux>>, let xdg_config_home =
pub cert_path: Option<PathBuf>, env::var("XDG_CONFIG_HOME").unwrap_or(format!("{}/.config", env::var("HOME")?));
pub left: Option<TomlClient>, format!("{xdg_config_home}/lan-mouse/")
pub right: Option<TomlClient>, };
pub top: Option<TomlClient>,
pub bottom: Option<TomlClient>, #[cfg(not(unix))]
pub authorized_fingerprints: Option<HashMap<String, String>>, 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, Eq, PartialEq)] #[derive(Serialize, Deserialize, Debug)]
pub struct TomlClient { struct ConfigToml {
pub hostname: Option<String>, capture_backend: Option<CaptureBackend>,
pub host_name: Option<String>, emulation_backend: Option<EmulationBackend>,
pub ips: Option<Vec<IpAddr>>, port: Option<u16>,
pub port: Option<u16>, release_bind: Option<Vec<scancode::Linux>>,
pub activate_on_startup: Option<bool>, cert_path: Option<PathBuf>,
pub enter_hook: Option<String>, clients: Vec<TomlClient>,
authorized_fingerprints: Option<HashMap<String, String>>,
}
#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
struct TomlClient {
hostname: Option<String>,
host_name: Option<String>,
ips: Option<Vec<IpAddr>>,
port: Option<u16>,
pos: Option<Position>,
activate_on_startup: Option<bool>,
enter_hook: Option<String>,
} }
impl ConfigToml { impl ConfigToml {
pub fn new(path: &Path) -> Result<ConfigToml, ConfigError> { 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)?)
} }
@@ -58,36 +75,33 @@ 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)]
pub struct Args { 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)]
pub config: Option<PathBuf>, config: Option<PathBuf>,
#[command(subcommand)]
pub command: Option<Command>,
/// capture backend override /// capture backend override
#[arg(long)] #[arg(long)]
pub capture_backend: Option<CaptureBackend>, capture_backend: Option<CaptureBackend>,
/// emulation backend override /// emulation backend override
#[arg(long)] #[arg(long)]
pub emulation_backend: Option<EmulationBackend>, emulation_backend: Option<EmulationBackend>,
/// path to non-default certificate location /// path to non-default certificate location
#[arg(long)] #[arg(long)]
pub cert_path: Option<PathBuf>, cert_path: Option<PathBuf>,
/// subcommands
#[command(subcommand)]
command: Option<Command>,
} }
#[derive(Subcommand, Debug, Eq, PartialEq)] #[derive(Subcommand, Clone, Debug, Eq, PartialEq)]
pub enum Command { pub enum Command {
/// test input emulation /// test input emulation
TestEmulation(TestEmulationArgs), TestEmulation(TestEmulationArgs),
@@ -220,48 +234,16 @@ 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 {
/// the path to the configuration file used /// command line arguments
pub path: PathBuf, args: Args,
/// public key fingerprints authorized for connection /// path to the certificate file used
pub authorized_fingerprints: HashMap<String, String>, cert_path: PathBuf,
/// optional input-capture backend override /// path to the config file used
pub capture_backend: Option<CaptureBackend>, config_path: PathBuf,
/// optional input-emulation backend override /// the (optional) toml config and it's path
pub emulation_backend: Option<EmulationBackend>, config_toml: Option<ConfigToml>,
/// 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 {
@@ -273,6 +255,25 @@ 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)]
@@ -287,133 +288,99 @@ const DEFAULT_RELEASE_KEYS: [scancode::Linux; 4] =
[KeyLeftCtrl, KeyLeftShift, KeyLeftMeta, KeyLeftAlt]; [KeyLeftCtrl, KeyLeftShift, KeyLeftMeta, KeyLeftAlt];
impl Config { impl Config {
pub fn new(args: &Args) -> Result<Self, ConfigError> { pub fn new() -> Result<Self, ConfigError> {
const CONFIG_FILE_NAME: &str = "config.toml"; let args = Args::parse();
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_file = args.config.clone().unwrap_or(config_file); let config_path = args
.config
.clone()
.unwrap_or(default_path()?.join(CONFIG_FILE_NAME));
let mut config_toml = match ConfigToml::new(&config_file) { let config_toml = match ConfigToml::new(&config_path) {
Err(e) => { Err(e) => {
log::warn!("{config_file:?}: {e}"); log::warn!("{config_path:?}: {e}");
log::warn!("Continuing without config file ..."); log::warn!("Continuing without config file ...");
None None
} }
Ok(c) => Some(c), Ok(c) => Some(c),
}; };
let frontend_arg = args.frontend; // --cert-path <file> overrules default location
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(config_path.join(CERT_FILE_NAME)); .unwrap_or(default_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 {
path: config_path, args,
authorized_fingerprints,
capture_backend,
emulation_backend,
frontend,
clients,
port,
release_bind,
test_capture,
test_emulation,
cert_path, cert_path,
config_path,
config_toml,
}) })
} }
pub fn get_clients(&self) -> Vec<ConfigClient> { /// the command to run
self.clients pub fn command(&self) -> Option<Command> {
.iter() self.args.command.clone()
.map(|(c, pos)| { }
let port = c.port.unwrap_or(DEFAULT_PORT);
let ips: HashSet<IpAddr> = if let Some(ips) = c.ips.as_ref() { pub fn config_path(&self) -> &Path {
HashSet::from_iter(ips.iter().cloned()) &self.config_path
} else { }
HashSet::new()
}; /// public key fingerprints authorized for connection
let hostname = match &c.hostname { pub fn authorized_fingerprints(&self) -> HashMap<String, String> {
Some(h) => Some(h.clone()), self.config_toml
None => c.host_name.clone(), .as_ref()
}; .and_then(|c| c.authorized_fingerprints.clone())
let active = c.activate_on_startup.unwrap_or(false); .unwrap_or_default()
let enter_hook = c.enter_hook.clone(); }
ConfigClient {
ips, /// path to certificate
hostname, pub fn cert_path(&self) -> &Path {
port, &self.cert_path
pos: *pos, }
active,
enter_hook, /// optional input-capture backend override
} 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, Debug, Eq, PartialEq)] #[derive(Args, Clone, 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,19 +1,20 @@
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, Config, ConfigError, Frontend}, config::{self, Command, Config, ConfigError},
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, Command}, process::{self, Child},
}; };
use thiserror::Error; use thiserror::Error;
use tokio::task::LocalSet; use tokio::task::LocalSet;
@@ -32,6 +33,9 @@ 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),
} }
@@ -48,15 +52,13 @@ fn main() {
} }
fn run() -> Result<(), LanMouseError> { fn run() -> Result<(), LanMouseError> {
// parse config file + cli args let config = config::Config::new()?;
let args = config::Args::parse(); match config.command() {
let config = config::Config::new(&args)?;
match args.command {
Some(command) => match command { Some(command) => match command {
config::Command::TestEmulation(args) => run_async(emulation_test::run(config, args))?, Command::TestEmulation(args) => run_async(emulation_test::run(config, args))?,
config::Command::TestCapture(args) => run_async(capture_test::run(config, args))?, Command::TestCapture(args) => run_async(capture_test::run(config, args))?,
config::Command::Cli(cli_args) => run_async(lan_mouse_cli::run(cli_args))?, Command::Cli(cli_args) => run_async(lan_mouse_cli::run(cli_args))?,
config::Command::Daemon => { 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(
@@ -69,18 +71,32 @@ 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
let mut service = start_service()?; #[cfg(feature = "gtk")]
run_frontend(&config)?;
#[cfg(unix)]
{ {
// on unix we give the service a chance to terminate gracefully let mut service = start_service()?;
let pid = service.id() as libc::pid_t; let res = lan_mouse_gtk::run();
unsafe { #[cfg(unix)]
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()?;
} }
} }
@@ -103,7 +119,7 @@ where
} }
fn start_service() -> Result<Child, io::Error> { fn start_service() -> Result<Child, io::Error> {
let child = Command::new(std::env::current_exe()?) let child = process::Command::new(std::env::current_exe()?)
.args(std::env::args().skip(1)) .args(std::env::args().skip(1))
.arg("daemon") .arg("daemon")
.spawn()?; .spawn()?;
@@ -111,25 +127,12 @@ fn start_service() -> Result<Child, io::Error> {
} }
async fn run_service(config: Config) -> Result<(), ServiceError> { async fn run_service(config: Config) -> Result<(), ServiceError> {
log::info!("using config: {:?}", config.path); let release_bind = config.release_bind();
log::info!("Press {:?} to release the mouse", config.release_bind); let config_path = config.config_path().to_owned();
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.get_clients() { for client in config.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.clone())); let authorized_keys = Arc::new(RwLock::new(config.authorized_fingerprints()));
// 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.clone()); let capture = Capture::new(capture_backend, conn, config.release_bind());
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,11 +142,15 @@ impl Service {
} }
pub async fn run(&mut self) -> Result<(), ServiceError> { pub async fn run(&mut self) -> Result<(), ServiceError> {
for handle in self.client_manager.active_clients() { let active = 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);
} }