wire frontend

This commit is contained in:
Ferdinand Schober
2024-07-11 15:12:59 +02:00
parent 9990e5b578
commit d73ced7b16
17 changed files with 315 additions and 121 deletions

View File

@@ -1,4 +1,3 @@
use std::io;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@@ -27,15 +26,15 @@ impl Default for DummyInputCapture {
#[async_trait] #[async_trait]
impl InputCapture for DummyInputCapture { impl InputCapture for DummyInputCapture {
async fn create(&mut self, _handle: CaptureHandle, _pos: Position) -> io::Result<()> { async fn create(&mut self, _handle: CaptureHandle, _pos: Position) -> Result<(), CaptureError> {
Ok(()) Ok(())
} }
async fn destroy(&mut self, _handle: CaptureHandle) -> io::Result<()> { async fn destroy(&mut self, _handle: CaptureHandle) -> Result<(), CaptureError> {
Ok(()) Ok(())
} }
async fn release(&mut self) -> io::Result<()> { async fn release(&mut self) -> Result<(), CaptureError> {
Ok(()) Ok(())
} }

View File

@@ -1,4 +1,4 @@
use std::{fmt::Display, io}; use std::fmt::Display;
use async_trait::async_trait; use async_trait::async_trait;
use futures_core::Stream; use futures_core::Stream;
@@ -98,13 +98,13 @@ pub trait InputCapture:
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) -> io::Result<()>; async fn create(&mut self, id: CaptureHandle, pos: Position) -> Result<(), CaptureError>;
/// destroy the client with the given id, if it exists /// destroy the client with the given id, if it exists
async fn destroy(&mut self, id: CaptureHandle) -> io::Result<()>; async fn destroy(&mut self, id: CaptureHandle) -> Result<(), CaptureError>;
/// release mouse /// release mouse
async fn release(&mut self) -> io::Result<()>; async fn release(&mut self) -> Result<(), CaptureError>;
/// destroy the input acpture /// destroy the input acpture
async fn terminate(&mut self) -> Result<(), CaptureError>; async fn terminate(&mut self) -> Result<(), CaptureError>;

View File

@@ -37,10 +37,9 @@ use once_cell::sync::Lazy;
use input_event::{Event, KeyboardEvent, PointerEvent}; use input_event::{Event, KeyboardEvent, PointerEvent};
use crate::error::{CaptureError, ReisConvertEventStreamError};
use super::{ use super::{
error::LibeiCaptureCreationError, CaptureHandle, InputCapture as LanMouseInputCapture, Position, error::{CaptureError, LibeiCaptureCreationError, ReisConvertEventStreamError},
CaptureHandle, InputCapture as LanMouseInputCapture, 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
@@ -648,7 +647,7 @@ fn to_input_events(ei_event: EiEvent) -> Events {
#[async_trait] #[async_trait]
impl<'a> LanMouseInputCapture for LibeiInputCapture<'a> { impl<'a> LanMouseInputCapture for LibeiInputCapture<'a> {
async fn create(&mut self, handle: CaptureHandle, pos: Position) -> io::Result<()> { async fn create(&mut self, handle: CaptureHandle, pos: Position) -> Result<(), CaptureError> {
let _ = self let _ = self
.notify_capture .notify_capture
.send(CaptureEvent::Create(handle, pos)) .send(CaptureEvent::Create(handle, pos))
@@ -656,7 +655,7 @@ impl<'a> LanMouseInputCapture for LibeiInputCapture<'a> {
Ok(()) Ok(())
} }
async fn destroy(&mut self, handle: CaptureHandle) -> io::Result<()> { async fn destroy(&mut self, handle: CaptureHandle) -> Result<(), CaptureError> {
let _ = self let _ = self
.notify_capture .notify_capture
.send(CaptureEvent::Destroy(handle)) .send(CaptureEvent::Destroy(handle))
@@ -664,7 +663,7 @@ impl<'a> LanMouseInputCapture for LibeiInputCapture<'a> {
Ok(()) Ok(())
} }
async fn release(&mut self) -> io::Result<()> { async fn release(&mut self) -> Result<(), CaptureError> {
let _ = self.notify_capture_session.send(ReleaseCaptureEvent).await; let _ = self.notify_capture_session.send(ReleaseCaptureEvent).await;
Ok(()) Ok(())
} }

View File

@@ -25,15 +25,15 @@ impl Stream for MacOSInputCapture {
#[async_trait] #[async_trait]
impl InputCapture for MacOSInputCapture { impl InputCapture for MacOSInputCapture {
async fn create(&mut self, _id: CaptureHandle, _pos: Position) -> io::Result<()> { async fn create(&mut self, _id: CaptureHandle, _pos: Position) -> Result<(), CaptureError> {
Ok(()) Ok(())
} }
async fn destroy(&mut self, _id: CaptureHandle) -> io::Result<()> { async fn destroy(&mut self, _id: CaptureHandle) -> Result<(), CaptureError> {
Ok(()) Ok(())
} }
async fn release(&mut self) -> io::Result<()> { async fn release(&mut self) -> Result<(), CaptureError> {
Ok(()) Ok(())
} }

View File

@@ -566,23 +566,23 @@ impl Inner {
#[async_trait] #[async_trait]
impl InputCapture for WaylandInputCapture { impl InputCapture for WaylandInputCapture {
async fn create(&mut self, handle: CaptureHandle, pos: Position) -> io::Result<()> { 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();
inner.flush_events() Ok(inner.flush_events()?)
} }
async fn destroy(&mut self, handle: CaptureHandle) -> io::Result<()> { async fn destroy(&mut self, handle: CaptureHandle) -> Result<(), CaptureError> {
self.delete_client(handle); self.delete_client(handle);
let inner = self.0.get_mut(); let inner = self.0.get_mut();
inner.flush_events() Ok(inner.flush_events()?)
} }
async fn release(&mut self) -> io::Result<()> { async fn release(&mut self) -> Result<(), CaptureError> {
log::debug!("releasing pointer"); log::debug!("releasing pointer");
let inner = self.0.get_mut(); let inner = self.0.get_mut();
inner.state.ungrab(); inner.state.ungrab();
inner.flush_events() Ok(inner.flush_events()?)
} }
async fn terminate(&mut self) -> Result<(), CaptureError> { async fn terminate(&mut self) -> Result<(), CaptureError> {

View File

@@ -65,7 +65,7 @@ unsafe fn signal_message_thread(event_type: EventType) {
#[async_trait] #[async_trait]
impl InputCapture for WindowsInputCapture { impl InputCapture for WindowsInputCapture {
async fn create(&mut self, handle: CaptureHandle, pos: Position) -> io::Result<()> { async fn create(&mut self, handle: CaptureHandle, pos: Position) -> Result<(), CaptureError> {
unsafe { unsafe {
{ {
let mut requests = REQUEST_BUFFER.lock().unwrap(); let mut requests = REQUEST_BUFFER.lock().unwrap();
@@ -76,7 +76,7 @@ impl InputCapture for WindowsInputCapture {
Ok(()) Ok(())
} }
async fn destroy(&mut self, handle: CaptureHandle) -> io::Result<()> { async fn destroy(&mut self, handle: CaptureHandle) -> Result<(), CaptureError> {
unsafe { unsafe {
{ {
let mut requests = REQUEST_BUFFER.lock().unwrap(); let mut requests = REQUEST_BUFFER.lock().unwrap();
@@ -87,7 +87,7 @@ impl InputCapture for WindowsInputCapture {
Ok(()) Ok(())
} }
async fn release(&mut self) -> io::Result<()> { async fn release(&mut self) -> Result<(), CaptureError> {
unsafe { signal_message_thread(EventType::Release) }; unsafe { signal_message_thread(EventType::Release) };
Ok(()) Ok(())
} }

View File

@@ -1,4 +1,3 @@
use std::io;
use std::task::Poll; use std::task::Poll;
use async_trait::async_trait; use async_trait::async_trait;
@@ -22,15 +21,15 @@ impl X11InputCapture {
#[async_trait] #[async_trait]
impl InputCapture for X11InputCapture { impl InputCapture for X11InputCapture {
async fn create(&mut self, _id: CaptureHandle, _pos: Position) -> io::Result<()> { async fn create(&mut self, _id: CaptureHandle, _pos: Position) -> Result<(), CaptureError> {
Ok(()) Ok(())
} }
async fn destroy(&mut self, _id: CaptureHandle) -> io::Result<()> { async fn destroy(&mut self, _id: CaptureHandle) -> Result<(), CaptureError> {
Ok(()) Ok(())
} }
async fn release(&mut self) -> io::Result<()> { async fn release(&mut self) -> Result<(), CaptureError> {
Ok(()) Ok(())
} }

View File

@@ -45,11 +45,12 @@
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="spacing">12</property> <property name="spacing">12</property>
<child> <child>
<object class="AdwPreferencesGroup"> <object class="AdwPreferencesGroup" id="capture_emulation_group">
<property name="title" translatable="yes">Capture / Emulation Status</property> <property name="title" translatable="yes">Capture / Emulation Status</property>
<child> <child>
<object class="AdwActionRow"> <object class="AdwActionRow" id="capture_status_row">
<property name="title">input capture is disabled</property> <property name="title">input capture is disabled</property>
<property name="subtitle">required for outgoing and incoming connections</property>
<property name="icon-name">dialog-warning-symbolic</property> <property name="icon-name">dialog-warning-symbolic</property>
<child> <child>
<object class="GtkButton" id="input_capture_button"> <object class="GtkButton" id="input_capture_button">
@@ -73,8 +74,9 @@
</object> </object>
</child> </child>
<child> <child>
<object class="AdwActionRow"> <object class="AdwActionRow" id="emulation_status_row">
<property name="title">input emulation is disabled</property> <property name="title">input emulation is disabled</property>
<property name="subtitle">required for incoming connections</property>
<property name="icon-name">dialog-warning-symbolic</property> <property name="icon-name">dialog-warning-symbolic</property>
<child> <child>
<object class="GtkButton" id="input_emulation_button"> <object class="GtkButton" id="input_emulation_button">

View File

@@ -115,6 +115,21 @@ pub enum FrontendRequest {
EnableEmulation, EnableEmulation,
} }
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum Status {
Enabled,
Disabled,
}
impl From<Status> for bool {
fn from(status: Status) -> Self {
match status {
Status::Enabled => true,
Status::Disabled => false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FrontendEvent { pub enum FrontendEvent {
/// a client was created /// a client was created
@@ -131,6 +146,10 @@ pub enum FrontendEvent {
Enumerate(Vec<(ClientHandle, ClientConfig, ClientState)>), Enumerate(Vec<(ClientHandle, ClientConfig, ClientState)>),
/// an error occured /// an error occured
Error(String), Error(String),
/// capture status
CaptureStatus(Status),
/// emulation status
EmulationStatus(Status),
} }
pub struct FrontendListener { pub struct FrontendListener {

View File

@@ -273,6 +273,12 @@ impl<'a> Cli<'a> {
FrontendEvent::Error(e) => { FrontendEvent::Error(e) => {
eprintln!("ERROR: {e}"); eprintln!("ERROR: {e}");
} }
FrontendEvent::CaptureStatus(s) => {
eprintln!("capture status: {s:?}")
}
FrontendEvent::EmulationStatus(s) => {
eprintln!("emulation status: {s:?}")
}
} }
} }

View File

@@ -150,6 +150,12 @@ fn build_ui(app: &Application) {
} }
window.imp().set_port(port); window.imp().set_port(port);
} }
FrontendEvent::CaptureStatus(s) => {
window.set_capture(s.into());
}
FrontendEvent::EmulationStatus(s) => {
window.set_emulation(s.into());
}
} }
} }
})); }));

View File

@@ -222,6 +222,7 @@ impl Window {
pub fn request_emulation(&self) { pub fn request_emulation(&self) {
self.request(FrontendRequest::EnableEmulation); self.request(FrontendRequest::EnableEmulation);
} }
pub fn request_client_state(&self, client: &ClientObject) { pub fn request_client_state(&self, client: &ClientObject) {
let handle = client.handle(); let handle = client.handle();
let event = FrontendRequest::GetState(handle); let event = FrontendRequest::GetState(handle);
@@ -286,4 +287,24 @@ impl Window {
let toast_overlay = &self.imp().toast_overlay; let toast_overlay = &self.imp().toast_overlay;
toast_overlay.add_toast(toast); toast_overlay.add_toast(toast);
} }
pub fn set_capture(&self, active: bool) {
self.imp().capture_active.replace(active);
self.update_capture_emulation_status();
}
pub fn set_emulation(&self, active: bool) {
self.imp().emulation_active.replace(active);
self.update_capture_emulation_status();
}
fn update_capture_emulation_status(&self) {
let capture = self.imp().capture_active.get();
let emulation = self.imp().emulation_active.get();
self.imp().capture_status_row.set_visible(!capture);
self.imp().emulation_status_row.set_visible(!emulation);
self.imp()
.capture_emulation_group
.set_visible(!capture || !emulation);
}
} }

View File

@@ -6,7 +6,7 @@ use std::net::TcpStream;
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use adw::{prelude::*, ActionRow, ToastOverlay}; use adw::{prelude::*, ActionRow, PreferencesGroup, ToastOverlay};
use glib::subclass::InitializingObject; use glib::subclass::InitializingObject;
use gtk::glib::clone; use gtk::glib::clone;
use gtk::{gdk, gio, glib, Button, CompositeTemplate, Entry, Label, ListBox}; use gtk::{gdk, gio, glib, Button, CompositeTemplate, Entry, Label, ListBox};
@@ -31,6 +31,12 @@ pub struct Window {
#[template_child] #[template_child]
pub toast_overlay: TemplateChild<ToastOverlay>, pub toast_overlay: TemplateChild<ToastOverlay>,
#[template_child] #[template_child]
pub capture_emulation_group: TemplateChild<PreferencesGroup>,
#[template_child]
pub capture_status_row: TemplateChild<ActionRow>,
#[template_child]
pub emulation_status_row: TemplateChild<ActionRow>,
#[template_child]
pub input_emulation_button: TemplateChild<Button>, pub input_emulation_button: TemplateChild<Button>,
#[template_child] #[template_child]
pub input_capture_button: TemplateChild<Button>, pub input_capture_button: TemplateChild<Button>,
@@ -40,6 +46,8 @@ pub struct Window {
#[cfg(windows)] #[cfg(windows)]
pub stream: RefCell<Option<TcpStream>>, pub stream: RefCell<Option<TcpStream>>,
pub port: Cell<u16>, pub port: Cell<u16>,
pub capture_active: Cell<bool>,
pub emulation_active: Cell<bool>,
} }
#[glib::object_subclass] #[glib::object_subclass]
@@ -111,7 +119,6 @@ impl Window {
#[template_callback] #[template_callback]
fn handle_capture(&self) { fn handle_capture(&self) {
log::info!("requesting capture");
self.obj().request_capture(); self.obj().request_capture();
} }

View File

@@ -102,15 +102,21 @@ impl Server {
let (mut udp_task, sender_tx, receiver_rx, port_tx) = let (mut udp_task, sender_tx, receiver_rx, port_tx) =
network_task::new(self.clone(), frontend_notify_tx.clone()).await?; network_task::new(self.clone(), frontend_notify_tx.clone()).await?;
// restart notify tokens
let notify_capture = Arc::new(Notify::new());
let notify_emulation = Arc::new(Notify::new());
// input capture // input capture
let (mut capture_task, capture_channel) = capture_task::new( let (mut capture_task, capture_channel) = capture_task::new(
capture_backend, capture_backend,
self.clone(), self.clone(),
sender_tx.clone(), sender_tx.clone(),
frontend_notify_tx.clone(),
timer_notify.clone(), timer_notify.clone(),
self.release_bind.clone(), self.release_bind.clone(),
cancellation_token.clone(), cancellation_token.clone(),
)?; notify_capture.clone(),
);
// input emulation // input emulation
let (mut emulation_task, emulate_channel) = emulation_task::new( let (mut emulation_task, emulate_channel) = emulation_task::new(
@@ -119,8 +125,10 @@ impl Server {
receiver_rx, receiver_rx,
sender_tx.clone(), sender_tx.clone(),
capture_channel.clone(), capture_channel.clone(),
frontend_notify_tx.clone(),
timer_notify.clone(), timer_notify.clone(),
cancellation_token.clone(), cancellation_token.clone(),
notify_emulation.clone(),
); );
// create dns resolver // create dns resolver
@@ -133,6 +141,8 @@ impl Server {
frontend, frontend,
frontend_notify_rx, frontend_notify_rx,
self.clone(), self.clone(),
notify_emulation,
notify_capture,
capture_channel.clone(), capture_channel.clone(),
emulate_channel.clone(), emulate_channel.clone(),
resolve_tx.clone(), resolve_tx.clone(),
@@ -175,16 +185,8 @@ impl Server {
_ = signal::ctrl_c() => { _ = signal::ctrl_c() => {
log::info!("terminating service"); log::info!("terminating service");
} }
e = &mut capture_task => { _ = &mut capture_task => { }
if let Ok(Err(e)) = e { _ = &mut emulation_task => { }
log::error!("error in input capture task: {e}");
}
}
e = &mut emulation_task => {
if let Ok(Err(e)) = e {
log::error!("error in input emulation task: {e}");
}
}
e = &mut frontend_task => { e = &mut frontend_task => {
if let Ok(Err(e)) = e { if let Ok(Err(e)) = e {
log::error!("error in frontend listener: {e}"); log::error!("error in frontend listener: {e}");
@@ -198,20 +200,13 @@ impl Server {
cancellation_token.cancel(); cancellation_token.cancel();
if !capture_task.is_finished() { if !capture_task.is_finished() {
if let Err(e) = capture_task.await { let _ = capture_task.await;
log::error!("error in input capture task: {e}");
}
} }
if !emulation_task.is_finished() { if !emulation_task.is_finished() {
if let Err(e) = emulation_task.await { let _ = emulation_task.await;
log::error!("error in input emulation task: {e}");
}
} }
if !frontend_task.is_finished() { if !frontend_task.is_finished() {
if let Err(e) = frontend_task.await { let _ = frontend_task.await;
log::error!("error in frontend listener: {e}");
}
} }
resolver_task.abort(); resolver_task.abort();

View File

@@ -1,22 +1,40 @@
use anyhow::{anyhow, Result};
use futures::StreamExt; use futures::StreamExt;
use std::{collections::HashSet, net::SocketAddr, sync::Arc}; use std::{collections::HashSet, net::SocketAddr, sync::Arc};
use thiserror::Error;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
use tokio::{ use tokio::{
process::Command, process::Command,
sync::{mpsc::Sender, Notify}, sync::{
mpsc::{Receiver, Sender},
Notify,
},
task::JoinHandle, task::JoinHandle,
}; };
use input_capture::{self, error::CaptureCreationError, CaptureHandle, InputCapture, Position}; use input_capture::{
self, error::CaptureCreationError, CaptureError, CaptureHandle, InputCapture, Position,
};
use input_event::{scancode, Event, KeyboardEvent}; use input_event::{scancode, Event, KeyboardEvent};
use crate::{client::ClientHandle, config::CaptureBackend, server::State}; use crate::{
client::ClientHandle,
config::CaptureBackend,
frontend::{FrontendEvent, Status},
server::State,
};
use super::Server; use super::Server;
#[derive(Debug, Error)]
pub enum LanMouseCaptureError {
#[error("error creating input-capture: `{0}`")]
Create(#[from] CaptureCreationError),
#[error("error while capturing input: `{0}`")]
Capture(#[from] CaptureError),
}
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum CaptureEvent { pub enum CaptureEvent {
/// capture must release the mouse /// capture must release the mouse
@@ -25,60 +43,125 @@ pub enum CaptureEvent {
Create(CaptureHandle, Position), Create(CaptureHandle, Position),
/// destory a capture client /// destory a capture client
Destroy(CaptureHandle), Destroy(CaptureHandle),
/// restart input capture
Restart,
} }
pub fn new( pub fn new(
backend: Option<CaptureBackend>, backend: Option<CaptureBackend>,
server: Server, server: Server,
sender_tx: Sender<(Event, SocketAddr)>, sender_tx: Sender<(Event, SocketAddr)>,
frontend_tx: Sender<FrontendEvent>,
timer_notify: Arc<Notify>, timer_notify: Arc<Notify>,
release_bind: Vec<scancode::Linux>, release_bind: Vec<scancode::Linux>,
cancellation_token: CancellationToken, cancellation_token: CancellationToken,
) -> Result<(JoinHandle<Result<()>>, Sender<CaptureEvent>), CaptureCreationError> { notify_capture: Arc<Notify>,
let (tx, mut rx) = tokio::sync::mpsc::channel(32); ) -> (JoinHandle<()>, Sender<CaptureEvent>) {
let (tx, rx) = tokio::sync::mpsc::channel(32);
let backend = backend.map(|b| b.into()); let backend = backend.map(|b| b.into());
let task = tokio::task::spawn_local(async move { let task = tokio::task::spawn_local(capture_task(
let mut capture = input_capture::create(backend).await?; backend,
let mut pressed_keys = HashSet::new(); server,
loop { sender_tx,
tokio::select! { rx,
event = capture.next() => { frontend_tx,
match event { timer_notify,
Some(Ok(event)) => handle_capture_event(&server, &mut capture, &sender_tx, &timer_notify, event, &mut pressed_keys, &release_bind).await?, release_bind,
Some(Err(e)) => return Err(anyhow!("input capture: {e:?}")), cancellation_token,
None => return Err(anyhow!("input capture terminated")), notify_capture,
} ));
} (task, tx)
e = rx.recv() => { }
log::debug!("input capture notify rx: {e:?}");
match e { async fn capture_task(
Some(e) => match e { backend: Option<input_capture::Backend>,
CaptureEvent::Release => { server: Server,
capture.release().await?; sender_tx: Sender<(Event, SocketAddr)>,
server.state.replace(State::Receiving); mut notify_rx: Receiver<CaptureEvent>,
} frontend_tx: Sender<FrontendEvent>,
CaptureEvent::Create(h, p) => capture.create(h, p).await?, timer_notify: Arc<Notify>,
CaptureEvent::Destroy(h) => capture.destroy(h).await?, release_bind: Vec<scancode::Linux>,
CaptureEvent::Restart => { cancellation_token: CancellationToken,
let clients = server.client_manager.borrow().get_client_states().map(|(h, (c,_))| (h, c.pos)).collect::<Vec<_>>(); notify_capture: Arc<Notify>,
capture.terminate().await?; ) {
capture = input_capture::create(backend).await?; loop {
for (handle, pos) in clients { if let Err(e) = do_capture(
capture.create(handle, pos.into()).await?; backend,
} &server,
} &sender_tx,
}, &mut notify_rx,
None => break, &frontend_tx,
} &timer_notify,
} &release_bind,
_ = cancellation_token.cancelled() => break, &cancellation_token,
} )
.await
{
log::warn!("input emulation exited: {e}");
} }
anyhow::Ok(()) let _ = frontend_tx
}); .send(FrontendEvent::CaptureStatus(Status::Disabled))
Ok((task, tx)) .await;
if cancellation_token.is_cancelled() {
break;
}
notify_capture.notified().await;
}
}
async fn do_capture(
backend: Option<input_capture::Backend>,
server: &Server,
sender_tx: &Sender<(Event, SocketAddr)>,
notify_rx: &mut Receiver<CaptureEvent>,
frontend_tx: &Sender<FrontendEvent>,
timer_notify: &Notify,
release_bind: &[scancode::Linux],
cancellation_token: &CancellationToken,
) -> Result<(), LanMouseCaptureError> {
let mut capture = input_capture::create(backend).await?;
let _ = frontend_tx
.send(FrontendEvent::CaptureStatus(Status::Enabled))
.await;
// FIXME DUPLICATES
let clients = server
.client_manager
.borrow()
.get_client_states()
.map(|(h, (c, _))| (h, c.pos))
.collect::<Vec<_>>();
for (handle, pos) in clients {
capture.create(handle, pos.into()).await?;
}
let mut pressed_keys = HashSet::new();
loop {
tokio::select! {
event = capture.next() => {
match event {
Some(Ok(event)) => handle_capture_event(server, &mut capture, sender_tx, timer_notify, event, &mut pressed_keys, release_bind).await?,
Some(Err(e)) => return Err(e.into()),
None => return Ok(()),
}
}
e = notify_rx.recv() => {
log::debug!("input capture notify rx: {e:?}");
match e {
Some(e) => match e {
CaptureEvent::Release => {
capture.release().await?;
server.state.replace(State::Receiving);
}
CaptureEvent::Create(h, p) => capture.create(h, p).await?,
CaptureEvent::Destroy(h) => capture.destroy(h).await?,
},
None => break,
}
}
_ = cancellation_token.cancelled() => break,
}
}
capture.terminate().await?;
Ok(())
} }
fn update_pressed_keys(pressed_keys: &mut HashSet<scancode::Linux>, key: u32, state: u8) { fn update_pressed_keys(pressed_keys: &mut HashSet<scancode::Linux>, key: u32, state: u8) {
@@ -99,7 +182,7 @@ async fn handle_capture_event(
event: (CaptureHandle, Event), event: (CaptureHandle, Event),
pressed_keys: &mut HashSet<scancode::Linux>, pressed_keys: &mut HashSet<scancode::Linux>,
release_bind: &[scancode::Linux], release_bind: &[scancode::Linux],
) -> Result<()> { ) -> Result<(), CaptureError> {
let (handle, mut e) = event; let (handle, mut e) = event;
log::trace!("({handle}) {e:?}"); log::trace!("({handle}) {e:?}");

View File

@@ -13,6 +13,7 @@ use tokio_util::sync::CancellationToken;
use crate::{ use crate::{
client::{ClientHandle, ClientManager}, client::{ClientHandle, ClientManager},
config::EmulationBackend, config::EmulationBackend,
frontend::{FrontendEvent, Status},
server::State, server::State,
}; };
use input_emulation::{ use input_emulation::{
@@ -32,8 +33,6 @@ pub enum EmulationEvent {
Destroy(EmulationHandle), Destroy(EmulationHandle),
/// input emulation must release keys for client /// input emulation must release keys for client
ReleaseKeys(ClientHandle), ReleaseKeys(ClientHandle),
/// restart input emulation
Restart,
} }
pub fn new( pub fn new(
@@ -42,12 +41,11 @@ pub fn new(
udp_rx: Receiver<Result<(Event, SocketAddr), NetworkError>>, udp_rx: Receiver<Result<(Event, SocketAddr), NetworkError>>,
sender_tx: Sender<(Event, SocketAddr)>, sender_tx: Sender<(Event, SocketAddr)>,
capture_tx: Sender<CaptureEvent>, capture_tx: Sender<CaptureEvent>,
frontend_tx: Sender<FrontendEvent>,
timer_notify: Arc<Notify>, timer_notify: Arc<Notify>,
cancellation_token: CancellationToken, cancellation_token: CancellationToken,
) -> ( notify_emulation: Arc<Notify>,
JoinHandle<Result<(), LanMouseEmulationError>>, ) -> (JoinHandle<()>, Sender<EmulationEvent>) {
Sender<EmulationEvent>,
) {
let (tx, rx) = tokio::sync::mpsc::channel(32); let (tx, rx) = tokio::sync::mpsc::channel(32);
let emulation_task = emulation_task( let emulation_task = emulation_task(
backend, backend,
@@ -56,8 +54,10 @@ pub fn new(
udp_rx, udp_rx,
sender_tx, sender_tx,
capture_tx, capture_tx,
frontend_tx,
timer_notify, timer_notify,
cancellation_token, cancellation_token,
notify_emulation,
); );
let emulate_task = tokio::task::spawn_local(emulation_task); let emulate_task = tokio::task::spawn_local(emulation_task);
(emulate_task, tx) (emulate_task, tx)
@@ -78,11 +78,68 @@ async fn emulation_task(
mut udp_rx: Receiver<Result<(Event, SocketAddr), NetworkError>>, mut udp_rx: Receiver<Result<(Event, SocketAddr), NetworkError>>,
sender_tx: Sender<(Event, SocketAddr)>, sender_tx: Sender<(Event, SocketAddr)>,
capture_tx: Sender<CaptureEvent>, capture_tx: Sender<CaptureEvent>,
frontend_tx: Sender<FrontendEvent>,
timer_notify: Arc<Notify>, timer_notify: Arc<Notify>,
cancellation_token: CancellationToken, cancellation_token: CancellationToken,
notify_emulation: Arc<Notify>,
) {
loop {
match do_emulation(
backend,
&mut rx,
&server,
&mut udp_rx,
&sender_tx,
&capture_tx,
&frontend_tx,
&timer_notify,
&cancellation_token,
)
.await
{
Ok(()) => {}
Err(e) => {
log::warn!("input emulation exited: {e}");
}
}
let _ = frontend_tx
.send(FrontendEvent::EmulationStatus(Status::Disabled))
.await;
if cancellation_token.is_cancelled() {
break;
}
notify_emulation.notified().await;
}
}
async fn do_emulation(
backend: Option<EmulationBackend>,
rx: &mut Receiver<EmulationEvent>,
server: &Server,
udp_rx: &mut Receiver<Result<(Event, SocketAddr), NetworkError>>,
sender_tx: &Sender<(Event, SocketAddr)>,
capture_tx: &Sender<CaptureEvent>,
frontend_tx: &Sender<FrontendEvent>,
timer_notify: &Notify,
cancellation_token: &CancellationToken,
) -> Result<(), LanMouseEmulationError> { ) -> Result<(), LanMouseEmulationError> {
let backend = backend.map(|b| b.into()); let backend = backend.map(|b| b.into());
let mut emulation = input_emulation::create(backend).await?; let mut emulation = input_emulation::create(backend).await?;
let _ = frontend_tx
.send(FrontendEvent::EmulationStatus(Status::Enabled))
.await;
// FIMXE DUPLICATES
// add clients
let clients = server
.client_manager
.borrow()
.get_client_states()
.map(|(h, _)| h)
.collect::<Vec<_>>();
for handle in clients {
emulation.create(handle).await;
}
let mut last_ignored = None; let mut last_ignored = None;
loop { loop {
@@ -104,13 +161,6 @@ async fn emulation_task(
EmulationEvent::Create(h) => emulation.create(h).await, EmulationEvent::Create(h) => emulation.create(h).await,
EmulationEvent::Destroy(h) => emulation.destroy(h).await, EmulationEvent::Destroy(h) => emulation.destroy(h).await,
EmulationEvent::ReleaseKeys(c) => release_keys(&server, &mut emulation, c).await?, EmulationEvent::ReleaseKeys(c) => release_keys(&server, &mut emulation, c).await?,
EmulationEvent::Restart => {
let clients = server.client_manager.borrow().get_client_states().map(|(h, _)| h).collect::<Vec<_>>();
emulation = input_emulation::create(backend).await?;
for handle in clients {
emulation.create(handle).await;
}
},
}, },
None => break, None => break,
} }

View File

@@ -2,6 +2,7 @@ use std::{
collections::HashSet, collections::HashSet,
io::ErrorKind, io::ErrorKind,
net::{IpAddr, SocketAddr}, net::{IpAddr, SocketAddr},
sync::Arc,
}; };
#[cfg(unix)] #[cfg(unix)]
use tokio::net::UnixStream; use tokio::net::UnixStream;
@@ -12,7 +13,10 @@ use tokio::net::TcpStream;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use tokio::{ use tokio::{
io::ReadHalf, io::ReadHalf,
sync::mpsc::{Receiver, Sender}, sync::{
mpsc::{Receiver, Sender},
Notify,
},
task::JoinHandle, task::JoinHandle,
}; };
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
@@ -30,6 +34,8 @@ pub(crate) fn new(
mut frontend: FrontendListener, mut frontend: FrontendListener,
mut notify_rx: Receiver<FrontendEvent>, mut notify_rx: Receiver<FrontendEvent>,
server: Server, server: Server,
notify_capture: Arc<Notify>,
notify_emulation: Arc<Notify>,
capture: Sender<CaptureEvent>, capture: Sender<CaptureEvent>,
emulate: Sender<EmulationEvent>, emulate: Sender<EmulationEvent>,
resolve_ch: Sender<DnsRequest>, resolve_ch: Sender<DnsRequest>,
@@ -54,7 +60,7 @@ pub(crate) fn new(
} }
event = event_rx.recv() => { event = event_rx.recv() => {
let frontend_event = event.ok_or(anyhow!("frontend channel closed"))?; let frontend_event = event.ok_or(anyhow!("frontend channel closed"))?;
if handle_frontend_event(&server, &capture, &emulate, &resolve_ch, &mut frontend, &port_tx, frontend_event).await { if handle_frontend_event(&server, &notify_capture, &notify_emulation, &capture, &emulate, &resolve_ch, &mut frontend, &port_tx, frontend_event).await {
break; break;
} }
} }
@@ -115,6 +121,8 @@ async fn listen_frontend(
async fn handle_frontend_event( async fn handle_frontend_event(
server: &Server, server: &Server,
notify_capture: &Notify,
notify_emulation: &Notify,
capture: &Sender<CaptureEvent>, capture: &Sender<CaptureEvent>,
emulate: &Sender<EmulationEvent>, emulate: &Sender<EmulationEvent>,
resolve_tx: &Sender<DnsRequest>, resolve_tx: &Sender<DnsRequest>,
@@ -125,10 +133,10 @@ async fn handle_frontend_event(
log::debug!("frontend: {event:?}"); log::debug!("frontend: {event:?}");
match event { match event {
FrontendRequest::EnableCapture => { FrontendRequest::EnableCapture => {
let _ = capture.send(CaptureEvent::Restart).await; notify_capture.notify_waiters();
} }
FrontendRequest::EnableEmulation => { FrontendRequest::EnableEmulation => {
let _ = emulate.send(EmulationEvent::Restart).await; notify_emulation.notify_waiters();
} }
FrontendRequest::Create => { FrontendRequest::Create => {
let handle = add_client(server, frontend).await; let handle = add_client(server, frontend).await;