fix(backend/tty): add delayed rescan for connectors missing EDID on hotplug#3409
fix(backend/tty): add delayed rescan for connectors missing EDID on hotplug#3409coleleavitt wants to merge 1 commit intoniri-wm:mainfrom
Conversation
Upstream Root Cause & FixAfter deeper analysis, the underlying issue is in smithay's I've filed an issue and PR upstream:
Why this PR is still valuable even with the smithay fixThe smithay fix ensures that when a rescan happens and modes have appeared, the compositor gets notified. However, this niri-level rescan timer is still needed because:
|
…otplug USB-C docks with DP MST/alt-mode may report connectors as Connected before EDID data is available, causing pick_mode() to return None and connector_connected() to skip activation. Smithay's ConnectorScanner does not re-emit events for already-connected connectors, leaving the output in a permanent dead state. Add a bounded retry mechanism: after device_changed() processes connectors, schedule_rescan_if_needed() checks for connected connectors that have no matching surface (not yet activated). If found, it schedules a calloop timer (2 s delay) that re-runs device_changed(), giving the kernel time to complete EDID reads. The retry is capped at 3 attempts and self-clears when all connectors are activated or on device removal.
92ea363 to
67b12ec
Compare
|
I suppose this needs updating to use the new Changed event instead? |
yes I'll update it today if I get time; thanks |
Problem
USB-C docks with DP MST / alt-mode (e.g. Lenovo ThinkPad USB-C Dock Gen 2) can report connectors as
Connectedto the kernel DRM subsystem before EDID data has been read. When this happens:connector.modes()returns an empty listpick_mode()returnsNoneconnector_connected()logs"no mode"and skips activationSmithay's
ConnectorScannertreats(Connected, Connected)as a no-op — it does not re-emit events for already-connected connectors. This means the output gets stuck in a permanent "connected but never activated" dead state.Whether activation succeeds depends entirely on timing: if a second
UdevEvent::Changedfires after EDID completes, the connector recovers. This makes the bug intermittent — monitors sometimes come up and sometimes don't on the same hardware.Root Cause
The EDID race is in the kernel/dock firmware timing, but niri has no retry path for connectors that were connected but could not be activated due to missing modes.
Fix
Add a bounded retry mechanism in
Tty:device_changed()processes connectors,schedule_rescan_if_needed()checks for connected connectors that have no matching surface (not yet activated) and are not non-desktop connectorscalloop::Timer(2 s delay) that re-invokesdevice_changed(), giving the kernel time to complete EDID readsMAX_RESCAN_RETRIES(3) to prevent infinite reschedulingNew fields on
OutputDevicerescan_timer_token: Option<RegistrationToken>— handle to cancel pending timersrescan_retry_count: u8— bounded counter, reset on successful activationTesting
cargo check,cargo clippy,cargo build --release)tty.rs(e.g. VRR timers, redraw timers)Alternatives Considered
(Connected, Connected) => {}behavior to re-emit events, which could have broader implications for all smithay consumers