mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-09 22:31:07 +03:00
Compare commits
3 Commits
include-co
...
lan-mouse-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70e2d9fecb | ||
|
|
ede8cd4acb | ||
|
|
1e1476d58e |
23
Cargo.lock
generated
23
Cargo.lock
generated
@@ -1196,15 +1196,10 @@ version = "0.2.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"ashpd",
|
"ashpd",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bitflags 2.6.0",
|
|
||||||
"core-foundation",
|
|
||||||
"core-foundation-sys",
|
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"input-event",
|
"input-event",
|
||||||
"keycode",
|
|
||||||
"libc",
|
|
||||||
"log",
|
"log",
|
||||||
"memmap",
|
"memmap",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -1329,7 +1324,6 @@ dependencies = [
|
|||||||
"lan-mouse-proto",
|
"lan-mouse-proto",
|
||||||
"libadwaita",
|
"libadwaita",
|
||||||
"libc",
|
"libc",
|
||||||
"local-channel",
|
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -1409,23 +1403,6 @@ version = "0.4.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "local-channel"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-sink",
|
|
||||||
"local-waker",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "local-waker"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ slab = "0.4.9"
|
|||||||
endi = "1.1.0"
|
endi = "1.1.0"
|
||||||
thiserror = "1.0.61"
|
thiserror = "1.0.61"
|
||||||
tokio-util = "0.7.11"
|
tokio-util = "0.7.11"
|
||||||
local-channel = "0.1.5"
|
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2.148"
|
libc = "0.2.148"
|
||||||
|
|||||||
14
build.rs
14
build.rs
@@ -1,18 +1,4 @@
|
|||||||
use std::process::Command;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// commit hash
|
|
||||||
let git_describe = Command::new("git")
|
|
||||||
.arg("describe")
|
|
||||||
.arg("--always")
|
|
||||||
.arg("--dirty")
|
|
||||||
.arg("--tags")
|
|
||||||
.output()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let git_describe = String::from_utf8(git_describe.stdout).unwrap();
|
|
||||||
println!("cargo::rustc-env=GIT_DESCRIBE={git_describe}");
|
|
||||||
|
|
||||||
// composite_templates
|
// composite_templates
|
||||||
#[cfg(feature = "gtk")]
|
#[cfg(feature = "gtk")]
|
||||||
glib_build_tools::compile_resources(
|
glib_build_tools::compile_resources(
|
||||||
|
|||||||
@@ -53,11 +53,9 @@
|
|||||||
libadwaita
|
libadwaita
|
||||||
librsvg
|
librsvg
|
||||||
xorg.libXtst
|
xorg.libXtst
|
||||||
] ++ lib.optionals stdenv.isDarwin
|
] ++ lib.optionals stdenv.isDarwin [
|
||||||
(with darwin.apple_sdk_11_0.frameworks; [
|
darwin.apple_sdk_11_0.frameworks.CoreGraphics
|
||||||
CoreGraphics
|
];
|
||||||
ApplicationServices
|
|
||||||
]);
|
|
||||||
|
|
||||||
RUST_SRC_PATH = "${rust}/lib/rustlib/src/rust/library";
|
RUST_SRC_PATH = "${rust}/lib/rustlib/src/rust/library";
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,11 +47,6 @@ reis = { version = "0.2", features = ["tokio"], optional = true }
|
|||||||
|
|
||||||
[target.'cfg(target_os="macos")'.dependencies]
|
[target.'cfg(target_os="macos")'.dependencies]
|
||||||
core-graphics = { version = "0.23", features = ["highsierra"] }
|
core-graphics = { version = "0.23", features = ["highsierra"] }
|
||||||
core-foundation = "0.9.4"
|
|
||||||
core-foundation-sys = "0.8.6"
|
|
||||||
libc = "0.2.155"
|
|
||||||
keycode = "0.4.0"
|
|
||||||
bitflags = "2.5.0"
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
windows = { version = "0.58.0", features = [
|
windows = { version = "0.58.0", features = [
|
||||||
|
|||||||
@@ -22,9 +22,6 @@ use ashpd::desktop::ResponseError;
|
|||||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||||
use reis::tokio::{EiConvertEventStreamError, HandshakeError};
|
use reis::tokio::{EiConvertEventStreamError, HandshakeError};
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
use core_graphics::base::CGError;
|
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
#[error("error in libei stream: {inner:?}")]
|
#[error("error in libei stream: {inner:?}")]
|
||||||
@@ -59,21 +56,6 @@ pub enum CaptureError {
|
|||||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||||
#[error("libei disconnected - reason: `{0}`")]
|
#[error("libei disconnected - reason: `{0}`")]
|
||||||
Disconnected(String),
|
Disconnected(String),
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
#[error("failed to warp mouse cursor: `{0}`")]
|
|
||||||
WarpCursor(CGError),
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
#[error("reset_mouse_position called without a connected client")]
|
|
||||||
ResetMouseWithoutClient,
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
#[error("core-graphics error: {0}")]
|
|
||||||
CoreGraphics(CGError),
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
#[error("unable to map key event: {0}")]
|
|
||||||
KeyMapError(i64),
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
#[error("Event tap disabled")]
|
|
||||||
EventTapDisabled,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
@@ -89,12 +71,12 @@ pub enum CaptureCreationError {
|
|||||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||||
#[error("error creating x11 capture backend: `{0}`")]
|
#[error("error creating x11 capture backend: `{0}`")]
|
||||||
X11(#[from] X11InputCaptureCreationError),
|
X11(#[from] X11InputCaptureCreationError),
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
#[error("error creating macos capture backend: `{0}`")]
|
||||||
|
Macos(#[from] MacOSInputCaptureCreationError),
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
#[error("error creating windows capture backend")]
|
#[error("error creating windows capture backend")]
|
||||||
Windows,
|
Windows,
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
#[error("error creating macos capture backend")]
|
|
||||||
MacOS(#[from] MacosCaptureCreationError),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CaptureCreationError {
|
impl CaptureCreationError {
|
||||||
@@ -162,12 +144,7 @@ pub enum X11InputCaptureCreationError {
|
|||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum MacosCaptureCreationError {
|
pub enum MacOSInputCaptureCreationError {
|
||||||
#[error("event source creation failed!")]
|
#[error("MacOS input capture is not yet implemented :(")]
|
||||||
EventSourceCreation,
|
NotImplemented,
|
||||||
#[error("failed to set CG Cursor property")]
|
|
||||||
CGCursorProperty,
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
#[error("failed to get display ids: {0}")]
|
|
||||||
ActiveDisplays(CGError),
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ async fn create_backend(
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
Backend::Windows => Ok(Box::new(windows::WindowsInputCapture::new())),
|
Backend::Windows => Ok(Box::new(windows::WindowsInputCapture::new())),
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
Backend::MacOs => Ok(Box::new(macos::MacOSInputCapture::new().await?)),
|
Backend::MacOs => Ok(Box::new(macos::MacOSInputCapture::new()?)),
|
||||||
Backend::Dummy => Ok(Box::new(dummy::DummyInputCapture::new())),
|
Backend::Dummy => Ok(Box::new(dummy::DummyInputCapture::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,522 +1,39 @@
|
|||||||
use super::{
|
use crate::{
|
||||||
error::MacosCaptureCreationError, Capture, CaptureError, CaptureEvent, CaptureHandle, Position,
|
error::MacOSInputCaptureCreationError, Capture, CaptureError, CaptureEvent, CaptureHandle,
|
||||||
|
Position,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bitflags::bitflags;
|
|
||||||
use core_foundation::base::{kCFAllocatorDefault, CFRelease};
|
|
||||||
use core_foundation::date::CFTimeInterval;
|
|
||||||
use core_foundation::number::{kCFBooleanTrue, CFBooleanRef};
|
|
||||||
use core_foundation::runloop::{kCFRunLoopCommonModes, CFRunLoop, CFRunLoopSource};
|
|
||||||
use core_foundation::string::{kCFStringEncodingUTF8, CFStringCreateWithCString, CFStringRef};
|
|
||||||
use core_graphics::base::{kCGErrorSuccess, CGError};
|
|
||||||
use core_graphics::display::{CGDisplay, CGPoint};
|
|
||||||
use core_graphics::event::{
|
|
||||||
CGEvent, CGEventFlags, CGEventTap, CGEventTapLocation, CGEventTapOptions, CGEventTapPlacement,
|
|
||||||
CGEventTapProxy, CGEventType, EventField,
|
|
||||||
};
|
|
||||||
use core_graphics::event_source::{CGEventSource, CGEventSourceStateID};
|
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use input_event::{Event, KeyboardEvent, PointerEvent, BTN_LEFT, BTN_MIDDLE, BTN_RIGHT};
|
|
||||||
use keycode::{KeyMap, KeyMapping};
|
|
||||||
use libc::c_void;
|
|
||||||
use once_cell::unsync::Lazy;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::ffi::{c_char, CString};
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::task::{Context, Poll};
|
||||||
use std::task::{ready, Context, Poll};
|
|
||||||
use std::thread::{self};
|
|
||||||
use tokio::sync::mpsc::{Receiver, Sender};
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
pub struct MacOSInputCapture;
|
||||||
struct Bounds {
|
|
||||||
xmin: f64,
|
|
||||||
xmax: f64,
|
|
||||||
ymin: f64,
|
|
||||||
ymax: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct InputCaptureState {
|
|
||||||
client_for_pos: Lazy<HashMap<Position, CaptureHandle>>,
|
|
||||||
current_client: Option<(CaptureHandle, Position)>,
|
|
||||||
bounds: Bounds,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum ProducerEvent {
|
|
||||||
Release,
|
|
||||||
Create(CaptureHandle, Position),
|
|
||||||
Destroy(CaptureHandle),
|
|
||||||
Grab((CaptureHandle, Position)),
|
|
||||||
EventTapDisabled,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InputCaptureState {
|
|
||||||
fn new() -> Result<Self, MacosCaptureCreationError> {
|
|
||||||
let mut res = Self {
|
|
||||||
client_for_pos: Lazy::new(HashMap::new),
|
|
||||||
current_client: None,
|
|
||||||
bounds: Bounds::default(),
|
|
||||||
};
|
|
||||||
res.update_bounds()?;
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn crossed(&mut self, event: &CGEvent) -> Option<(CaptureHandle, Position)> {
|
|
||||||
let location = event.location();
|
|
||||||
let relative_x = event.get_double_value_field(EventField::MOUSE_EVENT_DELTA_X);
|
|
||||||
let relative_y = event.get_double_value_field(EventField::MOUSE_EVENT_DELTA_Y);
|
|
||||||
|
|
||||||
for (position, client) in self.client_for_pos.iter() {
|
|
||||||
if (position == &Position::Left && (location.x + relative_x) <= self.bounds.xmin)
|
|
||||||
|| (position == &Position::Right && (location.x + relative_x) >= self.bounds.xmax)
|
|
||||||
|| (position == &Position::Top && (location.y + relative_y) <= self.bounds.ymin)
|
|
||||||
|| (position == &Position::Bottom && (location.y + relative_y) >= self.bounds.ymax)
|
|
||||||
{
|
|
||||||
log::debug!("Crossed barrier into client: {client}, {position:?}");
|
|
||||||
return Some((*client, *position));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the max bounds of all displays
|
|
||||||
fn update_bounds(&mut self) -> Result<(), MacosCaptureCreationError> {
|
|
||||||
let active_ids =
|
|
||||||
CGDisplay::active_displays().map_err(MacosCaptureCreationError::ActiveDisplays)?;
|
|
||||||
active_ids.iter().for_each(|d| {
|
|
||||||
let bounds = CGDisplay::new(*d).bounds();
|
|
||||||
self.bounds.xmin = self.bounds.xmin.min(bounds.origin.x);
|
|
||||||
self.bounds.xmax = self.bounds.xmax.max(bounds.origin.x + bounds.size.width);
|
|
||||||
self.bounds.ymin = self.bounds.ymin.min(bounds.origin.y);
|
|
||||||
self.bounds.ymax = self.bounds.ymax.max(bounds.origin.y + bounds.size.height);
|
|
||||||
});
|
|
||||||
|
|
||||||
log::debug!("Updated displays bounds: {0:?}", self.bounds);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can't disable mouse movement when in a client so we need to reset the cursor position
|
|
||||||
// to the edge of the screen, the cursor will be hidden but we dont want it to appear in a
|
|
||||||
// random location when we exit the client
|
|
||||||
fn reset_mouse_position(&self, event: &CGEvent) -> Result<(), CaptureError> {
|
|
||||||
if let Some((_, pos)) = self.current_client {
|
|
||||||
let location = event.location();
|
|
||||||
let edge_offset = 1.0;
|
|
||||||
|
|
||||||
// After the cursor is warped no event is produced but the next event
|
|
||||||
// will carry the delta from the warp so only half the delta is needed to move the cursor
|
|
||||||
let delta_y = event.get_double_value_field(EventField::MOUSE_EVENT_DELTA_Y) / 2.0;
|
|
||||||
let delta_x = event.get_double_value_field(EventField::MOUSE_EVENT_DELTA_X) / 2.0;
|
|
||||||
|
|
||||||
let mut new_x = location.x + delta_x;
|
|
||||||
let mut new_y = location.y + delta_y;
|
|
||||||
|
|
||||||
match pos {
|
|
||||||
Position::Left => {
|
|
||||||
new_x = self.bounds.xmin + edge_offset;
|
|
||||||
}
|
|
||||||
Position::Right => {
|
|
||||||
new_x = self.bounds.xmax - edge_offset;
|
|
||||||
}
|
|
||||||
Position::Top => {
|
|
||||||
new_y = self.bounds.ymin + edge_offset;
|
|
||||||
}
|
|
||||||
Position::Bottom => {
|
|
||||||
new_y = self.bounds.ymax - edge_offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let new_pos = CGPoint::new(new_x, new_y);
|
|
||||||
|
|
||||||
log::trace!("Resetting cursor position to: {new_x}, {new_y}");
|
|
||||||
|
|
||||||
return CGDisplay::warp_mouse_cursor_position(new_pos)
|
|
||||||
.map_err(CaptureError::WarpCursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(CaptureError::ResetMouseWithoutClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_producer_event(
|
|
||||||
&mut self,
|
|
||||||
producer_event: ProducerEvent,
|
|
||||||
) -> Result<(), CaptureError> {
|
|
||||||
log::debug!("handling event: {producer_event:?}");
|
|
||||||
match producer_event {
|
|
||||||
ProducerEvent::Release => {
|
|
||||||
if self.current_client.is_some() {
|
|
||||||
CGDisplay::show_cursor(&CGDisplay::main())
|
|
||||||
.map_err(CaptureError::CoreGraphics)?;
|
|
||||||
self.current_client = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProducerEvent::Grab(client) => {
|
|
||||||
if self.current_client.is_none() {
|
|
||||||
CGDisplay::hide_cursor(&CGDisplay::main())
|
|
||||||
.map_err(CaptureError::CoreGraphics)?;
|
|
||||||
self.current_client = Some(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProducerEvent::Create(c, p) => {
|
|
||||||
self.client_for_pos.insert(p, c);
|
|
||||||
}
|
|
||||||
ProducerEvent::Destroy(c) => {
|
|
||||||
for pos in [
|
|
||||||
Position::Left,
|
|
||||||
Position::Right,
|
|
||||||
Position::Top,
|
|
||||||
Position::Bottom,
|
|
||||||
] {
|
|
||||||
if let Some((current_c, _)) = self.current_client {
|
|
||||||
if current_c == c {
|
|
||||||
CGDisplay::show_cursor(&CGDisplay::main())
|
|
||||||
.map_err(CaptureError::CoreGraphics)?;
|
|
||||||
self.current_client = None;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if self.client_for_pos.get(&pos).copied() == Some(c) {
|
|
||||||
self.client_for_pos.remove(&pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProducerEvent::EventTapDisabled => return Err(CaptureError::EventTapDisabled),
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_events(
|
|
||||||
ev_type: &CGEventType,
|
|
||||||
ev: &CGEvent,
|
|
||||||
result: &mut Vec<CaptureEvent>,
|
|
||||||
) -> Result<(), CaptureError> {
|
|
||||||
fn map_pointer_event(ev: &CGEvent) -> PointerEvent {
|
|
||||||
PointerEvent::Motion {
|
|
||||||
time: 0,
|
|
||||||
dx: ev.get_double_value_field(EventField::MOUSE_EVENT_DELTA_X),
|
|
||||||
dy: ev.get_double_value_field(EventField::MOUSE_EVENT_DELTA_Y),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_key(ev: &CGEvent) -> Result<u32, CaptureError> {
|
|
||||||
let code = ev.get_integer_value_field(EventField::KEYBOARD_EVENT_KEYCODE);
|
|
||||||
match KeyMap::from_key_mapping(KeyMapping::Mac(code as u16)) {
|
|
||||||
Ok(k) => Ok(k.evdev as u32),
|
|
||||||
Err(()) => Err(CaptureError::KeyMapError(code)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match ev_type {
|
|
||||||
CGEventType::KeyDown => {
|
|
||||||
let k = map_key(ev)?;
|
|
||||||
result.push(CaptureEvent::Input(Event::Keyboard(KeyboardEvent::Key {
|
|
||||||
time: 0,
|
|
||||||
key: k,
|
|
||||||
state: 1,
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
CGEventType::KeyUp => {
|
|
||||||
let k = map_key(ev)?;
|
|
||||||
result.push(CaptureEvent::Input(Event::Keyboard(KeyboardEvent::Key {
|
|
||||||
time: 0,
|
|
||||||
key: k,
|
|
||||||
state: 0,
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
CGEventType::FlagsChanged => {
|
|
||||||
let mut mods = XMods::empty();
|
|
||||||
let mut mods_locked = XMods::empty();
|
|
||||||
let cg_flags = ev.get_flags();
|
|
||||||
|
|
||||||
if cg_flags.contains(CGEventFlags::CGEventFlagShift) {
|
|
||||||
mods |= XMods::ShiftMask;
|
|
||||||
}
|
|
||||||
if cg_flags.contains(CGEventFlags::CGEventFlagControl) {
|
|
||||||
mods |= XMods::ControlMask;
|
|
||||||
}
|
|
||||||
if cg_flags.contains(CGEventFlags::CGEventFlagAlternate) {
|
|
||||||
mods |= XMods::Mod1Mask;
|
|
||||||
}
|
|
||||||
if cg_flags.contains(CGEventFlags::CGEventFlagCommand) {
|
|
||||||
mods |= XMods::Mod4Mask;
|
|
||||||
}
|
|
||||||
if cg_flags.contains(CGEventFlags::CGEventFlagAlphaShift) {
|
|
||||||
mods |= XMods::LockMask;
|
|
||||||
mods_locked |= XMods::LockMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
let modifier_event = KeyboardEvent::Modifiers {
|
|
||||||
depressed: mods.bits(),
|
|
||||||
latched: 0,
|
|
||||||
locked: mods_locked.bits(),
|
|
||||||
group: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
result.push(CaptureEvent::Input(Event::Keyboard(modifier_event)));
|
|
||||||
}
|
|
||||||
CGEventType::MouseMoved => {
|
|
||||||
result.push(CaptureEvent::Input(Event::Pointer(map_pointer_event(ev))))
|
|
||||||
}
|
|
||||||
CGEventType::LeftMouseDragged => {
|
|
||||||
result.push(CaptureEvent::Input(Event::Pointer(map_pointer_event(ev))))
|
|
||||||
}
|
|
||||||
CGEventType::RightMouseDragged => {
|
|
||||||
result.push(CaptureEvent::Input(Event::Pointer(map_pointer_event(ev))))
|
|
||||||
}
|
|
||||||
CGEventType::OtherMouseDragged => {
|
|
||||||
result.push(CaptureEvent::Input(Event::Pointer(map_pointer_event(ev))))
|
|
||||||
}
|
|
||||||
CGEventType::LeftMouseDown => {
|
|
||||||
result.push(CaptureEvent::Input(Event::Pointer(PointerEvent::Button {
|
|
||||||
time: 0,
|
|
||||||
button: BTN_LEFT,
|
|
||||||
state: 1,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
CGEventType::LeftMouseUp => {
|
|
||||||
result.push(CaptureEvent::Input(Event::Pointer(PointerEvent::Button {
|
|
||||||
time: 0,
|
|
||||||
button: BTN_LEFT,
|
|
||||||
state: 0,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
CGEventType::RightMouseDown => {
|
|
||||||
result.push(CaptureEvent::Input(Event::Pointer(PointerEvent::Button {
|
|
||||||
time: 0,
|
|
||||||
button: BTN_RIGHT,
|
|
||||||
state: 1,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
CGEventType::RightMouseUp => {
|
|
||||||
result.push(CaptureEvent::Input(Event::Pointer(PointerEvent::Button {
|
|
||||||
time: 0,
|
|
||||||
button: BTN_RIGHT,
|
|
||||||
state: 0,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
CGEventType::OtherMouseDown => {
|
|
||||||
result.push(CaptureEvent::Input(Event::Pointer(PointerEvent::Button {
|
|
||||||
time: 0,
|
|
||||||
button: BTN_MIDDLE,
|
|
||||||
state: 1,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
CGEventType::OtherMouseUp => {
|
|
||||||
result.push(CaptureEvent::Input(Event::Pointer(PointerEvent::Button {
|
|
||||||
time: 0,
|
|
||||||
button: BTN_MIDDLE,
|
|
||||||
state: 0,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
CGEventType::ScrollWheel => {
|
|
||||||
let v = ev.get_integer_value_field(EventField::SCROLL_WHEEL_EVENT_POINT_DELTA_AXIS_1);
|
|
||||||
let h = ev.get_integer_value_field(EventField::SCROLL_WHEEL_EVENT_POINT_DELTA_AXIS_2);
|
|
||||||
if v != 0 {
|
|
||||||
result.push(CaptureEvent::Input(Event::Pointer(PointerEvent::Axis {
|
|
||||||
time: 0,
|
|
||||||
axis: 0, // Vertical
|
|
||||||
value: v as f64,
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
if h != 0 {
|
|
||||||
result.push(CaptureEvent::Input(Event::Pointer(PointerEvent::Axis {
|
|
||||||
time: 0,
|
|
||||||
axis: 1, // Horizontal
|
|
||||||
value: h as f64,
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn event_tap_thread(
|
|
||||||
client_state: Arc<Mutex<InputCaptureState>>,
|
|
||||||
event_tx: Sender<(CaptureHandle, CaptureEvent)>,
|
|
||||||
notify_tx: Sender<ProducerEvent>,
|
|
||||||
exit: tokio::sync::oneshot::Sender<Result<(), &'static str>>,
|
|
||||||
) {
|
|
||||||
let cg_events_of_interest: Vec<CGEventType> = vec![
|
|
||||||
CGEventType::LeftMouseDown,
|
|
||||||
CGEventType::LeftMouseUp,
|
|
||||||
CGEventType::RightMouseDown,
|
|
||||||
CGEventType::RightMouseUp,
|
|
||||||
CGEventType::OtherMouseDown,
|
|
||||||
CGEventType::OtherMouseUp,
|
|
||||||
CGEventType::MouseMoved,
|
|
||||||
CGEventType::LeftMouseDragged,
|
|
||||||
CGEventType::RightMouseDragged,
|
|
||||||
CGEventType::OtherMouseDragged,
|
|
||||||
CGEventType::ScrollWheel,
|
|
||||||
CGEventType::KeyDown,
|
|
||||||
CGEventType::KeyUp,
|
|
||||||
CGEventType::FlagsChanged,
|
|
||||||
];
|
|
||||||
|
|
||||||
let tap = CGEventTap::new(
|
|
||||||
CGEventTapLocation::Session,
|
|
||||||
CGEventTapPlacement::HeadInsertEventTap,
|
|
||||||
CGEventTapOptions::Default,
|
|
||||||
cg_events_of_interest,
|
|
||||||
|_proxy: CGEventTapProxy, event_type: CGEventType, cg_ev: &CGEvent| {
|
|
||||||
log::trace!("Got event from tap: {event_type:?}");
|
|
||||||
let mut state = client_state.blocking_lock();
|
|
||||||
let mut client = None;
|
|
||||||
let mut res_events = vec![];
|
|
||||||
|
|
||||||
if matches!(
|
|
||||||
event_type,
|
|
||||||
CGEventType::TapDisabledByTimeout | CGEventType::TapDisabledByUserInput
|
|
||||||
) {
|
|
||||||
log::error!("CGEventTap disabled");
|
|
||||||
notify_tx
|
|
||||||
.blocking_send(ProducerEvent::EventTapDisabled)
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
log::error!("Failed to send notification: {e}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Are we in a client?
|
|
||||||
if let Some((current_client, _)) = state.current_client {
|
|
||||||
client = Some(current_client);
|
|
||||||
get_events(&event_type, cg_ev, &mut res_events).unwrap_or_else(|e| {
|
|
||||||
log::error!("Failed to get events: {e}");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Keep (hidden) cursor at the edge of the screen
|
|
||||||
if matches!(event_type, CGEventType::MouseMoved) {
|
|
||||||
state.reset_mouse_position(cg_ev).unwrap_or_else(|e| {
|
|
||||||
log::error!("Failed to reset mouse position: {e}");
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Did we cross a barrier?
|
|
||||||
else if matches!(event_type, CGEventType::MouseMoved) {
|
|
||||||
if let Some((new_client, pos)) = state.crossed(cg_ev) {
|
|
||||||
client = Some(new_client);
|
|
||||||
res_events.push(CaptureEvent::Begin);
|
|
||||||
notify_tx
|
|
||||||
.blocking_send(ProducerEvent::Grab((new_client, pos)))
|
|
||||||
.expect("Failed to send notification");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(client) = client {
|
|
||||||
res_events.iter().for_each(|e| {
|
|
||||||
event_tx
|
|
||||||
.blocking_send((client, *e))
|
|
||||||
.expect("Failed to send event");
|
|
||||||
});
|
|
||||||
// Returning None should stop the event from being processed
|
|
||||||
// but core fundation still returns the event
|
|
||||||
cg_ev.set_type(CGEventType::Null);
|
|
||||||
}
|
|
||||||
Some(cg_ev.to_owned())
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.expect("Failed creating tap");
|
|
||||||
|
|
||||||
let tap_source: CFRunLoopSource = tap
|
|
||||||
.mach_port
|
|
||||||
.create_runloop_source(0)
|
|
||||||
.expect("Failed creating loop source");
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
CFRunLoop::get_current().add_source(&tap_source, kCFRunLoopCommonModes);
|
|
||||||
}
|
|
||||||
|
|
||||||
CFRunLoop::run_current();
|
|
||||||
|
|
||||||
let _ = exit.send(Err("tap thread exited"));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MacOSInputCapture {
|
|
||||||
event_rx: Receiver<(CaptureHandle, CaptureEvent)>,
|
|
||||||
notify_tx: Sender<ProducerEvent>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MacOSInputCapture {
|
impl MacOSInputCapture {
|
||||||
pub async fn new() -> Result<Self, MacosCaptureCreationError> {
|
pub fn new() -> std::result::Result<Self, MacOSInputCaptureCreationError> {
|
||||||
let state = Arc::new(Mutex::new(InputCaptureState::new()?));
|
Err(MacOSInputCaptureCreationError::NotImplemented)
|
||||||
let (event_tx, event_rx) = tokio::sync::mpsc::channel(32);
|
}
|
||||||
let (notify_tx, mut notify_rx) = tokio::sync::mpsc::channel(32);
|
}
|
||||||
let (tap_exit_tx, mut tap_exit_rx) = tokio::sync::oneshot::channel();
|
|
||||||
|
|
||||||
unsafe {
|
impl Stream for MacOSInputCapture {
|
||||||
configure_cf_settings()?;
|
type Item = Result<(CaptureHandle, CaptureEvent), CaptureError>;
|
||||||
}
|
|
||||||
|
|
||||||
log::info!("Enabling CGEvent tap");
|
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
let event_tap_thread_state = state.clone();
|
Poll::Pending
|
||||||
let event_tap_notify = notify_tx.clone();
|
|
||||||
thread::spawn(move || {
|
|
||||||
event_tap_thread(
|
|
||||||
event_tap_thread_state,
|
|
||||||
event_tx,
|
|
||||||
event_tap_notify,
|
|
||||||
tap_exit_tx,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let _tap_task: tokio::task::JoinHandle<()> = tokio::task::spawn_local(async move {
|
|
||||||
loop {
|
|
||||||
tokio::select! {
|
|
||||||
producer_event = notify_rx.recv() => {
|
|
||||||
let producer_event = producer_event.expect("channel closed");
|
|
||||||
let mut state = state.lock().await;
|
|
||||||
state.handle_producer_event(producer_event).await.unwrap_or_else(|e| {
|
|
||||||
log::error!("Failed to handle producer event: {e}");
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
res = &mut tap_exit_rx => {
|
|
||||||
if let Err(e) = res.expect("channel closed") {
|
|
||||||
log::error!("Tap thread failed: {:?}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
event_rx,
|
|
||||||
notify_tx,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Capture for MacOSInputCapture {
|
impl Capture for MacOSInputCapture {
|
||||||
async fn create(&mut self, id: CaptureHandle, pos: Position) -> Result<(), CaptureError> {
|
async fn create(&mut self, _id: CaptureHandle, _pos: Position) -> Result<(), CaptureError> {
|
||||||
let notify_tx = self.notify_tx.clone();
|
|
||||||
tokio::task::spawn_local(async move {
|
|
||||||
log::debug!("creating client {id}, {pos}");
|
|
||||||
let _ = notify_tx.send(ProducerEvent::Create(id, pos)).await;
|
|
||||||
log::debug!("done !");
|
|
||||||
});
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn destroy(&mut self, id: CaptureHandle) -> Result<(), CaptureError> {
|
async fn destroy(&mut self, _id: CaptureHandle) -> Result<(), CaptureError> {
|
||||||
let notify_tx = self.notify_tx.clone();
|
|
||||||
tokio::task::spawn_local(async move {
|
|
||||||
log::debug!("destroying client {id}");
|
|
||||||
let _ = notify_tx.send(ProducerEvent::Destroy(id)).await;
|
|
||||||
log::debug!("done !");
|
|
||||||
});
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn release(&mut self) -> Result<(), CaptureError> {
|
async fn release(&mut self) -> Result<(), CaptureError> {
|
||||||
let notify_tx = self.notify_tx.clone();
|
|
||||||
tokio::task::spawn_local(async move {
|
|
||||||
log::debug!("notifying Release");
|
|
||||||
let _ = notify_tx.send(ProducerEvent::Release).await;
|
|
||||||
});
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -524,79 +41,3 @@ impl Capture for MacOSInputCapture {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stream for MacOSInputCapture {
|
|
||||||
type Item = Result<(CaptureHandle, CaptureEvent), CaptureError>;
|
|
||||||
|
|
||||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
|
||||||
match ready!(self.event_rx.poll_recv(cx)) {
|
|
||||||
None => Poll::Ready(None),
|
|
||||||
Some(e) => Poll::Ready(Some(Ok(e))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type CGSConnectionID = u32;
|
|
||||||
|
|
||||||
#[link(name = "ApplicationServices", kind = "framework")]
|
|
||||||
extern "C" {
|
|
||||||
fn CGSSetConnectionProperty(
|
|
||||||
cid: CGSConnectionID,
|
|
||||||
targetCID: CGSConnectionID,
|
|
||||||
key: CFStringRef,
|
|
||||||
value: CFBooleanRef,
|
|
||||||
) -> CGError;
|
|
||||||
fn _CGSDefaultConnection() -> CGSConnectionID;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
fn CGEventSourceSetLocalEventsSuppressionInterval(
|
|
||||||
event_source: CGEventSource,
|
|
||||||
seconds: CFTimeInterval,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn configure_cf_settings() -> Result<(), MacosCaptureCreationError> {
|
|
||||||
// When we warp the cursor using CGWarpMouseCursorPosition local events are suppressed for a short time
|
|
||||||
// this leeds to the cursor not flowing when crossing back from a clinet, set this to to 0 stops the warp
|
|
||||||
// from working, set a low value by trial and error, 0.05s seems good. 0.25s is the default
|
|
||||||
let event_source = CGEventSource::new(CGEventSourceStateID::CombinedSessionState)
|
|
||||||
.map_err(|_| MacosCaptureCreationError::EventSourceCreation)?;
|
|
||||||
CGEventSourceSetLocalEventsSuppressionInterval(event_source, 0.05);
|
|
||||||
|
|
||||||
// This is a private settings that allows the cursor to be hidden while in the background.
|
|
||||||
// It is used by Barrier and other apps.
|
|
||||||
let key = CString::new("SetsCursorInBackground").unwrap();
|
|
||||||
let cf_key = CFStringCreateWithCString(
|
|
||||||
kCFAllocatorDefault,
|
|
||||||
key.as_ptr() as *const c_char,
|
|
||||||
kCFStringEncodingUTF8,
|
|
||||||
);
|
|
||||||
if CGSSetConnectionProperty(
|
|
||||||
_CGSDefaultConnection(),
|
|
||||||
_CGSDefaultConnection(),
|
|
||||||
cf_key,
|
|
||||||
kCFBooleanTrue,
|
|
||||||
) != kCGErrorSuccess
|
|
||||||
{
|
|
||||||
return Err(MacosCaptureCreationError::CGCursorProperty);
|
|
||||||
}
|
|
||||||
CFRelease(cf_key as *const c_void);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// From X11/X.h
|
|
||||||
bitflags! {
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
|
||||||
struct XMods: u32 {
|
|
||||||
const ShiftMask = (1<<0);
|
|
||||||
const LockMask = (1<<1);
|
|
||||||
const ControlMask = (1<<2);
|
|
||||||
const Mod1Mask = (1<<3);
|
|
||||||
const Mod2Mask = (1<<4);
|
|
||||||
const Mod3Mask = (1<<5);
|
|
||||||
const Mod4Mask = (1<<6);
|
|
||||||
const Mod5Mask = (1<<7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ rustPlatform.buildRustPackage {
|
|||||||
version = version;
|
version = version;
|
||||||
|
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
git
|
|
||||||
pkg-config
|
pkg-config
|
||||||
cmake
|
cmake
|
||||||
makeWrapper
|
makeWrapper
|
||||||
@@ -24,11 +23,9 @@ rustPlatform.buildRustPackage {
|
|||||||
gtk4
|
gtk4
|
||||||
libadwaita
|
libadwaita
|
||||||
xorg.libXtst
|
xorg.libXtst
|
||||||
] ++ lib.optionals stdenv.isDarwin
|
] ++ lib.optionals stdenv.isDarwin [
|
||||||
(with darwin.apple_sdk_11_0.frameworks; [
|
darwin.apple_sdk_11_0.frameworks.CoreGraphics
|
||||||
CoreGraphics
|
];
|
||||||
ApplicationServices
|
|
||||||
]);
|
|
||||||
|
|
||||||
src = builtins.path {
|
src = builtins.path {
|
||||||
name = pname;
|
name = pname;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ impl ConfigToml {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(author, version=env!("GIT_DESCRIBE"), about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
struct CliArgs {
|
struct CliArgs {
|
||||||
/// the listen port for lan-mouse
|
/// the listen port for lan-mouse
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use local_channel::mpsc::Receiver;
|
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
use tokio::sync::mpsc::Receiver;
|
||||||
|
|
||||||
use hickory_resolver::{error::ResolveError, TokioAsyncResolver};
|
use hickory_resolver::{error::ResolveError, TokioAsyncResolver};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use capture_task::CaptureRequest;
|
use capture_task::CaptureRequest;
|
||||||
use emulation_task::EmulationRequest;
|
use emulation_task::EmulationRequest;
|
||||||
use local_channel::mpsc::{channel, Sender};
|
|
||||||
use log;
|
use log;
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
@@ -9,7 +8,15 @@ use std::{
|
|||||||
net::{IpAddr, SocketAddr},
|
net::{IpAddr, SocketAddr},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
use tokio::{io::ReadHalf, join, signal, sync::Notify, task::JoinHandle};
|
use tokio::{
|
||||||
|
io::ReadHalf,
|
||||||
|
join, signal,
|
||||||
|
sync::{
|
||||||
|
mpsc::{channel, Sender},
|
||||||
|
Notify,
|
||||||
|
},
|
||||||
|
task::JoinHandle,
|
||||||
|
};
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -123,12 +130,12 @@ impl Server {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (capture_tx, capture_rx) = channel(); /* requests for input capture */
|
let (capture_tx, capture_rx) = channel(1); /* requests for input capture */
|
||||||
let (emulation_tx, emulation_rx) = channel(); /* emulation requests */
|
let (emulation_tx, emulation_rx) = channel(1); /* emulation requests */
|
||||||
let (udp_recv_tx, udp_recv_rx) = channel(); /* udp receiver */
|
let (udp_recv_tx, udp_recv_rx) = channel(1); /* udp receiver */
|
||||||
let (udp_send_tx, udp_send_rx) = channel(); /* udp sender */
|
let (udp_send_tx, udp_send_rx) = channel(1); /* udp sender */
|
||||||
let (request_tx, mut request_rx) = channel(); /* frontend requests */
|
let (request_tx, mut request_rx) = channel(1); /* frontend requests */
|
||||||
let (dns_tx, dns_rx) = channel(); /* dns requests */
|
let (dns_tx, dns_rx) = channel(1); /* dns requests */
|
||||||
|
|
||||||
// udp task
|
// udp task
|
||||||
let network = network_task::new(self.clone(), udp_recv_tx.clone(), udp_send_rx).await?;
|
let network = network_task::new(self.clone(), udp_recv_tx.clone(), udp_send_rx).await?;
|
||||||
@@ -193,7 +200,7 @@ impl Server {
|
|||||||
let request = self.pending_dns_requests.borrow_mut().pop_front();
|
let request = self.pending_dns_requests.borrow_mut().pop_front();
|
||||||
request
|
request
|
||||||
} {
|
} {
|
||||||
dns_tx.send(request).expect("channel closed");
|
dns_tx.send(request).await.expect("channel closed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = self.cancelled() => break,
|
_ = self.cancelled() => break,
|
||||||
@@ -206,6 +213,13 @@ impl Server {
|
|||||||
|
|
||||||
log::info!("terminating service");
|
log::info!("terminating service");
|
||||||
|
|
||||||
|
assert!(!capture_tx.is_closed());
|
||||||
|
assert!(!emulation_tx.is_closed());
|
||||||
|
assert!(!udp_recv_tx.is_closed());
|
||||||
|
assert!(!udp_send_tx.is_closed());
|
||||||
|
assert!(!request_tx.is_closed());
|
||||||
|
assert!(!dns_tx.is_closed());
|
||||||
|
|
||||||
self.cancel();
|
self.cancel();
|
||||||
futures::future::join_all(join_handles).await;
|
futures::future::join_all(join_handles).await;
|
||||||
let _ = join!(capture, dns_task, emulation, network, ping);
|
let _ = join!(capture, dns_task, emulation, network, ping);
|
||||||
@@ -363,8 +377,8 @@ impl Server {
|
|||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = capture.send(CaptureRequest::Destroy(handle));
|
let _ = capture.send(CaptureRequest::Destroy(handle)).await;
|
||||||
let _ = emulate.send(EmulationRequest::Destroy(handle));
|
let _ = emulate.send(EmulationRequest::Destroy(handle)).await;
|
||||||
log::debug!("deactivating client {handle} done");
|
log::debug!("deactivating client {handle} done");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,8 +410,10 @@ impl Server {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* notify emulation, capture and frontends */
|
/* notify emulation, capture and frontends */
|
||||||
let _ = capture.send(CaptureRequest::Create(handle, pos.into()));
|
let _ = capture
|
||||||
let _ = emulate.send(EmulationRequest::Create(handle));
|
.send(CaptureRequest::Create(handle, pos.into()))
|
||||||
|
.await;
|
||||||
|
let _ = emulate.send(EmulationRequest::Create(handle)).await;
|
||||||
log::debug!("activating client {handle} done");
|
log::debug!("activating client {handle} done");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,8 +433,8 @@ impl Server {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if active {
|
if active {
|
||||||
let _ = capture.send(CaptureRequest::Destroy(handle));
|
let _ = capture.send(CaptureRequest::Destroy(handle)).await;
|
||||||
let _ = emulate.send(EmulationRequest::Destroy(handle));
|
let _ = emulate.send(EmulationRequest::Destroy(handle)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,11 +517,13 @@ impl Server {
|
|||||||
// update state in event input emulator & input capture
|
// update state in event input emulator & input capture
|
||||||
if changed {
|
if changed {
|
||||||
if active {
|
if active {
|
||||||
let _ = capture.send(CaptureRequest::Destroy(handle));
|
let _ = capture.send(CaptureRequest::Destroy(handle)).await;
|
||||||
let _ = emulate.send(EmulationRequest::Destroy(handle));
|
let _ = emulate.send(EmulationRequest::Destroy(handle)).await;
|
||||||
}
|
}
|
||||||
let _ = capture.send(CaptureRequest::Create(handle, pos.into()));
|
let _ = capture
|
||||||
let _ = emulate.send(EmulationRequest::Create(handle));
|
.send(CaptureRequest::Create(handle, pos.into()))
|
||||||
|
.await;
|
||||||
|
let _ = emulate.send(EmulationRequest::Create(handle)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -577,7 +595,7 @@ async fn listen_frontend(
|
|||||||
let request = frontend::wait_for_request(&mut stream).await;
|
let request = frontend::wait_for_request(&mut stream).await;
|
||||||
match request {
|
match request {
|
||||||
Ok(request) => {
|
Ok(request) => {
|
||||||
let _ = request_tx.send(request);
|
let _ = request_tx.send(request).await;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let Some(e) = e.downcast_ref::<io::Error>() {
|
if let Some(e) = e.downcast_ref::<io::Error>() {
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use lan_mouse_proto::ProtoEvent;
|
use lan_mouse_proto::ProtoEvent;
|
||||||
use local_channel::mpsc::{Receiver, Sender};
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use tokio::{process::Command, task::JoinHandle};
|
use tokio::{
|
||||||
|
process::Command,
|
||||||
|
sync::mpsc::{Receiver, Sender},
|
||||||
|
task::JoinHandle,
|
||||||
|
};
|
||||||
|
|
||||||
use input_capture::{
|
use input_capture::{
|
||||||
self, CaptureError, CaptureEvent, CaptureHandle, InputCapture, InputCaptureError, Position,
|
self, CaptureError, CaptureEvent, CaptureHandle, InputCapture, InputCaptureError, Position,
|
||||||
@@ -158,7 +161,7 @@ async fn handle_capture_event(
|
|||||||
/* released capture */
|
/* released capture */
|
||||||
State::Receiving => ProtoEvent::Leave(0),
|
State::Receiving => ProtoEvent::Leave(0),
|
||||||
};
|
};
|
||||||
sender_tx.send((event, addr)).expect("sender closed");
|
sender_tx.send((event, addr)).await.expect("sender closed");
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use local_channel::mpsc::{Receiver, Sender};
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use lan_mouse_proto::ProtoEvent;
|
use lan_mouse_proto::ProtoEvent;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::{
|
||||||
|
sync::mpsc::{Receiver, Sender},
|
||||||
|
task::JoinHandle,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{ClientHandle, ClientManager},
|
client::{ClientHandle, ClientManager},
|
||||||
@@ -138,7 +140,7 @@ async fn handle_incoming_event(
|
|||||||
match (event, addr) {
|
match (event, addr) {
|
||||||
(ProtoEvent::Pong, _) => { /* ignore pong events */ }
|
(ProtoEvent::Pong, _) => { /* ignore pong events */ }
|
||||||
(ProtoEvent::Ping, addr) => {
|
(ProtoEvent::Ping, addr) => {
|
||||||
let _ = sender_tx.send((ProtoEvent::Pong, addr));
|
let _ = sender_tx.send((ProtoEvent::Pong, addr)).await;
|
||||||
}
|
}
|
||||||
(ProtoEvent::Leave(_), _) => emulate.release_keys(handle).await?,
|
(ProtoEvent::Leave(_), _) => emulate.release_keys(handle).await?,
|
||||||
(ProtoEvent::Ack(_), _) => server.set_state(State::Sending),
|
(ProtoEvent::Ack(_), _) => server.set_state(State::Sending),
|
||||||
@@ -146,6 +148,7 @@ async fn handle_incoming_event(
|
|||||||
server.set_state(State::Receiving);
|
server.set_state(State::Receiving);
|
||||||
sender_tx
|
sender_tx
|
||||||
.send((ProtoEvent::Ack(0), addr))
|
.send((ProtoEvent::Ack(0), addr))
|
||||||
|
.await
|
||||||
.expect("no channel")
|
.expect("no channel")
|
||||||
}
|
}
|
||||||
(ProtoEvent::Input(e), _) => {
|
(ProtoEvent::Input(e), _) => {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
use local_channel::mpsc::{Receiver, Sender};
|
|
||||||
use std::{io, net::SocketAddr};
|
use std::{io, net::SocketAddr};
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::{net::UdpSocket, task::JoinHandle};
|
use tokio::{
|
||||||
|
net::UdpSocket,
|
||||||
|
sync::mpsc::{Receiver, Sender},
|
||||||
|
task::JoinHandle,
|
||||||
|
};
|
||||||
|
|
||||||
use super::Server;
|
use super::Server;
|
||||||
use lan_mouse_proto::{ProtoEvent, ProtocolError};
|
use lan_mouse_proto::{ProtoEvent, ProtocolError};
|
||||||
@@ -62,7 +65,7 @@ async fn udp_receiver(
|
|||||||
) {
|
) {
|
||||||
loop {
|
loop {
|
||||||
let event = receive_event(socket).await;
|
let event = receive_event(socket).await;
|
||||||
receiver_tx.send(event).expect("channel closed");
|
receiver_tx.send(event).await.expect("channel closed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use std::{net::SocketAddr, time::Duration};
|
use std::{net::SocketAddr, time::Duration};
|
||||||
|
|
||||||
use lan_mouse_proto::ProtoEvent;
|
use lan_mouse_proto::ProtoEvent;
|
||||||
use local_channel::mpsc::Sender;
|
use tokio::{sync::mpsc::Sender, task::JoinHandle};
|
||||||
use tokio::task::JoinHandle;
|
|
||||||
|
|
||||||
use crate::client::ClientHandle;
|
use crate::client::ClientHandle;
|
||||||
|
|
||||||
@@ -86,7 +85,7 @@ async fn ping_task(
|
|||||||
|
|
||||||
// ping clients
|
// ping clients
|
||||||
for addr in ping_addrs {
|
for addr in ping_addrs {
|
||||||
if sender_ch.send((ProtoEvent::Ping, addr)).is_err() {
|
if sender_ch.send((ProtoEvent::Ping, addr)).await.is_err() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,14 +122,14 @@ async fn ping_task(
|
|||||||
if receiving {
|
if receiving {
|
||||||
for h in unresponsive_clients {
|
for h in unresponsive_clients {
|
||||||
log::warn!("device not responding, releasing keys!");
|
log::warn!("device not responding, releasing keys!");
|
||||||
let _ = emulate_notify.send(EmulationRequest::ReleaseKeys(h));
|
let _ = emulate_notify.send(EmulationRequest::ReleaseKeys(h)).await;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// release pointer if the active client has not responded
|
// release pointer if the active client has not responded
|
||||||
if !unresponsive_clients.is_empty() {
|
if !unresponsive_clients.is_empty() {
|
||||||
log::warn!("client not responding, releasing pointer!");
|
log::warn!("client not responding, releasing pointer!");
|
||||||
server.state.replace(State::Receiving);
|
server.state.replace(State::Receiving);
|
||||||
let _ = capture_notify.send(CaptureRequest::Release);
|
let _ = capture_notify.send(CaptureRequest::Release).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user