diff options
| author | srdusr <[email protected]> | 2024-11-30 23:03:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2024-11-30 23:03:00 +0200 |
| commit | 583063c3094ca0cf9f6cceba92d710b238547f6b (patch) | |
| tree | a1ff8f181027892f4a45846b67898beb136a9312 /crates/core/src/manager/capture.rs | |
| parent | 87f5c04b2593dc49409644e69e7962b73d26b9d8 (diff) | |
| download | srdwm-583063c3094ca0cf9f6cceba92d710b238547f6b.tar.gz srdwm-583063c3094ca0cf9f6cceba92d710b238547f6b.zip | |
Accumulate core-crate additions: capture requests, focus/workspace fixes, test coverage
Bundles several related changes to crates/core built up over this
session rather than committed incrementally:
- WindowManager::request_capture_workspace/drain_capture_requests (new
manager/capture.rs) - backend-agnostic queuing for an off-screen
workspace render, see the wayland-side commit for why this exists.
- focus_window now switches workspace as a side effect when the target
isn't on the current one, matching Hyprland's focuswindow convention
(manager/focus.rs).
- Assorted window/rules/theme field additions and their test coverage.
Left less granular than the repo's usual one-purpose-per-commit
convention deliberately: these accumulated across a long session
without being committed as they landed, and are too entangled
line-by-line to safely split apart now without risking mis-attributing
changes to the wrong commit message.
Diffstat (limited to 'crates/core/src/manager/capture.rs')
| -rw-r--r-- | crates/core/src/manager/capture.rs | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/crates/core/src/manager/capture.rs b/crates/core/src/manager/capture.rs new file mode 100644 index 0000000..41fe0cd --- /dev/null +++ b/crates/core/src/manager/capture.rs @@ -0,0 +1,78 @@ +//! Requesting an off-screen render of a workspace's window tree to a file. +//! Split out the same way `lock.rs` is - see `super` (`mod.rs`) for +//! `WindowManager`'s field definitions. + +use super::*; + +/// One queued `srd capture workspace <id> <path> [WxH]` request. Core has +/// no renderer of its own (that's backend-owned, same boundary +/// `request_lock`/`request_output_position` already cross) - this is +/// just the request's data, drained and acted on by whichever backend is +/// actually running. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CaptureRequest { + pub workspace: WorkspaceId, + pub path: String, + /// `None` renders at the workspace's own monitor resolution. + pub size: Option<(u32, u32)>, +} + +impl WindowManager { + /// Queues an off-screen capture of `workspace`'s window tree, written + /// to `path` as a PPM image once the backend's next poll drains it. + /// Exists for exactly one reason: a workspace switcher (AGS's Overview) + /// wanting a thumbnail of a workspace that is not the one currently on + /// screen. `wlr-screencopy` (what `grim`, and this compositor's own + /// `screencopy.rs`, use) can only ever see what an output is actually + /// presenting - it has no way to see a workspace that is not the + /// active one, which is exactly the case a workspace-switcher preview + /// needs most. This is not a re-implementation of screencopy; it is + /// the one thing screencopy structurally cannot do, requested the same + /// cross-boundary way `request_lock` is. + /// + /// Multiple requests for the same workspace queue independently (unlike + /// `request_lock`'s single flag) - a caller asking for two different + /// sizes, or overwriting a previous request for the same workspace + /// before the backend gets to it, are both legitimate. + pub fn request_capture_workspace(&mut self, workspace: WorkspaceId, path: String, size: Option<(u32, u32)>) { + self.capture_requests.push(CaptureRequest { workspace, path, size }); + } + + /// Takes every currently-queued capture request, leaving none pending. + /// The backend calls this once per poll, same as + /// `drain_output_position_requests`. + pub fn drain_capture_requests(&mut self) -> Vec<CaptureRequest> { + std::mem::take(&mut self.capture_requests) + } + + /// Front-to-back window ids for an arbitrary (not necessarily current) + /// workspace - the same "topmost first" convention + /// `visible_windows_front_to_back` already gives the current one, but + /// that method is hardcoded to `self.current_workspace`, and a capture + /// request's whole reason for existing is targeting a workspace that + /// usually is *not* the current one. `order` is back-to-front (see its + /// own field doc comment), hence the same `.rev()` that method uses. + pub fn window_ids_on_workspace_front_to_back(&self, workspace: WorkspaceId) -> Vec<WindowId> { + self.order.iter().rev().filter(|&&id| self.windows.get(&id).is_some_and(|w| w.workspace == workspace && !w.minimized)).copied().collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn capture_requests_drain_in_arrival_order() { + let mut wm = WindowManager::new(); + wm.request_capture_workspace(0, "/tmp/a.ppm".into(), None); + wm.request_capture_workspace(1, "/tmp/b.ppm".into(), Some((100, 100))); + assert_eq!( + wm.drain_capture_requests(), + vec![ + CaptureRequest { workspace: 0, path: "/tmp/a.ppm".into(), size: None }, + CaptureRequest { workspace: 1, path: "/tmp/b.ppm".into(), size: Some((100, 100)) }, + ] + ); + assert!(wm.drain_capture_requests().is_empty(), "must not report the same requests twice"); + } +} |