Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[*.toml]
indent_style = space
indent_size = 4
8 changes: 7 additions & 1 deletion config.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
# example configuration

# configure release bind
release_bind = [ "KeyA", "KeyS", "KeyD", "KeyF" ]
release_bind = ["KeyA", "KeyS", "KeyD", "KeyF"]

# optional port (defaults to 4242)
port = 4242

[input]
# linear mouse acceleration
mouse_sensitivity = 1.0
# invert scrolling direction
invert_scroll = false

# list of authorized tls certificate fingerprints that
# are accepted for incoming traffic
[authorized_fingerprints]
Expand Down
2 changes: 1 addition & 1 deletion input-emulation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ tokio = { version = "1.32.0", features = [
"rt",
"sync",
"signal",
"time"
"time",
] }
once_cell = "1.19.0"

Expand Down
70 changes: 65 additions & 5 deletions input-emulation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{
fmt::Display,
};

use input_event::{Event, KeyboardEvent};
use input_event::{Event, KeyboardEvent, PointerEvent};

pub use self::error::{EmulationCreationError, EmulationError, InputEmulationError};

Expand Down Expand Up @@ -69,14 +69,33 @@ impl Display for Backend {
}
}

#[derive(Clone, Copy, Debug)]
pub struct InputConfig {
pub invert_scroll: bool,
pub mouse_sensitivity: f64,
}

impl InputConfig {
pub fn new(invert_scroll: bool, mouse_sensitivity: f64) -> Self {
InputConfig {
invert_scroll,
mouse_sensitivity,
}
}
}

pub struct InputEmulation {
emulation: Box<dyn Emulation>,
handles: HashSet<EmulationHandle>,
input_config: InputConfig,
pressed_keys: HashMap<EmulationHandle, HashSet<u32>>,
}

