mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-04-23 01:23:20 +03:00
Compare commits
3 Commits
v0.5.1
...
x11-event-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2594a83e6a | ||
|
|
b81e5806ab | ||
|
|
a129e27a26 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1226,7 +1226,7 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lan-mouse"
|
name = "lan-mouse"
|
||||||
version = "0.5.1"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ashpd",
|
"ashpd",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lan-mouse"
|
name = "lan-mouse"
|
||||||
description = "Software KVM Switch / mouse & keyboard sharing software for Local Area Networks"
|
description = "Software KVM Switch / mouse & keyboard sharing software for Local Area Networks"
|
||||||
version = "0.5.1"
|
version = "0.5.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
repository = "https://github.com/ferdinandschober/lan-mouse"
|
repository = "https://github.com/ferdinandschober/lan-mouse"
|
||||||
|
|||||||
23
README.md
23
README.md
@@ -14,7 +14,7 @@ The primary target is Wayland on Linux but Windows and MacOS and Linux on Xorg h
|
|||||||
</picture>
|
</picture>
|
||||||
|
|
||||||
|
|
||||||
Goal of this project is to be an open-source replacement for proprietary tools like [Synergy 2/3](https://symless.com/synergy), [Share Mouse](https://www.sharemouse.com/de/).
|
Goal of this project is to be an open-source replacement for proprietary tools like [Synergy](https://symless.com/synergy), [Share Mouse](https://www.sharemouse.com/de/).
|
||||||
|
|
||||||
Focus lies on performance and a clean, manageable implementation that can easily be expanded to support additional backends like e.g. Android, iOS, ... .
|
Focus lies on performance and a clean, manageable implementation that can easily be expanded to support additional backends like e.g. Android, iOS, ... .
|
||||||
|
|
||||||
@@ -22,18 +22,6 @@ Focus lies on performance and a clean, manageable implementation that can easily
|
|||||||
|
|
||||||
For an alternative (with slightly different goals) you may check out [Input Leap](https://github.com/input-leap).
|
For an alternative (with slightly different goals) you may check out [Input Leap](https://github.com/input-leap).
|
||||||
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> Since this tool has gained a bit of popularity over the past couple of days:
|
|
||||||
>
|
|
||||||
> All network traffic is currently **unencrypted** and sent in **plaintext**.
|
|
||||||
>
|
|
||||||
> A malicious actor with access to the network could read input data or send input events with spoofed IPs to take control over a device.
|
|
||||||
>
|
|
||||||
> Therefore you should only use this tool in your local network with trusted devices for now
|
|
||||||
> and I take no responsibility for any leakage of data!
|
|
||||||
|
|
||||||
|
|
||||||
## OS Support
|
## OS Support
|
||||||
|
|
||||||
The following table shows support for input emulation (to emulate events received from other clients) and
|
The following table shows support for input emulation (to emulate events received from other clients) and
|
||||||
@@ -50,10 +38,6 @@ input capture (to send events *to* other clients) on different operating systems
|
|||||||
|
|
||||||
Keycode translation is not yet implemented so on MacOS only mouse emulation works as of right now.
|
Keycode translation is not yet implemented so on MacOS only mouse emulation works as of right now.
|
||||||
|
|
||||||
> [!Important]
|
|
||||||
> If you are using [Wayfire](https://github.com/WayfireWM/wayfire), make sure to use a recent version (must be newer than October 23rd) and **add `shortcuts-inhibit` to the list of plugins in your wayfire config!**
|
|
||||||
> Otherwise input capture will not work.
|
|
||||||
|
|
||||||
## Build and Run
|
## Build and Run
|
||||||
|
|
||||||
### Install Dependencies
|
### Install Dependencies
|
||||||
@@ -226,12 +210,11 @@ Where `left` can be either `left`, `right`, `top` or `bottom`.
|
|||||||
- [x] respect xdg-config-home for config file location.
|
- [x] respect xdg-config-home for config file location.
|
||||||
- [x] IP Address switching
|
- [x] IP Address switching
|
||||||
- [x] Liveness tracking Automatically ungrab mouse when client unreachable
|
- [x] Liveness tracking Automatically ungrab mouse when client unreachable
|
||||||
- [x] Liveness tracking: Automatically release keys, when server offline
|
- [ ] Liveness tracking: Automatically release keys, when server offline
|
||||||
- [ ] Libei Input Capture
|
|
||||||
- [ ] X11 Input Capture
|
- [ ] X11 Input Capture
|
||||||
- [ ] Windows Input Capture
|
- [ ] Windows Input Capture
|
||||||
- [ ] MacOS Input Capture
|
- [ ] MacOS Input Capture
|
||||||
- [ ] MacOS KeyCode Translation
|
- [ ] MaxOS KeyCode Translation
|
||||||
- [ ] Latency measurement and visualization
|
- [ ] Latency measurement and visualization
|
||||||
- [ ] Bandwidth usage measurement and visualization
|
- [ ] Bandwidth usage measurement and visualization
|
||||||
- [ ] Clipboard support
|
- [ ] Clipboard support
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ Name=Lan Mouse
|
|||||||
StartupNotify=true
|
StartupNotify=true
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
Version=0.5.1
|
Version=0.5.0
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use std::time::Duration;
|
|
||||||
use tokio::task::AbortHandle;
|
|
||||||
use winapi::um::winuser::{SendInput, KEYEVENTF_EXTENDEDKEY};
|
use winapi::um::winuser::{SendInput, KEYEVENTF_EXTENDEDKEY};
|
||||||
use winapi::{
|
use winapi::{
|
||||||
self,
|
self,
|
||||||
@@ -23,16 +21,11 @@ use crate::{
|
|||||||
event::Event,
|
event::Event,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis(500);
|
pub struct WindowsConsumer {}
|
||||||
const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32);
|
|
||||||
|
|
||||||
pub struct WindowsConsumer {
|
|
||||||
repeat_task: Option<AbortHandle>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WindowsConsumer {
|
impl WindowsConsumer {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
Ok(Self { repeat_task: None })
|
Ok(Self {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,15 +58,7 @@ impl EventConsumer for WindowsConsumer {
|
|||||||
time: _,
|
time: _,
|
||||||
key,
|
key,
|
||||||
state,
|
state,
|
||||||
} => {
|
} => key_event(key, state),
|
||||||
match state {
|
|
||||||
// pressed
|
|
||||||
0 => self.kill_repeat_task(),
|
|
||||||
1 => self.spawn_repeat_task(key).await,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
key_event(key, state)
|
|
||||||
}
|
|
||||||
KeyboardEvent::Modifiers { .. } => {}
|
KeyboardEvent::Modifiers { .. } => {}
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -87,27 +72,6 @@ impl EventConsumer for WindowsConsumer {
|
|||||||
async fn destroy(&mut self) {}
|
async fn destroy(&mut self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowsConsumer {
|
|
||||||
async fn spawn_repeat_task(&mut self, key: u32) {
|
|
||||||
// there can only be one repeating key and it's
|
|
||||||
// always the last to be pressed
|
|
||||||
self.kill_repeat_task();
|
|
||||||
let repeat_task = tokio::task::spawn_local(async move {
|
|
||||||
tokio::time::sleep(DEFAULT_REPEAT_DELAY).await;
|
|
||||||
loop {
|
|
||||||
key_event(key, 1);
|
|
||||||
tokio::time::sleep(DEFAULT_REPEAT_INTERVAL).await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
self.repeat_task = Some(repeat_task.abort_handle());
|
|
||||||
}
|
|
||||||
fn kill_repeat_task(&mut self) {
|
|
||||||
if let Some(task) = self.repeat_task.take() {
|
|
||||||
task.abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_mouse_input(mi: MOUSEINPUT) {
|
fn send_mouse_input(mi: MOUSEINPUT) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut input = INPUT {
|
let mut input = INPUT {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use anyhow::{anyhow, Result};
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use x11::{
|
use x11::{
|
||||||
xlib::{self, XCloseDisplay},
|
xlib::{self, XCloseDisplay, XOpenDisplay},
|
||||||
xtest,
|
xtest,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ unsafe impl Send for X11Consumer {}
|
|||||||
impl X11Consumer {
|
impl X11Consumer {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
let display = unsafe {
|
let display = unsafe {
|
||||||
match xlib::XOpenDisplay(ptr::null()) {
|
match XOpenDisplay(ptr::null()) {
|
||||||
d if d == ptr::null::<xlib::Display>() as *mut xlib::Display => {
|
d if d == ptr::null::<xlib::Display>() as *mut xlib::Display => {
|
||||||
Err(anyhow!("could not open display"))
|
Err(anyhow!("could not open display"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -712,13 +712,6 @@ impl Dispatch<wl_pointer::WlPointer, ()> for State {
|
|||||||
app.pending_events.push_back((*client, Event::Enter()));
|
app.pending_events.push_back((*client, Event::Enter()));
|
||||||
}
|
}
|
||||||
wl_pointer::Event::Leave { .. } => {
|
wl_pointer::Event::Leave { .. } => {
|
||||||
/* There are rare cases, where when a window is opened in
|
|
||||||
* just the wrong moment, the pointer is released, while
|
|
||||||
* still grabbed.
|
|
||||||
* In that case, the pointer must be ungrabbed, otherwise
|
|
||||||
* it is impossible to grab it again (since the pointer
|
|
||||||
* lock, relative pointer,... objects are still in place)
|
|
||||||
*/
|
|
||||||
app.ungrab();
|
app.ungrab();
|
||||||
}
|
}
|
||||||
wl_pointer::Event::Button {
|
wl_pointer::Event::Button {
|
||||||
|
|||||||
@@ -1,19 +1,207 @@
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use std::io;
|
use x11::xinput::XGrabDevice;
|
||||||
use std::task::Poll;
|
use x11::xinput2::XIAllDevices;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::os::fd::{AsRawFd, RawFd};
|
||||||
|
use std::task::{ready, Poll};
|
||||||
|
use std::{io, ptr};
|
||||||
|
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
|
|
||||||
use crate::event::Event;
|
use crate::event::{Event, PointerEvent};
|
||||||
use crate::producer::EventProducer;
|
use crate::producer::EventProducer;
|
||||||
|
|
||||||
use crate::client::{ClientEvent, ClientHandle};
|
use crate::client::{ClientEvent, ClientHandle};
|
||||||
|
use tokio::io::unix::AsyncFd;
|
||||||
|
|
||||||
pub struct X11Producer {}
|
use x11::xlib::{
|
||||||
|
self, ButtonPress, ButtonPressMask, ButtonRelease, ButtonReleaseMask, CWBackPixel, CWEventMask,
|
||||||
|
CWOverrideRedirect, CopyFromParent, CurrentTime, EnterNotify, EnterWindowMask, ExposureMask,
|
||||||
|
GrabModeAsync, KeyPress, KeyPressMask, KeyRelease, KeyReleaseMask, LeaveWindowMask,
|
||||||
|
MotionNotify, PointerMotionMask, VisibilityChangeMask, XClassHint, XCloseDisplay,
|
||||||
|
XCreateWindow, XDefaultScreen, XFlush, XGetInputFocus, XGrabKeyboard,
|
||||||
|
XGrabPointer, XMapRaised, XNextEvent, XOpenDisplay, XPending, XRootWindow, XSetClassHint,
|
||||||
|
XSetWindowAttributes, XWhitePixel, XDefaultRootWindow,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct X11Producer(AsyncFd<Inner>);
|
||||||
|
|
||||||
|
struct Inner {
|
||||||
|
connection_fd: RawFd,
|
||||||
|
display: *mut xlib::Display,
|
||||||
|
pending_events: VecDeque<(ClientHandle, Event)>,
|
||||||
|
window: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Inner {
|
||||||
|
fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd {
|
||||||
|
self.connection_fd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl X11Producer {
|
impl X11Producer {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
Err(anyhow!("not implemented"))
|
let display = unsafe {
|
||||||
|
match XOpenDisplay(ptr::null()) {
|
||||||
|
d if d == ptr::null::<xlib::Display>() as *mut xlib::Display => {
|
||||||
|
Err(anyhow!("could not open display"))
|
||||||
|
}
|
||||||
|
display => Ok(display),
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
let screen = unsafe { XDefaultScreen(display) };
|
||||||
|
log::warn!("screen: {screen}");
|
||||||
|
|
||||||
|
let root_window = unsafe { XRootWindow(display, screen) };
|
||||||
|
log::warn!("root: {root_window}");
|
||||||
|
let mut attr: XSetWindowAttributes = unsafe { std::mem::zeroed() };
|
||||||
|
attr.override_redirect = true as i32;
|
||||||
|
attr.background_pixel = unsafe { XWhitePixel(display, screen) };
|
||||||
|
attr.event_mask = ExposureMask
|
||||||
|
| VisibilityChangeMask
|
||||||
|
| KeyPressMask
|
||||||
|
| KeyReleaseMask
|
||||||
|
| PointerMotionMask
|
||||||
|
| ButtonPressMask
|
||||||
|
| ButtonReleaseMask
|
||||||
|
| EnterWindowMask
|
||||||
|
| LeaveWindowMask;
|
||||||
|
let window = unsafe {
|
||||||
|
XCreateWindow(
|
||||||
|
display,
|
||||||
|
root_window,
|
||||||
|
0, /* x */
|
||||||
|
0, /* y */
|
||||||
|
2560, /* min width */
|
||||||
|
10, /* min height */
|
||||||
|
0, /* border width */
|
||||||
|
CopyFromParent, /* depth */
|
||||||
|
CopyFromParent as u32, /* class */
|
||||||
|
ptr::null_mut(), /* Visual *visual */
|
||||||
|
CWOverrideRedirect | CWBackPixel | CWEventMask,
|
||||||
|
&mut attr as *mut _,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let mut name: String = "lan-mouse".into();
|
||||||
|
let name = name.as_mut_ptr();
|
||||||
|
|
||||||
|
let mut class_hint = XClassHint {
|
||||||
|
res_name: name as *mut i8,
|
||||||
|
res_class: name as *mut i8,
|
||||||
|
};
|
||||||
|
unsafe { XSetClassHint(display, window, &mut class_hint as *mut _) };
|
||||||
|
log::warn!("window: {window}");
|
||||||
|
// unsafe { XSelectInput(display, window, event_mask as i64) };
|
||||||
|
unsafe { XMapRaised(display, window) };
|
||||||
|
unsafe { XFlush(display) };
|
||||||
|
|
||||||
|
/* can not fail */
|
||||||
|
let connection_fd = unsafe { xlib::XConnectionNumber(display) };
|
||||||
|
let pending_events = VecDeque::new();
|
||||||
|
let inner = Inner {
|
||||||
|
connection_fd,
|
||||||
|
display,
|
||||||
|
window,
|
||||||
|
pending_events,
|
||||||
|
};
|
||||||
|
let async_fd = AsyncFd::new(inner)?;
|
||||||
|
Ok(X11Producer(async_fd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inner {
|
||||||
|
fn decode(&mut self, xevent: xlib::XEvent) -> Option<(u32, Event)> {
|
||||||
|
log::info!("decoding {xevent:?}");
|
||||||
|
match xevent.get_type() {
|
||||||
|
t if t == KeyPress || t == KeyRelease => {
|
||||||
|
let key_event: xlib::XKeyEvent = unsafe { xevent.key };
|
||||||
|
let code = key_event.keycode;
|
||||||
|
let linux_code = code - 8;
|
||||||
|
let state = (xevent.get_type() == KeyPress) as u8;
|
||||||
|
return Some((
|
||||||
|
0,
|
||||||
|
Event::Keyboard(crate::event::KeyboardEvent::Key {
|
||||||
|
time: 0,
|
||||||
|
key: linux_code,
|
||||||
|
state,
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
t if t == EnterNotify => {
|
||||||
|
let mut prev_win = 0;
|
||||||
|
unsafe {
|
||||||
|
XGetInputFocus(
|
||||||
|
self.display,
|
||||||
|
&mut self.window as *mut _,
|
||||||
|
&mut prev_win as *mut _,
|
||||||
|
);
|
||||||
|
XGrabKeyboard(
|
||||||
|
self.display,
|
||||||
|
XDefaultRootWindow(self.display),
|
||||||
|
true as i32,
|
||||||
|
GrabModeAsync,
|
||||||
|
GrabModeAsync,
|
||||||
|
CurrentTime,
|
||||||
|
);
|
||||||
|
XGrabPointer(
|
||||||
|
self.display,
|
||||||
|
self.window, /* window to grab */
|
||||||
|
true as i32, /* owner_events */
|
||||||
|
(PointerMotionMask | ButtonPressMask | ButtonReleaseMask) as u32, /* event mask */
|
||||||
|
GrabModeAsync, /* pointer_mode */
|
||||||
|
GrabModeAsync, /* keyboard_mode */
|
||||||
|
self.window, /* confine_to */
|
||||||
|
0, /* cursor */
|
||||||
|
CurrentTime,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((0, Event::Enter()))
|
||||||
|
}
|
||||||
|
t if t == MotionNotify => {
|
||||||
|
let pointer_event = unsafe { xevent.motion };
|
||||||
|
let (abs_x, abs_y) = (pointer_event.x, pointer_event.y);
|
||||||
|
let event = Event::Pointer(PointerEvent::Motion {
|
||||||
|
time: 0,
|
||||||
|
relative_x: abs_x as f64,
|
||||||
|
relative_y: abs_y as f64,
|
||||||
|
});
|
||||||
|
Some((0, event))
|
||||||
|
}
|
||||||
|
t if t == ButtonPress || t == ButtonRelease => {
|
||||||
|
let button_event = unsafe { xevent.button };
|
||||||
|
log::info!("{:?}", xevent);
|
||||||
|
Some((0, Event::Pointer(PointerEvent::Button {
|
||||||
|
time: 0,
|
||||||
|
button: button_event.button,
|
||||||
|
state: button_event.state,
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch(&mut self) -> io::Result<bool> {
|
||||||
|
unsafe {
|
||||||
|
if XPending(self.display) > 0 {
|
||||||
|
let mut xevent: xlib::XEvent = std::mem::zeroed();
|
||||||
|
XNextEvent(self.display, &mut xevent as *mut _);
|
||||||
|
if let Some(event) = self.decode(xevent) {
|
||||||
|
self.pending_events.push_back(event);
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Inner {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
XCloseDisplay(self.display);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,9 +219,37 @@ impl Stream for X11Producer {
|
|||||||
type Item = io::Result<(ClientHandle, Event)>;
|
type Item = io::Result<(ClientHandle, Event)>;
|
||||||
|
|
||||||
fn poll_next(
|
fn poll_next(
|
||||||
self: std::pin::Pin<&mut Self>,
|
mut self: std::pin::Pin<&mut Self>,
|
||||||
_cx: &mut std::task::Context<'_>,
|
cx: &mut std::task::Context<'_>,
|
||||||
) -> std::task::Poll<Option<Self::Item>> {
|
) -> Poll<Option<Self::Item>> {
|
||||||
Poll::Pending
|
if let Some(event) = self.0.get_mut().pending_events.pop_front() {
|
||||||
|
return Poll::Ready(Some(Ok(event)));
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
let mut guard = ready!(self.0.poll_read_ready_mut(cx))?;
|
||||||
|
{
|
||||||
|
let inner = guard.get_inner_mut();
|
||||||
|
loop {
|
||||||
|
if match inner.dispatch() {
|
||||||
|
Ok(event) => event,
|
||||||
|
Err(e) => {
|
||||||
|
guard.clear_ready();
|
||||||
|
return Poll::Ready(Some(Err(e)));
|
||||||
|
}
|
||||||
|
} == false
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
guard.clear_ready();
|
||||||
|
|
||||||
|
match guard.get_inner_mut().pending_events.pop_front() {
|
||||||
|
Some(event) => {
|
||||||
|
return Poll::Ready(Some(Ok(event)));
|
||||||
|
}
|
||||||
|
None => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pub const BTN_MIDDLE: u32 = 0x112;
|
|||||||
pub const BTN_BACK: u32 = 0x113;
|
pub const BTN_BACK: u32 = 0x113;
|
||||||
pub const BTN_FORWARD: u32 = 0x114;
|
pub const BTN_FORWARD: u32 = 0x114;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum PointerEvent {
|
pub enum PointerEvent {
|
||||||
Motion {
|
Motion {
|
||||||
time: u32,
|
time: u32,
|
||||||
@@ -31,7 +31,7 @@ pub enum PointerEvent {
|
|||||||
Frame {},
|
Frame {},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum KeyboardEvent {
|
pub enum KeyboardEvent {
|
||||||
Key {
|
Key {
|
||||||
time: u32,
|
time: u32,
|
||||||
@@ -46,7 +46,7 @@ pub enum KeyboardEvent {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
/// pointer event (motion / button / axis)
|
/// pointer event (motion / button / axis)
|
||||||
Pointer(PointerEvent),
|
Pointer(PointerEvent),
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ impl Server {
|
|||||||
tokio::select! {
|
tokio::select! {
|
||||||
event = producer.next() => {
|
event = producer.next() => {
|
||||||
let event = event.ok_or(anyhow!("event producer closed"))??;
|
let event = event.ok_or(anyhow!("event producer closed"))??;
|
||||||
|
log::debug!("producer event: {event:?}");
|
||||||
server.handle_producer_event(&mut producer, &sender_ch, &timer_ch, event).await?;
|
server.handle_producer_event(&mut producer, &sender_ch, &timer_ch, event).await?;
|
||||||
}
|
}
|
||||||
e = producer_notify_rx.recv() => {
|
e = producer_notify_rx.recv() => {
|
||||||
@@ -720,7 +721,6 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
State::Receiving => {
|
State::Receiving => {
|
||||||
let mut ignore_event = false;
|
|
||||||
if let Event::Keyboard(KeyboardEvent::Key {
|
if let Event::Keyboard(KeyboardEvent::Key {
|
||||||
time: _,
|
time: _,
|
||||||
key,
|
key,
|
||||||
@@ -736,21 +736,15 @@ impl Server {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if state == 0 {
|
if state == 0 {
|
||||||
// ignore release event if key not pressed
|
client_state.pressed_keys.remove(&key);
|
||||||
ignore_event = !client_state.pressed_keys.remove(&key);
|
|
||||||
} else {
|
} else {
|
||||||
// ignore press event if key not released
|
client_state.pressed_keys.insert(key);
|
||||||
ignore_event = !client_state.pressed_keys.insert(key);
|
|
||||||
let _ = timer_tx.try_send(());
|
let _ = timer_tx.try_send(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ignore double press / release events to
|
// consume event
|
||||||
// workaround buggy rdp backend.
|
consumer.consume(event, handle).await;
|
||||||
if !ignore_event {
|
log::trace!("{event:?} => consumer");
|
||||||
// consume event
|
|
||||||
consumer.consume(event, handle).await;
|
|
||||||
log::trace!("{event:?} => consumer");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
State::AwaitingLeave => {
|
State::AwaitingLeave => {
|
||||||
// we just entered the deadzone of a client, so
|
// we just entered the deadzone of a client, so
|
||||||
@@ -830,11 +824,6 @@ impl Server {
|
|||||||
start_timer = true;
|
start_timer = true;
|
||||||
log::trace!("STATE ===> AwaitingLeave");
|
log::trace!("STATE ===> AwaitingLeave");
|
||||||
enter = true;
|
enter = true;
|
||||||
} else {
|
|
||||||
// ignore any potential events in receiving mode
|
|
||||||
if self.state.get() == State::Receiving && e != Event::Disconnect() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(client_state.active_addr, enter, start_timer)
|
(client_state.active_addr, enter, start_timer)
|
||||||
|
|||||||
Reference in New Issue
Block a user