mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-08 04:20:01 +03:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db96717044 | ||
|
|
be8124a190 | ||
|
|
dcee2933a2 | ||
|
|
8aaff9fb58 | ||
|
|
742b1585d7 | ||
|
|
78c9de45c7 | ||
|
|
a491c0e9e3 | ||
|
|
af02cccc2a | ||
|
|
4a6399f866 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: [feschber]
|
||||
21
.github/workflows/pre-release.yml
vendored
21
.github/workflows/pre-release.yml
vendored
@@ -90,9 +90,25 @@ jobs:
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lan-mouse-macos
|
||||
name: lan-mouse-macos-intel
|
||||
path: lan-mouse-macos-intel
|
||||
|
||||
macos-aarch64-release-build:
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install dependencies
|
||||
run: brew install gtk4 libadwaita
|
||||
- name: Release Build
|
||||
run: |
|
||||
cargo build --release
|
||||
cp target/release/lan-mouse lan-mouse-macos-aarch64
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lan-mouse-macos-aarch64
|
||||
path: lan-mouse-macos-aarch64
|
||||
|
||||
pre-release:
|
||||
name: "Pre Release"
|
||||
needs: [windows-release-build, linux-release-build, macos-release-build]
|
||||
@@ -109,5 +125,6 @@ jobs:
|
||||
title: "Development Build"
|
||||
files: |
|
||||
lan-mouse-linux/lan-mouse
|
||||
lan-mouse-macos/lan-mouse-macos-intel
|
||||
lan-mouse-macos-intel/lan-mouse-macos-intel
|
||||
lan-mouse-macos-aarch64/lan-mouse-macos-aarch64
|
||||
lan-mouse-windows/lan-mouse-windows.zip
|
||||
|
||||
20
.github/workflows/rust.yml
vendored
20
.github/workflows/rust.yml
vendored
@@ -110,3 +110,23 @@ jobs:
|
||||
with:
|
||||
name: lan-mouse-macos
|
||||
path: target/debug/lan-mouse
|
||||
|
||||
build-macos-aarch64:
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install dependencies
|
||||
run: brew install gtk4 libadwaita
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
- name: Check Formatting
|
||||
run: cargo fmt --check
|
||||
- name: Clippy
|
||||
run: cargo clippy --all-features --all-targets -- --deny warnings
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lan-mouse-macos-aarch64
|
||||
path: target/debug/lan-mouse
|
||||
|
||||
21
.github/workflows/tagged-release.yml
vendored
21
.github/workflows/tagged-release.yml
vendored
@@ -86,9 +86,25 @@ jobs:
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lan-mouse-macos
|
||||
name: lan-mouse-macos-intel
|
||||
path: lan-mouse-macos-intel
|
||||
|
||||
macos-aarch64-release-build:
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install dependencies
|
||||
run: brew install gtk4 libadwaita
|
||||
- name: Release Build
|
||||
run: |
|
||||
cargo build --release
|
||||
cp target/release/lan-mouse lan-mouse-macos-aarch64
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lan-mouse-macos-aarch64
|
||||
path: lan-mouse-macos-aarch64
|
||||
|
||||
tagged-release:
|
||||
name: "Tagged Release"
|
||||
needs: [windows-release-build, linux-release-build, macos-release-build]
|
||||
@@ -103,5 +119,6 @@ jobs:
|
||||
prerelease: false
|
||||
files: |
|
||||
lan-mouse-linux/lan-mouse
|
||||
lan-mouse-macos/lan-mouse-macos-intel
|
||||
lan-mouse-macos-intel/lan-mouse-macos-intel
|
||||
lan-mouse-macos-aarch64/lan-mouse-macos-aarch64
|
||||
lan-mouse-windows/lan-mouse-windows.zip
|
||||
|
||||
33
Cargo.lock
generated
33
Cargo.lock
generated
@@ -1228,7 +1228,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lan-mouse"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ashpd",
|
||||
@@ -1258,7 +1258,7 @@ dependencies = [
|
||||
"wayland-protocols",
|
||||
"wayland-protocols-misc",
|
||||
"wayland-protocols-wlr",
|
||||
"winapi",
|
||||
"windows",
|
||||
"x11",
|
||||
]
|
||||
|
||||
@@ -2309,6 +2309,35 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.54.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.54.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "lan-mouse"
|
||||
description = "Software KVM Switch / mouse & keyboard sharing software for Local Area Networks"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-or-later"
|
||||
repository = "https://github.com/ferdinandschober/lan-mouse"
|
||||
@@ -49,7 +49,7 @@ reis = { version = "0.2", features = [ "tokio" ], optional = true }
|
||||
core-graphics = { version = "0.23", features = ["highsierra"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.9", features = ["winuser"] }
|
||||
windows = { version = "0.54.0", features = [ "Win32_UI_Input_KeyboardAndMouse" ] }
|
||||
|
||||
[build-dependencies]
|
||||
glib-build-tools = "0.19.0"
|
||||
|
||||
@@ -58,6 +58,10 @@ input capture (to send events *to* other clients) on different operating systems
|
||||
> Otherwise input capture will not work.
|
||||
|
||||
## Installation
|
||||
### Install with cargo
|
||||
```sh
|
||||
cargo install lan-mouse
|
||||
```
|
||||
|
||||
### Download from Releases
|
||||
The easiest way to install Lan Mouse is to download precompiled release binaries from the [releases section](https://github.com/feschber/lan-mouse/releases).
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod consumer;
|
||||
pub mod producer;
|
||||
@@ -1,20 +0,0 @@
|
||||
#[cfg(windows)]
|
||||
pub mod windows;
|
||||
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
pub mod x11;
|
||||
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
pub mod wlroots;
|
||||
|
||||
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
|
||||
pub mod xdg_desktop_portal;
|
||||
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
pub mod libei;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod macos;
|
||||
|
||||
/// fallback consumer
|
||||
pub mod dummy;
|
||||
@@ -1,17 +0,0 @@
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
pub mod libei;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod macos;
|
||||
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
pub mod wayland;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub mod windows;
|
||||
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
pub mod x11;
|
||||
|
||||
/// fallback event producer
|
||||
pub mod dummy;
|
||||
78
src/capture.rs
Normal file
78
src/capture.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use std::io;
|
||||
|
||||
use futures_core::Stream;
|
||||
|
||||
use crate::{
|
||||
client::{ClientEvent, ClientHandle},
|
||||
event::Event,
|
||||
};
|
||||
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
pub mod libei;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod macos;
|
||||
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
pub mod wayland;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub mod windows;
|
||||
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
pub mod x11;
|
||||
|
||||
/// fallback input capture (does not produce events)
|
||||
pub mod dummy;
|
||||
|
||||
pub async fn create() -> Box<dyn InputCapture> {
|
||||
#[cfg(target_os = "macos")]
|
||||
match macos::MacOSInputCapture::new() {
|
||||
Ok(p) => return Box::new(p),
|
||||
Err(e) => log::info!("macos input capture not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
match windows::WindowsInputCapture::new() {
|
||||
Ok(p) => return Box::new(p),
|
||||
Err(e) => log::info!("windows input capture not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
match libei::LibeiInputCapture::new().await {
|
||||
Ok(p) => {
|
||||
log::info!("using libei input capture");
|
||||
return Box::new(p);
|
||||
}
|
||||
Err(e) => log::info!("libei input capture not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
match wayland::WaylandInputCapture::new() {
|
||||
Ok(p) => {
|
||||
log::info!("using layer-shell input capture");
|
||||
return Box::new(p);
|
||||
}
|
||||
Err(e) => log::info!("layer_shell input capture not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
match x11::X11InputCapture::new() {
|
||||
Ok(p) => {
|
||||
log::info!("using x11 input capture");
|
||||
return Box::new(p);
|
||||
}
|
||||
Err(e) => log::info!("x11 input capture not available: {e}"),
|
||||
}
|
||||
|
||||
log::error!("falling back to dummy input capture");
|
||||
Box::new(dummy::DummyInputCapture::new())
|
||||
}
|
||||
|
||||
pub trait InputCapture: Stream<Item = io::Result<(ClientHandle, Event)>> + Unpin {
|
||||
/// notify input capture of configuration changes
|
||||
fn notify(&mut self, event: ClientEvent) -> io::Result<()>;
|
||||
|
||||
/// release mouse
|
||||
fn release(&mut self) -> io::Result<()>;
|
||||
}
|
||||
@@ -4,26 +4,26 @@ use std::task::{Context, Poll};
|
||||
|
||||
use futures_core::Stream;
|
||||
|
||||
use crate::capture::InputCapture;
|
||||
use crate::event::Event;
|
||||
use crate::producer::EventProducer;
|
||||
|
||||
use crate::client::{ClientEvent, ClientHandle};
|
||||
|
||||
pub struct DummyProducer {}
|
||||
pub struct DummyInputCapture {}
|
||||
|
||||
impl DummyProducer {
|
||||
impl DummyInputCapture {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DummyProducer {
|
||||
impl Default for DummyInputCapture {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventProducer for DummyProducer {
|
||||
impl InputCapture for DummyInputCapture {
|
||||
fn notify(&mut self, _event: ClientEvent) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -33,7 +33,7 @@ impl EventProducer for DummyProducer {
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for DummyProducer {
|
||||
impl Stream for DummyInputCapture {
|
||||
type Item = io::Result<(ClientHandle, Event)>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
@@ -31,9 +31,9 @@ use futures_core::Stream;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
capture::InputCapture as LanMouseInputCapture,
|
||||
client::{ClientEvent, ClientHandle, Position},
|
||||
event::{Event, KeyboardEvent, PointerEvent},
|
||||
producer::EventProducer,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -43,7 +43,7 @@ enum ProducerEvent {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct LibeiProducer<'a> {
|
||||
pub struct LibeiInputCapture<'a> {
|
||||
input_capture: Pin<Box<InputCapture<'a>>>,
|
||||
libei_task: JoinHandle<Result<()>>,
|
||||
event_rx: tokio::sync::mpsc::Receiver<(u32, Event)>,
|
||||
@@ -123,7 +123,7 @@ async fn update_barriers(
|
||||
Ok(id_map)
|
||||
}
|
||||
|
||||
impl<'a> Drop for LibeiProducer<'a> {
|
||||
impl<'a> Drop for LibeiInputCapture<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.libei_task.abort();
|
||||
}
|
||||
@@ -212,7 +212,7 @@ async fn wait_for_active_client(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<'a> LibeiProducer<'a> {
|
||||
impl<'a> LibeiInputCapture<'a> {
|
||||
pub async fn new() -> Result<Self> {
|
||||
let input_capture = Box::pin(InputCapture::new().await?);
|
||||
let input_capture_ptr = input_capture.as_ref().get_ref() as *const InputCapture<'static>;
|
||||
@@ -522,7 +522,7 @@ async fn handle_ei_event(
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> EventProducer for LibeiProducer<'a> {
|
||||
impl<'a> LanMouseInputCapture for LibeiInputCapture<'a> {
|
||||
fn notify(&mut self, event: ClientEvent) -> io::Result<()> {
|
||||
let notify_tx = self.notify_tx.clone();
|
||||
tokio::task::spawn_local(async move {
|
||||
@@ -543,7 +543,7 @@ impl<'a> EventProducer for LibeiProducer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Stream for LibeiProducer<'a> {
|
||||
impl<'a> Stream for LibeiInputCapture<'a> {
|
||||
type Item = io::Result<(ClientHandle, Event)>;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
@@ -1,20 +1,20 @@
|
||||
use crate::capture::InputCapture;
|
||||
use crate::client::{ClientEvent, ClientHandle};
|
||||
use crate::event::Event;
|
||||
use crate::producer::EventProducer;
|
||||
use anyhow::{anyhow, Result};
|
||||
use futures_core::Stream;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{io, pin::Pin};
|
||||
|
||||
pub struct MacOSProducer;
|
||||
pub struct MacOSInputCapture;
|
||||
|
||||
impl MacOSProducer {
|
||||
impl MacOSInputCapture {
|
||||
pub fn new() -> Result<Self> {
|
||||
Err(anyhow!("not yet implemented"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for MacOSProducer {
|
||||
impl Stream for MacOSInputCapture {
|
||||
type Item = io::Result<(ClientHandle, Event)>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
@@ -22,7 +22,7 @@ impl Stream for MacOSProducer {
|
||||
}
|
||||
}
|
||||
|
||||
impl EventProducer for MacOSProducer {
|
||||
impl InputCapture for MacOSInputCapture {
|
||||
fn notify(&mut self, _event: ClientEvent) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
capture::InputCapture,
|
||||
client::{ClientEvent, ClientHandle, Position},
|
||||
producer::EventProducer,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
@@ -124,7 +124,7 @@ impl AsRawFd for Inner {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WaylandEventProducer(AsyncFd<Inner>);
|
||||
pub struct WaylandInputCapture(AsyncFd<Inner>);
|
||||
|
||||
struct Window {
|
||||
buffer: wl_buffer::WlBuffer,
|
||||
@@ -256,7 +256,7 @@ fn draw(f: &mut File, (width, height): (u32, u32)) {
|
||||
}
|
||||
}
|
||||
|
||||
impl WaylandEventProducer {
|
||||
impl WaylandInputCapture {
|
||||
pub fn new() -> Result<Self> {
|
||||
let conn = match Connection::connect_to_env() {
|
||||
Ok(c) => c,
|
||||
@@ -390,7 +390,7 @@ impl WaylandEventProducer {
|
||||
|
||||
let inner = AsyncFd::new(Inner { queue, state })?;
|
||||
|
||||
Ok(WaylandEventProducer(inner))
|
||||
Ok(WaylandInputCapture(inner))
|
||||
}
|
||||
|
||||
fn add_client(&mut self, handle: ClientHandle, pos: Position) {
|
||||
@@ -587,7 +587,7 @@ impl Inner {
|
||||
}
|
||||
}
|
||||
|
||||
impl EventProducer for WaylandEventProducer {
|
||||
impl InputCapture for WaylandInputCapture {
|
||||
fn notify(&mut self, client_event: ClientEvent) -> io::Result<()> {
|
||||
match client_event {
|
||||
ClientEvent::Create(handle, pos) => {
|
||||
@@ -609,7 +609,7 @@ impl EventProducer for WaylandEventProducer {
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for WaylandEventProducer {
|
||||
impl Stream for WaylandInputCapture {
|
||||
type Item = io::Result<(ClientHandle, Event)>;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
@@ -4,14 +4,14 @@ use futures::Stream;
|
||||
use std::{io, pin::Pin};
|
||||
|
||||
use crate::{
|
||||
capture::InputCapture,
|
||||
client::{ClientEvent, ClientHandle},
|
||||
event::Event,
|
||||
producer::EventProducer,
|
||||
};
|
||||
|
||||
pub struct WindowsProducer {}
|
||||
pub struct WindowsInputCapture {}
|
||||
|
||||
impl EventProducer for WindowsProducer {
|
||||
impl InputCapture for WindowsInputCapture {
|
||||
fn notify(&mut self, _event: ClientEvent) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -21,13 +21,13 @@ impl EventProducer for WindowsProducer {
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowsProducer {
|
||||
impl WindowsInputCapture {
|
||||
pub(crate) fn new() -> Result<Self> {
|
||||
Err(anyhow!("not implemented"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for WindowsProducer {
|
||||
impl Stream for WindowsInputCapture {
|
||||
type Item = io::Result<(ClientHandle, Event)>;
|
||||
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
Poll::Pending
|
||||
@@ -4,20 +4,20 @@ use std::task::Poll;
|
||||
|
||||
use futures_core::Stream;
|
||||
|
||||
use crate::capture::InputCapture;
|
||||
use crate::event::Event;
|
||||
use crate::producer::EventProducer;
|
||||
|
||||
use crate::client::{ClientEvent, ClientHandle};
|
||||
|
||||
pub struct X11Producer {}
|
||||
pub struct X11InputCapture {}
|
||||
|
||||
impl X11Producer {
|
||||
impl X11InputCapture {
|
||||
pub fn new() -> Result<Self> {
|
||||
Err(anyhow!("not implemented"))
|
||||
}
|
||||
}
|
||||
|
||||
impl EventProducer for X11Producer {
|
||||
impl InputCapture for X11InputCapture {
|
||||
fn notify(&mut self, _event: ClientEvent) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -27,7 +27,7 @@ impl EventProducer for X11Producer {
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for X11Producer {
|
||||
impl Stream for X11InputCapture {
|
||||
type Item = io::Result<(ClientHandle, Event)>;
|
||||
|
||||
fn poll_next(
|
||||
@@ -67,7 +67,7 @@ pub struct Client {
|
||||
/// fix ips, determined by the user
|
||||
pub fix_ips: Vec<IpAddr>,
|
||||
/// unique handle to refer to the client.
|
||||
/// This way any event consumer / producer backend does not
|
||||
/// This way any emulation / capture backend does not
|
||||
/// need to know anything about a client other than its handle.
|
||||
pub handle: ClientHandle,
|
||||
/// all ip addresses associated with a particular client
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
use async_trait::async_trait;
|
||||
use std::future;
|
||||
|
||||
use crate::{
|
||||
backend::consumer,
|
||||
client::{ClientEvent, ClientHandle},
|
||||
event::Event,
|
||||
};
|
||||
use anyhow::Result;
|
||||
|
||||
#[async_trait]
|
||||
pub trait EventConsumer: Send {
|
||||
async fn consume(&mut self, event: Event, client_handle: ClientHandle);
|
||||
async fn notify(&mut self, client_event: ClientEvent);
|
||||
/// this function is waited on continuously and can be used to handle events
|
||||
async fn dispatch(&mut self) -> Result<()> {
|
||||
let _: () = future::pending().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn destroy(&mut self);
|
||||
}
|
||||
|
||||
pub async fn create() -> Box<dyn EventConsumer> {
|
||||
#[cfg(windows)]
|
||||
match consumer::windows::WindowsConsumer::new() {
|
||||
Ok(c) => return Box::new(c),
|
||||
Err(e) => log::warn!("windows event consumer unavailable: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
match consumer::macos::MacOSConsumer::new() {
|
||||
Ok(c) => {
|
||||
log::info!("using macos event consumer");
|
||||
return Box::new(c);
|
||||
}
|
||||
Err(e) => log::error!("macos consumer not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
match consumer::wlroots::WlrootsConsumer::new() {
|
||||
Ok(c) => {
|
||||
log::info!("using wlroots event consumer");
|
||||
return Box::new(c);
|
||||
}
|
||||
Err(e) => log::info!("wayland backend not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
match consumer::libei::LibeiConsumer::new().await {
|
||||
Ok(c) => {
|
||||
log::info!("using libei event consumer");
|
||||
return Box::new(c);
|
||||
}
|
||||
Err(e) => log::info!("libei not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
|
||||
match consumer::xdg_desktop_portal::DesktopPortalConsumer::new().await {
|
||||
Ok(c) => {
|
||||
log::info!("using xdg-remote-desktop-portal event consumer");
|
||||
return Box::new(c);
|
||||
}
|
||||
Err(e) => log::info!("remote desktop portal not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
match consumer::x11::X11Consumer::new() {
|
||||
Ok(c) => {
|
||||
log::info!("using x11 event consumer");
|
||||
return Box::new(c);
|
||||
}
|
||||
Err(e) => log::info!("x11 consumer not available: {e}"),
|
||||
}
|
||||
|
||||
log::error!("falling back to dummy event consumer");
|
||||
Box::new(consumer::dummy::DummyConsumer::new())
|
||||
}
|
||||
98
src/emulate.rs
Normal file
98
src/emulate.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
use async_trait::async_trait;
|
||||
use std::future;
|
||||
|
||||
use crate::{
|
||||
client::{ClientEvent, ClientHandle},
|
||||
event::Event,
|
||||
};
|
||||
use anyhow::Result;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub mod windows;
|
||||
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
pub mod x11;
|
||||
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
pub mod wlroots;
|
||||
|
||||
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
|
||||
pub mod xdg_desktop_portal;
|
||||
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
pub mod libei;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod macos;
|
||||
|
||||
/// fallback input emulation (logs events)
|
||||
pub mod dummy;
|
||||
|
||||
#[async_trait]
|
||||
pub trait InputEmulation: Send {
|
||||
async fn consume(&mut self, event: Event, client_handle: ClientHandle);
|
||||
async fn notify(&mut self, client_event: ClientEvent);
|
||||
/// this function is waited on continuously and can be used to handle events
|
||||
async fn dispatch(&mut self) -> Result<()> {
|
||||
let _: () = future::pending().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn destroy(&mut self);
|
||||
}
|
||||
|
||||
pub async fn create() -> Box<dyn InputEmulation> {
|
||||
#[cfg(windows)]
|
||||
match windows::WindowsEmulation::new() {
|
||||
Ok(c) => return Box::new(c),
|
||||
Err(e) => log::warn!("windows input emulation unavailable: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
match macos::MacOSEmulation::new() {
|
||||
Ok(c) => {
|
||||
log::info!("using macos input emulation");
|
||||
return Box::new(c);
|
||||
}
|
||||
Err(e) => log::error!("macos input emulatino not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
match wlroots::WlrootsEmulation::new() {
|
||||
Ok(c) => {
|
||||
log::info!("using wlroots input emulation");
|
||||
return Box::new(c);
|
||||
}
|
||||
Err(e) => log::info!("wayland backend not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
match libei::LibeiEmulation::new().await {
|
||||
Ok(c) => {
|
||||
log::info!("using libei input emulation");
|
||||
return Box::new(c);
|
||||
}
|
||||
Err(e) => log::info!("libei not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
|
||||
match xdg_desktop_portal::DesktopPortalEmulation::new().await {
|
||||
Ok(c) => {
|
||||
log::info!("using xdg-remote-desktop-portal input emulation");
|
||||
return Box::new(c);
|
||||
}
|
||||
Err(e) => log::info!("remote desktop portal not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
match x11::X11Emulation::new() {
|
||||
Ok(c) => {
|
||||
log::info!("using x11 input emulation");
|
||||
return Box::new(c);
|
||||
}
|
||||
Err(e) => log::info!("x11 input emulation not available: {e}"),
|
||||
}
|
||||
|
||||
log::error!("falling back to dummy input emulation");
|
||||
Box::new(dummy::DummyEmulation::new())
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
use crate::{
|
||||
client::{ClientEvent, ClientHandle},
|
||||
consumer::EventConsumer,
|
||||
emulate::InputEmulation,
|
||||
event::Event,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DummyConsumer;
|
||||
pub struct DummyEmulation;
|
||||
|
||||
impl DummyConsumer {
|
||||
impl DummyEmulation {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EventConsumer for DummyConsumer {
|
||||
impl InputEmulation for DummyEmulation {
|
||||
async fn consume(&mut self, event: Event, client_handle: ClientHandle) {
|
||||
log::info!("received event: ({client_handle}) {event}");
|
||||
}
|
||||
@@ -23,11 +23,11 @@ use reis::{
|
||||
|
||||
use crate::{
|
||||
client::{ClientEvent, ClientHandle},
|
||||
consumer::EventConsumer,
|
||||
emulate::InputEmulation,
|
||||
event::Event,
|
||||
};
|
||||
|
||||
pub struct LibeiConsumer {
|
||||
pub struct LibeiEmulation {
|
||||
handshake: bool,
|
||||
context: ei::Context,
|
||||
events: EiEventStream,
|
||||
@@ -76,7 +76,7 @@ async fn get_ei_fd() -> Result<OwnedFd, ashpd::Error> {
|
||||
proxy.connect_to_eis(&session).await
|
||||
}
|
||||
|
||||
impl LibeiConsumer {
|
||||
impl LibeiEmulation {
|
||||
pub async fn new() -> Result<Self> {
|
||||
// fd is owned by the message, so we need to dup it
|
||||
let eifd = get_ei_fd().await?;
|
||||
@@ -107,7 +107,7 @@ impl LibeiConsumer {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EventConsumer for LibeiConsumer {
|
||||
impl InputEmulation for LibeiEmulation {
|
||||
async fn consume(&mut self, event: Event, _client_handle: ClientHandle) {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::client::{ClientEvent, ClientHandle};
|
||||
use crate::consumer::EventConsumer;
|
||||
use crate::emulate::InputEmulation;
|
||||
use crate::event::{Event, KeyboardEvent, PointerEvent};
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
@@ -16,7 +16,7 @@ use tokio::task::AbortHandle;
|
||||
const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis(500);
|
||||
const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32);
|
||||
|
||||
pub struct MacOSConsumer {
|
||||
pub struct MacOSEmulation {
|
||||
pub event_source: CGEventSource,
|
||||
repeat_task: Option<AbortHandle>,
|
||||
button_state: ButtonState,
|
||||
@@ -50,9 +50,9 @@ impl IndexMut<CGMouseButton> for ButtonState {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for MacOSConsumer {}
|
||||
unsafe impl Send for MacOSEmulation {}
|
||||
|
||||
impl MacOSConsumer {
|
||||
impl MacOSEmulation {
|
||||
pub fn new() -> Result<Self> {
|
||||
let event_source = match CGEventSource::new(CGEventSourceStateID::CombinedSessionState) {
|
||||
Ok(e) => e,
|
||||
@@ -108,7 +108,7 @@ fn key_event(event_source: CGEventSource, key: u16, state: u8) {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EventConsumer for MacOSConsumer {
|
||||
impl InputEmulation for MacOSEmulation {
|
||||
async fn consume(&mut self, event: Event, _client_handle: ClientHandle) {
|
||||
match event {
|
||||
Event::Pointer(pointer_event) => match pointer_event {
|
||||
@@ -1,21 +1,19 @@
|
||||
use crate::{
|
||||
consumer::EventConsumer,
|
||||
emulate::InputEmulation,
|
||||
event::{KeyboardEvent, PointerEvent},
|
||||
scancode,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use std::ops::BitOrAssign;
|
||||
use std::time::Duration;
|
||||
use tokio::task::AbortHandle;
|
||||
use winapi::um::winuser::{SendInput, KEYEVENTF_EXTENDEDKEY};
|
||||
use winapi::{
|
||||
self,
|
||||
um::winuser::{
|
||||
INPUT, INPUT_KEYBOARD, INPUT_MOUSE, KEYBDINPUT, KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE,
|
||||
LPINPUT, MOUSEEVENTF_HWHEEL, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP,
|
||||
MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_MOVE, MOUSEEVENTF_RIGHTDOWN,
|
||||
MOUSEEVENTF_RIGHTUP, MOUSEEVENTF_WHEEL, MOUSEINPUT,
|
||||
},
|
||||
use windows::Win32::UI::Input::KeyboardAndMouse::{SendInput, INPUT_0, KEYEVENTF_EXTENDEDKEY};
|
||||
use windows::Win32::UI::Input::KeyboardAndMouse::{
|
||||
INPUT, INPUT_KEYBOARD, INPUT_MOUSE, KEYBDINPUT, KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE,
|
||||
MOUSEEVENTF_HWHEEL, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEDOWN,
|
||||
MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_MOVE, MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP,
|
||||
MOUSEEVENTF_WHEEL, MOUSEINPUT,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -26,18 +24,18 @@ use crate::{
|
||||
const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis(500);
|
||||
const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32);
|
||||
|
||||
pub struct WindowsConsumer {
|
||||
pub struct WindowsEmulation {
|
||||
repeat_task: Option<AbortHandle>,
|
||||
}
|
||||
|
||||
impl WindowsConsumer {
|
||||
impl WindowsEmulation {
|
||||
pub fn new() -> Result<Self> {
|
||||
Ok(Self { repeat_task: None })
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EventConsumer for WindowsConsumer {
|
||||
impl InputEmulation for WindowsEmulation {
|
||||
async fn consume(&mut self, event: Event, _: ClientHandle) {
|
||||
match event {
|
||||
Event::Pointer(pointer_event) => match pointer_event {
|
||||
@@ -87,7 +85,7 @@ impl EventConsumer for WindowsConsumer {
|
||||
async fn destroy(&mut self) {}
|
||||
}
|
||||
|
||||
impl WindowsConsumer {
|
||||
impl WindowsEmulation {
|
||||
async fn spawn_repeat_task(&mut self, key: u32) {
|
||||
// there can only be one repeating key and it's
|
||||
// always the last to be pressed
|
||||
@@ -108,21 +106,30 @@ impl WindowsConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
fn send_mouse_input(mi: MOUSEINPUT) {
|
||||
fn send_input_safe(input: INPUT) {
|
||||
unsafe {
|
||||
let mut input = INPUT {
|
||||
type_: INPUT_MOUSE,
|
||||
u: std::mem::transmute(mi),
|
||||
};
|
||||
|
||||
SendInput(
|
||||
1_u32,
|
||||
&mut input as LPINPUT,
|
||||
std::mem::size_of::<INPUT>() as i32,
|
||||
);
|
||||
loop {
|
||||
/* retval = number of successfully submitted events */
|
||||
if SendInput(&[input], std::mem::size_of::<INPUT>() as i32) > 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_mouse_input(mi: MOUSEINPUT) {
|
||||
send_input_safe(INPUT {
|
||||
r#type: INPUT_MOUSE,
|
||||
Anonymous: INPUT_0 { mi },
|
||||
});
|
||||
}
|
||||
|
||||
fn send_keyboard_input(ki: KEYBDINPUT) {
|
||||
send_input_safe(INPUT {
|
||||
r#type: INPUT_KEYBOARD,
|
||||
Anonymous: INPUT_0 { ki },
|
||||
});
|
||||
}
|
||||
fn rel_mouse(dx: i32, dy: i32) {
|
||||
let mi = MOUSEINPUT {
|
||||
dx,
|
||||
@@ -186,33 +193,23 @@ fn key_event(key: u32, state: u8) {
|
||||
};
|
||||
let extended = scancode > 0xff;
|
||||
let scancode = scancode & 0xff;
|
||||
let mut flags = KEYEVENTF_SCANCODE;
|
||||
if extended {
|
||||
flags.bitor_assign(KEYEVENTF_EXTENDEDKEY);
|
||||
}
|
||||
if state == 0 {
|
||||
flags.bitor_assign(KEYEVENTF_KEYUP);
|
||||
}
|
||||
let ki = KEYBDINPUT {
|
||||
wVk: 0,
|
||||
wVk: Default::default(),
|
||||
wScan: scancode,
|
||||
dwFlags: KEYEVENTF_SCANCODE
|
||||
| if extended { KEYEVENTF_EXTENDEDKEY } else { 0 }
|
||||
| match state {
|
||||
0 => KEYEVENTF_KEYUP,
|
||||
1 => 0u32,
|
||||
_ => return,
|
||||
},
|
||||
dwFlags: flags,
|
||||
time: 0,
|
||||
dwExtraInfo: 0,
|
||||
};
|
||||
send_keyboard_input(ki);
|
||||
}
|
||||
|
||||
fn send_keyboard_input(ki: KEYBDINPUT) {
|
||||
unsafe {
|
||||
let mut input = INPUT {
|
||||
type_: INPUT_KEYBOARD,
|
||||
u: std::mem::zeroed(),
|
||||
};
|
||||
*input.u.ki_mut() = ki;
|
||||
SendInput(1_u32, &mut input, std::mem::size_of::<INPUT>() as i32);
|
||||
}
|
||||
}
|
||||
|
||||
fn linux_keycode_to_windows_scancode(linux_keycode: u32) -> Option<u16> {
|
||||
let linux_scancode = match scancode::Linux::try_from(linux_keycode) {
|
||||
Ok(s) => s,
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::client::{ClientEvent, ClientHandle};
|
||||
use crate::consumer::EventConsumer;
|
||||
use crate::emulate::InputEmulation;
|
||||
use async_trait::async_trait;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
@@ -40,13 +40,13 @@ struct State {
|
||||
}
|
||||
|
||||
// App State, implements Dispatch event handlers
|
||||
pub(crate) struct WlrootsConsumer {
|
||||
pub(crate) struct WlrootsEmulation {
|
||||
last_flush_failed: bool,
|
||||
state: State,
|
||||
queue: EventQueue<State>,
|
||||
}
|
||||
|
||||
impl WlrootsConsumer {
|
||||
impl WlrootsEmulation {
|
||||
pub fn new() -> Result<Self> {
|
||||
let conn = Connection::connect_to_env()?;
|
||||
let (globals, queue) = registry_queue_init::<State>(&conn)?;
|
||||
@@ -62,7 +62,7 @@ impl WlrootsConsumer {
|
||||
|
||||
let input_for_client: HashMap<ClientHandle, VirtualInput> = HashMap::new();
|
||||
|
||||
let mut consumer = WlrootsConsumer {
|
||||
let mut emulate = WlrootsEmulation {
|
||||
last_flush_failed: false,
|
||||
state: State {
|
||||
keymap: None,
|
||||
@@ -74,16 +74,13 @@ impl WlrootsConsumer {
|
||||
},
|
||||
queue,
|
||||
};
|
||||
while consumer.state.keymap.is_none() {
|
||||
consumer
|
||||
.queue
|
||||
.blocking_dispatch(&mut consumer.state)
|
||||
.unwrap();
|
||||
while emulate.state.keymap.is_none() {
|
||||
emulate.queue.blocking_dispatch(&mut emulate.state).unwrap();
|
||||
}
|
||||
// let fd = unsafe { &File::from_raw_fd(consumer.state.keymap.unwrap().1.as_raw_fd()) };
|
||||
// let fd = unsafe { &File::from_raw_fd(emulate.state.keymap.unwrap().1.as_raw_fd()) };
|
||||
// let mmap = unsafe { MmapOptions::new().map_copy(fd).unwrap() };
|
||||
// log::debug!("{:?}", &mmap[..100]);
|
||||
Ok(consumer)
|
||||
Ok(emulate)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +103,7 @@ impl State {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EventConsumer for WlrootsConsumer {
|
||||
impl InputEmulation for WlrootsEmulation {
|
||||
async fn consume(&mut self, event: Event, client_handle: ClientHandle) {
|
||||
if let Some(virtual_input) = self.state.input_for_client.get(&client_handle) {
|
||||
if self.last_flush_failed {
|
||||
@@ -8,19 +8,19 @@ use x11::{
|
||||
|
||||
use crate::{
|
||||
client::ClientHandle,
|
||||
consumer::EventConsumer,
|
||||
emulate::InputEmulation,
|
||||
event::{
|
||||
Event, KeyboardEvent, PointerEvent, BTN_BACK, BTN_FORWARD, BTN_LEFT, BTN_MIDDLE, BTN_RIGHT,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct X11Consumer {
|
||||
pub struct X11Emulation {
|
||||
display: *mut xlib::Display,
|
||||
}
|
||||
|
||||
unsafe impl Send for X11Consumer {}
|
||||
unsafe impl Send for X11Emulation {}
|
||||
|
||||
impl X11Consumer {
|
||||
impl X11Emulation {
|
||||
pub fn new() -> Result<Self> {
|
||||
let display = unsafe {
|
||||
match xlib::XOpenDisplay(ptr::null()) {
|
||||
@@ -91,7 +91,7 @@ impl X11Consumer {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for X11Consumer {
|
||||
impl Drop for X11Emulation {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
XCloseDisplay(self.display);
|
||||
@@ -100,7 +100,7 @@ impl Drop for X11Consumer {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EventConsumer for X11Consumer {
|
||||
impl InputEmulation for X11Emulation {
|
||||
async fn consume(&mut self, event: Event, _: ClientHandle) {
|
||||
match event {
|
||||
Event::Pointer(pointer_event) => match pointer_event {
|
||||
@@ -10,20 +10,20 @@ use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
client::ClientEvent,
|
||||
consumer::EventConsumer,
|
||||
emulate::InputEmulation,
|
||||
event::{
|
||||
Event::{Keyboard, Pointer},
|
||||
KeyboardEvent, PointerEvent,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct DesktopPortalConsumer<'a> {
|
||||
pub struct DesktopPortalEmulation<'a> {
|
||||
proxy: RemoteDesktop<'a>,
|
||||
session: Session<'a>,
|
||||
}
|
||||
|
||||
impl<'a> DesktopPortalConsumer<'a> {
|
||||
pub async fn new() -> Result<DesktopPortalConsumer<'a>> {
|
||||
impl<'a> DesktopPortalEmulation<'a> {
|
||||
pub async fn new() -> Result<DesktopPortalEmulation<'a>> {
|
||||
log::debug!("connecting to org.freedesktop.portal.RemoteDesktop portal ...");
|
||||
let proxy = RemoteDesktop::new().await?;
|
||||
|
||||
@@ -59,7 +59,7 @@ impl<'a> DesktopPortalConsumer<'a> {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a> EventConsumer for DesktopPortalConsumer<'a> {
|
||||
impl<'a> InputEmulation for DesktopPortalEmulation<'a> {
|
||||
async fn consume(&mut self, event: crate::event::Event, _client: crate::client::ClientHandle) {
|
||||
match event {
|
||||
Pointer(p) => {
|
||||
@@ -129,7 +129,7 @@ fn build_ui(app: &Application) {
|
||||
window.imp().stream.borrow_mut().replace(tx);
|
||||
glib::spawn_future_local(clone!(@weak window => async move {
|
||||
loop {
|
||||
let notify = receiver.recv().await.unwrap();
|
||||
let notify = receiver.recv().await.unwrap_or_else(|_| process::exit(1));
|
||||
match notify {
|
||||
FrontendNotify::NotifyClientActivate(handle, active) => {
|
||||
window.activate_client(handle, active);
|
||||
|
||||
@@ -4,9 +4,8 @@ pub mod dns;
|
||||
pub mod event;
|
||||
pub mod server;
|
||||
|
||||
pub mod consumer;
|
||||
pub mod producer;
|
||||
pub mod capture;
|
||||
pub mod emulate;
|
||||
|
||||
pub mod backend;
|
||||
pub mod frontend;
|
||||
pub mod scancode;
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
use std::io;
|
||||
|
||||
use futures_core::Stream;
|
||||
|
||||
use crate::backend::producer;
|
||||
use crate::{
|
||||
client::{ClientEvent, ClientHandle},
|
||||
event::Event,
|
||||
};
|
||||
|
||||
pub async fn create() -> Box<dyn EventProducer> {
|
||||
#[cfg(target_os = "macos")]
|
||||
match producer::macos::MacOSProducer::new() {
|
||||
Ok(p) => return Box::new(p),
|
||||
Err(e) => log::info!("macos event producer not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
match producer::windows::WindowsProducer::new() {
|
||||
Ok(p) => return Box::new(p),
|
||||
Err(e) => log::info!("windows event producer not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
match producer::libei::LibeiProducer::new().await {
|
||||
Ok(p) => {
|
||||
log::info!("using libei event producer");
|
||||
return Box::new(p);
|
||||
}
|
||||
Err(e) => log::info!("libei event producer not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
match producer::wayland::WaylandEventProducer::new() {
|
||||
Ok(p) => {
|
||||
log::info!("using layer-shell event producer");
|
||||
return Box::new(p);
|
||||
}
|
||||
Err(e) => log::info!("layer_shell event producer not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
match producer::x11::X11Producer::new() {
|
||||
Ok(p) => {
|
||||
log::info!("using x11 event producer");
|
||||
return Box::new(p);
|
||||
}
|
||||
Err(e) => log::info!("x11 event producer not available: {e}"),
|
||||
}
|
||||
|
||||
log::error!("falling back to dummy event producer");
|
||||
Box::new(producer::dummy::DummyProducer::new())
|
||||
}
|
||||
|
||||
pub trait EventProducer: Stream<Item = io::Result<(ClientHandle, Event)>> + Unpin {
|
||||
/// notify event producer of configuration changes
|
||||
fn notify(&mut self, event: ClientEvent) -> io::Result<()>;
|
||||
|
||||
/// release mouse
|
||||
fn release(&mut self) -> io::Result<()>;
|
||||
}
|
||||
@@ -5,22 +5,22 @@ use std::{
|
||||
};
|
||||
use tokio::signal;
|
||||
|
||||
use crate::{capture, emulate};
|
||||
use crate::{
|
||||
client::{ClientHandle, ClientManager},
|
||||
config::Config,
|
||||
dns,
|
||||
frontend::{FrontendEvent, FrontendListener},
|
||||
server::producer_task::ProducerEvent,
|
||||
server::capture_task::CaptureEvent,
|
||||
};
|
||||
use crate::{consumer, producer};
|
||||
|
||||
use self::{consumer_task::ConsumerEvent, resolver_task::DnsRequest};
|
||||
use self::{emulation_task::EmulationEvent, resolver_task::DnsRequest};
|
||||
|
||||
mod consumer_task;
|
||||
mod capture_task;
|
||||
mod emulation_task;
|
||||
mod frontend_task;
|
||||
mod network_task;
|
||||
mod ping_task;
|
||||
mod producer_task;
|
||||
mod resolver_task;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@@ -78,7 +78,7 @@ impl Server {
|
||||
return anyhow::Ok(());
|
||||
}
|
||||
};
|
||||
let (consumer, producer) = tokio::join!(consumer::create(), producer::create());
|
||||
let (emulate, capture) = tokio::join!(emulate::create(), capture::create());
|
||||
|
||||
let (timer_tx, timer_rx) = tokio::sync::mpsc::channel(1);
|
||||
let (frontend_notify_tx, frontend_notify_rx) = tokio::sync::mpsc::channel(1);
|
||||
@@ -87,22 +87,22 @@ impl Server {
|
||||
let (mut udp_task, sender_tx, receiver_rx, port_tx) =
|
||||
network_task::new(self.clone(), frontend_notify_tx).await?;
|
||||
|
||||
// event producer
|
||||
let (mut producer_task, producer_channel) = producer_task::new(
|
||||
producer,
|
||||
// input capture
|
||||
let (mut capture_task, capture_channel) = capture_task::new(
|
||||
capture,
|
||||
self.clone(),
|
||||
sender_tx.clone(),
|
||||
timer_tx.clone(),
|
||||
self.release_bind.clone(),
|
||||
);
|
||||
|
||||
// event consumer
|
||||
let (mut consumer_task, consumer_channel) = consumer_task::new(
|
||||
consumer,
|
||||
// input emulation
|
||||
let (mut emulation_task, emulate_channel) = emulation_task::new(
|
||||
emulate,
|
||||
self.clone(),
|
||||
receiver_rx,
|
||||
sender_tx.clone(),
|
||||
producer_channel.clone(),
|
||||
capture_channel.clone(),
|
||||
timer_tx,
|
||||
);
|
||||
|
||||
@@ -115,8 +115,8 @@ impl Server {
|
||||
frontend,
|
||||
frontend_notify_rx,
|
||||
self.clone(),
|
||||
producer_channel.clone(),
|
||||
consumer_channel.clone(),
|
||||
capture_channel.clone(),
|
||||
emulate_channel.clone(),
|
||||
resolve_tx.clone(),
|
||||
port_tx,
|
||||
);
|
||||
@@ -125,8 +125,8 @@ impl Server {
|
||||
let mut ping_task = ping_task::new(
|
||||
self.clone(),
|
||||
sender_tx.clone(),
|
||||
consumer_channel.clone(),
|
||||
producer_channel.clone(),
|
||||
emulate_channel.clone(),
|
||||
capture_channel.clone(),
|
||||
timer_rx,
|
||||
);
|
||||
|
||||
@@ -156,14 +156,14 @@ impl Server {
|
||||
_ = signal::ctrl_c() => {
|
||||
log::info!("terminating service");
|
||||
}
|
||||
e = &mut producer_task => {
|
||||
e = &mut capture_task => {
|
||||
if let Ok(Err(e)) = e {
|
||||
log::error!("error in event producer: {e}");
|
||||
log::error!("error in input capture task: {e}");
|
||||
}
|
||||
}
|
||||
e = &mut consumer_task => {
|
||||
e = &mut emulation_task => {
|
||||
if let Ok(Err(e)) = e {
|
||||
log::error!("error in event consumer: {e}");
|
||||
log::error!("error in input emulation task: {e}");
|
||||
}
|
||||
}
|
||||
e = &mut frontend_task => {
|
||||
@@ -176,18 +176,18 @@ impl Server {
|
||||
_ = &mut ping_task => { }
|
||||
}
|
||||
|
||||
let _ = consumer_channel.send(ConsumerEvent::Terminate).await;
|
||||
let _ = producer_channel.send(ProducerEvent::Terminate).await;
|
||||
let _ = emulate_channel.send(EmulationEvent::Terminate).await;
|
||||
let _ = capture_channel.send(CaptureEvent::Terminate).await;
|
||||
let _ = frontend_tx.send(FrontendEvent::Shutdown()).await;
|
||||
|
||||
if !producer_task.is_finished() {
|
||||
if let Err(e) = producer_task.await {
|
||||
log::error!("error in event producer: {e}");
|
||||
if !capture_task.is_finished() {
|
||||
if let Err(e) = capture_task.await {
|
||||
log::error!("error in input capture task: {e}");
|
||||
}
|
||||
}
|
||||
if !consumer_task.is_finished() {
|
||||
if let Err(e) = consumer_task.await {
|
||||
log::error!("error in event consumer: {e}");
|
||||
if !emulation_task.is_finished() {
|
||||
if let Err(e) = emulation_task.await {
|
||||
log::error!("error in input emulation task: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ use std::{collections::HashSet, net::SocketAddr};
|
||||
use tokio::{sync::mpsc::Sender, task::JoinHandle};
|
||||
|
||||
use crate::{
|
||||
capture::InputCapture,
|
||||
client::{ClientEvent, ClientHandle},
|
||||
event::{Event, KeyboardEvent},
|
||||
producer::EventProducer,
|
||||
scancode,
|
||||
server::State,
|
||||
};
|
||||
@@ -15,45 +15,45 @@ use crate::{
|
||||
use super::Server;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ProducerEvent {
|
||||
/// producer must release the mouse
|
||||
pub enum CaptureEvent {
|
||||
/// capture must release the mouse
|
||||
Release,
|
||||
/// producer is notified of a change in client states
|
||||
/// capture is notified of a change in client states
|
||||
ClientEvent(ClientEvent),
|
||||
/// termination signal
|
||||
Terminate,
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
mut producer: Box<dyn EventProducer>,
|
||||
mut capture: Box<dyn InputCapture>,
|
||||
server: Server,
|
||||
sender_tx: Sender<(Event, SocketAddr)>,
|
||||
timer_tx: Sender<()>,
|
||||
release_bind: Vec<scancode::Linux>,
|
||||
) -> (JoinHandle<Result<()>>, Sender<ProducerEvent>) {
|
||||
) -> (JoinHandle<Result<()>>, Sender<CaptureEvent>) {
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel(32);
|
||||
let task = tokio::task::spawn_local(async move {
|
||||
let mut pressed_keys = HashSet::new();
|
||||
loop {
|
||||
tokio::select! {
|
||||
event = producer.next() => {
|
||||
event = capture.next() => {
|
||||
match event {
|
||||
Some(Ok(event)) => handle_producer_event(&server, &mut producer, &sender_tx, &timer_tx, event, &mut pressed_keys, &release_bind).await?,
|
||||
Some(Err(e)) => return Err(anyhow!("event producer: {e:?}")),
|
||||
None => return Err(anyhow!("event producer closed")),
|
||||
Some(Ok(event)) => handle_capture_event(&server, &mut capture, &sender_tx, &timer_tx, event, &mut pressed_keys, &release_bind).await?,
|
||||
Some(Err(e)) => return Err(anyhow!("input capture: {e:?}")),
|
||||
None => return Err(anyhow!("input capture terminated")),
|
||||
}
|
||||
}
|
||||
e = rx.recv() => {
|
||||
log::debug!("producer notify rx: {e:?}");
|
||||
log::debug!("input capture notify rx: {e:?}");
|
||||
match e {
|
||||
Some(e) => match e {
|
||||
ProducerEvent::Release => {
|
||||
producer.release()?;
|
||||
CaptureEvent::Release => {
|
||||
capture.release()?;
|
||||
server.state.replace(State::Receiving);
|
||||
|
||||
}
|
||||
ProducerEvent::ClientEvent(e) => producer.notify(e)?,
|
||||
ProducerEvent::Terminate => break,
|
||||
CaptureEvent::ClientEvent(e) => capture.notify(e)?,
|
||||
CaptureEvent::Terminate => break,
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
@@ -75,9 +75,9 @@ fn update_pressed_keys(pressed_keys: &mut HashSet<scancode::Linux>, key: u32, st
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_producer_event(
|
||||
async fn handle_capture_event(
|
||||
server: &Server,
|
||||
producer: &mut Box<dyn EventProducer>,
|
||||
capture: &mut Box<dyn InputCapture>,
|
||||
sender_tx: &Sender<(Event, SocketAddr)>,
|
||||
timer_tx: &Sender<()>,
|
||||
event: (ClientHandle, Event),
|
||||
@@ -93,7 +93,7 @@ async fn handle_producer_event(
|
||||
if release_bind.iter().all(|k| pressed_keys.contains(k)) {
|
||||
pressed_keys.clear();
|
||||
log::info!("releasing pointer");
|
||||
producer.release()?;
|
||||
capture.release()?;
|
||||
server.state.replace(State::Receiving);
|
||||
log::trace!("STATE ===> Receiving");
|
||||
// send an event to release all the modifiers
|
||||
@@ -112,7 +112,7 @@ async fn handle_producer_event(
|
||||
None => {
|
||||
// should not happen
|
||||
log::warn!("unknown client!");
|
||||
producer.release()?;
|
||||
capture.release()?;
|
||||
server.state.replace(State::Receiving);
|
||||
log::trace!("STATE ===> Receiving");
|
||||
return Ok(());
|
||||
@@ -8,53 +8,53 @@ use tokio::{
|
||||
|
||||
use crate::{
|
||||
client::{ClientEvent, ClientHandle},
|
||||
consumer::EventConsumer,
|
||||
emulate::InputEmulation,
|
||||
event::{Event, KeyboardEvent},
|
||||
scancode,
|
||||
server::State,
|
||||
};
|
||||
|
||||
use super::{ProducerEvent, Server};
|
||||
use super::{CaptureEvent, Server};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ConsumerEvent {
|
||||
/// consumer is notified of a change in client states
|
||||
pub enum EmulationEvent {
|
||||
/// input emulation is notified of a change in client states
|
||||
ClientEvent(ClientEvent),
|
||||
/// consumer must release keys for client
|
||||
/// input emulation must release keys for client
|
||||
ReleaseKeys(ClientHandle),
|
||||
/// termination signal
|
||||
Terminate,
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
mut consumer: Box<dyn EventConsumer>,
|
||||
mut emulate: Box<dyn InputEmulation>,
|
||||
server: Server,
|
||||
mut udp_rx: Receiver<Result<(Event, SocketAddr)>>,
|
||||
sender_tx: Sender<(Event, SocketAddr)>,
|
||||
producer_tx: Sender<ProducerEvent>,
|
||||
capture_tx: Sender<CaptureEvent>,
|
||||
timer_tx: Sender<()>,
|
||||
) -> (JoinHandle<Result<()>>, Sender<ConsumerEvent>) {
|
||||
) -> (JoinHandle<Result<()>>, Sender<EmulationEvent>) {
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel(32);
|
||||
let consumer_task = tokio::task::spawn_local(async move {
|
||||
let emulate_task = tokio::task::spawn_local(async move {
|
||||
let mut last_ignored = None;
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
udp_event = udp_rx.recv() => {
|
||||
let udp_event = udp_event.ok_or(anyhow!("receiver closed"))??;
|
||||
handle_udp_rx(&server, &producer_tx, &mut consumer, &sender_tx, &mut last_ignored, udp_event, &timer_tx).await;
|
||||
handle_udp_rx(&server, &capture_tx, &mut emulate, &sender_tx, &mut last_ignored, udp_event, &timer_tx).await;
|
||||
}
|
||||
consumer_event = rx.recv() => {
|
||||
match consumer_event {
|
||||
emulate_event = rx.recv() => {
|
||||
match emulate_event {
|
||||
Some(e) => match e {
|
||||
ConsumerEvent::ClientEvent(e) => consumer.notify(e).await,
|
||||
ConsumerEvent::ReleaseKeys(c) => release_keys(&server, &mut consumer, c).await,
|
||||
ConsumerEvent::Terminate => break,
|
||||
EmulationEvent::ClientEvent(e) => emulate.notify(e).await,
|
||||
EmulationEvent::ReleaseKeys(c) => release_keys(&server, &mut emulate, c).await,
|
||||
EmulationEvent::Terminate => break,
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
res = consumer.dispatch() => {
|
||||
res = emulate.dispatch() => {
|
||||
res?;
|
||||
}
|
||||
}
|
||||
@@ -68,20 +68,20 @@ pub fn new(
|
||||
.map(|s| s.client.handle)
|
||||
.collect::<Vec<_>>();
|
||||
for client in clients {
|
||||
release_keys(&server, &mut consumer, client).await;
|
||||
release_keys(&server, &mut emulate, client).await;
|
||||
}
|
||||
|
||||
// destroy consumer
|
||||
consumer.destroy().await;
|
||||
// destroy emulator
|
||||
emulate.destroy().await;
|
||||
anyhow::Ok(())
|
||||
});
|
||||
(consumer_task, tx)
|
||||
(emulate_task, tx)
|
||||
}
|
||||
|
||||
async fn handle_udp_rx(
|
||||
server: &Server,
|
||||
producer_notify_tx: &Sender<ProducerEvent>,
|
||||
consumer: &mut Box<dyn EventConsumer>,
|
||||
capture_tx: &Sender<CaptureEvent>,
|
||||
emulate: &mut Box<dyn InputEmulation>,
|
||||
sender_tx: &Sender<(Event, SocketAddr)>,
|
||||
last_ignored: &mut Option<SocketAddr>,
|
||||
event: (Event, SocketAddr),
|
||||
@@ -127,7 +127,7 @@ async fn handle_udp_rx(
|
||||
let _ = sender_tx.send((Event::Pong(), addr)).await;
|
||||
}
|
||||
(Event::Disconnect(), _) => {
|
||||
release_keys(server, consumer, handle).await;
|
||||
release_keys(server, emulate, handle).await;
|
||||
}
|
||||
(event, addr) => {
|
||||
// tell clients that we are ready to receive events
|
||||
@@ -143,7 +143,7 @@ async fn handle_udp_rx(
|
||||
} else {
|
||||
// upon receiving any event, we go back to receiving mode
|
||||
server.state.replace(State::Receiving);
|
||||
let _ = producer_notify_tx.send(ProducerEvent::Release).await;
|
||||
let _ = capture_tx.send(CaptureEvent::Release).await;
|
||||
log::trace!("STATE ===> Receiving");
|
||||
}
|
||||
}
|
||||
@@ -176,8 +176,8 @@ async fn handle_udp_rx(
|
||||
// workaround buggy rdp backend.
|
||||
if !ignore_event {
|
||||
// consume event
|
||||
consumer.consume(event, handle).await;
|
||||
log::trace!("{event:?} => consumer");
|
||||
emulate.consume(event, handle).await;
|
||||
log::trace!("{event:?} => emulate");
|
||||
}
|
||||
}
|
||||
State::AwaitingLeave => {
|
||||
@@ -194,7 +194,7 @@ async fn handle_udp_rx(
|
||||
// event should still be possible
|
||||
if let Event::Enter() = event {
|
||||
server.state.replace(State::Receiving);
|
||||
let _ = producer_notify_tx.send(ProducerEvent::Release).await;
|
||||
let _ = capture_tx.send(CaptureEvent::Release).await;
|
||||
log::trace!("STATE ===> Receiving");
|
||||
}
|
||||
}
|
||||
@@ -205,7 +205,7 @@ async fn handle_udp_rx(
|
||||
|
||||
async fn release_keys(
|
||||
server: &Server,
|
||||
consumer: &mut Box<dyn EventConsumer>,
|
||||
emulate: &mut Box<dyn InputEmulation>,
|
||||
client: ClientHandle,
|
||||
) {
|
||||
let keys = server
|
||||
@@ -222,7 +222,7 @@ async fn release_keys(
|
||||
key,
|
||||
state: 0,
|
||||
});
|
||||
consumer.consume(event, client).await;
|
||||
emulate.consume(event, client).await;
|
||||
if let Ok(key) = scancode::Linux::try_from(key) {
|
||||
log::warn!("releasing stuck key: {key:?}");
|
||||
}
|
||||
@@ -234,7 +234,7 @@ async fn release_keys(
|
||||
mods_locked: 0,
|
||||
group: 0,
|
||||
};
|
||||
consumer
|
||||
emulate
|
||||
.consume(Event::Keyboard(modifiers_event), client)
|
||||
.await;
|
||||
}
|
||||
@@ -22,15 +22,15 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
consumer_task::ConsumerEvent, producer_task::ProducerEvent, resolver_task::DnsRequest, Server,
|
||||
capture_task::CaptureEvent, emulation_task::EmulationEvent, resolver_task::DnsRequest, Server,
|
||||
};
|
||||
|
||||
pub(crate) fn new(
|
||||
mut frontend: FrontendListener,
|
||||
mut notify_rx: Receiver<FrontendNotify>,
|
||||
server: Server,
|
||||
producer_notify: Sender<ProducerEvent>,
|
||||
consumer_notify: Sender<ConsumerEvent>,
|
||||
capture_notify: Sender<CaptureEvent>,
|
||||
emulate_notify: Sender<EmulationEvent>,
|
||||
resolve_ch: Sender<DnsRequest>,
|
||||
port_tx: Sender<u16>,
|
||||
) -> (JoinHandle<Result<()>>, Sender<FrontendEvent>) {
|
||||
@@ -51,7 +51,7 @@ pub(crate) fn new(
|
||||
}
|
||||
event = event_rx.recv() => {
|
||||
let frontend_event = event.ok_or(anyhow!("frontend channel closed"))?;
|
||||
if handle_frontend_event(&server, &producer_notify, &consumer_notify, &resolve_ch, &mut frontend, &port_tx, frontend_event).await {
|
||||
if handle_frontend_event(&server, &capture_notify, &emulate_notify, &resolve_ch, &mut frontend, &port_tx, frontend_event).await {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -98,8 +98,8 @@ async fn handle_frontend_stream(
|
||||
|
||||
async fn handle_frontend_event(
|
||||
server: &Server,
|
||||
producer_tx: &Sender<ProducerEvent>,
|
||||
consumer_tx: &Sender<ConsumerEvent>,
|
||||
capture_tx: &Sender<CaptureEvent>,
|
||||
emulate_tx: &Sender<EmulationEvent>,
|
||||
resolve_tx: &Sender<DnsRequest>,
|
||||
frontend: &mut FrontendListener,
|
||||
port_tx: &Sender<u16>,
|
||||
@@ -120,7 +120,7 @@ async fn handle_frontend_event(
|
||||
Some(FrontendNotify::NotifyClientCreate(client))
|
||||
}
|
||||
FrontendEvent::ActivateClient(handle, active) => {
|
||||
activate_client(server, producer_tx, consumer_tx, handle, active).await;
|
||||
activate_client(server, capture_tx, emulate_tx, handle, active).await;
|
||||
Some(FrontendNotify::NotifyClientActivate(handle, active))
|
||||
}
|
||||
FrontendEvent::ChangePort(port) => {
|
||||
@@ -128,7 +128,7 @@ async fn handle_frontend_event(
|
||||
None
|
||||
}
|
||||
FrontendEvent::DelClient(handle) => {
|
||||
remove_client(server, producer_tx, consumer_tx, frontend, handle).await;
|
||||
remove_client(server, capture_tx, emulate_tx, frontend, handle).await;
|
||||
Some(FrontendNotify::NotifyClientDelete(handle))
|
||||
}
|
||||
FrontendEvent::Enumerate() => {
|
||||
@@ -147,8 +147,8 @@ async fn handle_frontend_event(
|
||||
FrontendEvent::UpdateClient(handle, hostname, port, pos) => {
|
||||
update_client(
|
||||
server,
|
||||
producer_tx,
|
||||
consumer_tx,
|
||||
capture_tx,
|
||||
emulate_tx,
|
||||
resolve_tx,
|
||||
(handle, hostname, port, pos),
|
||||
)
|
||||
@@ -204,8 +204,8 @@ pub async fn add_client(
|
||||
|
||||
pub async fn activate_client(
|
||||
server: &Server,
|
||||
producer_notify_tx: &Sender<ProducerEvent>,
|
||||
consumer_notify_tx: &Sender<ConsumerEvent>,
|
||||
capture_notify_tx: &Sender<CaptureEvent>,
|
||||
emulate_notify_tx: &Sender<EmulationEvent>,
|
||||
client: ClientHandle,
|
||||
active: bool,
|
||||
) {
|
||||
@@ -217,26 +217,28 @@ pub async fn activate_client(
|
||||
None => return,
|
||||
};
|
||||
if active {
|
||||
let _ = producer_notify_tx
|
||||
.send(ProducerEvent::ClientEvent(ClientEvent::Create(client, pos)))
|
||||
let _ = capture_notify_tx
|
||||
.send(CaptureEvent::ClientEvent(ClientEvent::Create(client, pos)))
|
||||
.await;
|
||||
let _ = consumer_notify_tx
|
||||
.send(ConsumerEvent::ClientEvent(ClientEvent::Create(client, pos)))
|
||||
let _ = emulate_notify_tx
|
||||
.send(EmulationEvent::ClientEvent(ClientEvent::Create(
|
||||
client, pos,
|
||||
)))
|
||||
.await;
|
||||
} else {
|
||||
let _ = producer_notify_tx
|
||||
.send(ProducerEvent::ClientEvent(ClientEvent::Destroy(client)))
|
||||
let _ = capture_notify_tx
|
||||
.send(CaptureEvent::ClientEvent(ClientEvent::Destroy(client)))
|
||||
.await;
|
||||
let _ = consumer_notify_tx
|
||||
.send(ConsumerEvent::ClientEvent(ClientEvent::Destroy(client)))
|
||||
let _ = emulate_notify_tx
|
||||
.send(EmulationEvent::ClientEvent(ClientEvent::Destroy(client)))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remove_client(
|
||||
server: &Server,
|
||||
producer_notify_tx: &Sender<ProducerEvent>,
|
||||
consumer_notify_tx: &Sender<ConsumerEvent>,
|
||||
capture_notify_tx: &Sender<CaptureEvent>,
|
||||
emulate_notify_tx: &Sender<EmulationEvent>,
|
||||
frontend: &mut FrontendListener,
|
||||
client: ClientHandle,
|
||||
) -> Option<ClientHandle> {
|
||||
@@ -250,11 +252,11 @@ pub async fn remove_client(
|
||||
};
|
||||
|
||||
if active {
|
||||
let _ = producer_notify_tx
|
||||
.send(ProducerEvent::ClientEvent(ClientEvent::Destroy(client)))
|
||||
let _ = capture_notify_tx
|
||||
.send(CaptureEvent::ClientEvent(ClientEvent::Destroy(client)))
|
||||
.await;
|
||||
let _ = consumer_notify_tx
|
||||
.send(ConsumerEvent::ClientEvent(ClientEvent::Destroy(client)))
|
||||
let _ = emulate_notify_tx
|
||||
.send(EmulationEvent::ClientEvent(ClientEvent::Destroy(client)))
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -268,8 +270,8 @@ pub async fn remove_client(
|
||||
|
||||
async fn update_client(
|
||||
server: &Server,
|
||||
producer_notify_tx: &Sender<ProducerEvent>,
|
||||
consumer_notify_tx: &Sender<ConsumerEvent>,
|
||||
capture_notify_tx: &Sender<CaptureEvent>,
|
||||
emulate_notify_tx: &Sender<EmulationEvent>,
|
||||
resolve_tx: &Sender<DnsRequest>,
|
||||
client_update: (ClientHandle, Option<String>, u16, Position),
|
||||
) {
|
||||
@@ -311,25 +313,30 @@ async fn update_client(
|
||||
)
|
||||
};
|
||||
|
||||
// update state in event consumer & producer
|
||||
if changed && active {
|
||||
// resolve dns if something changed
|
||||
if changed {
|
||||
// resolve dns
|
||||
if let Some(hostname) = hostname {
|
||||
let _ = resolve_tx.send(DnsRequest { hostname, handle }).await;
|
||||
}
|
||||
}
|
||||
|
||||
// update state in event input emulator & input capture
|
||||
if changed && active {
|
||||
// update state
|
||||
let _ = producer_notify_tx
|
||||
.send(ProducerEvent::ClientEvent(ClientEvent::Destroy(handle)))
|
||||
let _ = capture_notify_tx
|
||||
.send(CaptureEvent::ClientEvent(ClientEvent::Destroy(handle)))
|
||||
.await;
|
||||
let _ = consumer_notify_tx
|
||||
.send(ConsumerEvent::ClientEvent(ClientEvent::Destroy(handle)))
|
||||
let _ = emulate_notify_tx
|
||||
.send(EmulationEvent::ClientEvent(ClientEvent::Destroy(handle)))
|
||||
.await;
|
||||
let _ = producer_notify_tx
|
||||
.send(ProducerEvent::ClientEvent(ClientEvent::Create(handle, pos)))
|
||||
let _ = capture_notify_tx
|
||||
.send(CaptureEvent::ClientEvent(ClientEvent::Create(handle, pos)))
|
||||
.await;
|
||||
let _ = consumer_notify_tx
|
||||
.send(ConsumerEvent::ClientEvent(ClientEvent::Create(handle, pos)))
|
||||
let _ = emulate_notify_tx
|
||||
.send(EmulationEvent::ClientEvent(ClientEvent::Create(
|
||||
handle, pos,
|
||||
)))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,6 @@ fn send_event(sock: &UdpSocket, e: Event, addr: SocketAddr) -> Result<usize> {
|
||||
log::trace!("{:20} ------>->->-> {addr}", e.to_string());
|
||||
let data: Vec<u8> = (&e).into();
|
||||
// When udp blocks, we dont want to block the event loop.
|
||||
// Dropping events is better than potentially crashing the event
|
||||
// producer.
|
||||
// Dropping events is better than potentially crashing the input capture.
|
||||
Ok(sock.try_send_to(&data, addr)?)
|
||||
}
|
||||
|
||||
@@ -7,15 +7,15 @@ use tokio::{
|
||||
|
||||
use crate::{client::ClientHandle, event::Event};
|
||||
|
||||
use super::{consumer_task::ConsumerEvent, producer_task::ProducerEvent, Server, State};
|
||||
use super::{capture_task::CaptureEvent, emulation_task::EmulationEvent, Server, State};
|
||||
|
||||
const MAX_RESPONSE_TIME: Duration = Duration::from_millis(500);
|
||||
|
||||
pub fn new(
|
||||
server: Server,
|
||||
sender_ch: Sender<(Event, SocketAddr)>,
|
||||
consumer_notify: Sender<ConsumerEvent>,
|
||||
producer_notify: Sender<ProducerEvent>,
|
||||
emulate_notify: Sender<EmulationEvent>,
|
||||
capture_notify: Sender<CaptureEvent>,
|
||||
mut timer_rx: Receiver<()>,
|
||||
) -> JoinHandle<()> {
|
||||
// timer task
|
||||
@@ -114,14 +114,14 @@ pub fn new(
|
||||
if receiving {
|
||||
for c in unresponsive_clients {
|
||||
log::warn!("device not responding, releasing keys!");
|
||||
let _ = consumer_notify.send(ConsumerEvent::ReleaseKeys(c)).await;
|
||||
let _ = emulate_notify.send(EmulationEvent::ReleaseKeys(c)).await;
|
||||
}
|
||||
} else {
|
||||
// release pointer if the active client has not responded
|
||||
if !unresponsive_clients.is_empty() {
|
||||
log::warn!("client not responding, releasing pointer!");
|
||||
server.state.replace(State::Receiving);
|
||||
let _ = producer_notify.send(ProducerEvent::Release).await;
|
||||
let _ = capture_notify.send(CaptureEvent::Release).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user