Skip to main content

Module run_loop

Module run_loop 

Source

StructsΒ§

GuardOutcome πŸ”’
Result of evaluating the per-tick suppression guards: either no guard fires, or exactly one wins and dictates the tray icon (reason) plus whether the event-log records a GuardSuppress entry (log_as).
IdleProbeBackoff πŸ”’
Per-tick gate that throttles UserIdle::get_time() once it starts failing. While the probe succeeds we attempt it every tick; once it fails we skip an exponentially-growing number of ticks before retrying, so a windowing system that rejects the call (X11 without MIT-SCREEN-SAVER, a denied Wayland portal) doesn’t get hammered β€” and re-spammed β€” once per second.
TickClock πŸ”’
The wall-clock decomposition the guards reason about: minute-of-day and weekday (days-since-Monday, 0=Mon … 6=Sun), both sampled from the single Local::now() taken at the top of the tick. Grouped so the two related time values travel together and evaluate_guards stays under the positional-argument limit.

EnumsΒ§

LockTransition πŸ”’
What (if anything) to log about a tick’s lock-state transition. None is the common case: no confidently-known transition this tick. The wrapper turns the other variants into log::info! calls.

ConstantsΒ§

IDLE_PROBE_BACKOFF_MAX_SECS πŸ”’
Ceiling for the idle-probe back-off. Once the probe has failed enough times in a row, we settle at one attempt every five minutes β€” frequent enough to recover if the windowing-system extension reappears, rare enough that a permanently-missing one (e.g. X11 with no MIT-SCREEN-SAVER) no longer floods stderr with libX11 warnings.
LOCK_PREV_LOCKED πŸ”’
LOCK_PREV_UNKNOWN πŸ”’
LOCK_PREV_UNLOCKED πŸ”’
SUSPEND_GAP_THRESHOLD πŸ”’
Inter-tick wall-clock gap above which we treat the tick as the first one after a wake from suspend. Well clear of the 1s cadence and any scheduler jitter, but far below the smallest useful bedtime interval.
USER_IDLE_WARN_INTERVAL_SECS πŸ”’
Rate-limit window for repeated UserIdle::get_time failure warnings. One log line per 60 s is enough to surface a persistent platform-API breakage without spamming the log file once per tick.

StaticsΒ§

LOCK_STATE_PREV πŸ”’
Encoded previous lock state for the transition logger: 0 = unknown / haven’t seen yet (the initial value) 1 = last seen as Some(false) (confidently unlocked) 2 = last seen as Some(true) (confidently locked) Option<bool> directly is what we want logically, but we need an atomic so the run-loop closure can mutate the previous-state across ticks without locking. AtomicU8 is the smallest fit.
USER_IDLE_LAST_WARN_EPOCH πŸ”’
Epoch seconds (SystemTime::UNIX_EPOCH) at which the last UserIdle failure was logged; 0 means β€œnever warned yet” (also the at-rest value before the scheduler boots).

FunctionsΒ§

