diff options
| author | srdusr <[email protected]> | 2025-08-29 22:42:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2025-08-29 22:42:00 +0200 |
| commit | be5c4efa6866b5d23e1e81ecfc3b8d276540860f (patch) | |
| tree | 0732d942bf28d26cc6996549fadd1785e566a637 /crates/core/src/manager/mod.rs | |
| parent | bd7141718901c6f41511137e4b34a3bd9e2705b1 (diff) | |
| download | srdwm-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/mod.rs')
| -rw-r--r-- | crates/core/src/manager/mod.rs | 111 |
1 files changed, 93 insertions, 18 deletions
diff --git a/crates/core/src/manager/mod.rs b/crates/core/src/manager/mod.rs index 848eca5..a5cde36 100644 --- a/crates/core/src/manager/mod.rs +++ b/crates/core/src/manager/mod.rs @@ -74,6 +74,13 @@ pub struct WindowManager { /// backend's next monitor query, same as any other hotplug/reconfigure. output_position_requests: Vec<(MonitorId, i32, i32)>, /// Same cross-boundary-request pattern as `output_position_requests` + /// just above, for Phase 2 of the multi-cursor plan - pinning a + /// virtual pointer object (identified by the owning client's pid, not + /// an opaque per-object id nothing outside the Wayland backend could + /// ever learn) to a specific window. See `input_pin.rs`'s own doc + /// comment. + pin_input_requests: Vec<(i32, Option<WindowId>)>, + /// Same cross-boundary-request pattern as `output_position_requests` /// just above, for enable/disable - see `request_output_enabled`'s /// own doc comment for why this is keyed by name, not `MonitorId`. output_enable_requests: Vec<(String, bool)>, @@ -206,6 +213,24 @@ pub struct WindowManager { /// without touching config - `udev::platform::connect` attempts the /// probe if *either* this or the env var says to. pub gpu_enabled: bool, + /// Read from `general.phone_mode` - `false` by default. Optional + /// single-app-at-a-time placement policy for a phone-shaped display: + /// see `add_window`'s own use of this (a new window defaults to + /// maximized instead of floating/tiled small, unless a rule says + /// otherwise) for the concrete effect. Deliberately just a placement + /// default, not a distinct "mode" this crate tracks any other state + /// for - toggling it live via `srd set phone_mode <bool>` only + /// changes how the *next* new window opens, same as any other + /// default-policy config value (`general.animations`, `general. + /// shadows`) already behaves, not a live re-layout of every window + /// already open. Also exposed read-only via `srd settings` so a shell + /// panel (AGS, concretely) can adapt its own chrome to the same + /// signal without needing a second, separate way to ask "is this a + /// phone-shaped session" - the actual "optional phone mode for AGS" + /// half of this ask is real work in *that* project, not this one; + /// this is the one thing srdwm itself needed to add so AGS has + /// something real to read. + pub phone_mode: bool, /// Whether srdwm draws real desktop icons (Home/Computer/Trash plus one /// per real `~/Desktop` entry) on the primary output's wallpaper -- /// read from `general.desktop_icons`. Unlike `gpu_enabled`, this @@ -213,6 +238,44 @@ pub struct WindowManager { /// feature with no hardware-support question to hedge against, not an /// experimental backend path that needs an opt-in safety net. pub desktop_icons_enabled: bool, + /// Whether desktop icons are mirrored onto every enabled monitor's own + /// corner, or only drawn on the primary monitor - read from `general. + /// desktop_icons_all_monitors`. Defaults to `true`, matching real macOS + /// convention (each display gets its own Desktop icons view) rather + /// than the older Windows-style "icons live on monitor 1 only" - a + /// directly reported gap ("in other monitor it's not showing the + /// desktop icons"), not a hardware question to hedge on like `gpu_ + /// enabled`. The same underlying icon set/cells are shared across every + /// mirror: dragging a copy on one monitor moves the one real icon, + /// which then shows in its new cell on every monitor it's mirrored to. + pub desktop_icons_all_monitors: bool, + /// Static minimum space reserved on each edge of every monitor, + /// logical pixels, read from `general.reserve_top`/`_bottom`/`_left`/ + /// `_right` - `0` (no static reservation) by default. Exists for the + /// gap between "the compositor starts rendering/placing things" and + /// "the bar/dock has actually connected and called `set_exclusive_ + /// zone`": a layer-shell client's own reserved strip only exists once + /// that client has mapped a real surface, which is reliably *after* + /// this compositor's own first render pass and first-window placement + /// decisions (autostart spawns the compositor's own children, which + /// then have to connect, negotiate, and commit before their zone is + /// real). Desktop icons already re-derive their own origin every frame + /// so they self-correct once the real zone lands (see `ensure_desktop_ + /// icons`'s own doc comment) - but a *window* placed in that gap gets + /// a one-time placement decision, not a continuously-corrected one, so + /// it can end up spawned under where the bar will render, with nothing + /// to nudge it out afterward. Set this to the bar/dock's own known + /// height/width (whatever `~/.config/ags` or another panel actually + /// reserves) so every usable-area computation (`Platform::monitors()`) + /// already accounts for it from the very first call, before any real + /// client has connected at all. Takes the *larger* of this and + /// whatever real exclusive zone currently exists per edge, never the + /// smaller - so a real, larger bar still wins once it registers, and + /// this is a floor, not a competing claim. + pub reserve_top: u32, + pub reserve_bottom: u32, + pub reserve_left: u32, + pub reserve_right: u32, /// External program desktop icons open into, read from `general. /// file_manager`. Empty (the default) means "shell out to `xdg-open /// <path>`" - the de-facto standard dispatcher to whatever the user's @@ -265,23 +328,27 @@ pub struct WindowManager { drag: Option<DragState>, resize: Option<ResizeState>, rules: Vec<WindowRule>, - /// Last floating size a user interactively resized each `app_id` to, - /// applied to that app's *next* new window instead of the fixed - /// 800x600 every backend otherwise hardcodes - see `end_resize` (where - /// this is recorded) and `add_window` (where it's read). Keyed by - /// `app_id` alone, not per-window: the ask is "my terminal should open - /// at the size I last used a terminal at", not per-window-instance - /// memory. Only an interactive drag-resize (`end_resize`) updates this - /// - not a maximize/fullscreen toggle (that's a separate, temporary - /// state with its own `restore_geometry`, not a new "size I want to - /// keep using") and not a drag-to-edge snap (a deliberate one-off - /// snap to a half/quarter of the screen isn't "the size I'll want my - /// next terminal to open at" either). Session-lifetime only, not - /// persisted to disk - a real per-app-size-memory feature that - /// survives a restart would need a config-file-backed store, which is - /// meaningfully more machinery than "remember it while running" asks - /// for. - remembered_sizes: HashMap<String, (u32, u32)>, + /// Last floating position+size a user interactively moved/resized each + /// `app_id` to, applied to that app's *next* new window instead of the + /// fixed 800x600-near-centre every backend otherwise hardcodes - see + /// `end_resize`/`end_drag` (where this is recorded) and `add_window` + /// (where it's read). Keyed by `app_id` alone, not per-window: the ask + /// is "my terminal should open where/how big I last left one", not + /// per-window-instance memory. Only an interactive drag/resize updates + /// this - not a maximize/fullscreen toggle (that's a separate, + /// temporary state with its own `restore_geometry`, not a new + /// "position/size I want to keep using") and not a drag-to-edge snap (a + /// deliberate one-off snap to a half/quarter of the screen isn't "where + /// I'll want my next terminal to open" either). + /// + /// In-memory here (this struct has no file I/O of its own - see + /// `srdwm_core`'s own module doc comment on why core stays pure logic); + /// `crates/wayland/src/window_memory.rs` is what actually persists this + /// to `$XDG_STATE_HOME/srd/window-memory.json` and re-seeds it via + /// `set_remembered_geometry` at startup, the same load/save-at-the- + /// platform-layer split `monitor_layout.rs`/`desktop_icons_state.rs` + /// already use for their own per-feature state. + remembered_geometry: HashMap<String, (i32, i32, u32, u32)>, /// Windows a client-close was requested for, drained once per tick by /// `main.rs`'s event loop and forwarded to `Platform::close`. Needed /// because `WindowManager` is platform-agnostic and has no way to send @@ -342,6 +409,7 @@ impl WindowManager { focused: None, monitors: Vec::new(), output_position_requests: Vec::new(), + pin_input_requests: Vec::new(), output_enable_requests: Vec::new(), disabled_monitors: HashMap::new(), monitor_splits: HashMap::new(), @@ -398,7 +466,13 @@ impl WindowManager { resize_margin: RESIZE_MARGIN, rounded_corners_enabled: None, gpu_enabled: false, + phone_mode: false, desktop_icons_enabled: true, + desktop_icons_all_monitors: true, + reserve_top: 0, + reserve_bottom: 0, + reserve_left: 0, + reserve_right: 0, file_manager: String::new(), desktop_icon_single_click: false, terminal: String::new(), @@ -410,7 +484,7 @@ impl WindowManager { drag: None, resize: None, rules: Vec::new(), - remembered_sizes: HashMap::new(), + remembered_geometry: HashMap::new(), close_requests: Vec::new(), keyboard_layout: String::new(), keyboard_layout_cycle_requests: 0, @@ -438,6 +512,7 @@ mod capture; mod dragresize; mod focus; mod hittest; +mod input_pin; mod layout; mod lock; mod monitors; |