-
Notifications
You must be signed in to change notification settings - Fork 245
Description
Problem
ConnectorScanner::scan() in smithay-drm-extras only tracks connector::State transitions (Connected ↔ Disconnected ↔ Unknown). When a connector remains Connected across two consecutive scans, the (Connected, Connected) => {} arm is a no-op — no event is emitted regardless of whether the connector's properties (specifically its mode list) have changed.
File: smithay-drm-extras/src/drm_scanner/connector_scanner.rs, line 55
(State::Connected, State::Connected) => {}Impact
This causes a permanent dead state for connectors on USB-C docks with DP MST/alt-mode (and potentially other hotplug scenarios):
- Kernel DRM reports a connector as
Connectedbefore EDID data is available (firmware negotiation in progress) get_connector(handle, true)returns the connector withstate() == Connectedbutmodes()returns an empty list- The compositor calls
pick_mode()→ getsNone→ skips activation - A subsequent udev
Changedevent triggers another scan get_connector()now returns populated modes, butConnectorScannersees(Connected, Connected)and emits nothing- The connector is stuck in "connected but never activated" state permanently
Affected Compositors
This affects any compositor using ConnectorScanner or DrmScanner from smithay-drm-extras:
- niri — YaLTeR/niri#869 (crash/freeze on USB-C dock), YaLTeR/niri#2691 (inconsistent display detection)
- Potentially any smithay-based compositor with USB-C dock or DP MST users
Cross-Compositor Analysis
For context, I analyzed how other compositors handle this:
| Compositor | Handling |
|---|---|
| Mutter (GNOME) | 3-second CONNECTION_CHANGE_TIMEOUT debounce timer per connector; detects mode list changes as META_KMS_RESOURCE_CHANGE_FULL triggering full reload |
| wlroots | No retry — accepts connected+empty-modes as-is, relies on subsequent udev events |
| KWin | isConnected() requires !m_driverModes.empty() — connected+no-modes treated as disconnected; no retry for this case |
Mutter is the only compositor that handles mode-list changes on already-connected connectors at the library level.
Proposed Fix
Compare the old and new connector::Info mode lists in the (Connected, Connected) arm and re-emit a Connected event when they differ:
(State::Connected, State::Connected) => {
if old.modes() != conn.modes() {
added.push(conn);
}
}This is safe because:
connector::InfoderivesPartialEq, andmodes()returns&[control::Mode]which also implementsPartialEq- Compositors already handle duplicate
Connectedevents gracefully (they check if a surface already exists for the CRTC) - The comparison is cheap for the common case (both mode lists are the same)
Reproduction
- Hardware: ThinkPad P16 Gen 3, NVIDIA RTX PRO 4000 (nvidia-drm 590.48.01), Intel iGPU (i915), Lenovo ThinkPad USB-C Dock Gen 2
- Monitors: Two LEN S27q-10 (2560x1440@60Hz) connected via HDMI + DP MST through dock
- Symptoms: Intermittent — sometimes both monitors activate on boot, sometimes one stays dark permanently until dock is re-plugged
Related
- Smithay Smithay doesn't play nice when EDID drops due to video switchbox changing inputs #1859 — "Smithay doesn't play nice when EDID drops due to video switchbox changing inputs" (same root cause, different trigger)