Skip to content

feat(idle): replace swayidle with a native ext-idle-notify-v1 idle manager (honors logind inhibitors, retires SIGSTOP hack) #204

Description

@vibechoom

Move the idle/lock pipeline off swayidle → native ext-idle-notify-v1 manager

Split off from #200 (keep-awake/caffeine). While speccing the logind-native caffeine toggle there, @annikahannig noted: "maybe we need to move away from swayidle :)" — this issue tracks that.

Why

Today the idle→dim→lock→suspend timeline lives entirely in swayidle (etc/swayidle/config):

timeout 240 'brightnessctl -s set 10%' resume 'brightnessctl -r'   # dim
timeout 300 'loginctl lock-session'                                # lock
timeout 600 'systemctl suspend'                                    # suspend
before-sleep 'loginctl lock-session'                               # relock on sleep

swayidle consumes ext-idle-notify-v1 from niri and is run as a user unit (etc/systemd/user/swayidle.service). Two problems this creates:

  1. swayidle doesn't honor logind inhibitors. That's exactly why add keep awake (caffeine) in panel with the brightness slider #200's caffeine has to bridge by SIGSTOP-ing swayidle (crates/hytte-services/src/screensaver.rs:304-320) instead of just holding a logind inhibitor. The pause is a process-level halt hack the code itself calls out as fragile (screensaver.rs:20-29).
  2. Idle state is split-brained between swayidle (timers) and logind (lock state, inhibitors) — the root tension surfaced all through add keep awake (caffeine) in panel with the brightness slider #200.

Proposed direction

Ship a native in-process idle manager in hytte-services — already a deliberately-deferred idea (etc/swayidle/README.md:6, screensaver.rs:395 "shipping our own ext-idle-notify-v1 client. Deprioritised"). trollshell becomes the idle daemon:

  • A reactive service consuming ext-idle-notify-v1 from niri (the same protocol swayidle reads).
  • Runs the dim/lock/suspend timers in-process, in the standard hytte service pattern.
  • Natively checks logind inhibitor state before firing — so a logind idle inhibitor (add keep awake (caffeine) in panel with the brightness slider #200's mechanism, plus Firefox/mpv) just works, and the SIGSTOP hack in screensaver.rs retires entirely.
  • before-sleep relock moves to a logind PrepareForSleep signal handler (we already have the logind.rs Manager wrapper and screensaver::lock()loginctl lock-session).

What it touches

  • New hytte-services idle module + a wayland-client/protocol binding for ext_idle_notifier_v1.
  • Retire etc/swayidle/ (config, unit, README) and the swayidle PID/SIGSTOP code in screensaver.rs.
  • etc/ session wiring (niri-session.target, kanshi README references swayidle — etc/kanshi/README.md:90).
  • The Nix module / etc/README.md (swayidle is currently a documented session dependency).

Open questions / risks

  • unsafe_code = "forbid" workspace-wide (except hytte-ecal). A wayland-client consumer is safe Rust, but if we need raw protocol glue, decide where the boundary sits (new FFI crate vs. a safe binding crate). Worth confirming a pure-safe path exists before committing.
  • Parity: swayidle's -w (wait for command completion) semantics, the dim-then-restore on resume, and not double-locking need faithful reproduction.
  • Scope vs. add keep awake (caffeine) in panel with the brightness slider #200: caffeine (add keep awake (caffeine) in panel with the brightness slider #200) ships fine on the logind-inhibitor plan without this migration (it keeps swayidle as the detector and SIGSTOP-bridges). This issue is the cleaner end-state that makes the bridge unnecessary — independent and larger.

@annikahannig — is the goal full swayidle retirement (trollshell owns idle end-to-end), or just "honor logind inhibitors" while keeping swayidle for now? The former is this issue; the latter is already covered by #200's plan. Happy to break this into impl chunks once you confirm the appetite.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Fields

    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions