mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-28 15:40:54 +03:00
Compare commits
7 Commits
derive-bar
...
input-emul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0509b51a61 | ||
|
|
281cb406dd | ||
|
|
06ac390dbf | ||
|
|
dcc9250b6d | ||
|
|
376ae50b45 | ||
|
|
0e2c749b29 | ||
|
|
127c3366bf |
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -1192,7 +1192,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "input-capture"
|
name = "input-capture"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ashpd",
|
"ashpd",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -1217,7 +1217,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "input-emulation"
|
name = "input-emulation"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ashpd",
|
"ashpd",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -1240,7 +1240,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "input-event"
|
name = "input-event"
|
||||||
version = "0.1.0"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"log",
|
"log",
|
||||||
|
|||||||
@@ -7,16 +7,16 @@ description = "Software KVM Switch / mouse & keyboard sharing software for Local
|
|||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
repository = "https://github.com/ferdinandschober/lan-mouse"
|
repository = "https://github.com/feschber/lan-mouse"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = true
|
strip = true
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
input-event = { path = "input-event", version = "0.1.0" }
|
input-event = { path = "input-event", version = "0.2.1" }
|
||||||
input-emulation = { path = "input-emulation", version = "0.1.0", default-features = false }
|
input-emulation = { path = "input-emulation", version = "0.2.0", default-features = false }
|
||||||
input-capture = { path = "input-capture", version = "0.1.0", default-features = false }
|
input-capture = { path = "input-capture", version = "0.2.0", default-features = false }
|
||||||
|
|
||||||
hickory-resolver = "0.24.1"
|
hickory-resolver = "0.24.1"
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "input-capture"
|
name = "input-capture"
|
||||||
description = "cross-platform input-capture library used by lan-mouse"
|
description = "cross-platform input-capture library used by lan-mouse"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
repository = "https://github.com/ferdinandschober/lan-mouse"
|
repository = "https://github.com/feschber/lan-mouse"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures = "0.3.28"
|
futures = "0.3.28"
|
||||||
futures-core = "0.3.30"
|
futures-core = "0.3.30"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
input-event = { path = "../input-event", version = "0.1.0" }
|
input-event = { path = "../input-event", version = "0.2.1" }
|
||||||
memmap = "0.7"
|
memmap = "0.7"
|
||||||
tempfile = "3.8"
|
tempfile = "3.8"
|
||||||
thiserror = "1.0.61"
|
thiserror = "1.0.61"
|
||||||
|
|||||||
@@ -91,13 +91,35 @@ fn pos_to_barrier(r: &Region, pos: Position) -> (i32, i32, i32, i32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ashpd does not expose fields
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
struct ICBarrier {
|
||||||
|
barrier_id: BarrierID,
|
||||||
|
position: (i32, i32, i32, i32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ICBarrier {
|
||||||
|
fn new(barrier_id: BarrierID, position: (i32, i32, i32, i32)) -> Self {
|
||||||
|
Self {
|
||||||
|
barrier_id,
|
||||||
|
position,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ICBarrier> for Barrier {
|
||||||
|
fn from(barrier: ICBarrier) -> Self {
|
||||||
|
Barrier::new(barrier.barrier_id, barrier.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn select_barriers(
|
fn select_barriers(
|
||||||
zones: &Zones,
|
zones: &Zones,
|
||||||
clients: &Vec<(CaptureHandle, Position)>,
|
clients: &[(CaptureHandle, Position)],
|
||||||
next_barrier_id: &mut u32,
|
next_barrier_id: &mut u32,
|
||||||
) -> (Vec<Barrier>, HashMap<BarrierID, CaptureHandle>) {
|
) -> (Vec<ICBarrier>, HashMap<BarrierID, CaptureHandle>) {
|
||||||
let mut client_for_barrier = HashMap::new();
|
let mut client_for_barrier = HashMap::new();
|
||||||
let mut barriers: Vec<Barrier> = vec![];
|
let mut barriers: Vec<ICBarrier> = vec![];
|
||||||
|
|
||||||
for (handle, pos) in clients {
|
for (handle, pos) in clients {
|
||||||
let mut client_barriers = zones
|
let mut client_barriers = zones
|
||||||
@@ -108,7 +130,7 @@ fn select_barriers(
|
|||||||
*next_barrier_id = id + 1;
|
*next_barrier_id = id + 1;
|
||||||
let position = pos_to_barrier(r, *pos);
|
let position = pos_to_barrier(r, *pos);
|
||||||
client_for_barrier.insert(id, *handle);
|
client_for_barrier.insert(id, *handle);
|
||||||
Barrier::new(id, position)
|
ICBarrier::new(id, position)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
barriers.append(&mut client_barriers);
|
barriers.append(&mut client_barriers);
|
||||||
@@ -119,9 +141,9 @@ fn select_barriers(
|
|||||||
async fn update_barriers(
|
async fn update_barriers(
|
||||||
input_capture: &InputCapture<'_>,
|
input_capture: &InputCapture<'_>,
|
||||||
session: &Session<'_, InputCapture<'_>>,
|
session: &Session<'_, InputCapture<'_>>,
|
||||||
active_clients: &Vec<(CaptureHandle, Position)>,
|
active_clients: &[(CaptureHandle, Position)],
|
||||||
next_barrier_id: &mut u32,
|
next_barrier_id: &mut u32,
|
||||||
) -> Result<HashMap<BarrierID, CaptureHandle>, ashpd::Error> {
|
) -> Result<(Vec<ICBarrier>, HashMap<BarrierID, CaptureHandle>), ashpd::Error> {
|
||||||
let zones = input_capture.zones(session).await?.response()?;
|
let zones = input_capture.zones(session).await?.response()?;
|
||||||
log::debug!("zones: {zones:?}");
|
log::debug!("zones: {zones:?}");
|
||||||
|
|
||||||
@@ -129,12 +151,13 @@ async fn update_barriers(
|
|||||||
log::debug!("barriers: {barriers:?}");
|
log::debug!("barriers: {barriers:?}");
|
||||||
log::debug!("client for barrier id: {id_map:?}");
|
log::debug!("client for barrier id: {id_map:?}");
|
||||||
|
|
||||||
|
let ashpd_barriers: Vec<Barrier> = barriers.iter().copied().map(|b| b.into()).collect();
|
||||||
let response = input_capture
|
let response = input_capture
|
||||||
.set_pointer_barriers(session, &barriers, zones.zone_set())
|
.set_pointer_barriers(session, &ashpd_barriers, zones.zone_set())
|
||||||
.await?;
|
.await?;
|
||||||
let response = response.response()?;
|
let response = response.response()?;
|
||||||
log::debug!("{response:?}");
|
log::debug!("{response:?}");
|
||||||
Ok(id_map)
|
Ok((barriers, id_map))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_session<'a>(
|
async fn create_session<'a>(
|
||||||
@@ -289,7 +312,7 @@ async fn do_capture(
|
|||||||
input_capture,
|
input_capture,
|
||||||
&mut session,
|
&mut session,
|
||||||
&event_tx,
|
&event_tx,
|
||||||
&mut active_clients,
|
&active_clients,
|
||||||
&mut next_barrier_id,
|
&mut next_barrier_id,
|
||||||
¬ify_release,
|
¬ify_release,
|
||||||
(cancel_session.clone(), cancel_update.clone()),
|
(cancel_session.clone(), cancel_update.clone()),
|
||||||
@@ -332,7 +355,7 @@ async fn do_capture_session(
|
|||||||
input_capture: &InputCapture<'_>,
|
input_capture: &InputCapture<'_>,
|
||||||
session: &mut Session<'_, InputCapture<'_>>,
|
session: &mut Session<'_, InputCapture<'_>>,
|
||||||
event_tx: &Sender<(CaptureHandle, Event)>,
|
event_tx: &Sender<(CaptureHandle, Event)>,
|
||||||
active_clients: &mut Vec<(CaptureHandle, Position)>,
|
active_clients: &[(CaptureHandle, Position)],
|
||||||
next_barrier_id: &mut u32,
|
next_barrier_id: &mut u32,
|
||||||
notify_release: &Notify,
|
notify_release: &Notify,
|
||||||
cancel: (CancellationToken, CancellationToken),
|
cancel: (CancellationToken, CancellationToken),
|
||||||
@@ -345,7 +368,7 @@ async fn do_capture_session(
|
|||||||
let (context, ei_event_stream) = connect_to_eis(input_capture, session).await?;
|
let (context, ei_event_stream) = connect_to_eis(input_capture, session).await?;
|
||||||
|
|
||||||
// set barriers
|
// set barriers
|
||||||
let client_for_barrier_id =
|
let (barriers, client_for_barrier_id) =
|
||||||
update_barriers(input_capture, session, active_clients, next_barrier_id).await?;
|
update_barriers(input_capture, session, active_clients, next_barrier_id).await?;
|
||||||
|
|
||||||
log::debug!("enabling session");
|
log::debug!("enabling session");
|
||||||
@@ -388,9 +411,15 @@ async fn do_capture_session(
|
|||||||
let activated = activated.ok_or(CaptureError::ActivationClosed)?;
|
let activated = activated.ok_or(CaptureError::ActivationClosed)?;
|
||||||
log::debug!("activated: {activated:?}");
|
log::debug!("activated: {activated:?}");
|
||||||
|
|
||||||
let client = *client_for_barrier_id
|
// get barrier id from activation
|
||||||
.get(&activated.barrier_id().expect("no barrier id reported by compositor!"))
|
let barrier_id = match activated.barrier_id() {
|
||||||
.expect("invalid barrier id");
|
Some(bid) => bid,
|
||||||
|
// workaround for KDE plasma not reporting barrier ids
|
||||||
|
None => find_corresponding_client(&barriers, activated.cursor_position().expect("no cursor position reported by compositor")),
|
||||||
|
};
|
||||||
|
|
||||||
|
// find client corresponding to barrier
|
||||||
|
let client = *client_for_barrier_id.get(&barrier_id).expect("invalid barrier id");
|
||||||
current_client.replace(Some(client));
|
current_client.replace(Some(client));
|
||||||
|
|
||||||
// client entered => send event
|
// client entered => send event
|
||||||
@@ -462,6 +491,7 @@ async fn release_capture<'a>(
|
|||||||
let (x, y) = activated
|
let (x, y) = activated
|
||||||
.cursor_position()
|
.cursor_position()
|
||||||
.expect("compositor did not report cursor position!");
|
.expect("compositor did not report cursor position!");
|
||||||
|
log::debug!("client entered @ ({x}, {y})");
|
||||||
let pos = active_clients
|
let pos = active_clients
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(c, _)| *c == current_client)
|
.filter(|(c, _)| *c == current_client)
|
||||||
@@ -483,6 +513,34 @@ async fn release_capture<'a>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_corresponding_client(barriers: &[ICBarrier], pos: (f32, f32)) -> BarrierID {
|
||||||
|
barriers
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.min_by_key(|b| {
|
||||||
|
let (x1, y1, x2, y2) = b.position;
|
||||||
|
let (x1, y1, x2, y2) = (x1 as f32, y1 as f32, x2 as f32, y2 as f32);
|
||||||
|
distance_to_line(((x1, y1), (x2, y2)), pos) as i32
|
||||||
|
})
|
||||||
|
.expect("could not find barrier corresponding to client")
|
||||||
|
.barrier_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn distance_to_line(line: ((f32, f32), (f32, f32)), p: (f32, f32)) -> f32 {
|
||||||
|
let ((x1, y1), (x2, y2)) = line;
|
||||||
|
let (x0, y0) = p;
|
||||||
|
/*
|
||||||
|
* we use the fact that for the triangle spanned by the line and p,
|
||||||
|
* the height of the triangle is the desired distance and can be calculated by
|
||||||
|
* h = 2A / b with b being the line_length and
|
||||||
|
*/
|
||||||
|
let double_triangle_area = ((y2 - y1) * x0 - (x2 - x1) * y0 + x2 * y1 - y2 * x1).abs();
|
||||||
|
let line_length = ((y2 - y1).powf(2.0) + (x2 - x1).powf(2.0)).sqrt();
|
||||||
|
let distance = double_triangle_area / line_length;
|
||||||
|
log::debug!("distance to line({line:?}, {p:?}) = {distance}");
|
||||||
|
distance
|
||||||
|
}
|
||||||
|
|
||||||
static ALL_CAPABILITIES: &[DeviceCapability] = &[
|
static ALL_CAPABILITIES: &[DeviceCapability] = &[
|
||||||
DeviceCapability::Pointer,
|
DeviceCapability::Pointer,
|
||||||
DeviceCapability::PointerAbsolute,
|
DeviceCapability::PointerAbsolute,
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "input-emulation"
|
name = "input-emulation"
|
||||||
description = "cross-platform input emulation library used by lan-mouse"
|
description = "cross-platform input emulation library used by lan-mouse"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
repository = "https://github.com/ferdinandschober/lan-mouse"
|
repository = "https://github.com/feschber/lan-mouse"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "0.1.80"
|
async-trait = "0.1.80"
|
||||||
futures = "0.3.28"
|
futures = "0.3.28"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
input-event = { path = "../input-event", version = "0.1.0" }
|
input-event = { path = "../input-event", version = "0.2.1" }
|
||||||
thiserror = "1.0.61"
|
thiserror = "1.0.61"
|
||||||
tokio = { version = "1.32.0", features = [
|
tokio = { version = "1.32.0", features = [
|
||||||
"io-util",
|
"io-util",
|
||||||
|
|||||||
@@ -87,19 +87,25 @@ pub enum EmulationCreationError {
|
|||||||
|
|
||||||
impl EmulationCreationError {
|
impl EmulationCreationError {
|
||||||
/// request was intentionally denied by the user
|
/// request was intentionally denied by the user
|
||||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
|
||||||
pub(crate) fn cancelled_by_user(&self) -> bool {
|
pub(crate) fn cancelled_by_user(&self) -> bool {
|
||||||
matches!(
|
#[cfg(feature = "libei")]
|
||||||
|
if matches!(
|
||||||
self,
|
self,
|
||||||
EmulationCreationError::Libei(LibeiEmulationCreationError::Ashpd(Response(
|
EmulationCreationError::Libei(LibeiEmulationCreationError::Ashpd(Response(
|
||||||
ResponseError::Cancelled,
|
ResponseError::Cancelled,
|
||||||
))) | EmulationCreationError::Xdp(XdpEmulationCreationError::Ashpd(Response(
|
)))
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#[cfg(feature = "xdg_desktop_portal")]
|
||||||
|
if matches!(
|
||||||
|
self,
|
||||||
|
EmulationCreationError::Xdp(XdpEmulationCreationError::Ashpd(Response(
|
||||||
ResponseError::Cancelled,
|
ResponseError::Cancelled,
|
||||||
)))
|
)))
|
||||||
)
|
) {
|
||||||
}
|
return true;
|
||||||
#[cfg(not(all(unix, feature = "libei", not(target_os = "macos"))))]
|
}
|
||||||
pub(crate) fn cancelled_by_user(&self) -> bool {
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "input-event"
|
name = "input-event"
|
||||||
description = "cross-platform input-event types for input-capture / input-emulation"
|
description = "cross-platform input-event types for input-capture / input-emulation"
|
||||||
version = "0.1.0"
|
version = "0.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
repository = "https://github.com/ferdinandschober/lan-mouse"
|
repository = "https://github.com/feschber/lan-mouse"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures-core = "0.3.30"
|
futures-core = "0.3.30"
|
||||||
|
|||||||
Reference in New Issue
Block a user