mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-04-16 06:31:26 +03:00
track pressed keys in input-capture (#170)
move pressed key tracking to input capture
This commit is contained in:
committed by
GitHub
parent
096567640c
commit
266ad28c6b
@@ -8,7 +8,7 @@ use input_event::Event;
|
|||||||
|
|
||||||
use crate::CaptureError;
|
use crate::CaptureError;
|
||||||
|
|
||||||
use super::{CaptureHandle, InputCapture, Position};
|
use super::{Capture, CaptureHandle, Position};
|
||||||
|
|
||||||
pub struct DummyInputCapture {}
|
pub struct DummyInputCapture {}
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ impl Default for DummyInputCapture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl InputCapture for DummyInputCapture {
|
impl Capture for DummyInputCapture {
|
||||||
async fn create(&mut self, _handle: CaptureHandle, _pos: Position) -> Result<(), CaptureError> {
|
async fn create(&mut self, _handle: CaptureHandle, _pos: Position) -> Result<(), CaptureError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,32 @@
|
|||||||
use std::fmt::Display;
|
use std::{collections::HashSet, fmt::Display, task::Poll};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use futures::StreamExt;
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
|
|
||||||
use input_event::Event;
|
use input_event::{scancode, Event, KeyboardEvent};
|
||||||
|
|
||||||
pub use error::{CaptureCreationError, CaptureError, InputCaptureError};
|
pub use error::{CaptureCreationError, CaptureError, InputCaptureError};
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
|
||||||
pub mod libei;
|
mod libei;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub mod macos;
|
mod macos;
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
#[cfg(all(unix, feature = "wayland", not(target_os = "macos")))]
|
||||||
pub mod wayland;
|
mod wayland;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub mod windows;
|
mod windows;
|
||||||
|
|
||||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
|
||||||
pub mod x11;
|
mod x11;
|
||||||
|
|
||||||
/// fallback input capture (does not produce events)
|
/// fallback input capture (does not produce events)
|
||||||
pub mod dummy;
|
mod dummy;
|
||||||
|
|
||||||
pub type CaptureHandle = u64;
|
pub type CaptureHandle = u64;
|
||||||
|
|
||||||
@@ -93,10 +94,79 @@ impl Display for Backend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct InputCapture {
|
||||||
|
capture: Box<dyn Capture>,
|
||||||
|
pressed_keys: HashSet<scancode::Linux>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputCapture {
|
||||||
|
/// create a new client with the given id
|
||||||
|
pub async fn create(&mut self, id: CaptureHandle, pos: Position) -> Result<(), CaptureError> {
|
||||||
|
self.capture.create(id, pos).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// destroy the client with the given id, if it exists
|
||||||
|
pub async fn destroy(&mut self, id: CaptureHandle) -> Result<(), CaptureError> {
|
||||||
|
self.capture.destroy(id).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// release mouse
|
||||||
|
pub async fn release(&mut self) -> Result<(), CaptureError> {
|
||||||
|
self.pressed_keys.clear();
|
||||||
|
self.capture.release().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// destroy the input capture
|
||||||
|
pub async fn terminate(&mut self) -> Result<(), CaptureError> {
|
||||||
|
self.capture.terminate().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// creates a new [`InputCapture`]
|
||||||
|
pub async fn new(backend: Option<Backend>) -> Result<Self, CaptureCreationError> {
|
||||||
|
let capture = create(backend).await?;
|
||||||
|
Ok(Self {
|
||||||
|
capture,
|
||||||
|
pressed_keys: HashSet::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// check whether the given keys are pressed
|
||||||
|
pub fn keys_pressed(&self, keys: &[scancode::Linux]) -> bool {
|
||||||
|
keys.iter().all(|k| self.pressed_keys.contains(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_pressed_keys(&mut self, key: u32, state: u8) {
|
||||||
|
if let Ok(scancode) = scancode::Linux::try_from(key) {
|
||||||
|
log::debug!("key: {key}, state: {state}, scancode: {scancode:?}");
|
||||||
|
match state {
|
||||||
|
1 => self.pressed_keys.insert(scancode),
|
||||||
|
_ => self.pressed_keys.remove(&scancode),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream for InputCapture {
|
||||||
|
type Item = Result<(CaptureHandle, Event), CaptureError>;
|
||||||
|
|
||||||
|
fn poll_next(
|
||||||
|
mut self: std::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
) -> Poll<Option<Self::Item>> {
|
||||||
|
match self.capture.poll_next_unpin(cx) {
|
||||||
|
Poll::Ready(e) => {
|
||||||
|
if let Some(Ok((_, Event::Keyboard(KeyboardEvent::Key { key, state, .. })))) = e {
|
||||||
|
self.update_pressed_keys(key, state);
|
||||||
|
}
|
||||||
|
Poll::Ready(e)
|
||||||
|
}
|
||||||
|
Poll::Pending => Poll::Pending,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait InputCapture:
|
trait Capture: Stream<Item = Result<(CaptureHandle, Event), CaptureError>> + Unpin {
|
||||||
Stream<Item = Result<(CaptureHandle, Event), CaptureError>> + Unpin
|
|
||||||
{
|
|
||||||
/// create a new client with the given id
|
/// create a new client with the given id
|
||||||
async fn create(&mut self, id: CaptureHandle, pos: Position) -> Result<(), CaptureError>;
|
async fn create(&mut self, id: CaptureHandle, pos: Position) -> Result<(), CaptureError>;
|
||||||
|
|
||||||
@@ -110,10 +180,10 @@ pub trait InputCapture:
|
|||||||
async fn terminate(&mut self) -> Result<(), CaptureError>;
|
async fn terminate(&mut self) -> Result<(), CaptureError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_backend(
|
async fn create_backend(
|
||||||
backend: Backend,
|
backend: Backend,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
Box<dyn InputCapture<Item = Result<(CaptureHandle, Event), CaptureError>>>,
|
Box<dyn Capture<Item = Result<(CaptureHandle, Event), CaptureError>>>,
|
||||||
CaptureCreationError,
|
CaptureCreationError,
|
||||||
> {
|
> {
|
||||||
match backend {
|
match backend {
|
||||||
@@ -131,10 +201,10 @@ pub async fn create_backend(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create(
|
async fn create(
|
||||||
backend: Option<Backend>,
|
backend: Option<Backend>,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
Box<dyn InputCapture<Item = Result<(CaptureHandle, Event), CaptureError>>>,
|
Box<dyn Capture<Item = Result<(CaptureHandle, Event), CaptureError>>>,
|
||||||
CaptureCreationError,
|
CaptureCreationError,
|
||||||
> {
|
> {
|
||||||
if let Some(backend) = backend {
|
if let Some(backend) = backend {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ use input_event::Event;
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
error::{CaptureError, LibeiCaptureCreationError, ReisConvertEventStreamError},
|
error::{CaptureError, LibeiCaptureCreationError, ReisConvertEventStreamError},
|
||||||
CaptureHandle, InputCapture as LanMouseInputCapture, Position,
|
Capture as LanMouseInputCapture, CaptureHandle, Position,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* there is a bug in xdg-remote-desktop-portal-gnome / mutter that
|
/* there is a bug in xdg-remote-desktop-portal-gnome / mutter that
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
error::MacOSInputCaptureCreationError, CaptureError, CaptureHandle, InputCapture, Position,
|
error::MacOSInputCaptureCreationError, Capture, CaptureError, CaptureHandle, Position,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
@@ -24,7 +24,7 @@ impl Stream for MacOSInputCapture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl InputCapture 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> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,15 +58,13 @@ use wayland_client::{
|
|||||||
Connection, Dispatch, DispatchError, EventQueue, QueueHandle, WEnum,
|
Connection, Dispatch, DispatchError, EventQueue, QueueHandle, WEnum,
|
||||||
};
|
};
|
||||||
|
|
||||||
use tempfile;
|
|
||||||
|
|
||||||
use input_event::{Event, KeyboardEvent, PointerEvent};
|
use input_event::{Event, KeyboardEvent, PointerEvent};
|
||||||
|
|
||||||
use crate::CaptureError;
|
use crate::CaptureError;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
error::{LayerShellCaptureCreationError, WaylandBindError},
|
error::{LayerShellCaptureCreationError, WaylandBindError},
|
||||||
CaptureHandle, InputCapture, Position,
|
Capture, CaptureHandle, Position,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Globals {
|
struct Globals {
|
||||||
@@ -561,7 +559,7 @@ impl Inner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl InputCapture for WaylandInputCapture {
|
impl Capture for WaylandInputCapture {
|
||||||
async fn create(&mut self, handle: CaptureHandle, pos: Position) -> Result<(), CaptureError> {
|
async fn create(&mut self, handle: CaptureHandle, pos: Position) -> Result<(), CaptureError> {
|
||||||
self.add_client(handle, pos);
|
self.add_client(handle, pos);
|
||||||
let inner = self.0.get_mut();
|
let inner = self.0.get_mut();
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ use input_event::{
|
|||||||
Event, KeyboardEvent, PointerEvent, BTN_BACK, BTN_FORWARD, BTN_LEFT, BTN_MIDDLE, BTN_RIGHT,
|
Event, KeyboardEvent, PointerEvent, BTN_BACK, BTN_FORWARD, BTN_LEFT, BTN_MIDDLE, BTN_RIGHT,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{CaptureError, CaptureHandle, InputCapture, Position};
|
use super::{Capture, CaptureError, CaptureHandle, Position};
|
||||||
|
|
||||||
enum Request {
|
enum Request {
|
||||||
Create(CaptureHandle, Position),
|
Create(CaptureHandle, Position),
|
||||||
@@ -64,7 +64,7 @@ unsafe fn signal_message_thread(event_type: EventType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl InputCapture for WindowsInputCapture {
|
impl Capture for WindowsInputCapture {
|
||||||
async fn create(&mut self, handle: CaptureHandle, pos: Position) -> Result<(), CaptureError> {
|
async fn create(&mut self, handle: CaptureHandle, pos: Position) -> Result<(), CaptureError> {
|
||||||
unsafe {
|
unsafe {
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use futures_core::Stream;
|
|||||||
|
|
||||||
use crate::CaptureError;
|
use crate::CaptureError;
|
||||||
|
|
||||||
use super::InputCapture;
|
use super::Capture;
|
||||||
use input_event::Event;
|
use input_event::Event;
|
||||||
|
|
||||||
use super::error::X11InputCaptureCreationError;
|
use super::error::X11InputCaptureCreationError;
|
||||||
@@ -20,7 +20,7 @@ impl X11InputCapture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl InputCapture for X11InputCapture {
|
impl Capture for X11InputCapture {
|
||||||
async fn create(&mut self, _id: CaptureHandle, _pos: Position) -> Result<(), CaptureError> {
|
async fn create(&mut self, _id: CaptureHandle, _pos: Position) -> Result<(), CaptureError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ async fn input_capture_test(config: Config) -> Result<(), InputCaptureError> {
|
|||||||
log::info!("creating input capture");
|
log::info!("creating input capture");
|
||||||
let backend = config.capture_backend.map(|b| b.into());
|
let backend = config.capture_backend.map(|b| b.into());
|
||||||
loop {
|
loop {
|
||||||
let mut input_capture = input_capture::create(backend).await?;
|
let mut input_capture = InputCapture::new(backend).await?;
|
||||||
log::info!("creating clients");
|
log::info!("creating clients");
|
||||||
input_capture.create(0, Position::Left).await?;
|
input_capture.create(0, Position::Left).await?;
|
||||||
input_capture.create(1, Position::Right).await?;
|
input_capture.create(1, Position::Right).await?;
|
||||||
@@ -33,7 +33,7 @@ async fn input_capture_test(config: Config) -> Result<(), InputCaptureError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn do_capture(input_capture: &mut Box<dyn InputCapture>) -> Result<(), CaptureError> {
|
async fn do_capture(input_capture: &mut InputCapture) -> Result<(), CaptureError> {
|
||||||
loop {
|
loop {
|
||||||
let (client, event) = input_capture
|
let (client, event) = input_capture
|
||||||
.next()
|
.next()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use std::{collections::HashSet, net::SocketAddr};
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use tokio::{
|
use tokio::{
|
||||||
process::Command,
|
process::Command,
|
||||||
@@ -9,7 +9,7 @@ use tokio::{
|
|||||||
|
|
||||||
use input_capture::{self, CaptureError, CaptureHandle, InputCapture, InputCaptureError, Position};
|
use input_capture::{self, CaptureError, CaptureHandle, InputCapture, InputCaptureError, Position};
|
||||||
|
|
||||||
use input_event::{scancode, Event, KeyboardEvent};
|
use input_event::Event;
|
||||||
|
|
||||||
use crate::{client::ClientHandle, frontend::Status, server::State};
|
use crate::{client::ClientHandle, frontend::Status, server::State};
|
||||||
|
|
||||||
@@ -68,36 +68,34 @@ async fn do_capture(
|
|||||||
) -> Result<(), InputCaptureError> {
|
) -> Result<(), InputCaptureError> {
|
||||||
/* allow cancelling capture request */
|
/* allow cancelling capture request */
|
||||||
let mut capture = tokio::select! {
|
let mut capture = tokio::select! {
|
||||||
r = input_capture::create(backend) => {
|
r = InputCapture::new(backend) => r?,
|
||||||
r?
|
|
||||||
},
|
|
||||||
_ = server.cancelled() => return Ok(()),
|
_ = server.cancelled() => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
server.set_capture_status(Status::Enabled);
|
server.set_capture_status(Status::Enabled);
|
||||||
|
|
||||||
// FIXME DUPLICATES
|
let clients = server.active_clients();
|
||||||
let clients = server
|
let clients = clients.iter().copied().map(|handle| {
|
||||||
.client_manager
|
(
|
||||||
.borrow()
|
handle,
|
||||||
.get_client_states()
|
server
|
||||||
.map(|(h, s)| (h, s.clone()))
|
.client_manager
|
||||||
.collect::<Vec<_>>();
|
.borrow()
|
||||||
log::info!("{clients:?}");
|
.get(handle)
|
||||||
for (handle, (config, _state)) in clients {
|
.map(|(c, _)| c.pos)
|
||||||
capture.create(handle, config.pos.into()).await?;
|
.expect("no such client"),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
for (handle, pos) in clients {
|
||||||
|
capture.create(handle, pos.into()).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pressed_keys = HashSet::new();
|
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
event = capture.next() => {
|
event = capture.next() => match event {
|
||||||
match event {
|
Some(event) => handle_capture_event(server, &mut capture, sender_tx, event?).await?,
|
||||||
Some(Ok(event)) => handle_capture_event(server, &mut capture, sender_tx, event, &mut pressed_keys).await?,
|
None => return Ok(()),
|
||||||
Some(Err(e)) => return Err(e.into()),
|
},
|
||||||
None => return Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e = notify_rx.recv() => {
|
e = notify_rx.recv() => {
|
||||||
log::debug!("input capture notify rx: {e:?}");
|
log::debug!("input capture notify rx: {e:?}");
|
||||||
match e {
|
match e {
|
||||||
@@ -119,38 +117,20 @@ async fn do_capture(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_pressed_keys(pressed_keys: &mut HashSet<scancode::Linux>, key: u32, state: u8) {
|
|
||||||
if let Ok(scancode) = scancode::Linux::try_from(key) {
|
|
||||||
log::debug!("key: {key}, state: {state}, scancode: {scancode:?}");
|
|
||||||
match state {
|
|
||||||
1 => pressed_keys.insert(scancode),
|
|
||||||
_ => pressed_keys.remove(&scancode),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_capture_event(
|
async fn handle_capture_event(
|
||||||
server: &Server,
|
server: &Server,
|
||||||
capture: &mut Box<dyn InputCapture>,
|
capture: &mut InputCapture,
|
||||||
sender_tx: &Sender<(Event, SocketAddr)>,
|
sender_tx: &Sender<(Event, SocketAddr)>,
|
||||||
event: (CaptureHandle, Event),
|
event: (CaptureHandle, Event),
|
||||||
pressed_keys: &mut HashSet<scancode::Linux>,
|
|
||||||
) -> Result<(), CaptureError> {
|
) -> Result<(), CaptureError> {
|
||||||
let (handle, mut e) = event;
|
let (handle, mut e) = event;
|
||||||
log::trace!("({handle}) {e:?}");
|
log::trace!("({handle}) {e:?}");
|
||||||
|
|
||||||
if let Event::Keyboard(KeyboardEvent::Key { key, state, .. }) = e {
|
// check release bind
|
||||||
update_pressed_keys(pressed_keys, key, state);
|
if capture.keys_pressed(&server.release_bind) {
|
||||||
log::debug!("{pressed_keys:?}");
|
capture.release().await?;
|
||||||
if server.release_bind.iter().all(|k| pressed_keys.contains(k)) {
|
server.state.replace(State::Receiving);
|
||||||
pressed_keys.clear();
|
e = Event::Disconnect();
|
||||||
log::info!("releasing pointer");
|
|
||||||
capture.release().await?;
|
|
||||||
server.state.replace(State::Receiving);
|
|
||||||
log::trace!("STATE ===> Receiving");
|
|
||||||
// send an event to release all the modifiers
|
|
||||||
e = Event::Disconnect();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let info = {
|
let info = {
|
||||||
|
|||||||
Reference in New Issue
Block a user