decide_lock_transition πŸ”’
Pure decision: given the previously-known lock state and the freshly-probed state, decide whether the transition is worth surfacing in the log.
decode_lock_state πŸ”’
deliver_scheduled_break πŸ”’
Build and surface a scheduled micro/long break: resolve the per-kind content from s, deliver it through the configured channel, fire the BreakStart hook, and log the event. Returns the resolved delivery so the caller can decide whether to mark an active_break.
deliver_sleep_break πŸ”’
Surface a Sleep (bedtime) break: fire the overlay, run the start hook, and log the event. Timer bookkeeping stays with the caller in run_loop. Sleep breaks are always overlay; they never go through the delivery-routing logic used by deliver_scheduled_break.
encode_lock_state πŸ”’
evaluate_guards πŸ”’
Pure decision: given the per-tick guard inputs, return which SuppressReason should fire (if any) and whether the run-loop should also write a GuardSuppress event for it.
fire_scheduled_break πŸ”’
Fire a scheduled micro/long break end to end: deliver it (see deliver_scheduled_break) and apply the post-fire timer bookkeeping under the timers lock (see record_scheduled_fire). fixed_key is Some((today, minute)) for a fixed-time fire (recording the dedupe key) or None for an interval fire; the fire Instant is stamped here.
fixed_break_due πŸ”’
Pure decision: is a fixed-time break for kind due at now_min?
idle_for_typing_defer πŸ”’
Idle seconds the typing-defer check should see. Some only when we can affirmatively judge activity β€” a real HID reading this tick, or a locked session (the user is definitely away). None when idle detection is unavailable and the lock state is unknown/unlocked, so the caller skips deferral rather than stalling every break for the full cap (#67). promoted_idle_secs already folds in the lock promotion, so the Some branch carries the value the rest of the scheduler sees. Pure so the unavailable-on-Wayland case is testable.
idle_probe_backoff_secs πŸ”’
Seconds to wait before the next idle probe after consecutive_failures failures in a row. Doubles from 2 s and saturates at IDLE_PROBE_BACKOFF_MAX_SECS; the first failure alone already stops the per-tick hammering. consecutive_failures is always >= 1 at the call site (it’s incremented before this runs), but 0 is handled defensively and yields the same first-step delay.
idle_secs_with_lock πŸ”’
Pure decision: given the raw HID-idle seconds and an Option<bool> lock signal, return the idle seconds the rest of the scheduler should see. When the OS confidently reports the session as locked, promote the value past both the micro- and long-break reset thresholds so every downstream check (screen-time, suppression, typing-defer) treats the user as idle. None (couldn’t determine lock state) leaves raw_idle_secs untouched β€” trust HID alone.
import_pending πŸ”’
Cheap atomic-load check that the run loop reads at the top of every tick. Pulled out of run_loop so the early-out condition is unit-testable without driving the full 1Hz loop body, which is bound to the production AppHandle<Wry> runtime and a real Scheduler with its camera/video/logger side threads.
log_suppressions πŸ”’
notify_break_coming πŸ”’
notify_screen_time_budget πŸ”’
now_epoch_secs_for_warn πŸ”’
Convert SystemTime::now() to seconds since the Unix epoch. Returns 0 if the system clock is somehow before 1970 β€” same fallback as the β€œnever warned” sentinel, which simply means the next warn fires.
prebreak_message πŸ”’
promote_idle_for_lock πŸ”’
Wrapper around idle_secs_with_lock that also emits a single info line on each locked⇄unlocked transition, so the log shows why idle-based suppression suddenly engaged or disengaged. The previous-state tracker is tri-valued (unknown / unlocked / locked) so a flaky probe that returns Some(true) β†’ None β†’ Some(true) doesn’t generate spurious β€œunlocked / locked” log pairs.
promote_idle_for_lock_with_cell πŸ”’
Cell-parameterised variant of promote_idle_for_lock so the orchestration (load β†’ decide β†’ log β†’ store β†’ promote) is testable with a local atomic β€” mirrors user_idle_warn_throttle above.
resolve_idle_secs πŸ”’
Resolve this tick’s raw idle seconds, driving the probe back-off. probe performs the platform UserIdle call, yielding the idle seconds on success or a display-error string on failure. The back-off state and the throttled warning are handled here; the only thing the caller keeps platform-bound is probe itself, so this decision logic is unit-testable without a windowing system.
resumed_after_gap πŸ”’
Whether the gap between the previous tick’s wall clock and now indicates a wake from suspend (or a forward clock leap). Pulled out of the loop body so the threshold logic is unit-testable without driving the 1Hz loop. A backwards clock step yields Err from duration_since and is treated as β€œnot resumed”.
run_loop πŸ”’
scheduled_break_event πŸ”’
Resolve the per-kind BreakEvent content for a scheduled micro/long break. Pure (no I/O) so the field resolution β€” which fields each kind draws, the strict-mode postpone lock, the break-health intensity gate β€” is unit-testable without a windowing runtime. intensity is the live value from the stats lock; the helper applies the break_health_enabled gate. Sleep never reaches here (bedtime path).
screen_time_body πŸ”’
sleep_break_event πŸ”’
Build the BreakEvent for the bedtime (Sleep) path. Sleep breaks never carry a guided routine, so routine_* fields are always empty/None.
suppressed_kinds πŸ”’
Pure decision: which break kinds were due (enabled and past their interval) at this tick and so are being suppressed by a guard. Split out from log_suppressions so the per-kind logic is unit-testable without a Logger.
user_idle_warn_throttle πŸ”’
Decide whether enough time has elapsed since the last UserIdle warn to fire another one, and update the timestamp atomically if so.
warn_user_idle_failure πŸ”’
Surface a UserIdle::get_time failure to the log, at most once per USER_IDLE_WARN_INTERVAL_SECS. Without this gate the production code silently fell back to β€œ0 = active” forever, so a broken platform call (X11 down, macOS API change, Wayland portal denied) would invisibly break idle suppression and screen-time tracking. backoff_secs is how long the probe is now suppressed for, so the log explains why the per-second errors stop.