mirror of
https://github.com/herdrdev/herdr.git
synced 2026-09-22 16:01:07 +00:00
* feat: add opt-in client-rendered shell * feat: add client-owned shell mouse controls * feat: complete client-owned shell chrome * feat: add client-owned shell settings and notifications * feat: run client shell custom commands * feat: render popup terminals in client shell * feat: add client-owned scrollback and copy mode * fix: preserve client worktree trust defaults * feat: render startup diagnostics in client shell * feat: render onboarding in client shell * feat: render product announcements in client shell * feat: render release notes in client shell * feat: render mobile switcher in client shell * feat: render kitty graphics in client shell * fix: preserve client shell lifecycle coherence * refactor: route client shell commands through endpoint connections * refactor: make the client-rendered shell the default * refactor: remove monolithic launch mode * refactor: complete client-rendered shell cutover * perf: retain client pane surface updates * perf: scale retained pane surface updates * perf: remove remote pane input latency * test: make client shell checks platform-independent * fix: prevent client timer starvation * test: fix client shell platform gates * test: accept retained surfaces in cross-area checks * test: make client mode assertions platform-neutral * fix: badge only outdated integrations
79 lines
2.1 KiB
Rust
79 lines
2.1 KiB
Rust
use std::time::{Duration, Instant};
|
|
|
|
pub(super) struct ClientLoopTimer {
|
|
deadline: Option<Instant>,
|
|
}
|
|
|
|
impl ClientLoopTimer {
|
|
pub(super) fn new() -> Self {
|
|
Self { deadline: None }
|
|
}
|
|
|
|
pub(super) fn deadline(&mut self, now: Instant, delay: Duration) -> Instant {
|
|
let requested = now.checked_add(delay).unwrap_or(now);
|
|
let deadline = self
|
|
.deadline
|
|
.map_or(requested, |current| current.min(requested));
|
|
self.deadline = Some(deadline);
|
|
deadline
|
|
}
|
|
|
|
pub(super) fn fired(&mut self) {
|
|
self.deadline = None;
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn incoming_events_do_not_postpone_timer_deadline() {
|
|
let start = Instant::now();
|
|
let delay = Duration::from_millis(100);
|
|
let mut timer = ClientLoopTimer::new();
|
|
|
|
let first_deadline = timer.deadline(start, delay);
|
|
assert_eq!(
|
|
timer.deadline(start + Duration::from_millis(25), delay),
|
|
first_deadline
|
|
);
|
|
assert_eq!(
|
|
timer.deadline(start + Duration::from_millis(50), delay),
|
|
first_deadline
|
|
);
|
|
assert_eq!(
|
|
timer.deadline(start + Duration::from_millis(75), delay),
|
|
first_deadline
|
|
);
|
|
|
|
timer.fired();
|
|
assert_eq!(
|
|
timer.deadline(first_deadline, delay),
|
|
first_deadline + delay
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn earlier_client_work_can_pull_the_timer_deadline_forward() {
|
|
let start = Instant::now();
|
|
let mut timer = ClientLoopTimer::new();
|
|
|
|
assert_eq!(
|
|
timer.deadline(start, Duration::from_millis(100)),
|
|
start + Duration::from_millis(100)
|
|
);
|
|
assert_eq!(
|
|
timer.deadline(start + Duration::from_millis(20), Duration::from_millis(10)),
|
|
start + Duration::from_millis(30)
|
|
);
|
|
assert_eq!(
|
|
timer.deadline(
|
|
start + Duration::from_millis(25),
|
|
Duration::from_millis(100)
|
|
),
|
|
start + Duration::from_millis(30)
|
|
);
|
|
}
|
|
}
|