use std::collections::BTreeMap;
use std::time::Instant;
use a7n_proto::v1::{Focus as WireFocus, InputEvent, Landing, envelope::Body};
use a7n_proto::{MachineId, Route};
pub mod edge;
pub mod held;
mod owner;
mod responder;
use held::HeldInputs;
#[derive(Debug, Clone, PartialEq)]
pub struct Take {
pub target: MachineId,
pub landing: Option<Landing>,
pub resistance_px: u32,
pub reach: Vec<MachineId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Cursor {
Home,
Driving(MachineId),
}
#[derive(Debug, Clone, PartialEq)]
pub enum Action {
Send {
peer: MachineId,
body: Body,
},
Release(Vec<InputEvent>),
Cursor(Cursor),
Driven {
owner: MachineId,
landing: Option<Landing>,
resistance_px: u32,
},
}
#[derive(Debug, Clone)]
struct Session {
take: Take,
route: Route,
}
#[derive(Debug)]
enum Owner {
Home,
Driving(Session),
Taking {
session: Session,
previous: Option<Session>,
deadline: Instant,
},
}
#[derive(Debug)]
struct Driven {
owner: MachineId,
route: Route,
last_input: Instant,
reach: Vec<MachineId>,
}
#[derive(Debug)]
pub struct Focus {
own_id: MachineId,
last_route: Route,
owner: Owner,
driven: Option<Driven>,
accepted: BTreeMap<MachineId, Route>,
last_local_input: Option<Instant>,
held: HeldInputs,
locked: bool,
}
impl Focus {
#[must_use]
pub fn new(own_id: MachineId) -> Self {
Self {
own_id,
last_route: Route(0),
owner: Owner::Home,
driven: None,
accepted: BTreeMap::new(),
last_local_input: None,
held: HeldInputs::default(),
locked: false,
}
}
pub fn set_locked(&mut self, locked: bool) {
self.locked = locked;
}
pub fn toggle_lock(&mut self) {
self.locked = !self.locked;
}
#[must_use]
pub fn is_locked(&self) -> bool {
self.locked
}
#[must_use]
pub fn cursor(&self) -> Cursor {
match &self.owner {
Owner::Home => Cursor::Home,
Owner::Driving(session) => Cursor::Driving(session.take.target),
Owner::Taking { previous, .. } => previous
.as_ref()
.map_or(Cursor::Home, |s| Cursor::Driving(s.take.target)),
}
}
#[must_use]
pub fn driving_route(&self) -> Option<Route> {
match &self.owner {
Owner::Home => None,
Owner::Driving(session) => Some(session.route),
Owner::Taking { previous, .. } => previous.as_ref().map(|s| s.route),
}
}
#[must_use]
pub fn is_driven(&self) -> bool {
self.driven.is_some()
}
#[must_use]
pub fn next_deadline(&self) -> Option<Instant> {
match self.owner {
Owner::Taking { deadline, .. } => Some(deadline),
_ => None,
}
}
fn next_route(&mut self) -> Option<Route> {
self.last_route = Route(self.last_route.0.checked_add(1)?);
Some(self.last_route)
}
fn send(peer: MachineId, body: Body) -> Action {
Action::Send { peer, body }
}
fn focus_message(session: &Session, target: MachineId, stay: bool) -> WireFocus {
WireFocus {
route: session.route.0,
target: target.as_bytes().to_vec(),
landing: if stay {
None
} else {
session.take.landing.clone()
},
resistance_px: session.take.resistance_px,
reach: session
.take
.reach
.iter()
.map(|id| id.as_bytes().to_vec())
.collect(),
stay,
}
}
}
#[cfg(test)]
mod tests;