mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-08 04:20:01 +03:00
Compare commits
13 Commits
revert-scr
...
configurab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a038a24317 | ||
|
|
a4cdc138f0 | ||
|
|
db2004fd02 | ||
|
|
3c2f9da5b7 | ||
|
|
1599e6e76a | ||
|
|
aba9ddbdcb | ||
|
|
984efe7dd7 | ||
|
|
160b2532ff | ||
|
|
51d98cc2eb | ||
|
|
b1d9180cca | ||
|
|
08f24293f8 | ||
|
|
e2162b905a | ||
|
|
2ac193a5b2 |
@@ -30,66 +30,57 @@ pub mod x11;
|
||||
/// fallback input capture (does not produce events)
|
||||
pub mod dummy;
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
pub async fn create_backend(
|
||||
backend: CaptureBackend,
|
||||
) -> Result<Box<dyn InputCapture<Item = io::Result<(ClientHandle, Event)>>>, CaptureCreationError> {
|
||||
match backend {
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
CaptureBackend::InputCapturePortal => Ok(Box::new(libei::LibeiInputCapture::new().await?)),
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
CaptureBackend::LayerShell => Ok(Box::new(wayland::WaylandInputCapture::new()?)),
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
CaptureBackend::X11 => Ok(Box::new(x11::X11InputCapture::new()?)),
|
||||
#[cfg(windows)]
|
||||
CaptureBackend::Windows => Ok(Box::new(windows::WindowsInputCapture::new())),
|
||||
#[cfg(target_os = "macos")]
|
||||
CaptureBackend::MacOs => Ok(Box::new(macos::MacOSInputCapture::new()?)),
|
||||
CaptureBackend::Dummy => Ok(Box::new(dummy::DummyInputCapture::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create(
|
||||
backend: Option<CaptureBackend>,
|
||||
) -> Result<Box<dyn InputCapture<Item = io::Result<(ClientHandle, Event)>>>, CaptureCreationError> {
|
||||
if let Some(backend) = backend {
|
||||
return match backend {
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
CaptureBackend::InputCapturePortal => {
|
||||
Ok(Box::new(libei::LibeiInputCapture::new().await?))
|
||||
let b = create_backend(backend).await;
|
||||
if b.is_ok() {
|
||||
log::info!("using capture backend: {backend}");
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
for backend in [
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
CaptureBackend::InputCapturePortal,
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
CaptureBackend::LayerShell,
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
CaptureBackend::X11,
|
||||
#[cfg(windows)]
|
||||
CaptureBackend::Windows,
|
||||
#[cfg(target_os = "macos")]
|
||||
CaptureBackend::MacOs,
|
||||
CaptureBackend::Dummy,
|
||||
] {
|
||||
match create_backend(backend).await {
|
||||
Ok(b) => {
|
||||
log::info!("using capture backend: {backend}");
|
||||
return Ok(b);
|
||||
}
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
CaptureBackend::LayerShell => Ok(Box::new(wayland::WaylandInputCapture::new()?)),
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
CaptureBackend::X11 => Ok(Box::new(x11::X11InputCapture::new()?)),
|
||||
#[cfg(windows)]
|
||||
CaptureBackend::Windows => Ok(Box::new(windows::WindowsInputCapture::new())),
|
||||
#[cfg(target_os = "macos")]
|
||||
CaptureBackend::MacOs => Ok(Box::new(macos::MacOSInputCapture::new()?)),
|
||||
CaptureBackend::Dummy => Ok(Box::new(dummy::DummyInputCapture::new())),
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
match macos::MacOSInputCapture::new() {
|
||||
Ok(p) => return Ok(Box::new(p)),
|
||||
Err(e) => log::info!("macos input capture not available: {e}"),
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
return Ok(Box::new(windows::WindowsInputCapture::new()));
|
||||
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
match libei::LibeiInputCapture::new().await {
|
||||
Ok(p) => {
|
||||
log::info!("using libei input capture");
|
||||
return Ok(Box::new(p));
|
||||
Err(e) => log::warn!("{backend} input capture backend unavailable: {e}"),
|
||||
}
|
||||
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 Ok(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 Ok(Box::new(p));
|
||||
}
|
||||
Err(e) => log::info!("x11 input capture not available: {e}"),
|
||||
}
|
||||
|
||||
log::error!("falling back to dummy input capture");
|
||||
Ok(Box::new(dummy::DummyInputCapture::new()))
|
||||
Err(CaptureCreationError::NoAvailableBackend)
|
||||
}
|
||||
|
||||
pub trait InputCapture: Stream<Item = io::Result<(ClientHandle, Event)>> + Unpin {
|
||||
|
||||
@@ -12,6 +12,7 @@ use wayland_client::{
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CaptureCreationError {
|
||||
NoAvailableBackend,
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
Libei(#[from] LibeiCaptureCreationError),
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
@@ -40,7 +41,8 @@ impl Display for CaptureCreationError {
|
||||
#[cfg(target_os = "macos")]
|
||||
CaptureCreationError::Macos(e) => format!("{e}"),
|
||||
#[cfg(windows)]
|
||||
CaptureCreationError::Windows => String::from(""),
|
||||
CaptureCreationError::Windows => String::new(),
|
||||
CaptureCreationError::NoAvailableBackend => "no available backend".to_string(),
|
||||
};
|
||||
write!(f, "could not create input capture: {reason}")
|
||||
}
|
||||
|
||||
120
src/config.rs
120
src/config.rs
@@ -3,6 +3,7 @@ use clap::{Parser, ValueEnum};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::fmt::Display;
|
||||
use std::net::IpAddr;
|
||||
use std::{error::Error, fs};
|
||||
use toml;
|
||||
@@ -16,8 +17,9 @@ pub const DEFAULT_PORT: u16 = 4242;
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ConfigToml {
|
||||
pub capture_backend: Option<CaptureBackend>,
|
||||
pub emulation_backend: Option<EmulationBackend>,
|
||||
pub port: Option<u16>,
|
||||
pub frontend: Option<String>,
|
||||
pub frontend: Option<Frontend>,
|
||||
pub release_bind: Option<Vec<scancode::Linux>>,
|
||||
pub left: Option<TomlClient>,
|
||||
pub right: Option<TomlClient>,
|
||||
@@ -53,7 +55,7 @@ struct CliArgs {
|
||||
|
||||
/// the frontend to use [cli | gtk]
|
||||
#[arg(short, long)]
|
||||
frontend: Option<String>,
|
||||
frontend: Option<Frontend>,
|
||||
|
||||
/// non-default config file location
|
||||
#[arg(short, long)]
|
||||
@@ -95,18 +97,81 @@ pub enum CaptureBackend {
|
||||
Dummy,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, ValueEnum)]
|
||||
pub enum EmulationBackend {}
|
||||
impl Display for CaptureBackend {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
CaptureBackend::InputCapturePortal => write!(f, "input-capture-portal"),
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
CaptureBackend::LayerShell => write!(f, "layer-shell"),
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
CaptureBackend::X11 => write!(f, "X11"),
|
||||
#[cfg(windows)]
|
||||
CaptureBackend::Windows => write!(f, "windows"),
|
||||
#[cfg(target_os = "macos")]
|
||||
CaptureBackend::MacOs => write!(f, "MacOS"),
|
||||
CaptureBackend::Dummy => write!(f, "dummy"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, ValueEnum)]
|
||||
pub enum EmulationBackend {
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
Wlroots,
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
Libei,
|
||||
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
|
||||
Xdp,
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
X11,
|
||||
#[cfg(windows)]
|
||||
Windows,
|
||||
#[cfg(target_os = "macos")]
|
||||
MacOs,
|
||||
Dummy,
|
||||
}
|
||||
|
||||
impl Display for EmulationBackend {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
EmulationBackend::Wlroots => write!(f, "wlroots"),
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
EmulationBackend::Libei => write!(f, "libei"),
|
||||
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
|
||||
EmulationBackend::Xdp => write!(f, "xdg-desktop-portal"),
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
EmulationBackend::X11 => write!(f, "X11"),
|
||||
#[cfg(windows)]
|
||||
EmulationBackend::Windows => write!(f, "windows"),
|
||||
#[cfg(target_os = "macos")]
|
||||
EmulationBackend::MacOs => write!(f, "macos"),
|
||||
EmulationBackend::Dummy => write!(f, "dummy"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize, ValueEnum)]
|
||||
pub enum Frontend {
|
||||
Gtk,
|
||||
Cli,
|
||||
}
|
||||
|
||||
impl Default for Frontend {
|
||||
fn default() -> Self {
|
||||
if cfg!(feature = "gtk") {
|
||||
Self::Gtk
|
||||
} else {
|
||||
Self::Cli
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
pub capture_backend: Option<CaptureBackend>,
|
||||
pub emulation_backend: Option<EmulationBackend>,
|
||||
pub frontend: Frontend,
|
||||
pub port: u16,
|
||||
pub clients: Vec<(TomlClient, Position)>,
|
||||
@@ -158,33 +223,14 @@ impl Config {
|
||||
Ok(c) => Some(c),
|
||||
};
|
||||
|
||||
let frontend = match args.frontend {
|
||||
None => match &config_toml {
|
||||
Some(c) => c.frontend.clone(),
|
||||
None => None,
|
||||
},
|
||||
frontend => frontend,
|
||||
};
|
||||
let frontend_arg = args.frontend;
|
||||
let frontend_cfg = config_toml.as_ref().and_then(|c| c.frontend);
|
||||
let frontend = frontend_arg.or(frontend_cfg).unwrap_or_default();
|
||||
|
||||
let frontend = match frontend {
|
||||
#[cfg(feature = "gtk")]
|
||||
None => Frontend::Gtk,
|
||||
#[cfg(not(feature = "gtk"))]
|
||||
None => Frontend::Cli,
|
||||
Some(s) => match s.as_str() {
|
||||
"cli" => Frontend::Cli,
|
||||
"gtk" => Frontend::Gtk,
|
||||
_ => Frontend::Cli,
|
||||
},
|
||||
};
|
||||
|
||||
let port = match args.port {
|
||||
Some(port) => port,
|
||||
None => match &config_toml {
|
||||
Some(c) => c.port.unwrap_or(DEFAULT_PORT),
|
||||
None => DEFAULT_PORT,
|
||||
},
|
||||
};
|
||||
let port = args
|
||||
.port
|
||||
.or(config_toml.as_ref().and_then(|c| c.port))
|
||||
.unwrap_or(DEFAULT_PORT);
|
||||
|
||||
log::debug!("{config_toml:?}");
|
||||
let release_bind = config_toml
|
||||
@@ -192,10 +238,13 @@ impl Config {
|
||||
.and_then(|c| c.release_bind.clone())
|
||||
.unwrap_or(Vec::from_iter(DEFAULT_RELEASE_KEYS.iter().cloned()));
|
||||
|
||||
let capture_backend = match args.capture_backend {
|
||||
Some(b) => Some(b),
|
||||
None => config_toml.as_ref().and_then(|c| c.capture_backend),
|
||||
};
|
||||
let capture_backend = args
|
||||
.capture_backend
|
||||
.or(config_toml.as_ref().and_then(|c| c.capture_backend));
|
||||
|
||||
let emulation_backend = args
|
||||
.emulation_backend
|
||||
.or(config_toml.as_ref().and_then(|c| c.emulation_backend));
|
||||
|
||||
let mut clients: Vec<(TomlClient, Position)> = vec![];
|
||||
|
||||
@@ -220,6 +269,7 @@ impl Config {
|
||||
|
||||
Ok(Config {
|
||||
capture_backend,
|
||||
emulation_backend,
|
||||
daemon,
|
||||
frontend,
|
||||
clients,
|
||||
|
||||
112
src/emulate.rs
112
src/emulate.rs
@@ -3,10 +3,13 @@ use std::future;
|
||||
|
||||
use crate::{
|
||||
client::{ClientEvent, ClientHandle},
|
||||
config::EmulationBackend,
|
||||
event::Event,
|
||||
};
|
||||
use anyhow::Result;
|
||||
|
||||
use self::error::EmulationCreationError;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub mod windows;
|
||||
|
||||
@@ -27,6 +30,7 @@ pub mod macos;
|
||||
|
||||
/// fallback input emulation (logs events)
|
||||
pub mod dummy;
|
||||
pub mod error;
|
||||
|
||||
#[async_trait]
|
||||
pub trait InputEmulation: Send {
|
||||
@@ -41,58 +45,60 @@ pub trait InputEmulation: Send {
|
||||
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}"),
|
||||
pub async fn create_backend(
|
||||
backend: EmulationBackend,
|
||||
) -> Result<Box<dyn InputEmulation>, EmulationCreationError> {
|
||||
match backend {
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
EmulationBackend::Wlroots => Ok(Box::new(wlroots::WlrootsEmulation::new()?)),
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
EmulationBackend::Libei => Ok(Box::new(libei::LibeiEmulation::new().await?)),
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
EmulationBackend::X11 => Ok(Box::new(x11::X11Emulation::new()?)),
|
||||
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
|
||||
EmulationBackend::Xdp => Ok(Box::new(
|
||||
xdg_desktop_portal::DesktopPortalEmulation::new().await?,
|
||||
)),
|
||||
#[cfg(windows)]
|
||||
EmulationBackend::Windows => Ok(Box::new(windows::WindowsEmulation::new()?)),
|
||||
#[cfg(target_os = "macos")]
|
||||
EmulationBackend::MacOs => Ok(Box::new(macos::MacOSEmulation::new()?)),
|
||||
EmulationBackend::Dummy => Ok(Box::new(dummy::DummyEmulation::new())),
|
||||
}
|
||||
|
||||
#[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())
|
||||
}
|
||||
|
||||
pub async fn create(
|
||||
backend: Option<EmulationBackend>,
|
||||
) -> Result<Box<dyn InputEmulation>, EmulationCreationError> {
|
||||
if let Some(backend) = backend {
|
||||
let b = create_backend(backend).await;
|
||||
if b.is_ok() {
|
||||
log::info!("using emulation backend: {backend}");
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
for backend in [
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
EmulationBackend::Wlroots,
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
EmulationBackend::Libei,
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
EmulationBackend::X11,
|
||||
#[cfg(windows)]
|
||||
EmulationBackend::Windows,
|
||||
#[cfg(target_os = "macos")]
|
||||
EmulationBackend::MacOs,
|
||||
EmulationBackend::Dummy,
|
||||
] {
|
||||
match create_backend(backend).await {
|
||||
Ok(b) => {
|
||||
log::info!("using emulation backend: {backend}");
|
||||
return Ok(b);
|
||||
}
|
||||
Err(e) => log::warn!("{e}"),
|
||||
}
|
||||
}
|
||||
|
||||
Err(EmulationCreationError::NoAvailableBackend)
|
||||
}
|
||||
|
||||
168
src/emulate/error.rs
Normal file
168
src/emulate/error.rs
Normal file
@@ -0,0 +1,168 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use thiserror::Error;
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
use wayland_client::{
|
||||
backend::WaylandError,
|
||||
globals::{BindError, GlobalError},
|
||||
ConnectError, DispatchError,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum EmulationCreationError {
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
Wlroots(#[from] WlrootsEmulationCreationError),
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
Libei(#[from] LibeiEmulationCreationError),
|
||||
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
|
||||
Xdp(#[from] XdpEmulationCreationError),
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
X11(#[from] X11EmulationCreationError),
|
||||
#[cfg(target_os = "macos")]
|
||||
MacOs(#[from] MacOSEmulationCreationError),
|
||||
#[cfg(windows)]
|
||||
Windows(#[from] WindowsEmulationCreationError),
|
||||
NoAvailableBackend,
|
||||
}
|
||||
|
||||
impl Display for EmulationCreationError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let reason = match self {
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
EmulationCreationError::Wlroots(e) => format!("wlroots backend: {e}"),
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
EmulationCreationError::Libei(e) => format!("libei backend: {e}"),
|
||||
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
|
||||
EmulationCreationError::Xdp(e) => format!("desktop portal backend: {e}"),
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
EmulationCreationError::X11(e) => format!("x11 backend: {e}"),
|
||||
#[cfg(target_os = "macos")]
|
||||
EmulationCreationError::MacOs(e) => format!("macos backend: {e}"),
|
||||
#[cfg(windows)]
|
||||
EmulationCreationError::Windows(e) => format!("windows backend: {e}"),
|
||||
EmulationCreationError::NoAvailableBackend => "no backend available".to_string(),
|
||||
};
|
||||
write!(f, "could not create input emulation backend: {reason}")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum WlrootsEmulationCreationError {
|
||||
Connect(#[from] ConnectError),
|
||||
Global(#[from] GlobalError),
|
||||
Wayland(#[from] WaylandError),
|
||||
Bind(#[from] WaylandBindError),
|
||||
Dispatch(#[from] DispatchError),
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
#[derive(Debug, Error)]
|
||||
pub struct WaylandBindError {
|
||||
inner: BindError,
|
||||
protocol: &'static str,
|
||||
}
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
impl WaylandBindError {
|
||||
pub(crate) fn new(inner: BindError, protocol: &'static str) -> Self {
|
||||
Self { inner, protocol }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
impl Display for WaylandBindError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{} protocol not supported: {}",
|
||||
self.protocol, self.inner
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||
impl Display for WlrootsEmulationCreationError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
WlrootsEmulationCreationError::Bind(e) => write!(f, "{e}"),
|
||||
WlrootsEmulationCreationError::Connect(e) => {
|
||||
write!(f, "could not connect to wayland compositor: {e}")
|
||||
}
|
||||
WlrootsEmulationCreationError::Global(e) => write!(f, "wayland error: {e}"),
|
||||
WlrootsEmulationCreationError::Wayland(e) => write!(f, "wayland error: {e}"),
|
||||
WlrootsEmulationCreationError::Dispatch(e) => {
|
||||
write!(f, "error dispatching wayland events: {e}")
|
||||
}
|
||||
WlrootsEmulationCreationError::Io(e) => write!(f, "io error: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum LibeiEmulationCreationError {
|
||||
Ashpd(#[from] ashpd::Error),
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||
impl Display for LibeiEmulationCreationError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
LibeiEmulationCreationError::Ashpd(e) => write!(f, "xdg-desktop-portal: {e}"),
|
||||
LibeiEmulationCreationError::Io(e) => write!(f, "io error: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum XdpEmulationCreationError {
|
||||
Ashpd(#[from] ashpd::Error),
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "xdg_desktop_portal", not(target_os = "macos")))]
|
||||
impl Display for XdpEmulationCreationError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
XdpEmulationCreationError::Ashpd(e) => write!(f, "portal error: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum X11EmulationCreationError {
|
||||
OpenDisplay,
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||
impl Display for X11EmulationCreationError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
X11EmulationCreationError::OpenDisplay => write!(f, "could not open display!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum MacOSEmulationCreationError {
|
||||
EventSourceCreation,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
impl Display for MacOSEmulationCreationError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
MacOSEmulationCreationError::EventSourceCreation => {
|
||||
write!(f, "could not create event source")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum WindowsEmulationCreationError {}
|
||||
@@ -1,10 +1,11 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io,
|
||||
os::{fd::OwnedFd, unix::net::UnixStream},
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use ashpd::{
|
||||
desktop::{
|
||||
remote_desktop::{DeviceType, RemoteDesktop},
|
||||
@@ -27,6 +28,8 @@ use crate::{
|
||||
event::Event,
|
||||
};
|
||||
|
||||
use super::error::LibeiEmulationCreationError;
|
||||
|
||||
pub struct LibeiEmulation {
|
||||
handshake: bool,
|
||||
context: ei::Context,
|
||||
@@ -77,14 +80,14 @@ async fn get_ei_fd() -> Result<OwnedFd, ashpd::Error> {
|
||||
}
|
||||
|
||||
impl LibeiEmulation {
|
||||
pub async fn new() -> Result<Self> {
|
||||
pub async fn new() -> Result<Self, LibeiEmulationCreationError> {
|
||||
// fd is owned by the message, so we need to dup it
|
||||
let eifd = get_ei_fd().await?;
|
||||
let stream = UnixStream::from(eifd);
|
||||
// let stream = UnixStream::connect("/run/user/1000/eis-0")?;
|
||||
stream.set_nonblocking(true)?;
|
||||
let context = ei::Context::new(stream)?;
|
||||
context.flush()?;
|
||||
context.flush().map_err(|e| io::Error::new(e.kind(), e))?;
|
||||
let events = EiEventStream::new(context.clone())?;
|
||||
Ok(Self {
|
||||
handshake: false,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::client::{ClientEvent, ClientHandle};
|
||||
use crate::emulate::InputEmulation;
|
||||
use crate::event::{Event, KeyboardEvent, PointerEvent};
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use core_graphics::display::{CGDisplayBounds, CGMainDisplayID, CGPoint};
|
||||
use core_graphics::event::{
|
||||
@@ -13,6 +12,8 @@ use std::ops::{Index, IndexMut};
|
||||
use std::time::Duration;
|
||||
use tokio::task::AbortHandle;
|
||||
|
||||
use super::error::MacOSEmulationCreationError;
|
||||
|
||||
const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis(500);
|
||||
const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32);
|
||||
|
||||
@@ -53,11 +54,9 @@ impl IndexMut<CGMouseButton> for ButtonState {
|
||||
unsafe impl Send for MacOSEmulation {}
|
||||
|
||||
impl MacOSEmulation {
|
||||
pub fn new() -> Result<Self> {
|
||||
let event_source = match CGEventSource::new(CGEventSourceStateID::CombinedSessionState) {
|
||||
Ok(e) => e,
|
||||
Err(_) => return Err(anyhow!("event source creation failed!")),
|
||||
};
|
||||
pub fn new() -> Result<Self, MacOSEmulationCreationError> {
|
||||
let event_source = CGEventSource::new(CGEventSourceStateID::CombinedSessionState)
|
||||
.map_err(|_| MacOSEmulationCreationError::EventSourceCreation)?;
|
||||
let button_state = ButtonState {
|
||||
left: false,
|
||||
right: false,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::error::WindowsEmulationCreationError;
|
||||
use crate::{
|
||||
emulate::InputEmulation,
|
||||
event::{KeyboardEvent, PointerEvent},
|
||||
scancode,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use std::ops::BitOrAssign;
|
||||
use std::time::Duration;
|
||||
@@ -33,7 +33,7 @@ pub struct WindowsEmulation {
|
||||
}
|
||||
|
||||
impl WindowsEmulation {
|
||||
pub fn new() -> Result<Self> {
|
||||
pub fn new() -> Result<Self, WindowsEmulationCreationError> {
|
||||
Ok(Self { repeat_task: None })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::client::{ClientEvent, ClientHandle};
|
||||
use crate::emulate::InputEmulation;
|
||||
use crate::emulate::{error::WlrootsEmulationCreationError, InputEmulation};
|
||||
use async_trait::async_trait;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
@@ -7,7 +7,6 @@ use std::os::fd::{AsFd, OwnedFd};
|
||||
use wayland_client::backend::WaylandError;
|
||||
use wayland_client::WEnum;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use wayland_client::protocol::wl_keyboard::{self, WlKeyboard};
|
||||
use wayland_client::protocol::wl_pointer::{Axis, ButtonState};
|
||||
use wayland_client::protocol::wl_seat::WlSeat;
|
||||
@@ -30,6 +29,8 @@ use wayland_client::{
|
||||
|
||||
use crate::event::{Event, KeyboardEvent, PointerEvent};
|
||||
|
||||
use super::error::WaylandBindError;
|
||||
|
||||
struct State {
|
||||
keymap: Option<(u32, OwnedFd, u32)>,
|
||||
input_for_client: HashMap<ClientHandle, VirtualInput>,
|
||||
@@ -47,18 +48,21 @@ pub(crate) struct WlrootsEmulation {
|
||||
}
|
||||
|
||||
impl WlrootsEmulation {
|
||||
pub fn new() -> Result<Self> {
|
||||
pub fn new() -> Result<Self, WlrootsEmulationCreationError> {
|
||||
let conn = Connection::connect_to_env()?;
|
||||
let (globals, queue) = registry_queue_init::<State>(&conn)?;
|
||||
let qh = queue.handle();
|
||||
|
||||
let seat: wl_seat::WlSeat = match globals.bind(&qh, 7..=8, ()) {
|
||||
Ok(wl_seat) => wl_seat,
|
||||
Err(_) => return Err(anyhow!("wl_seat >= v7 not supported")),
|
||||
};
|
||||
let seat: wl_seat::WlSeat = globals
|
||||
.bind(&qh, 7..=8, ())
|
||||
.map_err(|e| WaylandBindError::new(e, "wl_seat 7..=8"))?;
|
||||
|
||||
let vpm: VpManager = globals.bind(&qh, 1..=1, ())?;
|
||||
let vkm: VkManager = globals.bind(&qh, 1..=1, ())?;
|
||||
let vpm: VpManager = globals
|
||||
.bind(&qh, 1..=1, ())
|
||||
.map_err(|e| WaylandBindError::new(e, "wlr-virtual-pointer-unstable-v1"))?;
|
||||
let vkm: VkManager = globals
|
||||
.bind(&qh, 1..=1, ())
|
||||
.map_err(|e| WaylandBindError::new(e, "virtual-keyboard-unstable-v1"))?;
|
||||
|
||||
let input_for_client: HashMap<ClientHandle, VirtualInput> = HashMap::new();
|
||||
|
||||
@@ -75,7 +79,7 @@ impl WlrootsEmulation {
|
||||
queue,
|
||||
};
|
||||
while emulate.state.keymap.is_none() {
|
||||
emulate.queue.blocking_dispatch(&mut emulate.state).unwrap();
|
||||
emulate.queue.blocking_dispatch(&mut emulate.state)?;
|
||||
}
|
||||
// let fd = unsafe { &File::from_raw_fd(emulate.state.keymap.unwrap().1.as_raw_fd()) };
|
||||
// let mmap = unsafe { MmapOptions::new().map_copy(fd).unwrap() };
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use std::ptr;
|
||||
use x11::{
|
||||
@@ -14,6 +13,8 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::error::X11EmulationCreationError;
|
||||
|
||||
pub struct X11Emulation {
|
||||
display: *mut xlib::Display,
|
||||
}
|
||||
@@ -21,11 +22,11 @@ pub struct X11Emulation {
|
||||
unsafe impl Send for X11Emulation {}
|
||||
|
||||
impl X11Emulation {
|
||||
pub fn new() -> Result<Self> {
|
||||
pub fn new() -> Result<Self, X11EmulationCreationError> {
|
||||
let display = unsafe {
|
||||
match xlib::XOpenDisplay(ptr::null()) {
|
||||
d if d == ptr::null::<xlib::Display>() as *mut xlib::Display => {
|
||||
Err(anyhow!("could not open display"))
|
||||
Err(X11EmulationCreationError::OpenDisplay)
|
||||
}
|
||||
display => Ok(display),
|
||||
}
|
||||
|
||||
@@ -17,13 +17,15 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::error::XdpEmulationCreationError;
|
||||
|
||||
pub struct DesktopPortalEmulation<'a> {
|
||||
proxy: RemoteDesktop<'a>,
|
||||
session: Session<'a>,
|
||||
}
|
||||
|
||||
impl<'a> DesktopPortalEmulation<'a> {
|
||||
pub async fn new() -> Result<DesktopPortalEmulation<'a>> {
|
||||
pub async fn new() -> Result<DesktopPortalEmulation<'a>, XdpEmulationCreationError> {
|
||||
log::debug!("connecting to org.freedesktop.portal.RemoteDesktop portal ...");
|
||||
let proxy = RemoteDesktop::new().await?;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::client::{ClientEvent, Position};
|
||||
use crate::config::Config;
|
||||
use crate::emulate;
|
||||
use crate::event::{Event, PointerEvent};
|
||||
use anyhow::Result;
|
||||
@@ -13,14 +14,16 @@ pub fn run() -> Result<()> {
|
||||
.enable_time()
|
||||
.build()?;
|
||||
|
||||
runtime.block_on(LocalSet::new().run_until(input_emulation_test()))
|
||||
let config = Config::new()?;
|
||||
|
||||
runtime.block_on(LocalSet::new().run_until(input_emulation_test(config)))
|
||||
}
|
||||
|
||||
const FREQUENCY_HZ: f64 = 1.0;
|
||||
const RADIUS: f64 = 100.0;
|
||||
|
||||
async fn input_emulation_test() -> Result<()> {
|
||||
let mut emulation = emulate::create().await;
|
||||
async fn input_emulation_test(config: Config) -> Result<()> {
|
||||
let mut emulation = emulate::create(config.emulation_backend).await?;
|
||||
emulation
|
||||
.notify(ClientEvent::Create(0, Position::Left))
|
||||
.await;
|
||||
|
||||
@@ -71,7 +71,9 @@ fn run_service(config: &Config) -> Result<()> {
|
||||
log::info!("Press {:?} to release the mouse", config.release_bind);
|
||||
|
||||
let server = Server::new(config);
|
||||
server.run(config.capture_backend).await?;
|
||||
server
|
||||
.run(config.capture_backend, config.emulation_backend)
|
||||
.await?;
|
||||
|
||||
log::debug!("service exiting");
|
||||
anyhow::Ok(())
|
||||
|
||||
@@ -8,7 +8,7 @@ use tokio::signal;
|
||||
|
||||
use crate::{
|
||||
client::{ClientConfig, ClientHandle, ClientManager, ClientState},
|
||||
config::{CaptureBackend, Config},
|
||||
config::{CaptureBackend, Config, EmulationBackend},
|
||||
dns,
|
||||
frontend::{FrontendListener, FrontendRequest},
|
||||
server::capture_task::CaptureEvent,
|
||||
@@ -77,7 +77,11 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(&self, backend: Option<CaptureBackend>) -> anyhow::Result<()> {
|
||||
pub async fn run(
|
||||
&self,
|
||||
capture_backend: Option<CaptureBackend>,
|
||||
emulation_backend: Option<EmulationBackend>,
|
||||
) -> anyhow::Result<()> {
|
||||
// create frontend communication adapter
|
||||
let frontend = match FrontendListener::new().await {
|
||||
Some(f) => f?,
|
||||
@@ -97,7 +101,7 @@ impl Server {
|
||||
|
||||
// input capture
|
||||
let (mut capture_task, capture_channel) = capture_task::new(
|
||||
backend,
|
||||
capture_backend,
|
||||
self.clone(),
|
||||
sender_tx.clone(),
|
||||
timer_tx.clone(),
|
||||
@@ -106,12 +110,13 @@ impl Server {
|
||||
|
||||
// input emulation
|
||||
let (mut emulation_task, emulate_channel) = emulation_task::new(
|
||||
emulation_backend,
|
||||
self.clone(),
|
||||
receiver_rx,
|
||||
sender_tx.clone(),
|
||||
capture_channel.clone(),
|
||||
timer_tx,
|
||||
);
|
||||
)?;
|
||||
|
||||
// create dns resolver
|
||||
let resolver = dns::DnsResolver::new().await?;
|
||||
|
||||
@@ -8,7 +8,8 @@ use tokio::{
|
||||
|
||||
use crate::{
|
||||
client::{ClientEvent, ClientHandle},
|
||||
emulate::{self, InputEmulation},
|
||||
config::EmulationBackend,
|
||||
emulate::{self, error::EmulationCreationError, InputEmulation},
|
||||
event::{Event, KeyboardEvent},
|
||||
scancode,
|
||||
server::State,
|
||||
@@ -27,15 +28,16 @@ pub enum EmulationEvent {
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
backend: Option<EmulationBackend>,
|
||||
server: Server,
|
||||
mut udp_rx: Receiver<Result<(Event, SocketAddr)>>,
|
||||
sender_tx: Sender<(Event, SocketAddr)>,
|
||||
capture_tx: Sender<CaptureEvent>,
|
||||
timer_tx: Sender<()>,
|
||||
) -> (JoinHandle<Result<()>>, Sender<EmulationEvent>) {
|
||||
) -> Result<(JoinHandle<Result<()>>, Sender<EmulationEvent>), EmulationCreationError> {
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel(32);
|
||||
let emulate_task = tokio::task::spawn_local(async move {
|
||||
let mut emulate = emulate::create().await;
|
||||
let mut emulate = emulate::create(backend).await?;
|
||||
let mut last_ignored = None;
|
||||
|
||||
loop {
|
||||
@@ -75,7 +77,7 @@ pub fn new(
|
||||
emulate.destroy().await;
|
||||
anyhow::Ok(())
|
||||
});
|
||||
(emulate_task, tx)
|
||||
Ok((emulate_task, tx))
|
||||
}
|
||||
|
||||
async fn handle_udp_rx(
|
||||
|
||||
Reference in New Issue
Block a user