mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-04-22 21:13:17 +03:00
Async (#30)
- manual eventloop now replaced by asycn-await using the tokio runtime - dns no longer blocks the event loop - simplifies logic - makes xdg-desktop-portal easier to integrate
This commit is contained in:
committed by
GitHub
parent
d4d6f05802
commit
ab2514e508
27
Cargo.lock
generated
27
Cargo.lock
generated
@@ -685,11 +685,10 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"memmap",
|
"memmap",
|
||||||
"mio",
|
|
||||||
"mio-signals",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
"trust-dns-resolver",
|
"trust-dns-resolver",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
@@ -842,22 +841,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
|
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
|
||||||
"wasi",
|
"wasi",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mio-signals"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "21e9524e26c8749824640a1282b68a695b21d55862efa6d465b5f71107c93368"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"mio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.26.4"
|
version = "0.26.4"
|
||||||
@@ -1346,9 +1333,21 @@ dependencies = [
|
|||||||
"num_cpus",
|
"num_cpus",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
|
"tokio-macros",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.37",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.7.8"
|
version = "0.7.8"
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ serde = { version = "1.0", features = ["derive"] }
|
|||||||
anyhow = "1.0.71"
|
anyhow = "1.0.71"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
mio = { version = "0.8", features = ["os-ext"] }
|
|
||||||
libc = "0.2.148"
|
libc = "0.2.148"
|
||||||
serde_json = "1.0.107"
|
serde_json = "1.0.107"
|
||||||
|
tokio = {version = "1.32.0", features = ["io-util", "macros", "net", "rt", "sync" ] }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
wayland-client = { version="0.30.2", optional = true }
|
wayland-client = { version="0.30.2", optional = true }
|
||||||
@@ -31,7 +31,6 @@ wayland-protocols = { version="0.30.0", features=["client", "staging", "unstable
|
|||||||
wayland-protocols-wlr = { version="0.1.0", features=["client"], optional = true }
|
wayland-protocols-wlr = { version="0.1.0", features=["client"], optional = true }
|
||||||
wayland-protocols-misc = { version="0.1.0", features=["client"], optional = true }
|
wayland-protocols-misc = { version="0.1.0", features=["client"], optional = true }
|
||||||
wayland-protocols-plasma = { version="0.1.0", features=["client"], optional = true }
|
wayland-protocols-plasma = { version="0.1.0", features=["client"], optional = true }
|
||||||
mio-signals = "0.2.0"
|
|
||||||
x11 = { version = "2.21.0", features = ["xlib", "xtest"], optional = true }
|
x11 = { version = "2.21.0", features = ["xlib", "xtest"], optional = true }
|
||||||
gtk = { package = "gtk4", version = "0.7.2", features = ["v4_6"], optional = true }
|
gtk = { package = "gtk4", version = "0.7.2", features = ["v4_6"], optional = true }
|
||||||
adw = { package = "libadwaita", version = "0.5.2", features = ["v1_1"], optional = true }
|
adw = { package = "libadwaita", version = "0.5.2", features = ["v1_1"], optional = true }
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use crate::{client::{ClientHandle, Position, ClientEvent}, producer::EventProducer};
|
use crate::{client::{ClientHandle, Position, ClientEvent}, producer::EventProducer};
|
||||||
use mio::{event::Source, unix::SourceFd};
|
|
||||||
|
|
||||||
use std::{os::fd::RawFd, vec::Drain, io::ErrorKind, env};
|
use std::{os::fd::RawFd, vec::Drain, io::{ErrorKind, self}, env};
|
||||||
use memmap::MmapOptions;
|
use memmap::MmapOptions;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use tokio::io::unix::AsyncFd;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
@@ -421,29 +421,12 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source for WaylandEventProducer {
|
impl AsRawFd for WaylandEventProducer {
|
||||||
fn register(
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
&mut self,
|
self.state.wayland_fd
|
||||||
registry: &mio::Registry,
|
|
||||||
token: mio::Token,
|
|
||||||
interests: mio::Interest,
|
|
||||||
) -> std::io::Result<()> {
|
|
||||||
SourceFd(&self.state.wayland_fd).register(registry, token, interests)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reregister(
|
|
||||||
&mut self,
|
|
||||||
registry: &mio::Registry,
|
|
||||||
token: mio::Token,
|
|
||||||
interests: mio::Interest,
|
|
||||||
) -> std::io::Result<()> {
|
|
||||||
SourceFd(&self.state.wayland_fd).reregister(registry, token, interests)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deregister(&mut self, registry: &mio::Registry) -> std::io::Result<()> {
|
|
||||||
SourceFd(&self.state.wayland_fd).deregister(registry)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WaylandEventProducer {
|
impl WaylandEventProducer {
|
||||||
fn read(&mut self) -> bool {
|
fn read(&mut self) -> bool {
|
||||||
match self.state.read_guard.take().unwrap().read() {
|
match self.state.read_guard.take().unwrap().read() {
|
||||||
@@ -508,6 +491,10 @@ impl WaylandEventProducer {
|
|||||||
|
|
||||||
impl EventProducer for WaylandEventProducer {
|
impl EventProducer for WaylandEventProducer {
|
||||||
|
|
||||||
|
fn get_async_fd(&self) -> io::Result<AsyncFd<RawFd>> {
|
||||||
|
AsyncFd::new(self.as_raw_fd())
|
||||||
|
}
|
||||||
|
|
||||||
fn read_events(&mut self) -> Drain<(ClientHandle, Event)> {
|
fn read_events(&mut self) -> Drain<(ClientHandle, Event)> {
|
||||||
// read events
|
// read events
|
||||||
while self.read() {
|
while self.read() {
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
use std::vec::Drain;
|
use tokio::sync::mpsc::{self, Receiver, Sender};
|
||||||
|
|
||||||
use mio::{Token, Registry};
|
|
||||||
use mio::event::Source;
|
|
||||||
use std::io::Result;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{ClientHandle, ClientEvent},
|
client::{ClientHandle, ClientEvent},
|
||||||
@@ -11,48 +7,24 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub struct WindowsProducer {
|
pub struct WindowsProducer {
|
||||||
pending_events: Vec<(ClientHandle, Event)>,
|
_tx: Sender<(ClientHandle, Event)>,
|
||||||
|
rx: Option<Receiver<(ClientHandle, Event)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source for WindowsProducer {
|
|
||||||
fn register(
|
|
||||||
&mut self,
|
|
||||||
_registry: &Registry,
|
|
||||||
_token: Token,
|
|
||||||
_interests: mio::Interest,
|
|
||||||
) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reregister(
|
|
||||||
&mut self,
|
|
||||||
_registry: &Registry,
|
|
||||||
_token: Token,
|
|
||||||
_interests: mio::Interest,
|
|
||||||
) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deregister(&mut self, _registry: &Registry) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl EventProducer for WindowsProducer {
|
impl EventProducer for WindowsProducer {
|
||||||
fn notify(&mut self, _: ClientEvent) { }
|
fn notify(&mut self, _: ClientEvent) { }
|
||||||
|
|
||||||
fn read_events(&mut self) -> Drain<(ClientHandle, Event)> {
|
|
||||||
self.pending_events.drain(..)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn release(&mut self) { }
|
fn release(&mut self) { }
|
||||||
|
|
||||||
|
fn get_wait_channel(&mut self) -> Option<mpsc::Receiver<(ClientHandle, Event)>> {
|
||||||
|
self.rx.take()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowsProducer {
|
impl WindowsProducer {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self {
|
let (_tx, rx) = mpsc::channel(1);
|
||||||
pending_events: vec![],
|
let rx = Some(rx);
|
||||||
}
|
Self { _tx, rx }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
|
use std::io::Result;
|
||||||
|
use std::os::fd::{AsRawFd, self};
|
||||||
use std::vec::Drain;
|
use std::vec::Drain;
|
||||||
|
|
||||||
use mio::{Token, Registry};
|
use tokio::io::unix::AsyncFd;
|
||||||
use mio::event::Source;
|
|
||||||
use std::io::Result;
|
|
||||||
|
|
||||||
|
use crate::event::Event;
|
||||||
use crate::producer::EventProducer;
|
use crate::producer::EventProducer;
|
||||||
|
|
||||||
use crate::{client::{ClientHandle, ClientEvent}, event::Event};
|
use crate::client::{ClientEvent, ClientHandle};
|
||||||
|
|
||||||
pub struct X11Producer {
|
pub struct X11Producer {
|
||||||
pending_events: Vec<(ClientHandle, Event)>,
|
pending_events: Vec<(ClientHandle, Event)>,
|
||||||
@@ -20,27 +21,9 @@ impl X11Producer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source for X11Producer {
|
impl AsRawFd for X11Producer {
|
||||||
fn register(
|
fn as_raw_fd(&self) -> fd::RawFd {
|
||||||
&mut self,
|
todo!()
|
||||||
_registry: &Registry,
|
|
||||||
_token: Token,
|
|
||||||
_interests: mio::Interest,
|
|
||||||
) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reregister(
|
|
||||||
&mut self,
|
|
||||||
_registry: &Registry,
|
|
||||||
_token: Token,
|
|
||||||
_interests: mio::Interest,
|
|
||||||
) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deregister(&mut self, _registry: &Registry) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,4 +35,8 @@ impl EventProducer for X11Producer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn release(&mut self) {}
|
fn release(&mut self) {}
|
||||||
|
|
||||||
|
fn get_async_fd(&self) -> Result<AsyncFd<fd::RawFd>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/dns.rs
15
src/dns.rs
@@ -1,20 +1,23 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::{error::Error, net::IpAddr};
|
use std::{error::Error, net::IpAddr};
|
||||||
|
|
||||||
use trust_dns_resolver::Resolver;
|
use trust_dns_resolver::TokioAsyncResolver;
|
||||||
|
|
||||||
pub(crate) struct DnsResolver {
|
pub(crate) struct DnsResolver {
|
||||||
resolver: Resolver,
|
resolver: TokioAsyncResolver,
|
||||||
}
|
}
|
||||||
impl DnsResolver {
|
impl DnsResolver {
|
||||||
pub(crate) fn new() -> Result<Self> {
|
pub(crate) async fn new() -> Result<Self> {
|
||||||
let resolver = Resolver::from_system_conf()?;
|
let resolver = TokioAsyncResolver::tokio_from_system_conf()?;
|
||||||
Ok(Self { resolver })
|
Ok(Self { resolver })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve(&self, host: &str) -> Result<Vec<IpAddr>, Box<dyn Error>> {
|
pub(crate) async fn resolve(&self, host: &str) -> Result<Vec<IpAddr>, Box<dyn Error>> {
|
||||||
log::info!("resolving {host} ...");
|
log::info!("resolving {host} ...");
|
||||||
let response = self.resolver.lookup_ip(host)?;
|
let response = self.resolver.lookup_ip(host).await?;
|
||||||
|
for ip in response.iter() {
|
||||||
|
log::info!("{host}: adding ip {ip}");
|
||||||
|
}
|
||||||
Ok(response.iter().collect())
|
Ok(response.iter().collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
use std::{error::Error, io::Result, collections::HashSet, time::{Duration, Instant}, net::IpAddr};
|
use std::{error::Error, io::Result, collections::HashSet, time::{Duration, Instant}, net::IpAddr};
|
||||||
use log;
|
use log;
|
||||||
use mio::{Events, Poll, Interest, Token, net::UdpSocket, event::Source};
|
use tokio::{net::UdpSocket, io::ReadHalf, sync::mpsc::{Sender, Receiver}};
|
||||||
#[cfg(not(windows))]
|
|
||||||
use mio_signals::{Signals, Signal, SignalSet};
|
#[cfg(unix)]
|
||||||
|
use tokio::net::UnixStream;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
|
||||||
use std::{net::SocketAddr, io::ErrorKind};
|
use std::{net::SocketAddr, io::ErrorKind};
|
||||||
|
|
||||||
use crate::{client::{ClientEvent, ClientManager, Position, ClientHandle}, consumer::EventConsumer, producer::EventProducer, frontend::{FrontendEvent, FrontendListener, FrontendNotify}, dns::{self, DnsResolver}};
|
use crate::{client::{ClientEvent, ClientManager, Position, ClientHandle}, consumer::EventConsumer, producer::EventProducer, frontend::{FrontendEvent, FrontendListener, FrontendNotify, self}, dns::{self, DnsResolver}};
|
||||||
use super::Event;
|
use super::Event;
|
||||||
|
|
||||||
/// keeps track of state to prevent a feedback loop
|
/// keeps track of state to prevent a feedback loop
|
||||||
@@ -18,92 +22,134 @@ enum State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
poll: Poll,
|
|
||||||
socket: UdpSocket,
|
|
||||||
producer: Box<dyn EventProducer>,
|
|
||||||
consumer: Box<dyn EventConsumer>,
|
|
||||||
resolver: DnsResolver,
|
resolver: DnsResolver,
|
||||||
#[cfg(not(windows))]
|
|
||||||
signals: Signals,
|
|
||||||
frontend: FrontendListener,
|
|
||||||
client_manager: ClientManager,
|
client_manager: ClientManager,
|
||||||
state: State,
|
state: State,
|
||||||
next_token: usize,
|
frontend: FrontendListener,
|
||||||
|
consumer: Box<dyn EventConsumer>,
|
||||||
|
producer: Box<dyn EventProducer>,
|
||||||
|
socket: UdpSocket,
|
||||||
|
frontend_rx: Receiver<FrontendEvent>,
|
||||||
|
frontend_tx: Sender<FrontendEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const UDP_RX: Token = Token(0);
|
|
||||||
const FRONTEND_RX: Token = Token(1);
|
|
||||||
const PRODUCER_RX: Token = Token(2);
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
const SIGNAL: Token = Token(3);
|
|
||||||
|
|
||||||
const MAX_TOKEN: usize = 4;
|
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
pub fn new(
|
pub async fn new(
|
||||||
port: u16,
|
port: u16,
|
||||||
mut producer: Box<dyn EventProducer>,
|
frontend: FrontendListener,
|
||||||
consumer: Box<dyn EventConsumer>,
|
consumer: Box<dyn EventConsumer>,
|
||||||
mut frontend: FrontendListener,
|
producer: Box<dyn EventProducer>,
|
||||||
) -> anyhow::Result<Self> {
|
) -> anyhow::Result<Self> {
|
||||||
// bind the udp socket
|
|
||||||
let listen_addr = SocketAddr::new("0.0.0.0".parse().unwrap(), port);
|
|
||||||
let mut socket = UdpSocket::bind(listen_addr)?;
|
|
||||||
|
|
||||||
// create dns resolver
|
// create dns resolver
|
||||||
let resolver = dns::DnsResolver::new()?;
|
let resolver = dns::DnsResolver::new().await?;
|
||||||
|
|
||||||
// register event sources
|
// bind the udp socket
|
||||||
let poll = Poll::new()?;
|
let listen_addr = SocketAddr::new("0.0.0.0".parse().unwrap(), port);
|
||||||
|
let socket = UdpSocket::bind(listen_addr).await?;
|
||||||
// hand signal handling over to the event loop
|
let (frontend_tx, frontend_rx) = tokio::sync::mpsc::channel(1);
|
||||||
#[cfg(not(windows))]
|
|
||||||
let mut signals = Signals::new(SignalSet::all())?;
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
poll.registry().register(&mut signals, SIGNAL, Interest::READABLE)?;
|
|
||||||
poll.registry().register(&mut socket, UDP_RX, Interest::READABLE)?;
|
|
||||||
poll.registry().register(&mut producer, PRODUCER_RX, Interest::READABLE)?;
|
|
||||||
poll.registry().register(&mut frontend, FRONTEND_RX, Interest::READABLE)?;
|
|
||||||
|
|
||||||
// create client manager
|
// create client manager
|
||||||
let client_manager = ClientManager::new();
|
let client_manager = ClientManager::new();
|
||||||
Ok(Server {
|
Ok(Server {
|
||||||
poll, socket, consumer, producer,
|
frontend,
|
||||||
|
consumer,
|
||||||
|
producer,
|
||||||
resolver,
|
resolver,
|
||||||
#[cfg(not(windows))]
|
socket,
|
||||||
signals, frontend,
|
|
||||||
client_manager,
|
client_manager,
|
||||||
state: State::Receiving,
|
state: State::Receiving,
|
||||||
next_token: MAX_TOKEN,
|
frontend_rx,
|
||||||
|
frontend_tx,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> Result<()> {
|
pub async fn run(&mut self) -> Result<()> {
|
||||||
let mut events = Events::with_capacity(10);
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
let producer_fd = self.producer.get_async_fd()?;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
loop {
|
loop {
|
||||||
match self.poll.poll(&mut events, None) {
|
tokio::select! {
|
||||||
Ok(()) => (),
|
udp_event = receive_event(&self.socket) => {
|
||||||
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
|
match udp_event {
|
||||||
Err(e) => return Err(e),
|
Ok(e) => self.handle_udp_rx(e).await,
|
||||||
}
|
Err(e) => log::error!("error reading event: {e}"),
|
||||||
for event in &events {
|
}
|
||||||
if !event.is_readable() { continue }
|
}
|
||||||
match event.token() {
|
read_guard = producer_fd.readable() => {
|
||||||
UDP_RX => self.handle_udp_rx(),
|
let mut guard = match read_guard {
|
||||||
PRODUCER_RX => self.handle_producer_rx(),
|
Ok(g) => g,
|
||||||
FRONTEND_RX => self.handle_frontend_incoming(),
|
Err(e) => {
|
||||||
#[cfg(not(windows))]
|
log::error!("wayland_fd read_guard: {e}");
|
||||||
SIGNAL => if self.handle_signal() { return Ok(()) },
|
continue
|
||||||
_ => if self.handle_frontend_event(event.token()) { return Ok(()) },
|
}
|
||||||
|
};
|
||||||
|
self.handle_producer_rx().await;
|
||||||
|
guard.clear_ready_matching(tokio::io::Ready::READABLE);
|
||||||
|
}
|
||||||
|
stream = self.frontend.accept() => {
|
||||||
|
match stream {
|
||||||
|
Ok(s) => self.handle_frontend_stream(s).await,
|
||||||
|
Err(e) => log::error!("error connecting to frontend: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frontend_event = self.frontend_rx.recv() => {
|
||||||
|
if let Some(event) = frontend_event {
|
||||||
|
if self.handle_frontend_event(event).await {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let mut channel = self.producer.get_wait_channel().unwrap();
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
loop {
|
||||||
|
tokio::select! {
|
||||||
|
udp_event = receive_event(&self.socket) => {
|
||||||
|
match udp_event {
|
||||||
|
Ok(e) => self.handle_udp_rx(e).await,
|
||||||
|
Err(e) => log::error!("error reading event: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event = channel.recv() => {
|
||||||
|
if let Some((c,e)) = event {
|
||||||
|
self.handle_producer_event(c,e).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream = self.frontend.accept() => {
|
||||||
|
match stream {
|
||||||
|
Ok(s) => self.handle_frontend_stream(s).await,
|
||||||
|
Err(e) => log::error!("error connecting to frontend: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frontend_event = self.frontend_rx.recv() => {
|
||||||
|
if let Some(event) = frontend_event {
|
||||||
|
if self.handle_frontend_event(event).await {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_client(&mut self, hostname: Option<String>, mut addr: HashSet<IpAddr>, port: u16, pos: Position) -> ClientHandle {
|
pub async fn add_client(&mut self, hostname: Option<String>, mut addr: HashSet<IpAddr>, port: u16, pos: Position) -> ClientHandle {
|
||||||
let ips = if let Some(hostname) = hostname.as_ref() {
|
let ips = if let Some(hostname) = hostname.as_ref() {
|
||||||
HashSet::from_iter(self.resolver.resolve(hostname.as_str()).ok().iter().flatten().cloned())
|
match self.resolver.resolve(hostname.as_str()).await {
|
||||||
|
Ok(ips) => HashSet::from_iter(ips.iter().cloned()),
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("could not resolve host: {e}");
|
||||||
|
HashSet::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
HashSet::new()
|
HashSet::new()
|
||||||
};
|
};
|
||||||
@@ -112,7 +158,7 @@ impl Server {
|
|||||||
let client = self.client_manager.add_client(hostname.clone(), addr, port, pos);
|
let client = self.client_manager.add_client(hostname.clone(), addr, port, pos);
|
||||||
log::debug!("add_client {client}");
|
log::debug!("add_client {client}");
|
||||||
let notify = FrontendNotify::NotifyClientCreate(client, hostname, port, pos);
|
let notify = FrontendNotify::NotifyClientCreate(client, hostname, port, pos);
|
||||||
if let Err(e) = self.frontend.notify_all(notify) {
|
if let Err(e) = self.frontend.notify_all(notify).await {
|
||||||
log::error!("{e}");
|
log::error!("{e}");
|
||||||
};
|
};
|
||||||
client
|
client
|
||||||
@@ -131,13 +177,13 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_client(&mut self, client: ClientHandle) -> Option<ClientHandle> {
|
pub async fn remove_client(&mut self, client: ClientHandle) -> Option<ClientHandle> {
|
||||||
self.producer.notify(ClientEvent::Destroy(client));
|
self.producer.notify(ClientEvent::Destroy(client));
|
||||||
self.consumer.notify(ClientEvent::Destroy(client));
|
self.consumer.notify(ClientEvent::Destroy(client));
|
||||||
if let Some(client) = self.client_manager.remove_client(client).map(|s| s.client.handle) {
|
if let Some(client) = self.client_manager.remove_client(client).map(|s| s.client.handle) {
|
||||||
let notify = FrontendNotify::NotifyClientDelete(client);
|
let notify = FrontendNotify::NotifyClientDelete(client);
|
||||||
log::debug!("{notify:?}");
|
log::debug!("{notify:?}");
|
||||||
if let Err(e) = self.frontend.notify_all(notify) {
|
if let Err(e) = self.frontend.notify_all(notify).await {
|
||||||
log::error!("{e}");
|
log::error!("{e}");
|
||||||
}
|
}
|
||||||
Some(client)
|
Some(client)
|
||||||
@@ -146,7 +192,7 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_client(
|
pub async fn update_client(
|
||||||
&mut self,
|
&mut self,
|
||||||
client: ClientHandle,
|
client: ClientHandle,
|
||||||
hostname: Option<String>,
|
hostname: Option<String>,
|
||||||
@@ -184,89 +230,80 @@ impl Server {
|
|||||||
state.client.active_addr = None;
|
state.client.active_addr = None;
|
||||||
state.client.hostname = hostname;
|
state.client.hostname = hostname;
|
||||||
if let Some(hostname) = state.client.hostname.as_ref() {
|
if let Some(hostname) = state.client.hostname.as_ref() {
|
||||||
if let Ok(ips) = self.resolver.resolve(hostname.as_str()) {
|
match self.resolver.resolve(hostname.as_str()).await {
|
||||||
let addrs = ips.iter().map(|i| SocketAddr::new(*i, port));
|
Ok(ips) => {
|
||||||
state.client.addrs = HashSet::from_iter(addrs);
|
let addrs = ips.iter().map(|i| SocketAddr::new(*i, port));
|
||||||
|
state.client.addrs = HashSet::from_iter(addrs);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("could not resolve host: {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log::debug!("client updated: {:?}", state);
|
log::debug!("client updated: {:?}", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_udp_rx(&mut self) {
|
async fn handle_udp_rx(&mut self, event: (Event, SocketAddr)) {
|
||||||
loop {
|
let (event, addr) = event;
|
||||||
let (event, addr) = match self.receive_event() {
|
|
||||||
Ok(e) => e,
|
|
||||||
Err(e) => {
|
|
||||||
if e.is::<std::io::Error>() {
|
|
||||||
if let ErrorKind::WouldBlock = e.downcast_ref::<std::io::Error>()
|
|
||||||
.unwrap()
|
|
||||||
.kind() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log::error!("{}", e);
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// get handle for addr
|
// get handle for addr
|
||||||
let handle = match self.client_manager.get_client(addr) {
|
let handle = match self.client_manager.get_client(addr) {
|
||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => {
|
None => {
|
||||||
log::warn!("ignoring event from client {addr:?}");
|
log::warn!("ignoring event from client {addr:?}");
|
||||||
continue
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
log::trace!("{:20} <-<-<-<------ {addr} ({handle})", event.to_string());
|
|
||||||
let state = match self.client_manager.get_mut(handle) {
|
|
||||||
Some(s) => s,
|
|
||||||
None => {
|
|
||||||
log::error!("unknown handle");
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// reset ttl for client and
|
log::trace!("{:20} <-<-<-<------ {addr} ({handle})", event.to_string());
|
||||||
state.last_seen = Some(Instant::now());
|
let state = match self.client_manager.get_mut(handle) {
|
||||||
// set addr as new default for this client
|
Some(s) => s,
|
||||||
state.client.active_addr = Some(addr);
|
None => {
|
||||||
match (event, addr) {
|
log::error!("unknown handle");
|
||||||
(Event::Pong(), _) => {},
|
return;
|
||||||
(Event::Ping(), addr) => {
|
}
|
||||||
if let Err(e) = Self::send_event(&self.socket, Event::Pong(), addr) {
|
};
|
||||||
log::error!("udp send: {}", e);
|
|
||||||
}
|
|
||||||
// we release the mouse here,
|
|
||||||
// since its very likely, that we wont get a release event
|
|
||||||
self.producer.release();
|
|
||||||
}
|
|
||||||
(event, addr) => match self.state {
|
|
||||||
State::Sending => {
|
|
||||||
// in sending state, we dont want to process
|
|
||||||
// any events to avoid feedback loops,
|
|
||||||
// therefore we tell the event producer
|
|
||||||
// to release the pointer and move on
|
|
||||||
// first event -> release pointer
|
|
||||||
if let Event::Release() = event {
|
|
||||||
log::debug!("releasing pointer ...");
|
|
||||||
self.producer.release();
|
|
||||||
self.state = State::Receiving;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
State::Receiving => {
|
|
||||||
// consume event
|
|
||||||
self.consumer.consume(event, handle);
|
|
||||||
|
|
||||||
// let the server know we are still alive once every second
|
// reset ttl for client and
|
||||||
let last_replied = state.last_replied;
|
state.last_seen = Some(Instant::now());
|
||||||
if last_replied.is_none()
|
// set addr as new default for this client
|
||||||
|| last_replied.is_some()
|
state.client.active_addr = Some(addr);
|
||||||
&& last_replied.unwrap().elapsed() > Duration::from_secs(1) {
|
match (event, addr) {
|
||||||
state.last_replied = Some(Instant::now());
|
(Event::Pong(), _) => {},
|
||||||
if let Err(e) = Self::send_event(&self.socket, Event::Pong(), addr) {
|
(Event::Ping(), addr) => {
|
||||||
log::error!("udp send: {}", e);
|
if let Err(e) = send_event(&self.socket, Event::Pong(), addr).await {
|
||||||
}
|
log::error!("udp send: {}", e);
|
||||||
|
}
|
||||||
|
// we release the mouse here,
|
||||||
|
// since its very likely, that we wont get a release event
|
||||||
|
self.producer.release();
|
||||||
|
}
|
||||||
|
(event, addr) => match self.state {
|
||||||
|
State::Sending => {
|
||||||
|
// in sending state, we dont want to process
|
||||||
|
// any events to avoid feedback loops,
|
||||||
|
// therefore we tell the event producer
|
||||||
|
// to release the pointer and move on
|
||||||
|
// first event -> release pointer
|
||||||
|
if let Event::Release() = event {
|
||||||
|
log::debug!("releasing pointer ...");
|
||||||
|
self.producer.release();
|
||||||
|
self.state = State::Receiving;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State::Receiving => {
|
||||||
|
// consume event
|
||||||
|
self.consumer.consume(event, handle);
|
||||||
|
|
||||||
|
// let the server know we are still alive once every second
|
||||||
|
let last_replied = state.last_replied;
|
||||||
|
if last_replied.is_none()
|
||||||
|
|| last_replied.is_some()
|
||||||
|
&& last_replied.unwrap().elapsed() > Duration::from_secs(1) {
|
||||||
|
state.last_replied = Some(Instant::now());
|
||||||
|
if let Err(e) = send_event(&self.socket, Event::Pong(), addr).await {
|
||||||
|
log::error!("udp send: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,66 +311,71 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_producer_rx(&mut self) {
|
#[cfg(unix)]
|
||||||
let events = self.producer.read_events();
|
async fn handle_producer_rx(&mut self) {
|
||||||
let mut should_release = false;
|
let events: Vec<(ClientHandle, Event)> = self.producer.read_events().collect();
|
||||||
for (c, e) in events.into_iter() {
|
for (c,e) in events.into_iter() {
|
||||||
// in receiving state, only release events
|
self.handle_producer_event(c,e).await;
|
||||||
// must be transmitted
|
}
|
||||||
if let Event::Release() = e {
|
}
|
||||||
self.state = State::Sending;
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!("producer: ({c}) {e:?}");
|
async fn handle_producer_event(&mut self, c: ClientHandle, e: Event) {
|
||||||
let state = match self.client_manager.get_mut(c) {
|
let mut should_release = false;
|
||||||
Some(state) => state,
|
// in receiving state, only release events
|
||||||
None => {
|
// must be transmitted
|
||||||
log::warn!("unknown client!");
|
if let Event::Release() = e {
|
||||||
continue
|
self.state = State::Sending;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
// otherwise we should have an address to send to
|
log::trace!("producer: ({c}) {e:?}");
|
||||||
// transmit events to the corrensponding client
|
let state = match self.client_manager.get_mut(c) {
|
||||||
if let Some(addr) = state.client.active_addr {
|
Some(state) => state,
|
||||||
if let Err(e) = Self::send_event(&self.socket, e, addr) {
|
None => {
|
||||||
|
log::warn!("unknown client!");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// otherwise we should have an address to send to
|
||||||
|
// transmit events to the corrensponding client
|
||||||
|
if let Some(addr) = state.client.active_addr {
|
||||||
|
if let Err(e) = send_event(&self.socket, e, addr).await {
|
||||||
|
log::error!("udp send: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if client last responded > 2 seconds ago
|
||||||
|
// and we have not sent a ping since 500 milliseconds,
|
||||||
|
// send a ping
|
||||||
|
if state.last_seen.is_some()
|
||||||
|
&& state.last_seen.unwrap().elapsed() < Duration::from_secs(2) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// client last seen > 500ms ago
|
||||||
|
if state.last_ping.is_some()
|
||||||
|
&& state.last_ping.unwrap().elapsed() < Duration::from_millis(500) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// release mouse if client didnt respond to the first ping
|
||||||
|
if state.last_ping.is_some()
|
||||||
|
&& state.last_ping.unwrap().elapsed() < Duration::from_secs(1) {
|
||||||
|
should_release = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// last ping > 500ms ago -> ping all interfaces
|
||||||
|
state.last_ping = Some(Instant::now());
|
||||||
|
for addr in state.client.addrs.iter() {
|
||||||
|
log::debug!("pinging {addr}");
|
||||||
|
if let Err(e) = send_event(&self.socket, Event::Ping(), *addr).await {
|
||||||
|
if e.kind() != ErrorKind::WouldBlock {
|
||||||
log::error!("udp send: {}", e);
|
log::error!("udp send: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// send additional release event, in case client is still in sending mode
|
||||||
// if client last responded > 2 seconds ago
|
if let Err(e) = send_event(&self.socket, Event::Release(), *addr).await {
|
||||||
// and we have not sent a ping since 500 milliseconds,
|
if e.kind() != ErrorKind::WouldBlock {
|
||||||
// send a ping
|
log::error!("udp send: {}", e);
|
||||||
if state.last_seen.is_some()
|
|
||||||
&& state.last_seen.unwrap().elapsed() < Duration::from_secs(2) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// client last seen > 500ms ago
|
|
||||||
if state.last_ping.is_some()
|
|
||||||
&& state.last_ping.unwrap().elapsed() < Duration::from_millis(500) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// release mouse if client didnt respond to the first ping
|
|
||||||
if state.last_ping.is_some()
|
|
||||||
&& state.last_ping.unwrap().elapsed() < Duration::from_secs(1) {
|
|
||||||
should_release = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// last ping > 500ms ago -> ping all interfaces
|
|
||||||
state.last_ping = Some(Instant::now());
|
|
||||||
for addr in state.client.addrs.iter() {
|
|
||||||
log::debug!("pinging {addr}");
|
|
||||||
if let Err(e) = Self::send_event(&self.socket, Event::Ping(), *addr) {
|
|
||||||
if e.kind() != ErrorKind::WouldBlock {
|
|
||||||
log::error!("udp send: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// send additional release event, in case client is still in sending mode
|
|
||||||
if let Err(e) = Self::send_event(&self.socket, Event::Release(), *addr) {
|
|
||||||
if e.kind() != ErrorKind::WouldBlock {
|
|
||||||
log::error!("udp send: {}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,123 +385,76 @@ impl Server {
|
|||||||
self.producer.release();
|
self.producer.release();
|
||||||
self.state = State::Receiving;
|
self.state = State::Receiving;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_frontend_incoming(&mut self) {
|
#[cfg(unix)]
|
||||||
loop {
|
async fn handle_frontend_stream(&mut self, mut stream: ReadHalf<UnixStream>) {
|
||||||
let token = self.fresh_token();
|
let tx = self.frontend_tx.clone();
|
||||||
let poll = &mut self.poll;
|
tokio::task::spawn_local(async move {
|
||||||
match self.frontend.handle_incoming(|s, i| {
|
loop {
|
||||||
poll.registry().register(s, token, i)?;
|
let event = frontend::read_event(&mut stream).await;
|
||||||
Ok(token)
|
match event {
|
||||||
}) {
|
Ok(event) => tx.send(event).await.unwrap(),
|
||||||
Err(e) if e.kind() == ErrorKind::WouldBlock => break,
|
Err(e) => log::error!("error reading frontend event: {e}"),
|
||||||
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("{e}");
|
|
||||||
break
|
|
||||||
}
|
|
||||||
_ => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// notify new frontend connections of current clients
|
|
||||||
self.enumerate();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_frontend_event(&mut self, token: Token) -> bool {
|
|
||||||
loop {
|
|
||||||
let event = match self.frontend.read_event(token) {
|
|
||||||
Ok(event) => event,
|
|
||||||
Err(e) if e.kind() == ErrorKind::WouldBlock => return false,
|
|
||||||
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("{e}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Some(event) = event {
|
|
||||||
log::debug!("frontend: {event:?}");
|
|
||||||
match event {
|
|
||||||
FrontendEvent::AddClient(hostname, port, pos) => {
|
|
||||||
self.add_client(hostname, HashSet::new(), port, pos);
|
|
||||||
}
|
|
||||||
FrontendEvent::ActivateClient(client, active) => {
|
|
||||||
self.activate_client(client, active);
|
|
||||||
}
|
|
||||||
FrontendEvent::DelClient(client) => {
|
|
||||||
self.remove_client(client);
|
|
||||||
}
|
|
||||||
FrontendEvent::UpdateClient(client, hostname, port, pos) => {
|
|
||||||
self.update_client(client, hostname, port, pos);
|
|
||||||
}
|
|
||||||
FrontendEvent::Enumerate() => self.enumerate(),
|
|
||||||
FrontendEvent::Shutdown() => {
|
|
||||||
log::info!("terminating gracefully...");
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
self.enumerate().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enumerate(&mut self) {
|
#[cfg(windows)]
|
||||||
|
async fn handle_frontend_stream(&mut self, mut stream: ReadHalf<TcpStream>) {
|
||||||
|
let tx = self.frontend_tx.clone();
|
||||||
|
tokio::task::spawn_local(async move {
|
||||||
|
loop {
|
||||||
|
let event = frontend::read_event(&mut stream).await;
|
||||||
|
match event {
|
||||||
|
Ok(event) => tx.send(event).await.unwrap(),
|
||||||
|
Err(e) => log::error!("error reading frontend event: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.enumerate().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_frontend_event(&mut self, event: FrontendEvent) -> bool {
|
||||||
|
log::debug!("frontend: {event:?}");
|
||||||
|
match event {
|
||||||
|
FrontendEvent::AddClient(hostname, port, pos) => { self.add_client(hostname, HashSet::new(), port, pos).await; },
|
||||||
|
FrontendEvent::ActivateClient(client, active) => self.activate_client(client, active),
|
||||||
|
FrontendEvent::DelClient(client) => { self.remove_client(client).await; },
|
||||||
|
FrontendEvent::UpdateClient(client, hostname, port, pos) => self.update_client(client, hostname, port, pos).await,
|
||||||
|
FrontendEvent::Enumerate() => self.enumerate().await,
|
||||||
|
FrontendEvent::Shutdown() => {
|
||||||
|
log::info!("terminating gracefully...");
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn enumerate(&mut self) {
|
||||||
let clients = self.client_manager.enumerate();
|
let clients = self.client_manager.enumerate();
|
||||||
if let Err(e) = self.frontend.notify_all(FrontendNotify::Enumerate(clients)) {
|
if let Err(e) = self.frontend.notify_all(FrontendNotify::Enumerate(clients)).await {
|
||||||
log::error!("{e}");
|
log::error!("{e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
async fn receive_event(socket: &UdpSocket) -> std::result::Result<(Event, SocketAddr), Box<dyn Error>> {
|
||||||
fn handle_signal(&mut self) -> bool {
|
let mut buf = vec![0u8; 22];
|
||||||
#[cfg(windows)]
|
match socket.recv_from(&mut buf).await {
|
||||||
return false;
|
Ok((_amt, src)) => Ok((Event::try_from(buf)?, src)),
|
||||||
#[cfg(not(windows))]
|
Err(e) => Err(Box::new(e)),
|
||||||
loop {
|
|
||||||
match self.signals.receive() {
|
|
||||||
Err(e) if e.kind() == ErrorKind::WouldBlock => return false,
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("error reading signal: {e}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Ok(Some(Signal::Interrupt) | Some(Signal::Terminate)) => {
|
|
||||||
// terminate on SIG_INT or SIG_TERM
|
|
||||||
log::info!("terminating gracefully...");
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
Ok(Some(signal)) => {
|
|
||||||
log::info!("ignoring signal {signal:?}");
|
|
||||||
},
|
|
||||||
Ok(None) => return false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_event(sock: &UdpSocket, e: Event, addr: SocketAddr) -> Result<usize> {
|
|
||||||
log::trace!("{:20} ------>->->-> {addr}", e.to_string());
|
|
||||||
let data: Vec<u8> = (&e).into();
|
|
||||||
// We are currently abusing a blocking send to get the lowest possible latency.
|
|
||||||
// It may be better to set the socket to non-blocking and only send when ready.
|
|
||||||
sock.send_to(&data[..], addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn receive_event(&self) -> std::result::Result<(Event, SocketAddr), Box<dyn Error>> {
|
|
||||||
let mut buf = vec![0u8; 22];
|
|
||||||
match self.socket.recv_from(&mut buf) {
|
|
||||||
Ok((_amt, src)) => Ok((Event::try_from(buf)?, src)),
|
|
||||||
Err(e) => Err(Box::new(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fresh_token(&mut self) -> Token {
|
|
||||||
let token = self.next_token as usize;
|
|
||||||
self.next_token += 1;
|
|
||||||
Token(token)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_frontend(&mut self, source: &mut dyn Source, interests: Interest) -> Result<Token> {
|
|
||||||
let token = self.fresh_token();
|
|
||||||
self.poll.registry().register(source, token, interests)?;
|
|
||||||
Ok(token)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async fn send_event(sock: &UdpSocket, e: Event, addr: SocketAddr) -> Result<usize> {
|
||||||
|
log::trace!("{:20} ------>->->-> {addr}", e.to_string());
|
||||||
|
let data: Vec<u8> = (&e).into();
|
||||||
|
// We are currently abusing a blocking send to get the lowest possible latency.
|
||||||
|
// It may be better to set the socket to non-blocking and only send when ready.
|
||||||
|
sock.send_to(&data[..], addr).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
171
src/frontend.rs
171
src/frontend.rs
@@ -1,21 +1,20 @@
|
|||||||
use std::collections::HashMap;
|
use std::io::Result;
|
||||||
use std::io::{Read, Result, Write};
|
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::{env, path::{Path, PathBuf}};
|
use std::{env, path::{Path, PathBuf}};
|
||||||
|
|
||||||
use mio::Interest;
|
use tokio::io::{AsyncReadExt, WriteHalf, AsyncWriteExt};
|
||||||
use mio::{Registry, Token, event::Source};
|
use tokio::io::ReadHalf;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use mio::net::UnixStream;
|
use tokio::net::UnixStream;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use mio::net::UnixListener;
|
use tokio::net::UnixListener;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use mio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use mio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
@@ -60,11 +59,14 @@ pub struct FrontendListener {
|
|||||||
listener: UnixListener,
|
listener: UnixListener,
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
socket_path: PathBuf,
|
socket_path: PathBuf,
|
||||||
frontend_connections: HashMap<Token, FrontendConnection>,
|
#[cfg(unix)]
|
||||||
|
tx_streams: Vec<WriteHalf<UnixStream>>,
|
||||||
|
#[cfg(windows)]
|
||||||
|
tx_streams: Vec<WriteHalf<TcpStream>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FrontendListener {
|
impl FrontendListener {
|
||||||
pub fn new() -> std::result::Result<Self, Box<dyn std::error::Error>> {
|
pub async fn new() -> std::result::Result<Self, Box<dyn std::error::Error>> {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let socket_path = Path::new(env::var("XDG_RUNTIME_DIR")?.as_str()).join("lan-mouse-socket.sock");
|
let socket_path = Path::new(env::var("XDG_RUNTIME_DIR")?.as_str()).join("lan-mouse-socket.sock");
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@@ -77,86 +79,53 @@ impl FrontendListener {
|
|||||||
let listener = UnixListener::bind(&socket_path)?;
|
let listener = UnixListener::bind(&socket_path)?;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let listener = TcpListener::bind("127.0.0.1:5252".parse().unwrap())?; // abuse tcp
|
let listener = TcpListener::bind("127.0.0.1:5252").await?; // abuse tcp
|
||||||
|
|
||||||
let adapter = Self {
|
let adapter = Self {
|
||||||
listener,
|
listener,
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
socket_path,
|
socket_path,
|
||||||
frontend_connections: HashMap::new(),
|
tx_streams: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(adapter)
|
Ok(adapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub fn handle_incoming<F>(&mut self, register_frontend: F) -> Result<()>
|
pub async fn accept(&mut self) -> Result<ReadHalf<UnixStream>> {
|
||||||
where F: Fn(&mut UnixStream, Interest) -> Result<Token> {
|
|
||||||
let (mut stream, _) = self.listener.accept()?;
|
let stream = self.listener.accept().await?.0;
|
||||||
let token = register_frontend(&mut stream, Interest::READABLE)?;
|
let (rx, tx) = tokio::io::split(stream);
|
||||||
let con = FrontendConnection::new(stream);
|
self.tx_streams.push(tx);
|
||||||
self.frontend_connections.insert(token, con);
|
Ok(rx)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn handle_incoming<F>(&mut self, register_frontend: F) -> Result<()>
|
pub async fn accept(&mut self) -> Result<ReadHalf<TcpStream>> {
|
||||||
where F: Fn(&mut TcpStream, Interest) -> Result<Token> {
|
let stream = self.listener.accept().await?.0;
|
||||||
let (mut stream, _) = self.listener.accept()?;
|
let (rx, tx) = tokio::io::split(stream);
|
||||||
let token = register_frontend(&mut stream, Interest::READABLE)?;
|
self.tx_streams.push(tx);
|
||||||
let con = FrontendConnection::new(stream);
|
Ok(rx)
|
||||||
self.frontend_connections.insert(token, con);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_event(&mut self, token: Token) -> Result<Option<FrontendEvent>> {
|
|
||||||
if let Some(con) = self.frontend_connections.get_mut(&token) {
|
|
||||||
con.handle_event()
|
|
||||||
} else {
|
|
||||||
panic!("unknown token");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn notify_all(&mut self, notify: FrontendNotify) -> Result<()> {
|
pub(crate) async fn notify_all(&mut self, notify: FrontendNotify) -> Result<()> {
|
||||||
// encode event
|
// encode event
|
||||||
let json = serde_json::to_string(¬ify).unwrap();
|
let json = serde_json::to_string(¬ify).unwrap();
|
||||||
let payload = json.as_bytes();
|
let payload = json.as_bytes();
|
||||||
let len = payload.len().to_ne_bytes();
|
let len = payload.len().to_be_bytes();
|
||||||
log::debug!("json: {json}, len: {}", payload.len());
|
log::debug!("json: {json}, len: {}", payload.len());
|
||||||
|
|
||||||
for con in self.frontend_connections.values_mut() {
|
// TODO do simultaneously
|
||||||
|
for tx in self.tx_streams.iter_mut() {
|
||||||
// write len + payload
|
// write len + payload
|
||||||
con.stream.write(&len)?;
|
tx.write(&len).await?;
|
||||||
con.stream.write(payload)?;
|
tx.write(payload).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source for FrontendListener {
|
|
||||||
fn register(
|
|
||||||
&mut self,
|
|
||||||
registry: &Registry,
|
|
||||||
token: Token,
|
|
||||||
interests: mio::Interest,
|
|
||||||
) -> Result<()> {
|
|
||||||
self.listener.register(registry, token, interests)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reregister(
|
|
||||||
&mut self,
|
|
||||||
registry: &Registry,
|
|
||||||
token: Token,
|
|
||||||
interests: mio::Interest,
|
|
||||||
) -> Result<()> {
|
|
||||||
self.listener.reregister(registry, token, interests)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deregister(&mut self, registry: &Registry) -> Result<()> {
|
|
||||||
self.listener.deregister(registry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
impl Drop for FrontendListener {
|
impl Drop for FrontendListener {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
@@ -165,72 +134,20 @@ impl Drop for FrontendListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ReceiveState {
|
#[cfg(unix)]
|
||||||
Len, Data,
|
pub async fn read_event(stream: &mut ReadHalf<UnixStream>) -> Result<FrontendEvent> {
|
||||||
|
let len = stream.read_u64().await?;
|
||||||
|
assert!(len <= 256);
|
||||||
|
let mut buf = [0u8; 256];
|
||||||
|
stream.read_exact(&mut buf[..len as usize]).await?;
|
||||||
|
Ok(serde_json::from_slice(&buf[..len as usize])?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FrontendConnection {
|
#[cfg(windows)]
|
||||||
#[cfg(unix)]
|
pub async fn read_event(stream: &mut ReadHalf<TcpStream>) -> Result<FrontendEvent> {
|
||||||
stream: UnixStream,
|
let len = stream.read_u64().await?;
|
||||||
#[cfg(windows)]
|
let mut buf = [0u8; 256];
|
||||||
stream: TcpStream,
|
stream.read_exact(&mut buf[..len as usize]).await?;
|
||||||
state: ReceiveState,
|
Ok(serde_json::from_slice(&buf[..len as usize])?)
|
||||||
len: usize,
|
|
||||||
len_buf: [u8; std::mem::size_of::<usize>()],
|
|
||||||
recieve_buf: [u8; 256], // FIXME
|
|
||||||
pos: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FrontendConnection {
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub fn new(stream: UnixStream) -> Self {
|
|
||||||
Self {
|
|
||||||
stream,
|
|
||||||
state: ReceiveState::Len,
|
|
||||||
len: 0,
|
|
||||||
len_buf: [0u8; std::mem::size_of::<usize>()],
|
|
||||||
recieve_buf: [0u8; 256],
|
|
||||||
pos: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub fn new(stream: TcpStream) -> Self {
|
|
||||||
Self {
|
|
||||||
stream,
|
|
||||||
state: ReceiveState::Len,
|
|
||||||
len: 0,
|
|
||||||
len_buf: [0u8; std::mem::size_of::<usize>()],
|
|
||||||
recieve_buf: [0u8; 256],
|
|
||||||
pos: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_event(&mut self) -> Result<Option<FrontendEvent>> {
|
|
||||||
match self.state {
|
|
||||||
ReceiveState::Len => {
|
|
||||||
// we receive sizeof(usize) Bytes
|
|
||||||
let n = self.stream.read(&mut self.len_buf)?;
|
|
||||||
self.pos += n;
|
|
||||||
if self.pos == self.len_buf.len() {
|
|
||||||
self.state = ReceiveState::Data;
|
|
||||||
self.len = usize::from_ne_bytes(self.len_buf);
|
|
||||||
self.pos = 0;
|
|
||||||
}
|
|
||||||
Ok(None)
|
|
||||||
},
|
|
||||||
ReceiveState::Data => {
|
|
||||||
// read at most as many bytes as the length of the next event
|
|
||||||
let n = self.stream.read(&mut self.recieve_buf[..self.len])?;
|
|
||||||
self.pos += n;
|
|
||||||
if n == self.len {
|
|
||||||
self.state = ReceiveState::Len;
|
|
||||||
self.pos = 0;
|
|
||||||
Ok(Some(serde_json::from_slice(&self.recieve_buf[..self.len])?))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ pub fn start() -> Result<(JoinHandle<()>, JoinHandle<()>)> {
|
|||||||
for event in events.iter() {
|
for event in events.iter() {
|
||||||
let json = serde_json::to_string(&event).unwrap();
|
let json = serde_json::to_string(&event).unwrap();
|
||||||
let bytes = json.as_bytes();
|
let bytes = json.as_bytes();
|
||||||
let len = bytes.len().to_ne_bytes();
|
let len = bytes.len().to_be_bytes();
|
||||||
if let Err(e) = tx.write(&len) {
|
if let Err(e) = tx.write(&len) {
|
||||||
log::error!("error sending message: {e}");
|
log::error!("error sending message: {e}");
|
||||||
};
|
};
|
||||||
@@ -77,7 +77,7 @@ pub fn start() -> Result<(JoinHandle<()>, JoinHandle<()>)> {
|
|||||||
Err(e) if e.kind() == ErrorKind::UnexpectedEof => break,
|
Err(e) if e.kind() == ErrorKind::UnexpectedEof => break,
|
||||||
Err(e) => break log::error!("{e}"),
|
Err(e) => break log::error!("{e}"),
|
||||||
};
|
};
|
||||||
let len = usize::from_ne_bytes(len);
|
let len = usize::from_be_bytes(len);
|
||||||
|
|
||||||
// read payload
|
// read payload
|
||||||
let mut buf: Vec<u8> = vec![0u8; len];
|
let mut buf: Vec<u8> = vec![0u8; len];
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use self::client_object::ClientObject;
|
|||||||
use super::FrontendNotify;
|
use super::FrontendNotify;
|
||||||
|
|
||||||
pub fn start() -> Result<JoinHandle<glib::ExitCode>> {
|
pub fn start() -> Result<JoinHandle<glib::ExitCode>> {
|
||||||
|
log::debug!("starting gtk frontend");
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("gtk-thread".into())
|
.name("gtk-thread".into())
|
||||||
.spawn(gtk_main)
|
.spawn(gtk_main)
|
||||||
@@ -58,6 +59,7 @@ fn build_ui(app: &Application) {
|
|||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
log::debug!("connecting to lan-mouse-socket ... ");
|
||||||
let socket_path = Path::new(xdg_runtime_dir.as_str())
|
let socket_path = Path::new(xdg_runtime_dir.as_str())
|
||||||
.join("lan-mouse-socket.sock");
|
.join("lan-mouse-socket.sock");
|
||||||
let Ok(mut rx) = UnixStream::connect(&socket_path) else {
|
let Ok(mut rx) = UnixStream::connect(&socket_path) else {
|
||||||
@@ -71,6 +73,7 @@ fn build_ui(app: &Application) {
|
|||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
log::debug!("connected to lan-mouse-socket");
|
||||||
|
|
||||||
let (sender, receiver) = MainContext::channel::<FrontendNotify>(Priority::default());
|
let (sender, receiver) = MainContext::channel::<FrontendNotify>(Priority::default());
|
||||||
|
|
||||||
@@ -83,7 +86,7 @@ fn build_ui(app: &Application) {
|
|||||||
Err(e) if e.kind() == ErrorKind::UnexpectedEof => break Ok(()),
|
Err(e) if e.kind() == ErrorKind::UnexpectedEof => break Ok(()),
|
||||||
Err(e) => break Err(e),
|
Err(e) => break Err(e),
|
||||||
};
|
};
|
||||||
let len = usize::from_ne_bytes(len);
|
let len = usize::from_be_bytes(len);
|
||||||
|
|
||||||
// read payload
|
// read payload
|
||||||
let mut buf = vec![0u8; len];
|
let mut buf = vec![0u8; len];
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ impl Window {
|
|||||||
let mut stream = self.imp().stream.borrow_mut();
|
let mut stream = self.imp().stream.borrow_mut();
|
||||||
let stream = stream.as_mut().unwrap();
|
let stream = stream.as_mut().unwrap();
|
||||||
let bytes = json.as_bytes();
|
let bytes = json.as_bytes();
|
||||||
let len = bytes.len().to_ne_bytes();
|
let len = bytes.len().to_be_bytes();
|
||||||
if let Err(e) = stream.write(&len) {
|
if let Err(e) = stream.write(&len) {
|
||||||
log::error!("error sending message: {e}");
|
log::error!("error sending message: {e}");
|
||||||
};
|
};
|
||||||
|
|||||||
49
src/main.rs
49
src/main.rs
@@ -9,6 +9,7 @@ use lan_mouse::{
|
|||||||
|
|
||||||
#[cfg(all(unix, feature = "gtk"))]
|
#[cfg(all(unix, feature = "gtk"))]
|
||||||
use lan_mouse::frontend::gtk;
|
use lan_mouse::frontend::gtk;
|
||||||
|
use tokio::task::LocalSet;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
|
||||||
@@ -30,29 +31,39 @@ pub fn run() -> Result<(), Box<dyn Error>> {
|
|||||||
let producer = producer::create()?;
|
let producer = producer::create()?;
|
||||||
let consumer = consumer::create()?;
|
let consumer = consumer::create()?;
|
||||||
|
|
||||||
// create frontend communication adapter
|
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||||
let frontend_adapter = FrontendListener::new()?;
|
.enable_io()
|
||||||
|
.enable_time()
|
||||||
|
.build()?;
|
||||||
|
|
||||||
// start sending and receiving events
|
// run async event loop
|
||||||
let mut event_server = Server::new(config.port, producer, consumer, frontend_adapter)?;
|
runtime.block_on(LocalSet::new().run_until(async {
|
||||||
|
// create frontend communication adapter
|
||||||
|
let frontend_adapter = FrontendListener::new().await?;
|
||||||
|
|
||||||
// any threads need to be started after event_server sets up signal handling
|
// start frontend
|
||||||
match config.frontend {
|
match config.frontend {
|
||||||
#[cfg(all(unix, feature = "gtk"))]
|
#[cfg(all(unix, feature = "gtk"))]
|
||||||
Gtk => { gtk::start()?; }
|
Gtk => { gtk::start()?; }
|
||||||
#[cfg(any(not(feature = "gtk"), not(unix)))]
|
#[cfg(any(not(feature = "gtk"), not(unix)))]
|
||||||
Gtk => panic!("gtk frontend requested but feature not enabled!"),
|
Gtk => panic!("gtk frontend requested but feature not enabled!"),
|
||||||
Cli => { cli::start()?; }
|
Cli => { cli::start()?; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// add clients from config
|
// start sending and receiving events
|
||||||
config.get_clients().into_iter().for_each(|(c, h, port, p)| {
|
let mut event_server = Server::new(config.port, frontend_adapter, consumer, producer).await?;
|
||||||
event_server.add_client(h, c, port, p);
|
|
||||||
});
|
|
||||||
|
|
||||||
log::info!("Press Ctrl+Alt+Shift+Super to release the mouse");
|
// add clients from config
|
||||||
// run event loop
|
for (c,h,port,p) in config.get_clients().into_iter() {
|
||||||
event_server.run()?;
|
event_server.add_client(h, c, port, p).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("Press Ctrl+Alt+Shift+Super to release the mouse");
|
||||||
|
// run event loop
|
||||||
|
event_server.run().await?;
|
||||||
|
Result::<_, Box<dyn Error>>::Ok(())
|
||||||
|
}))?;
|
||||||
|
log::debug!("exiting main");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
use mio::event::Source;
|
use std::error::Error;
|
||||||
use std::{error::Error, vec::Drain};
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::{io, os::fd::RawFd, vec::Drain};
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use tokio::io::unix::AsyncFd;
|
||||||
|
|
||||||
use crate::{client::{ClientHandle, ClientEvent}, event::Event};
|
use crate::{client::{ClientHandle, ClientEvent}, event::Event};
|
||||||
use crate::backend::producer;
|
use crate::backend::producer;
|
||||||
|
|
||||||
@@ -49,15 +55,24 @@ pub fn create() -> Result<Box<dyn EventProducer>, Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait EventProducer: Source {
|
pub trait EventProducer {
|
||||||
|
|
||||||
/// notify event producer of configuration changes
|
/// notify event producer of configuration changes
|
||||||
fn notify(&mut self, event: ClientEvent);
|
fn notify(&mut self, event: ClientEvent);
|
||||||
|
|
||||||
|
/// release mouse
|
||||||
|
fn release(&mut self);
|
||||||
|
|
||||||
|
/// unix only
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn get_async_fd(&self) -> io::Result<AsyncFd<RawFd>>;
|
||||||
|
|
||||||
/// read an event
|
/// read an event
|
||||||
/// this function must be invoked to retrieve an Event after
|
/// this function must be invoked to retrieve an Event after
|
||||||
/// the eventfd indicates a pending Event
|
/// the eventfd indicates a pending Event
|
||||||
|
#[cfg(unix)]
|
||||||
fn read_events(&mut self) -> Drain<(ClientHandle, Event)>;
|
fn read_events(&mut self) -> Drain<(ClientHandle, Event)>;
|
||||||
|
|
||||||
/// release mouse
|
#[cfg(not(unix))]
|
||||||
fn release(&mut self);
|
fn get_wait_channel(&mut self) -> Option<tokio::sync::mpsc::Receiver<(ClientHandle, Event)>>;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user