srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/udev/virtual_heads.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2025-10-28 20:09:00 +0200
committersrdusr <[email protected]>2025-10-28 20:09:00 +0200
commit9592fd7acb5b68f3fae139dfac22b728c35b6198 (patch)
treeebdaab6064388b67ffe06a611d934d7a29120959 /crates/wayland/src/udev/virtual_heads.rs
parent304f3a374408bb6a5a04ebb7d4652c631395f5c6 (diff)
downloadsrdwm-9592fd7acb5b68f3fae139dfac22b728c35b6198.tar.gz
srdwm-9592fd7acb5b68f3fae139dfac22b728c35b6198.zip
Fix a fake monitor's layer-shell surfaces misrouting onto the real primary
Live incident, root-caused jointly with the AGS peer session: creating a fake monitor visibly shrank the real primary output's usable area (full_y stayed 0 throughout - its true position never moved) each time, tracking almost exactly one bar height per fake monitor created. create_virtual_head registered its new Output in udev.virtual_heads but never in CompState::outputs, the list output_for_wl searches to resolve a client-named wl_output back to anything. new_layer_surface's own fallback for an output it can't resolve is landing on the primary output - so AGS's own per-monitor bar, aimed at the fake monitor it reasonably believed was a new real one, silently landed on the real primary output instead, stacking its own exclusive-zone reservation on top of the real bar already there. Two fake monitors, two misrouted bars, two zone increments, matching the observed climb exactly. Fixed by registering (and, on removal, deregistering) a virtual head's Output in CompState::outputs the same way bring_up_head already does for a real one. Also adds Monitor::is_virtual / MonitorInfo's "virtual" JSON field, requested directly by the AGS peer session as the real discriminator their own temporary FAKE- name-pattern match was standing in for. The X-position half of this same incident was AGS's own remembered- layout restore treating a fake monitor's wl_output as a real hotplug -- already fixed on their side (readArrangeable() now filters split/virtual outputs).
Diffstat (limited to 'crates/wayland/src/udev/virtual_heads.rs')
-rw-r--r--crates/wayland/src/udev/virtual_heads.rs21
1 files changed, 21 insertions, 0 deletions
diff --git a/crates/wayland/src/udev/virtual_heads.rs b/crates/wayland/src/udev/virtual_heads.rs
index 38444e3..a1e3569 100644
--- a/crates/wayland/src/udev/virtual_heads.rs
+++ b/crates/wayland/src/udev/virtual_heads.rs
@@ -90,6 +90,23 @@ impl CompState {
output.set_preferred(mode);
let global = output.create_global::<CompState>(&self.dh);
+ // Also registered in `self.outputs` (mirroring `bring_up_head`'s own
+ // `entry` for a real head), not just `udev.virtual_heads` - without
+ // this, `output_for_wl` (which only ever searches `self.outputs`)
+ // can never resolve *this* output's own `wl_output` back to
+ // anything, so any client naming it in a `zwlr_layer_shell_v1::
+ // get_layer_surface` request (a per-monitor bar/panel, concretely)
+ // silently falls through `new_layer_surface`'s "or land on the
+ // primary output" fallback instead - landing a bar meant for this
+ // fake monitor on the real primary one, stacking its exclusive
+ // zone on top of whatever real bar is already reserving space
+ // there. Confirmed live: a second fake monitor's own bar attempt
+ // was exactly what pushed the real monitor's reserved top strip up
+ // by another zone's worth, on top of the real bar's own - reported
+ // as the real monitor's position drifting after a fake monitor
+ // appeared, which was actually its *usable* (bar-shrunk) area
+ // shrinking further, not its true position moving at all.
+ self.outputs.push(crate::state::OutputEntry { output: output.clone(), location });
self.udev.as_mut().unwrap().virtual_heads.push(VirtualHead { name, output, global, size: (width, height), location });
// Payload discarded unread - `main.rs`'s own handler for this
// event just re-queries the whole monitor list, same as a real
@@ -111,6 +128,10 @@ impl CompState {
};
let head = udev.virtual_heads.remove(index);
self.dh.remove_global::<CompState>(head.global);
+ // Mirrors `create_virtual_head`'s own registration - see that
+ // function's doc comment for why this output was in `self.outputs`
+ // at all.
+ self.outputs.retain(|e| e.output != head.output);
self.pending.borrow_mut().push(CoreEvent::MonitorRemoved(0));
Ok(())
}