cursor: measure the idle-hide deadline from the last pointer event
refresh_idle_timer armed for the full timeout measured from now, which is right only for notify_cursor_activity — it stamps activity immediately before arming. The other callers deliberately do not stamp, so with idle_timeout: Some(10) a user typing continuously never saw the cursor hide: every key press and release pushed the deadline out by a fresh ten seconds. That inverts the feature and regresses the shipped cursor_hide_timeout behaviour. resolve's RearmAfter cannot recover it — it only corrects a timer that fires too early. So the delay is now shortest_timeout minus the time already elapsed, extracted as CursorHideConfig::arm_delay and table tested; the arithmetic handed to Timer::from_duration was the one part of this path with no coverage. A zero delay is fine: the timer fires on the next loop iteration and resolve decides. Also rename fullscreen_focused to fullscreen_on_active_output, which is what seat.active_output() actually reports, and log an arming failure rather than silently disabling cursor hiding. Drafted with Claude (Anthropic); reviewed and tested by the committer.
This commit is contained in:
parent
7596cf7f2f
commit
115cdac039
2 changed files with 48 additions and 20 deletions
|
|
@ -251,11 +251,15 @@ impl CursorHideConfig {
|
|||
}
|
||||
}
|
||||
|
||||
/// The delay to arm the timer with, before context is known. Callers on the
|
||||
/// input path cannot read fullscreen state without deadlocking, so they arm
|
||||
/// pessimistically and `resolve` corrects it on fire.
|
||||
pub fn shortest_timeout(&self) -> Option<Duration> {
|
||||
self.effective_timeout(true)
|
||||
/// How long to wait before the idle timer should next fire, given how long
|
||||
/// it has been since the last pointer event. Pessimistic: fullscreen only
|
||||
/// ever shortens a timeout, so arming as if fullscreen cannot fire too
|
||||
/// late, and `resolve` corrects an early fire once the context is known.
|
||||
pub fn arm_delay(&self, since_pointer_activity: Duration) -> Option<Duration> {
|
||||
Some(
|
||||
self.effective_timeout(true)?
|
||||
.saturating_sub(since_pointer_activity),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -402,12 +406,6 @@ mod test {
|
|||
..OFF
|
||||
};
|
||||
assert_eq!(long_fs.resolve(secs(3), true), HideDecision::Hide);
|
||||
|
||||
// The arming delay is pessimistic: the shortest timeout that could
|
||||
// apply in any context, because arm-time code cannot read the shell.
|
||||
assert_eq!(short_fs.shortest_timeout(), Some(secs(3)));
|
||||
assert_eq!(long_fs.shortest_timeout(), Some(secs(3)));
|
||||
assert_eq!(OFF.shortest_timeout(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -423,4 +421,27 @@ mod test {
|
|||
assert_eq!(cfg.resolve(secs(5), false), HideDecision::Hide);
|
||||
assert_eq!(cfg.resolve(secs(6), false), HideDecision::Hide);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arm_delay_measures_from_the_last_pointer_event() {
|
||||
let cfg = CursorHideConfig {
|
||||
idle_timeout: Some(10),
|
||||
..OFF
|
||||
};
|
||||
assert_eq!(cfg.arm_delay(Duration::ZERO), Some(secs(10)));
|
||||
assert_eq!(cfg.arm_delay(secs(4)), Some(secs(6)));
|
||||
// Exactly due, and past due: fire immediately rather than deferring.
|
||||
assert_eq!(cfg.arm_delay(secs(10)), Some(Duration::ZERO));
|
||||
assert_eq!(cfg.arm_delay(secs(600)), Some(Duration::ZERO));
|
||||
|
||||
// Armed against the shortest timeout that any context could want.
|
||||
let fs = CursorHideConfig {
|
||||
idle_timeout: Some(10),
|
||||
fullscreen_idle_timeout: Some(3),
|
||||
..OFF
|
||||
};
|
||||
assert_eq!(fs.arm_delay(secs(1)), Some(secs(2)));
|
||||
|
||||
assert_eq!(OFF.arm_delay(Duration::ZERO), None);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue