mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-22 12:40:54 +03:00
Compare commits
1 Commits
configurab
...
fix-usize-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81e2c59e8f |
9
Cargo.lock
generated
9
Cargo.lock
generated
@@ -1312,7 +1312,6 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"slab",
|
"slab",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
@@ -2012,18 +2011,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.61"
|
version = "1.0.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.61"
|
version = "1.0.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ num_enum = "0.7.2"
|
|||||||
hostname = "0.4.0"
|
hostname = "0.4.0"
|
||||||
slab = "0.4.9"
|
slab = "0.4.9"
|
||||||
endi = "1.1.0"
|
endi = "1.1.0"
|
||||||
thiserror = "1.0.61"
|
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2.148"
|
libc = "0.2.148"
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -69,15 +69,7 @@ Precompiled release binaries for Windows, MacOS and Linux are available in the [
|
|||||||
For Windows, the depenedencies are included in the .zip file, for other operating systems see [Installing Dependencies](#installing-dependencies).
|
For Windows, the depenedencies are included in the .zip file, for other operating systems see [Installing Dependencies](#installing-dependencies).
|
||||||
|
|
||||||
### Arch Linux
|
### Arch Linux
|
||||||
|
Lan Mouse is available on the AUR:
|
||||||
Lan Mouse can be installed from the [official repositories](https://archlinux.org/packages/extra/x86_64/lan-mouse/):
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pacman -S lan-mouse
|
|
||||||
```
|
|
||||||
|
|
||||||
It is also available on the AUR:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# git version (includes latest changes)
|
# git version (includes latest changes)
|
||||||
paru -S lan-mouse-git
|
paru -S lan-mouse-git
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
# example configuration
|
# example configuration
|
||||||
|
|
||||||
# capture_backend = "LayerShell"
|
|
||||||
|
|
||||||
# release bind
|
# release bind
|
||||||
release_bind = [ "KeyA", "KeyS", "KeyD", "KeyF" ]
|
release_bind = [ "KeyA", "KeyS", "KeyD", "KeyF" ]
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,9 @@ use futures_core::Stream;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{ClientEvent, ClientHandle},
|
client::{ClientEvent, ClientHandle},
|
||||||
config::CaptureBackend,
|
|
||||||
event::Event,
|
event::Event,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::error::CaptureCreationError;
|
|
||||||
|
|
||||||
pub mod error;
|
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||||
pub mod libei;
|
pub mod libei;
|
||||||
|
|
||||||
@@ -30,42 +25,24 @@ pub mod x11;
|
|||||||
/// fallback input capture (does not produce events)
|
/// fallback input capture (does not produce events)
|
||||||
pub mod dummy;
|
pub mod dummy;
|
||||||
|
|
||||||
#[allow(unreachable_code)]
|
pub async fn create() -> Box<dyn InputCapture<Item = io::Result<(ClientHandle, Event)>>> {
|
||||||
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?))
|
|
||||||
}
|
|
||||||
#[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")]
|
#[cfg(target_os = "macos")]
|
||||||
match macos::MacOSInputCapture::new() {
|
match macos::MacOSInputCapture::new() {
|
||||||
Ok(p) => return Ok(Box::new(p)),
|
Ok(p) => return Box::new(p),
|
||||||
Err(e) => log::info!("macos input capture not available: {e}"),
|
Err(e) => log::info!("macos input capture not available: {e}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
return Ok(Box::new(windows::WindowsInputCapture::new()));
|
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")))]
|
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||||
match libei::LibeiInputCapture::new().await {
|
match libei::LibeiInputCapture::new().await {
|
||||||
Ok(p) => {
|
Ok(p) => {
|
||||||
log::info!("using libei input capture");
|
log::info!("using libei input capture");
|
||||||
return Ok(Box::new(p));
|
return Box::new(p);
|
||||||
}
|
}
|
||||||
Err(e) => log::info!("libei input capture not available: {e}"),
|
Err(e) => log::info!("libei input capture not available: {e}"),
|
||||||
}
|
}
|
||||||
@@ -74,7 +51,7 @@ pub async fn create(
|
|||||||
match wayland::WaylandInputCapture::new() {
|
match wayland::WaylandInputCapture::new() {
|
||||||
Ok(p) => {
|
Ok(p) => {
|
||||||
log::info!("using layer-shell input capture");
|
log::info!("using layer-shell input capture");
|
||||||
return Ok(Box::new(p));
|
return Box::new(p);
|
||||||
}
|
}
|
||||||
Err(e) => log::info!("layer_shell input capture not available: {e}"),
|
Err(e) => log::info!("layer_shell input capture not available: {e}"),
|
||||||
}
|
}
|
||||||
@@ -83,13 +60,13 @@ pub async fn create(
|
|||||||
match x11::X11InputCapture::new() {
|
match x11::X11InputCapture::new() {
|
||||||
Ok(p) => {
|
Ok(p) => {
|
||||||
log::info!("using x11 input capture");
|
log::info!("using x11 input capture");
|
||||||
return Ok(Box::new(p));
|
return Box::new(p);
|
||||||
}
|
}
|
||||||
Err(e) => log::info!("x11 input capture not available: {e}"),
|
Err(e) => log::info!("x11 input capture not available: {e}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
log::error!("falling back to dummy input capture");
|
log::error!("falling back to dummy input capture");
|
||||||
Ok(Box::new(dummy::DummyInputCapture::new()))
|
Box::new(dummy::DummyInputCapture::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait InputCapture: Stream<Item = io::Result<(ClientHandle, Event)>> + Unpin {
|
pub trait InputCapture: Stream<Item = io::Result<(ClientHandle, Event)>> + Unpin {
|
||||||
|
|||||||
@@ -1,140 +0,0 @@
|
|||||||
use std::fmt::Display;
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
|
||||||
use std::io;
|
|
||||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
|
||||||
use wayland_client::{
|
|
||||||
backend::WaylandError,
|
|
||||||
globals::{BindError, GlobalError},
|
|
||||||
ConnectError, DispatchError,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum CaptureCreationError {
|
|
||||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
|
||||||
Libei(#[from] LibeiCaptureCreationError),
|
|
||||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
|
||||||
LayerShell(#[from] LayerShellCaptureCreationError),
|
|
||||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
|
||||||
X11(#[from] X11InputCaptureCreationError),
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
Macos(#[from] MacOSInputCaptureCreationError),
|
|
||||||
#[cfg(windows)]
|
|
||||||
Windows,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for CaptureCreationError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let reason = match self {
|
|
||||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
|
||||||
CaptureCreationError::Libei(reason) => {
|
|
||||||
format!("error creating portal backend: {reason}")
|
|
||||||
}
|
|
||||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
|
||||||
CaptureCreationError::LayerShell(reason) => {
|
|
||||||
format!("error creating layer-shell backend: {reason}")
|
|
||||||
}
|
|
||||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
|
||||||
CaptureCreationError::X11(e) => format!("{e}"),
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
CaptureCreationError::Macos(e) => format!("{e}"),
|
|
||||||
#[cfg(windows)]
|
|
||||||
CaptureCreationError::Windows => String::from(""),
|
|
||||||
};
|
|
||||||
write!(f, "could not create input capture: {reason}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum LibeiCaptureCreationError {
|
|
||||||
Ashpd(#[from] ashpd::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
|
||||||
impl Display for LibeiCaptureCreationError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
LibeiCaptureCreationError::Ashpd(portal_error) => write!(f, "{portal_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")))]
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum LayerShellCaptureCreationError {
|
|
||||||
Connect(#[from] ConnectError),
|
|
||||||
Global(#[from] GlobalError),
|
|
||||||
Wayland(#[from] WaylandError),
|
|
||||||
Bind(#[from] WaylandBindError),
|
|
||||||
Dispatch(#[from] DispatchError),
|
|
||||||
Io(#[from] io::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
|
||||||
impl Display for LayerShellCaptureCreationError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
LayerShellCaptureCreationError::Bind(e) => write!(f, "{e}"),
|
|
||||||
LayerShellCaptureCreationError::Connect(e) => {
|
|
||||||
write!(f, "could not connect to wayland compositor: {e}")
|
|
||||||
}
|
|
||||||
LayerShellCaptureCreationError::Global(e) => write!(f, "wayland error: {e}"),
|
|
||||||
LayerShellCaptureCreationError::Wayland(e) => write!(f, "wayland error: {e}"),
|
|
||||||
LayerShellCaptureCreationError::Dispatch(e) => {
|
|
||||||
write!(f, "error dispatching wayland events: {e}")
|
|
||||||
}
|
|
||||||
LayerShellCaptureCreationError::Io(e) => write!(f, "io error: {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum X11InputCaptureCreationError {
|
|
||||||
NotImplemented,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
|
||||||
impl Display for X11InputCaptureCreationError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "X11 input capture is not yet implemented :(")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum MacOSInputCaptureCreationError {
|
|
||||||
NotImplemented,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
impl Display for MacOSInputCaptureCreationError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "macos input capture is not yet implemented :(")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -36,8 +36,6 @@ use crate::{
|
|||||||
event::{Event, KeyboardEvent, PointerEvent},
|
event::{Event, KeyboardEvent, PointerEvent},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::error::LibeiCaptureCreationError;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum ProducerEvent {
|
enum ProducerEvent {
|
||||||
Release,
|
Release,
|
||||||
@@ -133,7 +131,7 @@ impl<'a> Drop for LibeiInputCapture<'a> {
|
|||||||
|
|
||||||
async fn create_session<'a>(
|
async fn create_session<'a>(
|
||||||
input_capture: &'a InputCapture<'a>,
|
input_capture: &'a InputCapture<'a>,
|
||||||
) -> std::result::Result<(Session<'a>, BitFlags<Capabilities>), ashpd::Error> {
|
) -> Result<(Session<'a>, BitFlags<Capabilities>)> {
|
||||||
log::debug!("creating input capture session");
|
log::debug!("creating input capture session");
|
||||||
let (session, capabilities) = loop {
|
let (session, capabilities) = loop {
|
||||||
match input_capture
|
match input_capture
|
||||||
@@ -215,7 +213,7 @@ async fn wait_for_active_client(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LibeiInputCapture<'a> {
|
impl<'a> LibeiInputCapture<'a> {
|
||||||
pub async fn new() -> std::result::Result<Self, LibeiCaptureCreationError> {
|
pub async fn new() -> Result<Self> {
|
||||||
let input_capture = Box::pin(InputCapture::new().await?);
|
let input_capture = Box::pin(InputCapture::new().await?);
|
||||||
let input_capture_ptr = input_capture.as_ref().get_ref() as *const InputCapture<'static>;
|
let input_capture_ptr = input_capture.as_ref().get_ref() as *const InputCapture<'static>;
|
||||||
let mut first_session = Some(create_session(unsafe { &*input_capture_ptr }).await?);
|
let mut first_session = Some(create_session(unsafe { &*input_capture_ptr }).await?);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::capture::error::MacOSInputCaptureCreationError;
|
|
||||||
use crate::capture::InputCapture;
|
use crate::capture::InputCapture;
|
||||||
use crate::client::{ClientEvent, ClientHandle};
|
use crate::client::{ClientEvent, ClientHandle};
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::{io, pin::Pin};
|
use std::{io, pin::Pin};
|
||||||
@@ -9,8 +9,8 @@ use std::{io, pin::Pin};
|
|||||||
pub struct MacOSInputCapture;
|
pub struct MacOSInputCapture;
|
||||||
|
|
||||||
impl MacOSInputCapture {
|
impl MacOSInputCapture {
|
||||||
pub fn new() -> std::result::Result<Self, MacOSInputCaptureCreationError> {
|
pub fn new() -> Result<Self> {
|
||||||
Err(MacOSInputCaptureCreationError::NotImplemented)
|
Err(anyhow!("not yet implemented"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
capture::{error::WaylandBindError, InputCapture},
|
capture::InputCapture,
|
||||||
client::{ClientEvent, ClientHandle, Position},
|
client::{ClientEvent, ClientHandle, Position},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use memmap::MmapOptions;
|
use memmap::MmapOptions;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -67,8 +68,6 @@ use tempfile;
|
|||||||
|
|
||||||
use crate::event::{Event, KeyboardEvent, PointerEvent};
|
use crate::event::{Event, KeyboardEvent, PointerEvent};
|
||||||
|
|
||||||
use super::error::LayerShellCaptureCreationError;
|
|
||||||
|
|
||||||
struct Globals {
|
struct Globals {
|
||||||
compositor: wl_compositor::WlCompositor,
|
compositor: wl_compositor::WlCompositor,
|
||||||
pointer_constraints: ZwpPointerConstraintsV1,
|
pointer_constraints: ZwpPointerConstraintsV1,
|
||||||
@@ -259,37 +258,64 @@ fn draw(f: &mut File, (width, height): (u32, u32)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WaylandInputCapture {
|
impl WaylandInputCapture {
|
||||||
pub fn new() -> std::result::Result<Self, LayerShellCaptureCreationError> {
|
pub fn new() -> Result<Self> {
|
||||||
let conn = Connection::connect_to_env()?;
|
let conn = match Connection::connect_to_env() {
|
||||||
let (g, mut queue) = registry_queue_init::<State>(&conn)?;
|
Ok(c) => c,
|
||||||
|
Err(e) => return Err(anyhow!("could not connect to wayland compositor: {e:?}")),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (g, mut queue) = match registry_queue_init::<State>(&conn) {
|
||||||
|
Ok(q) => q,
|
||||||
|
Err(e) => return Err(anyhow!("failed to initialize wl_registry: {e:?}")),
|
||||||
|
};
|
||||||
|
|
||||||
let qh = queue.handle();
|
let qh = queue.handle();
|
||||||
|
|
||||||
let compositor: wl_compositor::WlCompositor = g
|
let compositor: wl_compositor::WlCompositor = match g.bind(&qh, 4..=5, ()) {
|
||||||
.bind(&qh, 4..=5, ())
|
Ok(compositor) => compositor,
|
||||||
.map_err(|e| WaylandBindError::new(e, "wl_compositor 4..=5"))?;
|
Err(_) => return Err(anyhow!("wl_compositor >= v4 not supported")),
|
||||||
let xdg_output_manager: ZxdgOutputManagerV1 = g
|
};
|
||||||
.bind(&qh, 1..=3, ())
|
|
||||||
.map_err(|e| WaylandBindError::new(e, "xdg_output_manager 1..=3"))?;
|
let xdg_output_manager: ZxdgOutputManagerV1 = match g.bind(&qh, 1..=3, ()) {
|
||||||
let shm: wl_shm::WlShm = g
|
Ok(xdg_output_manager) => xdg_output_manager,
|
||||||
.bind(&qh, 1..=1, ())
|
Err(_) => return Err(anyhow!("xdg_output not supported!")),
|
||||||
.map_err(|e| WaylandBindError::new(e, "wl_shm"))?;
|
};
|
||||||
let layer_shell: ZwlrLayerShellV1 = g
|
|
||||||
.bind(&qh, 3..=4, ())
|
let shm: wl_shm::WlShm = match g.bind(&qh, 1..=1, ()) {
|
||||||
.map_err(|e| WaylandBindError::new(e, "wlr_layer_shell 3..=4"))?;
|
Ok(wl_shm) => wl_shm,
|
||||||
let seat: wl_seat::WlSeat = g
|
Err(_) => return Err(anyhow!("wl_shm v1 not supported")),
|
||||||
.bind(&qh, 7..=8, ())
|
};
|
||||||
.map_err(|e| WaylandBindError::new(e, "wl_seat 7..=8"))?;
|
|
||||||
|
let layer_shell: ZwlrLayerShellV1 = match g.bind(&qh, 3..=4, ()) {
|
||||||
|
Ok(layer_shell) => layer_shell,
|
||||||
|
Err(_) => return Err(anyhow!("zwlr_layer_shell_v1 >= v3 not supported - required to display a surface at the edge of the screen")),
|
||||||
|
};
|
||||||
|
|
||||||
|
let seat: wl_seat::WlSeat = match g.bind(&qh, 7..=8, ()) {
|
||||||
|
Ok(wl_seat) => wl_seat,
|
||||||
|
Err(_) => return Err(anyhow!("wl_seat >= v7 not supported")),
|
||||||
|
};
|
||||||
|
|
||||||
|
let pointer_constraints: ZwpPointerConstraintsV1 = match g.bind(&qh, 1..=1, ()) {
|
||||||
|
Ok(pointer_constraints) => pointer_constraints,
|
||||||
|
Err(_) => return Err(anyhow!("zwp_pointer_constraints_v1 not supported")),
|
||||||
|
};
|
||||||
|
|
||||||
|
let relative_pointer_manager: ZwpRelativePointerManagerV1 = match g.bind(&qh, 1..=1, ()) {
|
||||||
|
Ok(relative_pointer_manager) => relative_pointer_manager,
|
||||||
|
Err(_) => return Err(anyhow!("zwp_relative_pointer_manager_v1 not supported")),
|
||||||
|
};
|
||||||
|
|
||||||
|
let shortcut_inhibit_manager: ZwpKeyboardShortcutsInhibitManagerV1 =
|
||||||
|
match g.bind(&qh, 1..=1, ()) {
|
||||||
|
Ok(shortcut_inhibit_manager) => shortcut_inhibit_manager,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"zwp_keyboard_shortcuts_inhibit_manager_v1 not supported"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let pointer_constraints: ZwpPointerConstraintsV1 = g
|
|
||||||
.bind(&qh, 1..=1, ())
|
|
||||||
.map_err(|e| WaylandBindError::new(e, "zwp_pointer_constraints_v1"))?;
|
|
||||||
let relative_pointer_manager: ZwpRelativePointerManagerV1 = g
|
|
||||||
.bind(&qh, 1..=1, ())
|
|
||||||
.map_err(|e| WaylandBindError::new(e, "zwp_relative_pointer_manager_v1"))?;
|
|
||||||
let shortcut_inhibit_manager: ZwpKeyboardShortcutsInhibitManagerV1 = g
|
|
||||||
.bind(&qh, 1..=1, ())
|
|
||||||
.map_err(|e| WaylandBindError::new(e, "zwp_keyboard_shortcuts_inhibit_manager_v1"))?;
|
|
||||||
let outputs = vec![];
|
let outputs = vec![];
|
||||||
|
|
||||||
let g = Globals {
|
let g = Globals {
|
||||||
|
|||||||
@@ -96,7 +96,8 @@ unsafe fn get_event_tid() -> Option<u32> {
|
|||||||
static mut ENTRY_POINT: (i32, i32) = (0, 0);
|
static mut ENTRY_POINT: (i32, i32) = (0, 0);
|
||||||
|
|
||||||
fn to_mouse_event(wparam: WPARAM, lparam: LPARAM) -> Option<PointerEvent> {
|
fn to_mouse_event(wparam: WPARAM, lparam: LPARAM) -> Option<PointerEvent> {
|
||||||
let mouse_low_level: MSLLHOOKSTRUCT = unsafe { *(lparam.0 as *const MSLLHOOKSTRUCT) };
|
let mouse_low_level: MSLLHOOKSTRUCT =
|
||||||
|
unsafe { *std::mem::transmute::<LPARAM, *const MSLLHOOKSTRUCT>(lparam) };
|
||||||
match wparam {
|
match wparam {
|
||||||
WPARAM(p) if p == WM_LBUTTONDOWN as usize => Some(PointerEvent::Button {
|
WPARAM(p) if p == WM_LBUTTONDOWN as usize => Some(PointerEvent::Button {
|
||||||
time: 0,
|
time: 0,
|
||||||
@@ -166,7 +167,8 @@ fn to_mouse_event(wparam: WPARAM, lparam: LPARAM) -> Option<PointerEvent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn to_key_event(wparam: WPARAM, lparam: LPARAM) -> Option<KeyboardEvent> {
|
unsafe fn to_key_event(wparam: WPARAM, lparam: LPARAM) -> Option<KeyboardEvent> {
|
||||||
let kybrdllhookstruct: KBDLLHOOKSTRUCT = *(lparam.0 as *const KBDLLHOOKSTRUCT);
|
let kybrdllhookstruct: KBDLLHOOKSTRUCT =
|
||||||
|
*std::mem::transmute::<LPARAM, *const KBDLLHOOKSTRUCT>(lparam);
|
||||||
let mut scan_code = kybrdllhookstruct.scanCode;
|
let mut scan_code = kybrdllhookstruct.scanCode;
|
||||||
log::trace!("scan_code: {scan_code}");
|
log::trace!("scan_code: {scan_code}");
|
||||||
if kybrdllhookstruct.flags.contains(LLKHF_EXTENDED) {
|
if kybrdllhookstruct.flags.contains(LLKHF_EXTENDED) {
|
||||||
@@ -245,7 +247,8 @@ unsafe fn check_client_activation(wparam: WPARAM, lparam: LPARAM) -> bool {
|
|||||||
if wparam.0 != WM_MOUSEMOVE as usize {
|
if wparam.0 != WM_MOUSEMOVE as usize {
|
||||||
return ACTIVE_CLIENT.is_some();
|
return ACTIVE_CLIENT.is_some();
|
||||||
}
|
}
|
||||||
let mouse_low_level: MSLLHOOKSTRUCT = *(lparam.0 as *const MSLLHOOKSTRUCT);
|
let mouse_low_level: MSLLHOOKSTRUCT =
|
||||||
|
unsafe { *std::mem::transmute::<LPARAM, *const MSLLHOOKSTRUCT>(lparam) };
|
||||||
static mut PREV_POS: Option<(i32, i32)> = None;
|
static mut PREV_POS: Option<(i32, i32)> = None;
|
||||||
let curr_pos = (mouse_low_level.pt.x, mouse_low_level.pt.y);
|
let curr_pos = (mouse_low_level.pt.x, mouse_low_level.pt.y);
|
||||||
let prev_pos = PREV_POS.unwrap_or(curr_pos);
|
let prev_pos = PREV_POS.unwrap_or(curr_pos);
|
||||||
@@ -575,7 +578,7 @@ fn update_clients(client_event: ClientEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WindowsInputCapture {
|
impl WindowsInputCapture {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Result<Self> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let (tx, rx) = channel(10);
|
let (tx, rx) = channel(10);
|
||||||
EVENT_TX.replace(tx);
|
EVENT_TX.replace(tx);
|
||||||
@@ -583,10 +586,10 @@ impl WindowsInputCapture {
|
|||||||
let msg_thread = Some(thread::spawn(|| message_thread(ready_tx)));
|
let msg_thread = Some(thread::spawn(|| message_thread(ready_tx)));
|
||||||
/* wait for thread to set its id */
|
/* wait for thread to set its id */
|
||||||
ready_rx.recv().expect("channel closed");
|
ready_rx.recv().expect("channel closed");
|
||||||
Self {
|
Ok(Self {
|
||||||
msg_thread,
|
msg_thread,
|
||||||
event_rx: rx,
|
event_rx: rx,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use anyhow::{anyhow, Result};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
|
|
||||||
@@ -8,13 +9,11 @@ use crate::event::Event;
|
|||||||
|
|
||||||
use crate::client::{ClientEvent, ClientHandle};
|
use crate::client::{ClientEvent, ClientHandle};
|
||||||
|
|
||||||
use super::error::X11InputCaptureCreationError;
|
|
||||||
|
|
||||||
pub struct X11InputCapture {}
|
pub struct X11InputCapture {}
|
||||||
|
|
||||||
impl X11InputCapture {
|
impl X11InputCapture {
|
||||||
pub fn new() -> std::result::Result<Self, X11InputCaptureCreationError> {
|
pub fn new() -> Result<Self> {
|
||||||
Err(X11InputCaptureCreationError::NotImplemented)
|
Err(anyhow!("not implemented"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use crate::capture;
|
use crate::capture;
|
||||||
use crate::client::{ClientEvent, Position};
|
use crate::client::{ClientEvent, Position};
|
||||||
use crate::config::Config;
|
|
||||||
use crate::event::{Event, KeyboardEvent};
|
use crate::event::{Event, KeyboardEvent};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
@@ -13,14 +12,12 @@ pub fn run() -> Result<()> {
|
|||||||
.enable_time()
|
.enable_time()
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let config = Config::new()?;
|
runtime.block_on(LocalSet::new().run_until(input_capture_test()))
|
||||||
|
|
||||||
runtime.block_on(LocalSet::new().run_until(input_capture_test(config)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn input_capture_test(config: Config) -> Result<()> {
|
async fn input_capture_test() -> Result<()> {
|
||||||
log::info!("creating input capture");
|
log::info!("creating input capture");
|
||||||
let mut input_capture = capture::create(config.capture_backend).await?;
|
let mut input_capture = capture::create().await;
|
||||||
log::info!("creating clients");
|
log::info!("creating clients");
|
||||||
input_capture.notify(ClientEvent::Create(0, Position::Left))?;
|
input_capture.notify(ClientEvent::Create(0, Position::Left))?;
|
||||||
input_capture.notify(ClientEvent::Create(1, Position::Right))?;
|
input_capture.notify(ClientEvent::Create(1, Position::Right))?;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{Parser, ValueEnum};
|
use clap::Parser;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::env;
|
use std::env;
|
||||||
@@ -15,7 +15,6 @@ pub const DEFAULT_PORT: u16 = 4242;
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct ConfigToml {
|
pub struct ConfigToml {
|
||||||
pub capture_backend: Option<CaptureBackend>,
|
|
||||||
pub port: Option<u16>,
|
pub port: Option<u16>,
|
||||||
pub frontend: Option<String>,
|
pub frontend: Option<String>,
|
||||||
pub release_bind: Option<Vec<scancode::Linux>>,
|
pub release_bind: Option<Vec<scancode::Linux>>,
|
||||||
@@ -27,7 +26,6 @@ pub struct ConfigToml {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||||
pub struct TomlClient {
|
pub struct TomlClient {
|
||||||
pub capture_backend: Option<CaptureBackend>,
|
|
||||||
pub hostname: Option<String>,
|
pub hostname: Option<String>,
|
||||||
pub host_name: Option<String>,
|
pub host_name: Option<String>,
|
||||||
pub ips: Option<Vec<IpAddr>>,
|
pub ips: Option<Vec<IpAddr>>,
|
||||||
@@ -70,34 +68,8 @@ struct CliArgs {
|
|||||||
/// test input emulation
|
/// test input emulation
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
test_emulation: bool,
|
test_emulation: bool,
|
||||||
|
|
||||||
/// capture backend override
|
|
||||||
#[arg(long)]
|
|
||||||
capture_backend: Option<CaptureBackend>,
|
|
||||||
|
|
||||||
/// emulation backend override
|
|
||||||
#[arg(long)]
|
|
||||||
emulation_backend: Option<EmulationBackend>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, ValueEnum)]
|
|
||||||
pub enum CaptureBackend {
|
|
||||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
|
||||||
InputCapturePortal,
|
|
||||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
|
||||||
LayerShell,
|
|
||||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
|
||||||
X11,
|
|
||||||
#[cfg(windows)]
|
|
||||||
Windows,
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
MacOs,
|
|
||||||
Dummy,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, ValueEnum)]
|
|
||||||
pub enum EmulationBackend {}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Frontend {
|
pub enum Frontend {
|
||||||
Gtk,
|
Gtk,
|
||||||
@@ -106,7 +78,6 @@ pub enum Frontend {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub capture_backend: Option<CaptureBackend>,
|
|
||||||
pub frontend: Frontend,
|
pub frontend: Frontend,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub clients: Vec<(TomlClient, Position)>,
|
pub clients: Vec<(TomlClient, Position)>,
|
||||||
@@ -192,11 +163,6 @@ impl Config {
|
|||||||
.and_then(|c| c.release_bind.clone())
|
.and_then(|c| c.release_bind.clone())
|
||||||
.unwrap_or(Vec::from_iter(DEFAULT_RELEASE_KEYS.iter().cloned()));
|
.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 mut clients: Vec<(TomlClient, Position)> = vec![];
|
let mut clients: Vec<(TomlClient, Position)> = vec![];
|
||||||
|
|
||||||
if let Some(config_toml) = config_toml {
|
if let Some(config_toml) = config_toml {
|
||||||
@@ -219,7 +185,6 @@ impl Config {
|
|||||||
let test_emulation = args.test_emulation;
|
let test_emulation = args.test_emulation;
|
||||||
|
|
||||||
Ok(Config {
|
Ok(Config {
|
||||||
capture_backend,
|
|
||||||
daemon,
|
daemon,
|
||||||
frontend,
|
frontend,
|
||||||
clients,
|
clients,
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ fn run_service(config: &Config) -> Result<()> {
|
|||||||
log::info!("Press Ctrl+Alt+Shift+Super to release the mouse");
|
log::info!("Press Ctrl+Alt+Shift+Super to release the mouse");
|
||||||
|
|
||||||
let server = Server::new(config);
|
let server = Server::new(config);
|
||||||
server.run(config.capture_backend).await?;
|
server.run().await?;
|
||||||
|
|
||||||
log::debug!("service exiting");
|
log::debug!("service exiting");
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use tokio::signal;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{ClientConfig, ClientHandle, ClientManager, ClientState},
|
client::{ClientConfig, ClientHandle, ClientManager, ClientState},
|
||||||
config::{CaptureBackend, Config},
|
config::Config,
|
||||||
dns,
|
dns,
|
||||||
frontend::{FrontendListener, FrontendRequest},
|
frontend::{FrontendListener, FrontendRequest},
|
||||||
server::capture_task::CaptureEvent,
|
server::capture_task::CaptureEvent,
|
||||||
@@ -77,7 +77,7 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(&self, backend: Option<CaptureBackend>) -> anyhow::Result<()> {
|
pub async fn run(&self) -> anyhow::Result<()> {
|
||||||
// create frontend communication adapter
|
// create frontend communication adapter
|
||||||
let frontend = match FrontendListener::new().await {
|
let frontend = match FrontendListener::new().await {
|
||||||
Some(f) => f?,
|
Some(f) => f?,
|
||||||
@@ -97,12 +97,11 @@ impl Server {
|
|||||||
|
|
||||||
// input capture
|
// input capture
|
||||||
let (mut capture_task, capture_channel) = capture_task::new(
|
let (mut capture_task, capture_channel) = capture_task::new(
|
||||||
backend,
|
|
||||||
self.clone(),
|
self.clone(),
|
||||||
sender_tx.clone(),
|
sender_tx.clone(),
|
||||||
timer_tx.clone(),
|
timer_tx.clone(),
|
||||||
self.release_bind.clone(),
|
self.release_bind.clone(),
|
||||||
)?;
|
);
|
||||||
|
|
||||||
// input emulation
|
// input emulation
|
||||||
let (mut emulation_task, emulate_channel) = emulation_task::new(
|
let (mut emulation_task, emulate_channel) = emulation_task::new(
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ use std::{collections::HashSet, net::SocketAddr};
|
|||||||
use tokio::{process::Command, sync::mpsc::Sender, task::JoinHandle};
|
use tokio::{process::Command, sync::mpsc::Sender, task::JoinHandle};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
capture::{self, error::CaptureCreationError, InputCapture},
|
capture::{self, InputCapture},
|
||||||
client::{ClientEvent, ClientHandle},
|
client::{ClientEvent, ClientHandle},
|
||||||
config::CaptureBackend,
|
|
||||||
event::{Event, KeyboardEvent},
|
event::{Event, KeyboardEvent},
|
||||||
scancode,
|
scancode,
|
||||||
server::State,
|
server::State,
|
||||||
@@ -26,15 +25,14 @@ pub enum CaptureEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
backend: Option<CaptureBackend>,
|
|
||||||
server: Server,
|
server: Server,
|
||||||
sender_tx: Sender<(Event, SocketAddr)>,
|
sender_tx: Sender<(Event, SocketAddr)>,
|
||||||
timer_tx: Sender<()>,
|
timer_tx: Sender<()>,
|
||||||
release_bind: Vec<scancode::Linux>,
|
release_bind: Vec<scancode::Linux>,
|
||||||
) -> Result<(JoinHandle<Result<()>>, Sender<CaptureEvent>), CaptureCreationError> {
|
) -> (JoinHandle<Result<()>>, Sender<CaptureEvent>) {
|
||||||
let (tx, mut rx) = tokio::sync::mpsc::channel(32);
|
let (tx, mut rx) = tokio::sync::mpsc::channel(32);
|
||||||
let task = tokio::task::spawn_local(async move {
|
let task = tokio::task::spawn_local(async move {
|
||||||
let mut capture = capture::create(backend).await?;
|
let mut capture = capture::create().await;
|
||||||
let mut pressed_keys = HashSet::new();
|
let mut pressed_keys = HashSet::new();
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
@@ -64,7 +62,7 @@ pub fn new(
|
|||||||
}
|
}
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
});
|
});
|
||||||
Ok((task, tx))
|
(task, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_pressed_keys(pressed_keys: &mut HashSet<scancode::Linux>, key: u32, state: u8) {
|
fn update_pressed_keys(pressed_keys: &mut HashSet<scancode::Linux>, key: u32, state: u8) {
|
||||||
|
|||||||
Reference in New Issue
Block a user