Skip to content

feat(daemon): handle dGPU external displays automatically - #139

Merged
luytan merged 16 commits into
OpenGamingCollective:mainfrom
JuanDelPueblo:external-display-daemon
Aug 3, 2026
Merged

feat(daemon): handle dGPU external displays automatically#139
luytan merged 16 commits into
OpenGamingCollective:mainfrom
JuanDelPueblo:external-display-daemon

Conversation

@JuanDelPueblo

Copy link
Copy Markdown
Contributor

Description

Add an opt-in external_display_auto_switch setting that keeps the dGPU available when a physical external display is connected to a dGPU-only port.

When enabled, the daemon monitors DRM topology changes and temporarily overrides Integrated or Smart mode with Hybrid. It preserves the requested mode, exposes and signals the effective mode over D-Bus, and restores the requested mode after the display disconnects. Connector status read failures are treated as unknown topology rather than as a confirmed disconnect.

This prevents Integrated and Smart modes from making a display-driving dGPU unavailable. The setting defaults to false, so existing installations retain their current behavior until it is explicitly enabled.

Related issue: none.

TODO

  • Implement and validate external-display auto-switching

Checklist:

  • My code follows the style guidelines of this project (cargo fmt)
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the mdBook documentation
  • My changes generate no new warnings (clippy/clang)
  • New and existing unit tests pass locally with my changes (either use nix flake check or wait for the ci)

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added optional automatic GPU mode switching when an external display is connected through dGPU-only ports.
    • Added configuration support for enabling or disabling external-display auto-switching, disabled by default.
    • Mode status now reflects the effective mode, while the requested mode remains separately visible.
    • Added automatic recovery, event handling, and delayed mode restoration after display changes.
  • Documentation

    • Updated installation and NixOS configuration examples with the new setting.

Walkthrough

The daemon adds an external-display setting, detects connected DRM displays, separates requested and effective GPU modes, and monitors display changes through shared mode state.

Changes

External display mode coordination

