git.lucas.co / cce-screenaver
screensaver
git clone https://git.lucas.co/cce-screenaver.git

commit2ac590b80aa6d3e2cf878e55d4831ef360b7e793
parent3b16153595
authorLucas Galante <[email protected]>
date2026-09-11 13:50
Hold the startup grace period on a wall clock, not tick's dt

`grace_timer` counted down by tick's `dt`. Under the Starfield and
Matrix styles that is honest — they request a frame every tick — but
Blank draws nothing, so the runner idles and clamps `dt` to one frame
per wake, stretching the one-second grace period toward a minute. What
saved it in practice is that the mouse wiggle it guards against wakes
the loop itself.

Keep a deadline instead, so the grace period is one second under every
style.

Co-Authored-By: Claude Opus 5 <[email protected]>

 src/main.rs | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index ece0c33..42c1cc5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,6 +3,10 @@ use cce_ui::engine::{Application, EngineState, LogicalPosition, LogicalSize, Win
 use cce_ui::widget::{MouseButton, ElementState, MouseScrollDelta, KeyEvent};
 use std::time::SystemTime;
 
+/// How long after the screensaver appears a mouse move is ignored, so the
+/// wiggle that was already in flight does not dismiss it instantly.
+const GRACE: std::time::Duration = std::time::Duration::from_secs(1);
+
 #[derive(Debug, Clone, Copy, PartialEq)]
 enum ScreensaverStyle {
     Blank,
@@ -53,7 +57,12 @@ struct ScreensaverApp {
     stars: Vec<Star>,
     matrix_columns: Vec<MatrixColumn>,
     init_cursor: Option<LogicalPosition>,
-    grace_timer: f32, // 1 second grace period to prevent instant exit on startup mouse wiggle
+    /// End of the one-second grace period that keeps the startup mouse
+    /// wiggle from dismissing the screensaver instantly. A wall clock, not a
+    /// `dt` countdown: the Blank style draws nothing and so asks for no
+    /// frames, and the runner clamps `dt` to one frame per idle wake — the
+    /// grace period outlasted a minute there.
+    grace_until: std::time::Instant,
 }
 
 #[derive(Debug, Clone)]
@@ -115,7 +124,7 @@ impl Application for ScreensaverApp {
             stars,
             matrix_columns: Vec::new(),
             init_cursor: None,
-            grace_timer: 1.0,
+            grace_until: std::time::Instant::now() + GRACE,
         }
     }
 
@@ -139,10 +148,6 @@ impl Application for ScreensaverApp {
     }
 
     fn tick(&mut self, dt: f32, needs_rebuild: &mut bool) {
-        if self.grace_timer > 0.0 {
-            self.grace_timer -= dt;
-        }
-
         match self.style {
             ScreensaverStyle::Starfield => {
                 for star in &mut self.stars {
@@ -256,7 +261,7 @@ impl Application for ScreensaverApp {
     }
 
     fn handle_pointer_move(&mut self, pos: LogicalPosition, needs_rebuild: &mut bool) {
-        if self.grace_timer > 0.0 {
+        if std::time::Instant::now() < self.grace_until {
             // Keep track of first position during grace period to measure movement distance
             if self.init_cursor.is_none() {
                 self.init_cursor = Some(pos);