mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-07 20:09:59 +03:00
add leave event to make entering a client more reliable (#50)
Instead of relying on release events not getting lost, every event now signals the opponent to release its pointer grab. There is one case that requires a Leave event: Consider a Sending client A and receiving Client B. If B enters the dead-zone of client A, it will send an enter event towards A but before A receives the Release event, it may still send additional events towards B that should not cause B to immediately revert to Receiving state again. Therefore B puts itself into AwaitingLeave state until it receives a Leave event coming from A. A responds to the Enter event coming from B with a leave event, to signify that it will no longer send any events and releases it's pointer. To guard against packet loss of the leave events, B sends additional enter events while it is in AwaitingLeave mode until it receives a Leave event at some point. This is still not resilient against possible packet reordering in UDP but in the (rare) case where a leave event arrives before some other event coming from A, the user would simply need to move the pointer into the dead-zone again.
This commit is contained in:
committed by
GitHub
parent
02d1b33e45
commit
735434438f
@@ -209,9 +209,7 @@ impl EventConsumer for MacOSConsumer {
|
||||
}
|
||||
KeyboardEvent::Modifiers { .. } => {}
|
||||
},
|
||||
Event::Release() => {}
|
||||
Event::Ping() => {}
|
||||
Event::Pong() => {}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -662,7 +662,7 @@ impl Dispatch<wl_pointer::WlPointer, ()> for State {
|
||||
.iter()
|
||||
.find(|(w, _c)| w.surface == surface)
|
||||
.unwrap();
|
||||
app.pending_events.push_back((*client, Event::Release()));
|
||||
app.pending_events.push_back((*client, Event::Enter()));
|
||||
}
|
||||
wl_pointer::Event::Leave { .. } => {
|
||||
app.ungrab();
|
||||
|
||||
30
src/event.rs
30
src/event.rs
@@ -47,10 +47,23 @@ pub enum KeyboardEvent {
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Event {
|
||||
/// pointer event (motion / button / axis)
|
||||
Pointer(PointerEvent),
|
||||
/// keyboard events (key / modifiers)
|
||||
Keyboard(KeyboardEvent),
|
||||
Release(),
|
||||
/// enter event: request to enter a client.
|
||||
/// The client must release the pointer if it is grabbed
|
||||
/// and reply with a leave event, as soon as its ready to
|
||||
/// receive events
|
||||
Enter(),
|
||||
/// leave event: this client is now ready to receive events and will
|
||||
/// not send any events after until it sends an enter event
|
||||
Leave(),
|
||||
/// ping a client, to see if it is still alive. A client that does
|
||||
/// not respond with a pong event will be assumed to be offline.
|
||||
Ping(),
|
||||
/// response to a ping event: this event signals that a client
|
||||
/// is still alive but must otherwise be ignored
|
||||
Pong(),
|
||||
}
|
||||
|
||||
@@ -103,7 +116,8 @@ impl Display for Event {
|
||||
match self {
|
||||
Event::Pointer(p) => write!(f, "{}", p),
|
||||
Event::Keyboard(k) => write!(f, "{}", k),
|
||||
Event::Release() => write!(f, "release"),
|
||||
Event::Enter() => write!(f, "enter"),
|
||||
Event::Leave() => write!(f, "leave"),
|
||||
Event::Ping() => write!(f, "ping"),
|
||||
Event::Pong() => write!(f, "pong"),
|
||||
}
|
||||
@@ -115,7 +129,8 @@ impl Event {
|
||||
match self {
|
||||
Self::Pointer(_) => EventType::Pointer,
|
||||
Self::Keyboard(_) => EventType::Keyboard,
|
||||
Self::Release() => EventType::Release,
|
||||
Self::Enter() => EventType::Enter,
|
||||
Self::Leave() => EventType::Leave,
|
||||
Self::Ping() => EventType::Ping,
|
||||
Self::Pong() => EventType::Pong,
|
||||
}
|
||||
@@ -155,7 +170,8 @@ enum KeyboardEventType {
|
||||
enum EventType {
|
||||
Pointer,
|
||||
Keyboard,
|
||||
Release,
|
||||
Enter,
|
||||
Leave,
|
||||
Ping,
|
||||
Pong,
|
||||
}
|
||||
@@ -196,7 +212,8 @@ impl From<&Event> for Vec<u8> {
|
||||
let event_data = match event {
|
||||
Event::Pointer(p) => p.into(),
|
||||
Event::Keyboard(k) => k.into(),
|
||||
Event::Release() => vec![],
|
||||
Event::Enter() => vec![],
|
||||
Event::Leave() => vec![],
|
||||
Event::Ping() => vec![],
|
||||
Event::Pong() => vec![],
|
||||
};
|
||||
@@ -224,7 +241,8 @@ impl TryFrom<Vec<u8>> for Event {
|
||||
match event_id {
|
||||
i if i == (EventType::Pointer as u8) => Ok(Event::Pointer(value.try_into()?)),
|
||||
i if i == (EventType::Keyboard as u8) => Ok(Event::Keyboard(value.try_into()?)),
|
||||
i if i == (EventType::Release as u8) => Ok(Event::Release()),
|
||||
i if i == (EventType::Enter as u8) => Ok(Event::Enter()),
|
||||
i if i == (EventType::Leave as u8) => Ok(Event::Leave()),
|
||||
i if i == (EventType::Ping as u8) => Ok(Event::Ping()),
|
||||
i if i == (EventType::Pong as u8) => Ok(Event::Pong()),
|
||||
_ => Err(Box::new(ProtocolError {
|
||||
|
||||
103
src/server.rs
103
src/server.rs
@@ -36,6 +36,7 @@ use crate::{
|
||||
enum State {
|
||||
Sending,
|
||||
Receiving,
|
||||
AwaitingLeave,
|
||||
}
|
||||
|
||||
pub struct Server {
|
||||
@@ -308,63 +309,91 @@ impl Server {
|
||||
state.client.active_addr = Some(addr);
|
||||
|
||||
match (event, addr) {
|
||||
(Event::Pong(), _) => {} // ignore pong events
|
||||
(Event::Pong(), _) => { /* ignore pong events */ }
|
||||
(Event::Ping(), addr) => {
|
||||
if let Err(e) = send_event(&self.socket, Event::Pong(), addr).await {
|
||||
log::error!("udp send: {}", e);
|
||||
}
|
||||
}
|
||||
(event, addr) => {
|
||||
// device is sending events => release pointer if captured
|
||||
if self.state == State::Sending {
|
||||
// Even in sending state, we can still receive events
|
||||
// from the client we entered until (our) release event
|
||||
// arrives there.
|
||||
//
|
||||
// Therefore we must wait until we receive
|
||||
// another release event to actually release the pointer.
|
||||
if let Event::Release() = event {
|
||||
log::debug!("releasing pointer ...");
|
||||
self.producer.release();
|
||||
self.state = State::Receiving;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// consume event
|
||||
self.consumer.consume(event, handle).await;
|
||||
log::trace!("{event:?} => consumer");
|
||||
|
||||
// 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 {
|
||||
// tell clients that we are ready to receive events
|
||||
if let Event::Enter() = event {
|
||||
if let Err(e) = send_event(&self.socket, Event::Leave(), addr).await {
|
||||
log::error!("udp send: {}", e);
|
||||
}
|
||||
}
|
||||
match self.state {
|
||||
State::Sending => {
|
||||
if let Event::Leave() = event {
|
||||
// ignore additional leave events that may
|
||||
// have been sent for redundancy
|
||||
} else {
|
||||
// upon receiving any event, we go back to receiving mode
|
||||
self.producer.release();
|
||||
self.state = State::Receiving;
|
||||
}
|
||||
},
|
||||
State::Receiving => {
|
||||
// consume event
|
||||
self.consumer.consume(event, handle).await;
|
||||
log::trace!("{event:?} => consumer");
|
||||
},
|
||||
State::AwaitingLeave => {
|
||||
// we just entered the deadzone of a client, so
|
||||
// we need to ignore events that may still
|
||||
// be on the way until a leave event occurs
|
||||
// telling us the client registered the enter
|
||||
if let Event::Leave() = event {
|
||||
self.state = State::Sending;
|
||||
}
|
||||
|
||||
// entering a client that is waiting for a leave
|
||||
// event should still be possible
|
||||
if let Event::Enter() = event {
|
||||
self.state = State::Receiving;
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
// let the server know we are still alive once every second
|
||||
if state.last_replied.is_none()
|
||||
|| state.last_replied.is_some()
|
||||
&& state.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_producer_event(&mut self, c: ClientHandle, e: Event) {
|
||||
log::trace!("producer: ({c}) {e:?}");
|
||||
|
||||
// we are sending events
|
||||
self.state = State::Sending;
|
||||
|
||||
// get client state for handle
|
||||
let state = match self.client_manager.get_mut(c) {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
// should not happen
|
||||
log::warn!("unknown client!");
|
||||
self.producer.release();
|
||||
self.state = State::Receiving;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// if we just entered the client we want to send additional enter events until
|
||||
// we get a leave event
|
||||
if let State::Receiving | State::AwaitingLeave = self.state {
|
||||
self.state = State::AwaitingLeave;
|
||||
if let Some(addr) = state.client.active_addr {
|
||||
if let Err(e) = send_event(&self.socket, Event::Enter(), addr).await {
|
||||
log::error!("udp send: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise we should have an address to
|
||||
// transmit events to the corrensponding client
|
||||
if let Some(addr) = state.client.active_addr {
|
||||
@@ -414,16 +443,6 @@ impl Server {
|
||||
log::error!("udp send: {}", e);
|
||||
}
|
||||
}
|
||||
// we can not assume the client is in receiving mode because a
|
||||
// client can always change mode from receiving to sending
|
||||
// by restarting.
|
||||
// To prevent both clients from being in sending mode,
|
||||
// we send a release event.
|
||||
if let Err(e) = send_event(&self.socket, Event::Release(), *addr).await {
|
||||
if e.kind() != ErrorKind::WouldBlock {
|
||||
log::error!("udp send: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user