srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/core/src/manager/input_pin.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2025-08-29 22:42:00 +0200
committersrdusr <[email protected]>2025-08-29 22:42:00 +0200
commitbe5c4efa6866b5d23e1e81ecfc3b8d276540860f (patch)
tree0732d942bf28d26cc6996549fadd1785e566a637 /crates/core/src/manager/input_pin.rs
parentbd7141718901c6f41511137e4b34a3bd9e2705b1 (diff)
downloadsrdwm-be5c4efa6866b5d23e1e81ecfc3b8d276540860f.tar.gz
srdwm-be5c4efa6866b5d23e1e81ecfc3b8d276540860f.zip
Core window manager: real fixes plus three new rule/placement primitives
Several independent, real pieces landed in crates/core this shift - see docs/TODO.md for each one's full root-cause/verification narrative: - "Primary" monitor is now picked by which head sits at physical (0, 0) (the user's own configured anchor), not whichever connector DRM happened to probe first - fixes desktop icons and new-window placement landing on the wrong monitor depending on hotplug/probe order. - A new window's target monitor now prioritizes the pointer's own current monitor over the last-focused window's monitor, which goes stale the moment the user's attention moves to empty desktop, a panel, or a dock. - aspect_ratio window-rule action ("W:H") plus ResizeEdge::apply_aspect_ ratio: holds a floating window's aspect ratio through an interactive resize. The real, scoped "phone monitor" primitive - matches any VM/ emulator/scrcpy window by app_id, nothing Android- or VM-specific here. - general.phone_mode (WindowManager::phone_mode): a new window defaults to maximized instead of floating/tiled small, unless a rule explicitly floats it or sets maximized - the one placement default a phone-shaped screen actually needs. Exposed read-only via IPC so a shell panel can adapt its own chrome to the same signal. - input_pin.rs: the core half of pinning a virtual pointer to a specific window (Multi-cursor Phase 2) - a backend-agnostic request queue, same cross-boundary shape output_position_requests/lock_requested already use, since core has no real Wayland protocol object to reach into itself. Full workspace test suite covers all of the above (aspect-ratio resize math for every edge case, phone-mode default-vs-rule-override behavior, the pin-input request queue, the monitor-picking fixes).
Diffstat (limited to 'crates/core/src/manager/input_pin.rs')
-rw-r--r--crates/core/src/manager/input_pin.rs64
1 files changed, 64 insertions, 0 deletions
diff --git a/crates/core/src/manager/input_pin.rs b/crates/core/src/manager/input_pin.rs
new file mode 100644
index 0000000..e3a7fbf
--- /dev/null
+++ b/crates/core/src/manager/input_pin.rs
@@ -0,0 +1,64 @@
+//! Requesting a virtual-pointer pin to a specific window - Phase 2 of
+//! this project's own multi-cursor plan (see `docs/TODO.md`'s "Multi-
+//! cursor Phase 2" entry, and `crates/wayland/src/virtual_pointer.rs`'s
+//! own module doc comment for the full design). Split out the same way
+//! `lock.rs` is: everything here is plain `impl WindowManager` methods:
+//! see `super` (`mod.rs`) for `WindowManager`'s field definitions.
+
+use super::*;
+
+impl WindowManager {
+ /// Queues a request to pin (`window` is `Some`) or unpin (`None`)
+ /// every virtual pointer object owned by the client with process id
+ /// `pid` - the only caller today is the IPC `"pin_input"` dispatch,
+ /// the compositor-agnostic side of `srd dispatch pin input`/`unpin
+ /// input`. Core has no real Wayland protocol object to reach into
+ /// itself (that's backend-owned, same as `output_position_requests`);
+ /// the Wayland backend drains and applies this on its own next poll.
+ ///
+ /// Replaces (not accumulates) any still-pending request for the same
+ /// `pid`, the same "last write wins" semantics `request_output_
+ /// position` already has - only the *latest* requested pin for a
+ /// given pid matters if several arrive before the backend's next
+ /// drain.
+ pub fn request_pin_input(&mut self, pid: i32, window: Option<WindowId>) {
+ self.pin_input_requests.retain(|(existing, _)| *existing != pid);
+ self.pin_input_requests.push((pid, window));
+ }
+
+ /// Takes every currently-queued pin-input request, leaving the queue
+ /// empty. The backend calls this once per poll, same as `drain_
+ /// output_position_requests`.
+ pub fn drain_pin_input_requests(&mut self) -> Vec<(i32, Option<WindowId>)> {
+ std::mem::take(&mut self.pin_input_requests)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn a_pin_request_is_reported_once_then_the_queue_is_empty() {
+ let mut wm = WindowManager::new();
+ assert!(wm.drain_pin_input_requests().is_empty());
+ wm.request_pin_input(1234, Some(7));
+ assert_eq!(wm.drain_pin_input_requests(), vec![(1234, Some(7))]);
+ assert!(wm.drain_pin_input_requests().is_empty());
+ }
+
+ #[test]
+ fn a_second_request_for_the_same_pid_replaces_the_first_before_a_drain() {
+ let mut wm = WindowManager::new();
+ wm.request_pin_input(1234, Some(7));
+ wm.request_pin_input(1234, Some(9));
+ assert_eq!(wm.drain_pin_input_requests(), vec![(1234, Some(9))]);
+ }
+
+ #[test]
+ fn unpinning_is_a_real_queued_request_too_not_a_no_op() {
+ let mut wm = WindowManager::new();
+ wm.request_pin_input(1234, None);
+ assert_eq!(wm.drain_pin_input_requests(), vec![(1234, None)]);
+ }
+}