impl InputEmulation {
async fn with_backend(backend: Backend) -> Result<InputEmulation, EmulationCreationError> {
async fn with_backend(
backend: Backend,
input_config: InputConfig,
) -> Result<InputEmulation, EmulationCreationError> {
let emulation: Box<dyn Emulation> = match backend {
#[cfg(all(unix, feature = "wlroots", not(target_os = "macos")))]
Backend::Wlroots => Box::new(wlroots::WlrootsEmulation::new()?),
Expand All @@ -94,14 +113,18 @@ impl InputEmulation {
};
Ok(Self {
emulation,
input_config,
handles: HashSet::new(),
pressed_keys: HashMap::new(),
})
}

pub async fn new(backend: Option<Backend>) -> Result<InputEmulation, EmulationCreationError> {
pub async fn new(
backend: Option<Backend>,
input_config: InputConfig,
) -> Result<InputEmulation, EmulationCreationError> {
if let Some(backend) = backend {
let b = Self::with_backend(backend).await;
let b = Self::with_backend(backend, input_config).await;
if b.is_ok() {
log::info!("using emulation backend: {backend}");
}
Expand All @@ -123,7 +146,7 @@ impl InputEmulation {
Backend::MacOs,
Backend::Dummy,
] {
match Self::with_backend(backend).await {
match Self::with_backend(backend, input_config).await {
Ok(b) => {
log::info!("using emulation backend: {backend}");
return Ok(b);
Expand All @@ -149,6 +172,39 @@ impl InputEmulation {
}
Ok(())
}

Event::Pointer(PointerEvent::Motion { time, dx, dy }) => {
// apply mouse mod
let (dx, dy) = (
dx * self.input_config.mouse_sensitivity,
dy * self.input_config.mouse_sensitivity,
);
let event = Event::Pointer(PointerEvent::Motion { time, dx, dy });
self.emulation.consume(event, handle).await?;
Ok(())
}

Event::Pointer(PointerEvent::AxisDiscrete120 { axis, value }) => {
if self.input_config.invert_scroll {
let value = -value;
let event = Event::Pointer(PointerEvent::AxisDiscrete120 { axis, value });
self.emulation.consume(event, handle).await?;
} else {
self.emulation.consume(event, handle).await?;
}
Ok(())
}

Event::Pointer(PointerEvent::Axis { time, axis, value }) => {
if self.input_config.invert_scroll {
let value = -value;
let event = Event::Pointer(PointerEvent::Axis { time, axis, value });
self.emulation.consume(event, handle).await?;
} else {
self.emulation.consume(event, handle).await?;
}
Ok(())
}
_ => self.emulation.consume(event, handle).await,
}
}
Expand Down Expand Up @@ -210,6 +266,10 @@ impl InputEmulation {
.is_some_and(|p| !p.is_empty())
}

pub fn update_config(&mut self, input_config: InputConfig) {
self.input_config = input_config;
}

/// update the pressed_keys for the given handle
/// returns whether the event should be processed
fn update_pressed_keys(&mut self, handle: EmulationHandle, key: u32, state: u8) -> bool {
Expand Down
19 changes: 17 additions & 2 deletions lan-mouse-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub enum CliError {
Ipc(#[from] IpcError),
}

#[derive(Parser, Clone, Debug, PartialEq, Eq)]
#[derive(Parser, Clone, Debug)]
#[command(name = "lan-mouse-cli", about = "LanMouse CLI interface")]
pub struct CliArgs {
#[command(subcommand)]
Expand All @@ -37,7 +37,7 @@ struct Client {
enter_hook: Option<String>,
}

#[derive(Clone, Subcommand, Debug, PartialEq, Eq)]
#[derive(Clone, Subcommand, Debug)]
enum CliSubcommand {
/// add a new client
AddClient(Client),
Expand All @@ -56,6 +56,13 @@ enum CliSubcommand {
},
/// change port
SetPort { id: ClientHandle, port: u16 },
/// invert scrolling direction
InvertScrolling {
#[clap(short, long)]
invert_scroll: bool,
},
/// set mouse mouse sensitivity
SetMouseSensitivity { mouse_sensitivity: f64 },
/// set position
SetPosition { id: ClientHandle, pos: Position },
/// set ips
Expand Down Expand Up @@ -140,6 +147,14 @@ async fn execute(cmd: CliSubcommand) -> Result<(), CliError> {
CliSubcommand::SetPort { id, port } => {
tx.request(FrontendRequest::UpdatePort(id, port)).await?
}
CliSubcommand::InvertScrolling { invert_scroll } => {
tx.request(FrontendRequest::UpdateScrollingInversion(invert_scroll))
.await?
}
CliSubcommand::SetMouseSensitivity { mouse_sensitivity } => {
tx.request(FrontendRequest::UpdateMouseSensitivity(mouse_sensitivity))
.await?
}
CliSubcommand::SetPosition { id, pos } => {
tx.request(FrontendRequest::UpdatePosition(id, pos)).await?
}
Expand Down
5 changes: 4 additions & 1 deletion lan-mouse-ipc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ pub enum FrontendEvent {
ConnectionAttempt { fingerprint: String },
}

#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FrontendRequest {
/// activate/deactivate client
Activate(ClientHandle, bool),
Expand Down Expand Up @@ -253,6 +253,9 @@ pub enum FrontendRequest {
RemoveAuthorizedKey(String),
/// change the hook command
UpdateEnterHook(u64, Option<String>),
/// update the input post-processing settings (invert-scroll, mouse_sensitivity)
UpdateScrollingInversion(bool),
UpdateMouseSensitivity(f64),
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize)]
Expand Down
24 changes: 23 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ struct ConfigToml {
cert_path: Option<PathBuf>,
clients: Option<Vec<TomlClient>>,
authorized_fingerprints: Option<HashMap<String, String>>,
input_post_processing: InputConfig,
}

#[derive(Serialize, Deserialize, Debug)]
struct InputConfig {
// TODO: implement scroll_sensitivity and mouse_acceleration
invert_scroll: Option<bool>,
mouse_sensitivity: Option<f64>,
}

#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -101,7 +109,7 @@ struct Args {
command: Option<Command>,
}

#[derive(Subcommand, Clone, Debug, Eq, PartialEq)]
#[derive(Subcommand, Clone, Debug)]
pub enum Command {
/// test input emulation
TestEmulation(TestEmulationArgs),
Expand Down Expand Up @@ -365,6 +373,20 @@ impl Config {
.unwrap_or(DEFAULT_PORT)
}

pub fn invert_scroll(&self) -> bool {
self.config_toml
.as_ref()
.and_then(|c| c.input_post_processing.invert_scroll)
.unwrap_or(false)
}

pub fn mouse_sensitivity(&self) -> f64 {
self.config_toml
.as_ref()
.and_then(|c| c.input_post_processing.mouse_sensitivity)
.unwrap_or(1.0)
}

/// list of configured clients
pub fn clients(&self) -> Vec<ConfigClient> {
self.config_toml
Expand Down
33 changes: 29 additions & 4 deletions src/emulation.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::listen::{LanMouseListener, ListenEvent, ListenerCreationError};
use futures::StreamExt;
use input_emulation::{EmulationHandle, InputEmulation, InputEmulationError};
use input_emulation::{EmulationHandle, InputConfig, InputEmulation, InputEmulationError};
use input_event::Event;
use lan_mouse_proto::{Position, ProtoEvent};
use local_channel::mpsc::{Receiver, Sender, channel};
Expand Down Expand Up @@ -59,14 +59,21 @@ enum EmulationRequest {
Release(SocketAddr),
ChangePort(u16),
Terminate,
UpdateScrollingInversion(bool),
UpdateMouseSensitivity(f64),
}

impl Emulation {
pub(crate) fn new(
backend: Option<input_emulation::Backend>,
listener: LanMouseListener,
input_config: (bool, f64),
) -> Self {
let emulation_proxy = EmulationProxy::new(backend);
let input_config = InputConfig {
invert_scroll: input_config.0,
mouse_sensitivity: input_config.1,
};
let emulation_proxy = EmulationProxy::new(backend, input_config);
let (request_tx, request_rx) = channel();
let (event_tx, event_rx) = channel();
let emulation_task = ListenTask {
Expand Down Expand Up @@ -101,6 +108,18 @@ impl Emulation {
.expect("channel closed")
}

pub(crate) fn request_scrolling_inversion(&self, invert_scroll: bool) {
self.request_tx
.send(EmulationRequest::UpdateScrollingInversion(invert_scroll))
.expect("channel closed")
}

pub(crate) fn request_mouse_sensitivity_change(&self, mouse_sensitivity: f64) {
self.request_tx
.send(EmulationRequest::UpdateMouseSensitivity(mouse_sensitivity))
.expect("channel closed")
}

pub(crate) async fn event(&mut self) -> EmulationEvent {
self.event_rx.recv().await.expect("channel closed")
}
Expand Down Expand Up @@ -172,6 +191,8 @@ impl ListenTask {
EmulationRequest::Reenable => self.emulation_proxy.reenable(),
// notify the other end that we hit a barrier (should release capture)
EmulationRequest::Release(addr) => self.listener.reply(addr, ProtoEvent::Leave(0)).await,
EmulationRequest::UpdateScrollingInversion(invert_scroll) => self.emulation_proxy.input_config.invert_scroll = invert_scroll,
EmulationRequest::UpdateMouseSensitivity(mouse_sensitivity) => self.emulation_proxy.input_config.mouse_sensitivity = mouse_sensitivity,
EmulationRequest::ChangePort(port) => {
self.listener.request_port_change(port);
let result = self.listener.port_changed().await;
Expand Down Expand Up @@ -206,6 +227,7 @@ pub(crate) struct EmulationProxy {
request_tx: Sender<ProxyRequest>,
event_rx: Receiver<EmulationEvent>,
task: JoinHandle<()>,
input_config: InputConfig,
}

enum ProxyRequest {
Expand All @@ -216,7 +238,7 @@ enum ProxyRequest {
}

impl EmulationProxy {
fn new(backend: Option<input_emulation::Backend>) -> Self {
fn new(backend: Option<input_emulation::Backend>, input_config: InputConfig) -> Self {
let (request_tx, request_rx) = channel();
let (event_tx, event_rx) = channel();
let emulation_active = Rc::new(Cell::new(false));
Expand All @@ -228,6 +250,7 @@ impl EmulationProxy {
event_tx,
handles: Default::default(),
next_id: 0,
input_config,
};
let task = spawn_local(emulation_task.run());
Self {
Expand All @@ -236,6 +259,7 @@ impl EmulationProxy {
request_tx,
task,
event_rx,
input_config,
}
}

Expand Down Expand Up @@ -287,6 +311,7 @@ struct EmulationTask {
event_tx: Sender<EmulationEvent>,
handles: HashMap<SocketAddr, EmulationHandle>,
next_id: EmulationHandle,
input_config: InputConfig,
}

impl EmulationTask {
Expand All @@ -313,7 +338,7 @@ impl EmulationTask {
async fn do_emulation(&mut self) -> Result<(), InputEmulationError> {
log::info!("creating input emulation ...");
let mut emulation = tokio::select! {
r = InputEmulation::new(self.backend) => r?,
r = InputEmulation::new(self.backend, self.input_config) => r?,
// allow termination event while requesting input emulation
_ = wait_for_termination(&mut self.request_rx) => return Ok(()),
};
Expand Down
8 changes: 6 additions & 2 deletions src/emulation_test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::config::Config;
use clap::Args;
use input_emulation::{InputEmulation, InputEmulationError};
use input_emulation::{InputConfig, InputEmulation, InputEmulationError};
use input_event::{Event, PointerEvent};
use std::f64::consts::PI;
use std::time::{Duration, Instant};
Expand All @@ -22,7 +22,11 @@ pub async fn run(config: Config, _args: TestEmulationArgs) -> Result<(), InputEm
log::info!("running input emulation test");

let backend = config.emulation_backend().map(|b| b.into());
let mut emulation = InputEmulation::new(backend).await?;
let input_config: InputConfig = InputConfig {
invert_scroll: config.invert_scroll(),
mouse_sensitivity: config.mouse_sensitivity(),
};
let mut emulation = InputEmulation::new(backend, input_config).await?;
emulation.create(0).await;

let start = Instant::now();
Expand Down
Loading
Loading