srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/platform/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/platform/src')
-rw-r--r--crates/platform/src/ipc/tests.rs24
-rw-r--r--crates/platform/src/ipc/types.rs20
2 files changed, 44 insertions, 0 deletions
diff --git a/crates/platform/src/ipc/tests.rs b/crates/platform/src/ipc/tests.rs
index aa86e95..591be56 100644
--- a/crates/platform/src/ipc/tests.rs
+++ b/crates/platform/src/ipc/tests.rs
@@ -497,6 +497,30 @@ fn set_monitor_split_with_neither_name_nor_a_resolvable_id_errors() {
}
#[test]
+fn monitors_query_marks_a_virtual_output_so_a_client_does_not_treat_it_as_a_real_hotplug() {
+ let dir = tempfile::tempdir().unwrap();
+ let mut server = IpcServer::bind_in(dir.path(), "test").unwrap();
+ let wm = Rc::new(RefCell::new(WindowManager::new()));
+ let real = srdwm_core::Monitor::new(0, "eDP-1", srdwm_core::Rect::new(0, 0, 1920, 1080));
+ let mut fake = srdwm_core::Monitor::new(1, "FAKE-1", srdwm_core::Rect::new(1920, 0, 1920, 1080));
+ fake.is_virtual = true;
+ wm.borrow_mut().set_monitors(vec![real, fake]);
+
+ let mut client = UnixStream::connect(&server.path).unwrap();
+ let mut reader = std::io::BufReader::new(client.try_clone().unwrap());
+ client.write_all(b"{\"cmd\":\"monitors\"}\n").unwrap();
+ server.poll(&wm);
+ let line = read_line(&mut reader);
+
+ let parsed: serde_json::Value = serde_json::from_str(&line).unwrap();
+ let monitors = parsed["monitors"].as_array().unwrap();
+ let real = monitors.iter().find(|m| m["name"] == "eDP-1").unwrap();
+ let fake = monitors.iter().find(|m| m["name"] == "FAKE-1").unwrap();
+ assert_eq!(real["virtual"], false, "a real output must not be marked virtual");
+ assert_eq!(fake["virtual"], true, "a fake monitor must be marked virtual so a client can tell it apart from a real hotplug");
+}
+
+#[test]
fn monitors_query_lists_a_disabled_output_alongside_live_ones() {
// What the AGS peer session asked for directly: a disabled output
// must not just vanish from `srd monitors` - it needs a row
diff --git a/crates/platform/src/ipc/types.rs b/crates/platform/src/ipc/types.rs
index 1dc95f6..df11592 100644
--- a/crates/platform/src/ipc/types.rs
+++ b/crates/platform/src/ipc/types.rs
@@ -288,6 +288,21 @@ pub(crate) struct MonitorInfo {
// actually displaying. See `WorkspaceInfo::monitor` for the same fact
// indexed from the other direction.
pub(crate) active_workspace: usize,
+ // `true` for a fully virtual/headless output (`srd dispatch create
+ // fake-monitor`) - a real `wl_output` global with no DRM connector
+ // behind it. Requested directly by the AGS peer session after a fake
+ // monitor's `wl_output` caused a real live incident: it looks like an
+ // ordinary new physical monitor to any client watching the core
+ // Wayland registry (unlike `wlr-output-management-v1`, which already
+ // excludes it), so AGS's own remembered-layout restore treated one
+ // appearing as a real hotplug and repositioned the *real* monitor to
+ // make room for it. Before this field existed, AGS's only option was
+ // matching the name against `^FAKE-` - this is the real
+ // discriminator that pattern was standing in for. `#[serde(rename)]`
+ // rather than a field literally named `virtual` because that word is
+ // a reserved identifier in Rust.
+ #[serde(rename = "virtual")]
+ pub(crate) is_virtual: bool,
}
/// Pushed to every subscriber (and used as `subscribe`'s own initial
@@ -446,6 +461,7 @@ pub(crate) fn monitor_snapshot(wm: &std::rc::Rc<std::cell::RefCell<WindowManager
split: m.split,
scale: m.scale,
active_workspace: wm.workspace_for_monitor(m.id),
+ is_virtual: m.is_virtual,
});
// Disabled-but-still-connected outputs, appended rather than merged in
// by name - see `MonitorInfo::enabled`'s own doc comment for why
@@ -482,6 +498,10 @@ pub(crate) fn monitor_snapshot(wm: &std::rc::Rc<std::cell::RefCell<WindowManager
// "shows nothing, not tracked" the same way `id: u32::MAX` above
// is a deliberate not-a-real-value sentinel for this same entry.
active_workspace: 0,
+ // A fake monitor is never administratively disabled/re-enabled --
+ // see `virtual_heads.rs`'s own module doc comment - so this
+ // branch (disabled-but-still-connected outputs) can never be one.
+ is_virtual: false,
});
live.chain(disabled).collect()
}