Master wanted two modes:
- Managed (existing wizard behaviour): auto-arrange grid via the
layout wizard.
- Free-arrange: launch windows without applying layout. Per-window
initial resolution. Apply-size button (not position). Daemon does
NOT keep overwriting resolution if operator manually resized.
Reference: KWin scripts apply-size / apply-position for the live
resize plumbing.
Implementation:
src/profile.rs:
* LayoutMode enum: Managed (default) | Free. Serde round-trips
as "managed" / "free".
* Layout.mode field added (default = Managed via LayoutMode derive).
* LayoutSlot split into initial_size (Option<(u32,u32)>),
pos (Option<(i32,i32)>), size_locked (bool). The legacy x/y/w/h
fields stay for Managed mode (cached geometry).
src/hypr.rs:
* move_resize_window split into resize_window(w, h) + move_window(x, y).
* move_resize_window kept as a thin wrapper so the original
layout-apply IPC path still works.
src/layout.rs:
* apply(slots, windows) split into apply_size + apply_pos; apply()
is now the thin wrapper that calls both.
* All 4 LayoutSlot literals (Stacked preset, Grid helper, main_strip,
capture_from) gained ..Default::default().
src/session.rs:
* New IPC verbs: resize-slot N, move-slot N, reset-slot N.
* resize_slot: applies initial_size (or cached w/h), sets
size_locked=true so the daemon does not undo future operator
resizes.
* move_slot: applies pos (or cached x/y).
* reset_slot_lock: clears size_locked on slot-N.
* All three bail with a clear message if the mode is not Free.
src/gui.rs:
* Mode toggle (Managed / Free) at the top of page_layout.
* page_layout_managed: existing wizard (presets, drag tiles,
Save and Apply).
* page_layout_free: per-window initial W/H picker, Apply-size,
Apply-position, Reset-size buttons. Per-slot lock indicator.
Uses DragValue::range (clamp_range is deprecated).
* App.pending_ipc: Vec<String> queue; the main loop will drain
it and fire the IPC verbs.
cargo test 103/103 (unchanged from Item 4 + 3 games tests); clippy clean.
Master quote: "i do not know why you have regex for the games? If you are launching the games you should be able to find out what you launched instead and then place it in the correct spot in the grid. programs spawn under a tree structure on linux so you should easily be able to figure out what you spawned."
Implementation:
- src/process.rs (new): pid_is_ancestor(ancestor, candidate) walks /proc/<pid>/status PPid chain upward. Bounded to 64 hops so a malformed /proc cannot spin us. Unit-tested with three cases: self, zero-pids, and the running test process against its own ppid.
- src/lib.rs: pub mod process registered.
- src/profile.rs:
* Profile.window_match field removed (was Option<String> class + Option<String> title regex).
* WindowMatch struct removed.
* HashSet import kept (still used elsewhere).
- src/session.rs:
* Session.spawned_pids: HashSet<u32> field added + initialised.
* refresh_slots: replaces regex filter with crate::process::pid_is_ancestor(root, c.pid) against every spawned root.
* profile-init log line dropped window_match.class / .title args.
* borderless path: removed class_re use; for now re-applies apply_vfx_window_rules (per-spawn windowrulev2 lands once the launcher wires the pid in Item 4).
* matches_regex helper removed (was only used by arm_auto_apply).
- src/layout.rs:
* select_windows now a placeholder: returns the first N visible clients in z-order. Real picker is in session::refresh_slots.
* Regex import dropped.
- src/gui.rs:
* WindowMatch import removed.
* Default profile literal: window_match field removed.
* Profile editor: the four class/title text-edit lines replaced with a comment explaining Item 3.
* arm_auto_apply: regex compile block + class_pat / title_pat / any_pattern bindings removed; the thread body now uses the process-tree match with a placeholder for the spawned-pids source until Item 4 wires it.
- src/team.rs + src/engine.rs: Profile literals with window_match: Default::default() removed.
- src/hypr.rs: untouched. apply_borderless_rules kept but no longer called by the daemon.
cargo test 100/100 (3 new pid_is_ancestor tests); clippy clean.