Layer / File(s) Summary
Configuration and external display detection
crates/cardwire-daemon/src/file/config.rs, crates/cardwire-daemon/src/interface/config.rs, crates/cardwire-daemon/src/core/gpu/*, nix/nixos-module.nix, docs/getting-started/installation.md
The setting defaults to false, persists through TOML and atomic configuration memory, and enables detection of connected external DRM displays.
Display mode coordination
crates/cardwire-daemon/src/tasks/monitor_display.rs, crates/cardwire-daemon/src/tasks/mod.rs
The monitor applies temporary Hybrid overrides, debounces DRM events, reconciles topology changes, restores modes after disconnects, and retries after failures.
D-Bus mode integration
crates/cardwire-daemon/src/interface/mode.rs, crates/cardwire-daemon/src/interface/debug.rs
ModeInterface separates requested and effective modes and emits effective-mode changes. DebugInterface reads the effective mode for GPU refresh logic.
Daemon startup and monitor wiring
crates/cardwire-daemon/src/models.rs, crates/cardwire-daemon/src/daemon.rs
DaemonManager shares ModeInterface, applies startup modes through it, and starts the display monitor with a D-Bus interface reference.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DRM
  participant DisplayMonitor
  participant GPUDiscovery
  participant ModeInterface
  participant GPU
  DRM->>DisplayMonitor: report connector topology change
  DisplayMonitor->>GPUDiscovery: inspect external connector status
  GPUDiscovery-->>DisplayMonitor: return connected display state
  DisplayMonitor->>ModeInterface: apply effective mode
  ModeInterface->>GPU: apply GPU mode and eBPF updates
  ModeInterface-->>DisplayMonitor: emit effective mode change
Loading

Possibly related PRs

  • OpenGamingCollective/cardwire#133: Implements related external-display auto-switch behavior across GPU discovery, configuration, mode coordination, daemon wiring, and display monitoring.

Suggested reviewers: luytan

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: automatic handling of external displays connected to dGPU-only ports.
Description check ✅ Passed The description explains the feature, motivation, behavior, defaults, testing, documentation, and checklist status in the required template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@JuanDelPueblo
JuanDelPueblo marked this pull request as ready for review August 2, 2026 17:09

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cardwire-daemon/src/interface/config.rs`:
- Line 19: Add matching #[zbus(property)] getter and setter methods for
ConfigMemory::external_display_auto_switch in the configuration D-Bus interface,
following the existing property methods’ naming, signatures, and Arc<AtomicBool>
load/store pattern so clients can read and update the setting live.

In `@crates/cardwire-daemon/src/interface/mode.rs`:
- Around line 262-266: Add a read-only D-Bus property alongside the mode method
that returns the persisted requested mode, while keeping mode() returning the
effective current_mode_value(). Use the existing requested-mode state/accessor
and expose both values so clients can distinguish configured and active modes.

In `@crates/cardwire-daemon/src/tasks/monitor_display.rs`:
- Around line 99-104: Update the reconcile flow around
external_display_connected so the synchronous sysfs read executes via Tokio’s
blocking-task mechanism rather than directly on the async worker thread, while
preserving its existing error mapping and None => false behavior. Await the
spawned blocking operation before assigning connected, and keep the transition
lock handling unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e7a16204-48b0-4f51-ae74-41602d86d1c2

📥 Commits

Reviewing files that changed from the base of the PR and between 4d03dcd and 1258dee.

📒 Files selected for processing (12)
  • crates/cardwire-daemon/src/core/gpu/discover.rs
  • crates/cardwire-daemon/src/core/gpu/mod.rs
  • crates/cardwire-daemon/src/daemon.rs
  • crates/cardwire-daemon/src/file/config.rs
  • crates/cardwire-daemon/src/interface/config.rs
  • crates/cardwire-daemon/src/interface/debug.rs
  • crates/cardwire-daemon/src/interface/mode.rs
  • crates/cardwire-daemon/src/models.rs
  • crates/cardwire-daemon/src/tasks/mod.rs
  • crates/cardwire-daemon/src/tasks/monitor_display.rs
  • docs/getting-started/installation.md
  • nix/nixos-module.nix

Comment thread crates/cardwire-daemon/src/interface/config.rs
Comment thread crates/cardwire-daemon/src/interface/mode.rs
Comment thread crates/cardwire-daemon/src/tasks/monitor_display.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/cardwire-daemon/src/tasks/monitor_display.rs (1)

134-144: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Keep the failed DRM replay spell pending.

When the write at Line 140 fails, this branch logs the error and still returns success. effective_mode is already Hybrid. A later reconciliation sees target == previous, so this block is skipped and the change event is not retried. If no new hotplug event arrives, the compositor may not discover the external display.

Track a pending replay and retry it during later reconciliation. Clear the pending state only after the write succeeds. Add a failure-and-retry test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-daemon/src/tasks/monitor_display.rs` around lines 134 - 144,
Track failed DRM change-event writes in the display monitor’s reconciliation
state, preserving the pending replay when the write in the Hybrid transition
branch fails. Retry that pending replay on subsequent reconciliation even when
target equals previous, and clear it only after tokio::fs::write succeeds; add a
test covering failure followed by a later successful retry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/cardwire-daemon/src/tasks/monitor_display.rs`:
- Around line 134-144: Track failed DRM change-event writes in the display
monitor’s reconciliation state, preserving the pending replay when the write in
the Hybrid transition branch fails. Retry that pending replay on subsequent
reconciliation even when target equals previous, and clear it only after
tokio::fs::write succeeds; add a test covering failure followed by a later
successful retry.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b8b1a2ca-6238-4947-a42b-f8146d928043

📥 Commits

Reviewing files that changed from the base of the PR and between 1258dee and d591a70.

📒 Files selected for processing (1)
  • crates/cardwire-daemon/src/tasks/monitor_display.rs

@luytan luytan left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will be honest, i think the complexity added in mode.rs is unnecessary and should be avoided. To set a mode, the execution goes throught set_mode() -> self.display_mode.set() -> self.apply_target() -> mode.apply_mode() -> mode.update_mode_bpf_map() -> mode.save_state.

A simpler approach would be to only hold an Arc<RwLock> for the effective mode inside the ModeInterface. ModeInterface can handle applying and persiting modes directly without delegating to an external DisplayMode struct.
The display monitor task can then remain a simple background listener that triggers temporary effective mode updates when displays are connected or disconnedted, eliminating the circular dependency and the extra DisplayMode.

You can add a function named effective_set_mode() inside mode.rs, that would be used by the task to set the mode without saving it to memory

Comment thread crates/cardwire-daemon/src/interface/mode.rs
Comment thread crates/cardwire-daemon/src/interface/mode.rs
Comment thread crates/cardwire-daemon/src/interface/mode.rs
Comment thread crates/cardwire-daemon/src/tasks/monitor_display.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cardwire-daemon/src/interface/mode.rs`:
- Around line 183-191: Remove the public(crate) gpu_list() and config()
accessors from ModeInterface, and add a detect_display_target method that
accepts the requested target and internally uses self.gpu_list and self.config
to call detect_external_display_target. Update both monitor_display.rs call
sites to invoke mode.detect_display_target(requested).await? so ModeInterface
retains ownership of its state.
- Around line 204-209: Update set_requested_mode to handle errors from
detect_external_display_target like apply_mode_at_startup: log a warning and
fall back to the requested mode instead of propagating the error. Preserve
successful target detection and ensure the requested mode is still applied and
persisted when external display detection fails.
- Around line 204-217: Update set_requested_mode so that after
save_mode(requested) completes, it emits the zbus-generated
requested_mode_changed signal through the SignalEmitter API, ensuring the
requested_mode property change is broadcast even though save_mode bypasses its
setter.

In `@crates/cardwire-daemon/src/tasks/monitor_display.rs`:
- Around line 74-111: Make reconcile_display_mode’s read-decide-apply sequence
atomic by adding a ModeInterface method that acquires the transition mutex once,
reads the requested mode, detects the display target, and applies the effective
mode while holding the lock. Keep the DISPLAY_RESTORE_WAIT sleep outside the
mutex, then perform the final requested-mode read, target detection, and
effective_set_mode operation through the new atomic method; preserve DRM event
replay and return values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9e1b26c2-e3d0-4ef4-9f66-6502fa6ba494

📥 Commits

Reviewing files that changed from the base of the PR and between d591a70 and 3908092.

📒 Files selected for processing (4)
  • crates/cardwire-daemon/src/interface/mode.rs
  • crates/cardwire-daemon/src/models.rs
  • crates/cardwire-daemon/src/tasks/mod.rs
  • crates/cardwire-daemon/src/tasks/monitor_display.rs

Comment thread crates/cardwire-daemon/src/interface/mode.rs Outdated
Comment thread crates/cardwire-daemon/src/interface/mode.rs Outdated
Comment thread crates/cardwire-daemon/src/interface/mode.rs
Comment thread crates/cardwire-daemon/src/tasks/monitor_display.rs
@JuanDelPueblo
JuanDelPueblo force-pushed the external-display-daemon branch from 175fb3e to 3d370ca Compare August 2, 2026 21:16

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
crates/cardwire-daemon/src/interface/mode.rs (2)

172-183: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

A signal is rung for a bell that never moved.

emit_mode_change fires both mode_changed and requested_mode_changed whenever changed is true. changed here reflects the effective mode changing (from reconcile_effective_mode/reconcile_display_mode in the monitor task), not the persisted requested mode. set_mode already emits requested_mode_changed explicitly on the D-Bus setter path (Line 346), covering the case where the user actually changes the request.

When the automatic display-topology reconciliation flips the effective mode, requested_mode in mode_state has not changed at all. Emitting requested_mode_changed here tells D-Bus clients that the persisted setting changed when it did not, which contradicts the whole point of separating mode (effective) from requested_mode (persisted) documented at Line 356-357.

🪄 Proposed fix
     pub async fn emit_mode_change(
         &self,
         interface: &InterfaceRef<ModeInterface>,
         changed: bool,
     ) -> zbus::Result<()> {
         if changed {
             self.mode_changed(interface.signal_emitter()).await?;
-            self.requested_mode_changed(interface.signal_emitter())
-                .await?;
         }
         Ok(())
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-daemon/src/interface/mode.rs` around lines 172 - 183, Update
emit_mode_change to emit only mode_changed when changed is true; remove the
requested_mode_changed call because this path reports effective-mode
reconciliation, while set_mode remains responsible for requested-mode
notifications.

213-222: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the unused effective_set_mode incantation. The repository contains no callers, so remove the method and its #[allow(dead_code)] attribute.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-daemon/src/interface/mode.rs` around lines 213 - 222, Remove
the unused effective_set_mode method from the mode interface, including its
#[allow(dead_code)] attribute; no replacement or caller changes are needed.
crates/cardwire-daemon/src/tasks/monitor_display.rs (1)

75-120: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

The restoration spell is cast before the waiting begins.

mode.reconcile_effective_mode() at Line 80 both detects the target and applies it atomically, before the info! at Line 86-89 and the tokio::time::sleep(DISPLAY_RESTORE_WAIT) at Line 90. So the revert to Integrated/Smart already happened by the time the log claims restoration will occur "after {} seconds". The wait and second reconcile_effective_mode() call only decide whether to flip back to Hybrid if the display reconnects quickly.

For a display that flickers or bounces on disconnect (common with KVM switches and USB-C docks), this causes two real hardware transitions in quick succession: an immediate revert to Integrated/Smart (blocking the dGPU, restarting nvidia-powerd.service through the shared apply_mode path), followed by a flip back to Hybrid moments later. This defeats the debounce the wait was meant to provide, and it directly undermines the feature's goal of keeping the dGPU available through a physical disconnect blip.

Peek the topology (without applying) before deciding to wait, and only call reconcile_effective_mode() once, after the wait settles the state.

🪄 Proposed fix
 async fn reconcile_display_mode(
     mode: &ModeInterface,
     was_connected: bool,
 ) -> fdo::Result<(bool, Option<u32>)> {
-    let (first_changed, first_requested, first_target, first_card) =
-        mode.reconcile_effective_mode().await?;
-
-    if was_connected
-        && first_card.is_none()
-        && matches!(first_requested, Modes::Integrated | Modes::Smart)
-    {
-        info!(
-            "external dGPU display disconnected; restoring the configured mode after {} seconds",
-            DISPLAY_RESTORE_WAIT.as_secs()
-        );
-        tokio::time::sleep(DISPLAY_RESTORE_WAIT).await;
-        let (changed, requested, target, card) = mode.reconcile_effective_mode().await?;
-        if card.is_some() {
-            info!("external display reconnected; keeping the current mode");
-        }
-        if changed
-            && target == Modes::Hybrid
-            && requested != Modes::Hybrid
-            && let Some(card) = card
-        {
-            let path = format!("/sys/class/drm/card{card}/uevent");
-            if let Err(err) = tokio::fs::write(&path, "change\n").await {
-                warn!("failed to replay DRM change event through {path}: {err}");
-            }
-        }
-        return Ok((changed, card));
-    }
-
-    if first_changed
-        && first_target == Modes::Hybrid
-        && first_requested != Modes::Hybrid
-        && let Some(card) = first_card
-    {
-        let path = format!("/sys/class/drm/card{card}/uevent");
-        if let Err(err) = tokio::fs::write(&path, "change\n").await {
-            warn!("failed to replay DRM change event through {path}: {err}");
-        }
-    }
-
-    Ok((first_changed, first_card))
+    let requested = mode.requested_mode_value().await;
+    if was_connected && matches!(requested, Modes::Integrated | Modes::Smart) {
+        let disconnected = matches!(mode.detect_display_target(requested).await, Ok((_, None)));
+        if disconnected {
+            info!(
+                "external dGPU display disconnected; restoring the configured mode after {} seconds",
+                DISPLAY_RESTORE_WAIT.as_secs()
+            );
+            tokio::time::sleep(DISPLAY_RESTORE_WAIT).await;
+        }
+    }
+
+    let (changed, requested, target, card) = mode.reconcile_effective_mode().await?;
+    if changed && target == Modes::Hybrid && requested != Modes::Hybrid && let Some(card) = card {
+        let path = format!("/sys/class/drm/card{card}/uevent");
+        if let Err(err) = tokio::fs::write(&path, "change\n").await {
+            warn!("failed to replay DRM change event through {path}: {err}");
+        }
+    }
+    Ok((changed, card))
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-daemon/src/tasks/monitor_display.rs` around lines 75 - 120,
Update reconcile_display_mode so the initial topology check does not apply a
mode; when a prior connection is lost and the requested mode is Integrated or
Smart, wait DISPLAY_RESTORE_WAIT before making the single
reconcile_effective_mode call. Recheck the settled topology before applying,
preserve the reconnect and DRM event handling, and avoid any mode transition
before the debounce wait.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/cardwire-daemon/src/interface/mode.rs`:
- Around line 172-183: Update emit_mode_change to emit only mode_changed when
changed is true; remove the requested_mode_changed call because this path
reports effective-mode reconciliation, while set_mode remains responsible for
requested-mode notifications.
- Around line 213-222: Remove the unused effective_set_mode method from the mode
interface, including its #[allow(dead_code)] attribute; no replacement or caller
changes are needed.

In `@crates/cardwire-daemon/src/tasks/monitor_display.rs`:
- Around line 75-120: Update reconcile_display_mode so the initial topology
check does not apply a mode; when a prior connection is lost and the requested
mode is Integrated or Smart, wait DISPLAY_RESTORE_WAIT before making the single
reconcile_effective_mode call. Recheck the settled topology before applying,
preserve the reconnect and DRM event handling, and avoid any mode transition
before the debounce wait.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 09990266-f99e-4c1f-bad7-c466647e3328

📥 Commits

Reviewing files that changed from the base of the PR and between 3908092 and 175fb3e.

📒 Files selected for processing (2)
  • crates/cardwire-daemon/src/interface/mode.rs
  • crates/cardwire-daemon/src/tasks/monitor_display.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/cardwire-daemon/src/interface/config.rs (1)

57-57: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Preserve the previous D-Bus binding for existing familiars. The rename updates cardwire-gui and cardwire-cli, but removes com.github.opengamingcollective.cardwire and com.github.opengamingcollective.cardwire.Config. Existing clients cannot connect. Keep the old endpoint during migration, or document this release as a breaking D-Bus API change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cardwire-daemon/src/interface/config.rs` at line 57, Update the D-Bus
interface declaration at the Config interface attribute to retain the previous
com.github.opengamingcollective.cardwire.Config binding alongside the renamed
org.opengamingcollective.cardwire.Config endpoint, preserving connectivity for
existing clients during migration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cardwire-daemon/src/daemon.rs`:
- Around line 70-79: Update the ModeInterface lookup in spawn_dbus_api to use
the same object path as its registration, /org/opengamingcollective/cardwire,
instead of the mismatched /com/github/opengamingcollective/cardwire path.
Preserve the existing error propagation and background task setup.

In `@crates/cardwire-daemon/src/interface/mode.rs`:
- Around line 172-183: Update emit_mode_change so the changed path emits only
mode_changed and no longer calls requested_mode_changed. Keep
requested_mode_changed emission confined to methods that actually modify the
persisted requested mode, such as set_mode, while preserving the existing
behavior and error propagation for mode_changed.

In `@crates/cardwire-daemon/src/tasks/monitor_display.rs`:
- Around line 34-72: Update detect_external_display_target and its callers to
represent external_display_connected failures as a distinct Unknown topology
result rather than treating them as disconnected or returning (requested, None).
Propagate Unknown through reconcile_display_mode, set_requested_mode, and
apply_mode_at_startup; skip effective-mode changes and connection-state updates
whenever topology is Unknown, preserving the current mode and state.

---

Outside diff comments:
In `@crates/cardwire-daemon/src/interface/config.rs`:
- Line 57: Update the D-Bus interface declaration at the Config interface
attribute to retain the previous com.github.opengamingcollective.cardwire.Config
binding alongside the renamed org.opengamingcollective.cardwire.Config endpoint,
preserving connectivity for existing clients during migration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 745be570-1f13-481b-829a-91a45f6fb116

📥 Commits

Reviewing files that changed from the base of the PR and between 175fb3e and 3d370ca.

📒 Files selected for processing (12)
  • crates/cardwire-daemon/src/core/gpu/discover.rs
  • crates/cardwire-daemon/src/core/gpu/mod.rs
  • crates/cardwire-daemon/src/daemon.rs
  • crates/cardwire-daemon/src/file/config.rs
  • crates/cardwire-daemon/src/interface/config.rs
  • crates/cardwire-daemon/src/interface/debug.rs
  • crates/cardwire-daemon/src/interface/mode.rs
  • crates/cardwire-daemon/src/models.rs
  • crates/cardwire-daemon/src/tasks/mod.rs
  • crates/cardwire-daemon/src/tasks/monitor_display.rs
  • docs/getting-started/installation.md
  • nix/nixos-module.nix

Comment thread crates/cardwire-daemon/src/daemon.rs
Comment thread crates/cardwire-daemon/src/interface/mode.rs
Comment thread crates/cardwire-daemon/src/tasks/monitor_display.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants