diff options
| author | srdusr <[email protected]> | 2025-10-26 22:58:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2025-10-26 22:58:00 +0200 |
| commit | 304f3a374408bb6a5a04ebb7d4652c631395f5c6 (patch) | |
| tree | 8a40628fe016bd3e31c295cc76985d65c6354561 /crates/platform/src | |
| parent | 9f07715b469a481849ee35ffa33d6dab34356c7d (diff) | |
| download | srdwm-304f3a374408bb6a5a04ebb7d4652c631395f5c6.tar.gz srdwm-304f3a374408bb6a5a04ebb7d4652c631395f5c6.zip | |
Live-expose monitor split, clean up leftover debug diagnostics
srd.monitor.split only ever ran at Lua config load despite being a plain
WindowManager mutation that every backend's monitors() already reads
fresh on each call. Adds srd dispatch set output split <name|id> <parts>
[rows|columns] (IPC set_monitor_split), same id-resolves-to-name pattern
set_output_enabled already uses.
Also removes eight log::warn!("XXX-DIAG ...") lines left behind from live
debugging in the multi-session shift that landed in 3c41fc4 - the same
"temporary, never removed" pattern already fixed twice earlier this
session. Several fired on genuinely constant interaction (every title
change, every workspace switch, every layer-shell surface hide), not
just a one-off leftover. Left xdg_shell.rs's own POPUP-GEOM-DIAG/
POPUP-GRAB-DIAG alone - that one is a still-open, self-documented
investigation, not litter.
Also documents (docs/TODO.md, not a code change) a live incident where
creating a second fake monitor visibly corrupted the real monitor's
position and kept drifting with no further input - not root-caused
srdwm-side, flagged to the AGS peer session since a fake monitor's real
wl_output global is indistinguishable from a real hotplug to GDK/GTK.
And documents a deliberate decision not to blind-port window decoration
rendering onto the experimental, never-live-tested GPU render path.
Diffstat (limited to 'crates/platform/src')
| -rw-r--r-- | crates/platform/src/ipc/dispatch.rs | 32 | ||||
| -rw-r--r-- | crates/platform/src/ipc/tests.rs | 71 |
2 files changed, 99 insertions, 4 deletions
diff --git a/crates/platform/src/ipc/dispatch.rs b/crates/platform/src/ipc/dispatch.rs index 313a79c..665cb59 100644 --- a/crates/platform/src/ipc/dispatch.rs +++ b/crates/platform/src/ipc/dispatch.rs @@ -219,7 +219,6 @@ pub(crate) fn handle_request(line: &[u8], wm: &std::rc::Rc<std::cell::RefCell<Wi // already reads serves both, same as every other dispatch arm. "activate_workspace" => { let Some(id) = id else { return (err("missing id"), false) }; - let before = wm.borrow().current_workspace(); // `switch_workspace_on_monitor` falls straight through to the // ordinary shared-mode `switch_workspace` when `workspace. // per_monitor` is off, so this is the one call site that works @@ -239,9 +238,6 @@ pub(crate) fn handle_request(line: &[u8], wm: &std::rc::Rc<std::cell::RefCell<Wi .unwrap_or(0); wm.switch_workspace_on_monitor(id as srdwm_core::WorkspaceId, monitor); } - let after = wm.borrow().current_workspace(); - let known: Vec<_> = wm.borrow().workspaces().iter().map(|w| w.id).collect(); - log::warn!("WS-IPC-DIAG requested_id={id} before={before} after={after} known_ids={known:?}"); (ok(), true) } // `{"cmd":"set_output_position","id":<monitor id>,"x":<i32>,"y":<i32>}` @@ -322,6 +318,34 @@ pub(crate) fn handle_request(line: &[u8], wm: &std::rc::Rc<std::cell::RefCell<Wi wm.borrow_mut().request_output_enabled(name, enabled); (ok(), true) } + // `{"cmd":"set_monitor_split","id"|"name":...,"parts":<u32>, + // "rows":<bool, optional, default false>}` - the live CLI/IPC path + // for `srd.monitor.split(name, parts, direction)` (`crates/config/ + // src/engine/general.rs`'s own `fn_monitor_split`), which until now + // only ever ran once at config load. `WindowManager:: + // set_monitor_split` just mutates `monitor_splits`, and every + // backend's own `monitors()` already reads that map fresh on every + // single call (see the udev platform's own `monitors()`) - so, + // unlike `set_output_position`/`set_output_enabled` above, this + // needs no queue-and-drain at all: the very next `monitors()` query + // already reflects it. `parts` <= 1 clears an existing split, same + // as the Lua function. Same "resolve id to a name first" fallback + // `set_output_enabled` above already uses, since a caller working + // from a numeric id shouldn't have to look the name up itself + // first just to turn around and split it. + "set_monitor_split" => { + let name = match req.get("name").and_then(|v| v.as_str()) { + Some(name) => Some(name.to_string()), + None => id.and_then(|id| wm.borrow().monitors().iter().find(|m| m.id == id as srdwm_core::MonitorId).map(|m| m.name.clone())), + }; + let Some(name) = name else { return (err("missing name, or an id matching a currently-connected monitor"), false) }; + let Some(parts) = req.get("parts").and_then(|v| v.as_u64()) else { + return (err("missing parts"), false); + }; + let rows = req.get("rows").and_then(|v| v.as_bool()).unwrap_or(false); + wm.borrow_mut().set_monitor_split(name, parts as u32, rows); + (ok(), true) + } // `{"cmd":"capture_workspace","id":<workspace id>,"path":<string>, // "width":<u32>,"height":<u32>}` - `width`/`height` are optional, // both or neither. Exists for a workspace switcher's thumbnail diff --git a/crates/platform/src/ipc/tests.rs b/crates/platform/src/ipc/tests.rs index cb56a7c..aa86e95 100644 --- a/crates/platform/src/ipc/tests.rs +++ b/crates/platform/src/ipc/tests.rs @@ -426,6 +426,77 @@ fn set_output_enabled_with_neither_name_nor_a_resolvable_id_errors() { } #[test] +fn set_monitor_split_accepts_a_name_directly_and_applies_immediately() { + // Unlike `set_output_position`/`set_output_enabled`, this one is a + // plain `WindowManager` mutation with nothing to drain - the very + // next `monitor_split` read already reflects it. + let dir = tempfile::tempdir().unwrap(); + let mut server = IpcServer::bind_in(dir.path(), "test").unwrap(); + let wm = Rc::new(RefCell::new(WindowManager::new())); + wm.borrow_mut().set_monitors(vec![srdwm_core::Monitor::new(0, "eDP-1", srdwm_core::Rect::new(0, 0, 1920, 1080))]); + + let mut client = UnixStream::connect(&server.path).unwrap(); + let mut reader = std::io::BufReader::new(client.try_clone().unwrap()); + client.write_all(b"{\"cmd\":\"set_monitor_split\",\"name\":\"eDP-1\",\"parts\":2,\"rows\":false}\n").unwrap(); + server.poll(&wm); + let _ = read_line(&mut reader); + + let split = wm.borrow().monitor_split("eDP-1").unwrap(); + assert_eq!(split.parts, 2); + assert!(!split.rows); +} + +#[test] +fn set_monitor_split_resolves_an_id_to_its_name() { + let dir = tempfile::tempdir().unwrap(); + let mut server = IpcServer::bind_in(dir.path(), "test").unwrap(); + let wm = Rc::new(RefCell::new(WindowManager::new())); + wm.borrow_mut().set_monitors(vec![srdwm_core::Monitor::new(3, "HDMI-A-1", srdwm_core::Rect::new(0, 0, 1920, 1080))]); + + let mut client = UnixStream::connect(&server.path).unwrap(); + let mut reader = std::io::BufReader::new(client.try_clone().unwrap()); + client.write_all(b"{\"cmd\":\"set_monitor_split\",\"id\":3,\"parts\":3,\"rows\":true}\n").unwrap(); + server.poll(&wm); + let _ = read_line(&mut reader); + + let split = wm.borrow().monitor_split("HDMI-A-1").unwrap(); + assert_eq!(split.parts, 3); + assert!(split.rows); +} + +#[test] +fn set_monitor_split_with_one_part_clears_an_existing_split() { + let dir = tempfile::tempdir().unwrap(); + let mut server = IpcServer::bind_in(dir.path(), "test").unwrap(); + let wm = Rc::new(RefCell::new(WindowManager::new())); + wm.borrow_mut().set_monitors(vec![srdwm_core::Monitor::new(0, "eDP-1", srdwm_core::Rect::new(0, 0, 1920, 1080))]); + wm.borrow_mut().set_monitor_split("eDP-1".to_string(), 2, false); + + let mut client = UnixStream::connect(&server.path).unwrap(); + let mut reader = std::io::BufReader::new(client.try_clone().unwrap()); + client.write_all(b"{\"cmd\":\"set_monitor_split\",\"name\":\"eDP-1\",\"parts\":1}\n").unwrap(); + server.poll(&wm); + let _ = read_line(&mut reader); + + assert!(wm.borrow().monitor_split("eDP-1").is_none()); +} + +#[test] +fn set_monitor_split_with_neither_name_nor_a_resolvable_id_errors() { + let dir = tempfile::tempdir().unwrap(); + let mut server = IpcServer::bind_in(dir.path(), "test").unwrap(); + let wm = Rc::new(RefCell::new(WindowManager::new())); + + let mut client = UnixStream::connect(&server.path).unwrap(); + let mut reader = std::io::BufReader::new(client.try_clone().unwrap()); + client.write_all(b"{\"cmd\":\"set_monitor_split\",\"parts\":2}\n").unwrap(); + server.poll(&wm); + let line = read_line(&mut reader); + + assert!(line.contains(r#""error""#)); +} + +#[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 |