remove cli frontend in favour of cli subcommand (#278)

this removes the cli frontend entirely, replacing it with a subcommand instead
This commit is contained in:
Ferdinand Schober
2025-03-15 18:20:25 +01:00
committed by GitHub
parent 7898f2362c
commit 2f6a3629ad
15 changed files with 269 additions and 497 deletions

View File

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

View File

@@ -199,6 +199,13 @@ impl ClientManager {
}
}
/// update the enter hook command of the client
pub(crate) fn set_enter_hook(&self, handle: ClientHandle, enter_hook: Option<String>) {
if let Some((c, _s)) = self.clients.borrow_mut().get_mut(handle as usize) {
c.cmd = enter_hook;
}
}
/// set resolving status of the client
pub(crate) fn set_resolving(&self, handle: ClientHandle, status: bool) {
if let Some((_, s)) = self.clients.borrow_mut().get_mut(handle as usize) {

View File

@@ -1,4 +1,6 @@
use clap::{Parser, ValueEnum};
use crate::capture_test::TestCaptureArgs;
use crate::emulation_test::TestEmulationArgs;
use clap::{Parser, Subcommand, ValueEnum};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::env::{self, VarError};
@@ -10,6 +12,7 @@ use std::{collections::HashSet, io};
use thiserror::Error;
use toml;
use lan_mouse_cli::CliArgs;
use lan_mouse_ipc::{Position, DEFAULT_PORT};
use input_event::scancode::{
@@ -55,7 +58,7 @@ impl ConfigToml {
#[derive(Parser, Debug)]
#[command(author, version=build::CLAP_LONG_VERSION, about, long_about = None)]
struct CliArgs {
pub struct Args {
/// the listen port for lan-mouse
#[arg(short, long)]
port: Option<u16>,
@@ -66,31 +69,34 @@ struct CliArgs {
/// non-default config file location
#[arg(short, long)]
config: Option<String>,
pub config: Option<PathBuf>,
/// run only the service as a daemon without the frontend
#[arg(short, long)]
daemon: bool,
/// test input capture
#[arg(long)]
test_capture: bool,
/// test input emulation
#[arg(long)]
test_emulation: bool,
#[command(subcommand)]
pub command: Option<Command>,
/// capture backend override
#[arg(long)]
capture_backend: Option<CaptureBackend>,
pub capture_backend: Option<CaptureBackend>,
/// emulation backend override
#[arg(long)]
emulation_backend: Option<EmulationBackend>,
pub emulation_backend: Option<EmulationBackend>,
/// path to non-default certificate location
#[arg(long)]
cert_path: Option<PathBuf>,
pub cert_path: Option<PathBuf>,
}
#[derive(Subcommand, Debug, Eq, PartialEq)]
pub enum Command {
/// test input emulation
TestEmulation(TestEmulationArgs),
/// test input capture
TestCapture(TestCaptureArgs),
/// Lan Mouse commandline interface
Cli(CliArgs),
/// run in daemon mode
Daemon,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, ValueEnum)]
@@ -218,8 +224,8 @@ impl Display for EmulationBackend {
pub enum Frontend {
#[serde(rename = "gtk")]
Gtk,
#[serde(rename = "cli")]
Cli,
#[serde(rename = "none")]
None,
}
impl Default for Frontend {
@@ -227,7 +233,7 @@ impl Default for Frontend {
if cfg!(feature = "gtk") {
Self::Gtk
} else {
Self::Cli
Self::None
}
}
}
@@ -248,8 +254,6 @@ pub struct Config {
pub port: u16,
/// list of clients
pub clients: Vec<(TomlClient, Position)>,
/// whether or not to run as a daemon
pub daemon: bool,
/// configured release bind
pub release_bind: Vec<scancode::Linux>,
/// test capture instead of running the app
@@ -283,8 +287,7 @@ const DEFAULT_RELEASE_KEYS: [scancode::Linux; 4] =
[KeyLeftCtrl, KeyLeftShift, KeyLeftMeta, KeyLeftAlt];
impl Config {
pub fn new() -> Result<Self, ConfigError> {
let args = CliArgs::parse();
pub fn new(args: &Args) -> Result<Self, ConfigError> {
const CONFIG_FILE_NAME: &str = "config.toml";
const CERT_FILE_NAME: &str = "lan-mouse.pem";
@@ -306,7 +309,7 @@ impl Config {
let config_file = config_path.join(CONFIG_FILE_NAME);
// --config <file> overrules default location
let config_file = args.config.map(PathBuf::from).unwrap_or(config_file);
let config_file = args.config.clone().unwrap_or(config_file);
let mut config_toml = match ConfigToml::new(&config_file) {
Err(e) => {
@@ -342,6 +345,7 @@ impl Config {
let cert_path = args
.cert_path
.clone()
.or(config_toml.as_ref().and_then(|c| c.cert_path.clone()))
.unwrap_or(config_path.join(CERT_FILE_NAME));
@@ -367,16 +371,14 @@ impl Config {
}
}
let daemon = args.daemon;
let test_capture = args.test_capture;
let test_emulation = args.test_emulation;
let test_capture = matches!(args.command, Some(Command::TestCapture(_)));
let test_emulation = matches!(args.command, Some(Command::TestEmulation(_)));
Ok(Config {
path: config_path,
authorized_fingerprints,
capture_backend,
emulation_backend,
daemon,
frontend,
clients,
port,

View File

@@ -1,4 +1,5 @@
use crate::config::Config;
use clap::Args;
use input_emulation::{InputEmulation, InputEmulationError};
use input_event::{Event, PointerEvent};
use std::f64::consts::PI;
@@ -7,7 +8,17 @@ use std::time::{Duration, Instant};
const FREQUENCY_HZ: f64 = 1.0;
const RADIUS: f64 = 100.0;
pub async fn run(config: Config) -> Result<(), InputEmulationError> {
#[derive(Args, Debug, Eq, PartialEq)]
pub struct TestEmulationArgs {
#[arg(long)]
mouse: bool,
#[arg(long)]
keyboard: bool,
#[arg(long)]
scroll: bool,
}
pub async fn run(config: Config, _args: TestEmulationArgs) -> Result<(), InputEmulationError> {
log::info!("running input emulation test");
let backend = config.emulation_backend.map(|b| b.into());

View File

@@ -1,12 +1,14 @@
use clap::Parser;
use env_logger::Env;
use input_capture::InputCaptureError;
use input_emulation::InputEmulationError;
use lan_mouse::{
capture_test,
config::{Config, ConfigError, Frontend},
config::{self, Config, ConfigError, Frontend},
emulation_test,
service::{Service, ServiceError},
};
use lan_mouse_cli::CliError;
use lan_mouse_ipc::{IpcError, IpcListenerCreationError};
use std::{
future::Future,
@@ -30,6 +32,8 @@ enum LanMouseError {
Capture(#[from] InputCaptureError),
#[error(transparent)]
Emulation(#[from] InputEmulationError),
#[error(transparent)]
Cli(#[from] CliError),
}
fn main() {
@@ -45,34 +49,39 @@ fn main() {
fn run() -> Result<(), LanMouseError> {
// parse config file + cli args
let config = Config::new()?;
if config.test_capture {
run_async(capture_test::run(config))?;
} else if config.test_emulation {
run_async(emulation_test::run(config))?;
} else if config.daemon {
// if daemon is specified we run the service
match run_async(run_service(config)) {
Err(LanMouseError::Service(ServiceError::IpcListen(
IpcListenerCreationError::AlreadyRunning,
))) => log::info!("service already running!"),
r => r?,
}
} else {
// otherwise start the service as a child process and
// run a frontend
let mut service = start_service()?;
run_frontend(&config)?;
#[cfg(unix)]
{
// 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);
let args = config::Args::parse();
let config = config::Config::new(&args)?;
match args.command {
Some(command) => match command {
config::Command::TestEmulation(args) => run_async(emulation_test::run(config, args))?,
config::Command::TestCapture(args) => run_async(capture_test::run(config, args))?,
config::Command::Cli(cli_args) => run_async(lan_mouse_cli::run(cli_args))?,
config::Command::Daemon => {
// if daemon is specified we run the service
match run_async(run_service(config)) {
Err(LanMouseError::Service(ServiceError::IpcListen(
IpcListenerCreationError::AlreadyRunning,
))) => log::info!("service already running!"),
r => r?,
}
}
service.wait()?;
},
None => {
// otherwise start the service as a child process and
// run a frontend
let mut service = start_service()?;
run_frontend(&config)?;
#[cfg(unix)]
{
// 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()?;
}
service.kill()?;
}
Ok(())
@@ -96,7 +105,7 @@ where
fn start_service() -> Result<Child, io::Error> {
let child = Command::new(std::env::current_exe()?)
.args(std::env::args().skip(1))
.arg("--daemon")
.arg("daemon")
.spawn()?;
Ok(child)
}
@@ -118,8 +127,8 @@ fn run_frontend(config: &Config) -> Result<(), IpcError> {
}
#[cfg(not(feature = "gtk"))]
Frontend::Gtk => panic!("gtk frontend requested but feature not enabled!"),
Frontend::Cli => {
lan_mouse_cli::run()?;
Frontend::None => {
log::warn!("no frontend available!");
}
};
Ok(())

View File

@@ -193,6 +193,9 @@ impl Service {
FrontendRequest::ResolveDns(handle) => self.resolve(handle),
FrontendRequest::Sync => self.sync_frontend(),
FrontendRequest::RemoveAuthorizedKey(key) => self.remove_authorized_key(key),
FrontendRequest::UpdateEnterHook(handle, enter_hook) => {
self.update_enter_hook(handle, enter_hook)
}
}
}
@@ -476,6 +479,11 @@ impl Service {
self.broadcast_client(handle);
}
fn update_enter_hook(&mut self, handle: ClientHandle, enter_hook: Option<String>) {
self.client_manager.set_enter_hook(handle, enter_hook);
self.broadcast_client(handle);
}
fn broadcast_client(&mut self, handle: ClientHandle) {
let event = self
.client_manager