srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/udev/outputs.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-07-16 21:25:00 +0200
committersrdusr <[email protected]>2024-07-16 21:25:00 +0200
commit6fad44b3ecffb22f77a78661ce179b304e9a6ccc (patch)
tree4bba4624f0be3a2aa34965740ae5c9ebcc12b759 /crates/wayland/src/udev/outputs.rs
parent2994f622b2998dc6aebbd38f52a6e6612ed8e7c8 (diff)
downloadsrdwm-6fad44b3ecffb22f77a78661ce179b304e9a6ccc.tar.gz
srdwm-6fad44b3ecffb22f77a78661ce179b304e9a6ccc.zip
Split crates/wayland/src/udev.rs (1643 lines) into udev/
Pure reorganization, no behavior change - verified by diffing the function-name and struct-name sets before/after (both identical) plus a full cargo test pass. Struct definitions (Card, DrmBuffer, UdevHead, UdevState) and their small impls stay in mod.rs, since default (crate- scoped) privacy there is visible to every descendant submodule without further changes. The rest splits by concern: - render.rs: the per-frame impl CompState block (render_udev_frame and the gamma/output-power methods) - still one ~520-line function, left intact rather than decomposed, given how much of its structure (the self.udev.as_mut() disjoint-borrow pattern threaded through it) is deliberate and already documented inline. - outputs.rs: hotplug reprobe/relayout (impl CompState). - platform.rs: UdevPlatform's struct/connect logic and its `impl Platform for UdevPlatform`, previously split apart in the flat file by ~250 lines of unrelated DRM/session code sitting between them. - drm.rs: mode/CRTC/framebuffer probing and setup. - session.rs: libseat/libinput/udev-monitor calloop registration and the libinput event handler. A few free functions and one struct (ConnectorProbe, bring_up_head, probe_connected, pick_crtc, the register_* functions) went from module-private to pub(crate): called across what are now sibling submodules, which - unlike a defining module's own descendants -- Rust's privacy model doesn't let see each other's private items. Matches this crate's existing pub(crate) convention rather than introducing pub(super), which crates/core's manager/ split used instead to match *that* crate's plain-private convention.
Diffstat (limited to 'crates/wayland/src/udev/outputs.rs')
-rw-r--r--crates/wayland/src/udev/outputs.rs108
1 files changed, 108 insertions, 0 deletions
diff --git a/crates/wayland/src/udev/outputs.rs b/crates/wayland/src/udev/outputs.rs
new file mode 100644
index 0000000..6e282d6
--- /dev/null
+++ b/crates/wayland/src/udev/outputs.rs
@@ -0,0 +1,108 @@
+use super::*;
+use super::drm::{bring_up_head, pick_crtc, probe_connected};
+
+impl CompState {
+ /// Re-probes connectors after a hotplug and reconciles the head list.
+ ///
+ /// Connectors that vanished have their head torn down (global removed,
+ /// output unmapped, DRM buffers freed); newly connected ones are brought
+ /// up exactly as they would have been at startup. Every head is then
+ /// repositioned left-to-right, because removing a monitor shifts the
+ /// ones after it.
+ pub(crate) fn reprobe_outputs(&mut self) {
+ let Some(udev) = self.udev.as_ref() else { return };
+ let card = udev.card.clone();
+
+ let probes = match probe_connected(&card) {
+ Ok(p) => p,
+ Err(e) => {
+ log::warn!("udev: hotplug re-probe failed: {e}");
+ return;
+ }
+ };
+ let present: Vec<connector::Handle> = probes.iter().map(|p| p.connector).collect();
+ let existing: Vec<connector::Handle> = udev.heads.iter().map(|h| h.connector).collect();
+
+ let gone: Vec<connector::Handle> = existing.iter().copied().filter(|c| !present.contains(c)).collect();
+ let added: Vec<usize> = probes
+ .iter()
+ .enumerate()
+ .filter(|(_, p)| !existing.contains(&p.connector))
+ .map(|(i, _)| i)
+ .collect();
+ if gone.is_empty() && added.is_empty() {
+ return; // a "changed" event that didn't change the connector set
+ }
+ log::info!("udev: hotplug - {} output(s) removed, {} added", gone.len(), added.len());
+
+ // ---- removals ----
+ for connector in &gone {
+ let Some(udev) = self.udev.as_mut() else { return };
+ let Some(index) = udev.heads.iter().position(|h| h.connector == *connector) else { continue };
+ let head = udev.heads.remove(index);
+ log::info!("udev: output {} disconnected", head.output.name());
+ self.dh.remove_global::<CompState>(head.global.clone());
+ self.space.unmap_output(&head.output);
+ self.outputs.retain(|e| e.output != head.output);
+ // A lock surface for a monitor that no longer exists would keep
+ // `confirm_lock_if_presented` waiting forever otherwise.
+ self.lock.surfaces.remove(&head.output.name());
+ self.lock.presented.remove(&head.output.name());
+ head.release(&card);
+ self.pending.borrow_mut().push(CoreEvent::MonitorRemoved(index as u32));
+ }
+
+ // ---- additions ----
+ for i in added {
+ let probe = &probes[i];
+ let used: Vec<crtc::Handle> =
+ self.udev.as_ref().map(|u| u.heads.iter().map(|h| h.crtc).collect()).unwrap_or_default();
+ let Some(crtc) = pick_crtc(&card, probe, &used) else {
+ log::warn!("udev: no free CRTC for newly connected {}; not driving it", probe.name);
+ continue;
+ };
+ // Placed at 0 for now; the re-layout below assigns real offsets.
+ match bring_up_head(&card, &self.dh.clone(), probe, crtc, 0) {
+ Ok((head, entry)) => {
+ log::info!("udev: output {} connected ({}x{})", probe.name, head.size.0, head.size.1);
+ let monitor_id = self.outputs.len() as u32;
+ let geometry = srdwm_core::Rect::new(0, 0, head.size.0 as u32, head.size.1 as u32);
+ if let Some(udev) = self.udev.as_mut() {
+ udev.heads.push(head);
+ }
+ self.outputs.push(entry);
+ self.pending
+ .borrow_mut()
+ .push(CoreEvent::MonitorAdded(srdwm_core::Monitor::new(monitor_id, probe.name.clone(), geometry)));
+ }
+ Err(e) => log::warn!("udev: failed to bring up {}: {e}", probe.name),
+ }
+ }
+
+ self.relayout_outputs();
+ }
+
+ /// Repositions every head left-to-right and republishes the new
+ /// positions to the output globals, the `Space`, and the layer maps.
+ fn relayout_outputs(&mut self) {
+ let Some(udev) = self.udev.as_mut() else { return };
+ let mut x = 0;
+ let mut placed: Vec<(Output, Point<i32, Logical>)> = Vec::new();
+ for head in &mut udev.heads {
+ head.location = (x, 0).into();
+ head.output.change_current_state(None, None, None, Some((x, 0).into()));
+ placed.push((head.output.clone(), head.location));
+ x += head.size.0;
+ }
+ for (output, location) in placed {
+ if let Some(entry) = self.outputs.iter_mut().find(|e| e.output == output) {
+ entry.location = location;
+ }
+ self.space.map_output(&output, (location.x, location.y));
+ // Bars are anchored to their output, so their geometry has to be
+ // recomputed against the moved output rectangle.
+ layer_map_for_output(&output).arrange();
+ }
+ }
+}
+