use a7n_core::focus::edge::quarter_crossed;
use a7n_ipc::config::FeedbackConfig;
use a7n_platform::feedback::{Command, Feedback};
use a7n_platform::input::Bounds;
use a7n_proto::v1::Edge;
const FEEDBACK_STEP_PERMILLE: u16 = 50;
#[derive(Debug, Default)]
pub(in crate::daemon) struct FeedbackSink {
backend: Option<Feedback>,
config: FeedbackConfig,
last: Option<(Bounds, Edge, u16)>,
}
impl FeedbackSink {
pub(super) fn attach(&mut self, backend: Option<Feedback>) {
self.backend = backend;
}
pub(in crate::daemon) fn configure(&mut self, config: FeedbackConfig) {
let changed = self.config.look != config.look;
if !config.edge {
self.clear();
}
self.config = config;
if config.edge
&& changed
&& let Some((display, edge, progress)) = self.last
{
self.send(Command::Edge {
display,
edge,
progress,
look: config.look,
});
}
}
pub(super) fn push(&mut self, display: Bounds, progress: Option<(Edge, u16)>) {
let Some((edge, value)) = progress else {
self.clear();
return;
};
let previous = self
.last
.filter(|(last_display, last_edge, _)| *last_display == display && *last_edge == edge);
let from = previous.map_or(0, |(_, _, last_value)| last_value);
if previous.is_some() && value.abs_diff(from) < FEEDBACK_STEP_PERMILLE {
return;
}
self.last = Some((display, edge, value));
if self.config.edge {
self.send(Command::Edge {
display,
edge,
progress: value,
look: self.config.look,
});
}
if self.config.haptics && quarter_crossed(from, value) {
self.send(Command::Tick);
}
}
pub(super) fn fade(&mut self, progress: Option<(Edge, u16)>) {
if let Some((display, _, _)) = self.last {
self.push(display, progress);
} else {
self.clear();
}
}
pub(super) fn clear(&mut self) {
if self.last.take().is_some() && self.config.edge {
self.send(Command::Clear);
}
}
pub(super) fn landing(&self, x: i32, y: i32) {
if self.config.landing {
self.send(Command::Landing { x, y });
}
}
fn send(&self, command: Command) {
if let Some(backend) = &self.backend {
backend.send(command);
}
}
}