Fix/idle compositor redraw loop + get back input devices after hotplug#30
Merged
Conversation
An idle desktop never stopped compositing (~43 fps continuously): build_scene damaged the cursor rect every frame regardless of movement, so the empty-damage skip in tick_one_output was unreachable whenever a cursor was on-screen. Continuous full-screen compose is the GPU-hammer behind the overnight VK_ERROR_DEVICE_LOST. Part A — cursor damage is change-gated via a per-output last_present_cursor footprint (all modes, advanced transactionally on retire). Damage = old ∪ new emitted only when the footprint/mode/sprite changed; a stationary same-mode cursor contributes nothing. The footprint also supplies the non-empty poke that carries Hw→Hidden's HideOnRetire (previously stranded, since the HW current-rect lived only in the visible branch). Part B — tick() clears scene_structure_dirty only when every output was Composed or SkippedEmpty (keeps it set on any defer skip or error). Fixes the dirty-but-empty hot-spin where a bare wake_for_damage / register_cursor left the flag set and next_wakeup spun. Part C — next_wakeup suppresses the scene + cursor-anim deadlines when DRM master is dropped or outputs are DPMS-off, but keeps the present-poll deadline (PresentBatchWait::Poll batches have no fd wake). Spec + 4-round codex review: docs/superpowers/specs/2026-06-14-idle-compositor-cursor-damage.md Unit coverage: cursor-damage gating (stationary / moved / sprite-swap / pure-HW hide), dirty-clear classification, scanout-disallowed wakeup suppression, present-poll survival under DPMS-off. HW (silence, e16, 2560x1440@60): survived a real monitor off->on cycle at <=22 fps with no storm / DEVICE_LOST / EINVAL (was a 14000-draw/s busy-loop before). Residual idle-rate confirmation pending a clean run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…retry) When the monitor's USB hub power-cycles (screen off→on), the kernel removes and re-enumerates the keyboard + mouse. yserver picked up the keyboard but the mouse stayed dead for many seconds — until an unrelated keypress — both under libseat (Cinnamon) and direct (lightdm) mode. Root cause (HW-traced on bee, confirmed by udevadm-vs-yserver timestamps): the re-enumerated device's libseat/logind open is DEFERRED by a lagging udev uaccess ACL, so libinput surfaces no DEVICE_ADDED on that wake. There is no later fd readiness edge once the ACL settles, so the idle core loop (and the direct-mode input thread, blocked on EpollTimeout::NONE) never re-dispatches libinput to retry the open — it waits for unrelated input. The recently-landed idle-sleep made this visible; it is a latent libinput-hotplug bug, not a regression of the compose path. Fix — a bounded retry window armed on any libinput device add/remove, so a deferred open is retried without needing unrelated input, then it converges and the loop returns to true idle: - Libseat (core loop): new `KmsBackendV2.libinput_hotplug_retry_until`; `on_libinput_ready` arms it (2.5s) on DeviceAdded/Removed; new `Backend::poll_deferred_input` (default no-op; called once per core-loop iteration in run.rs) re-dispatches while armed and clears on expiry; `next_wakeup` adds a ~250ms retry cadence while armed (ungated by scanout, so input recovers regardless of screen state). Only device churn extends the window → idle-sleep preserved. - Direct (input_thread.rs): the loop blocked on EpollTimeout::NONE forever; now epoll_wait takes a ~250ms timeout while a retry window is armed (armed on a batch containing DeviceAdded/Removed), so the existing per-iteration dispatch retries the deferred open. (Level-triggered epoll alone was insufficient — the udev event is consumed without surfacing the add.) Unit tests cover the scheduling seam (retry cadence in next_wakeup, window cleared on expiry). HW-verified on bee: monitor power-cycle under both Cinnamon (libseat) and lightdm (direct) — mouse returns on its own within the retry window, no keypress, and compose returns to 0 fps afterward (idle preserved). Also adds repro/diagnostic Justfile recipes: yserver-cinnamon-hotplug-probe, yserver-input-hotplug-probe, yserver-fvwm3-idle, yserver-e16-idle-trace, xorg-e16-idle-trace. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nic!
The idle-compose fix added a `#[ignore]`d placeholder whose body was
`panic!("enable this when Repaint::Clipped returns again")`. Plain `cargo test`
skips it, but CI runs `--include-ignored`, which force-runs it → guaranteed
failure.
Replace it with a real, non-ignored test that asserts `pick_repaint_region`
returns `Repaint::Full` unconditionally today (it does) and carries the
cursor-specific message. It passes now and fails — pointing at the requirement —
the day `Repaint::Clipped` is re-enabled, which is exactly the tripwire the
idle-compose spec wanted, without breaking `--include-ignored`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix: stop idle-compositor redraw loop + idle wakeups on master-drop
fix: re-acquire input devices after a deferred libseat open (hotplug retry)