track pressed keys in input-capture (#170)

move pressed key tracking to input capture
This commit is contained in:
Ferdinand Schober
2024-08-09 13:18:23 +02:00
committed by GitHub
parent 096567640c
commit 266ad28c6b
9 changed files with 125 additions and 77 deletions

View File

@@ -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(())
} }

View File

@@ -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 {

View File

@@ -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

View File

@@ -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(())
} }

View File

@@ -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();

View File

@@ -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 {
{ {

View File

@@ -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(())
} }

View File

@@ -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()

View File

@@ -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 = {