srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/config
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2025-02-15 14:56:00 +0200
committersrdusr <[email protected]>2025-02-15 14:56:00 +0200
commit0a4c4b5941fe982ccb3d3175e26d9d83f0025ffd (patch)
tree7672d0af277664f457c6c9462925c0005fe35dcf /crates/config
parent413daa7ba2ea0ebd1424c024fd0566423aaea3f8 (diff)
downloadsrdwm-0a4c4b5941fe982ccb3d3175e26d9d83f0025ffd.tar.gz
srdwm-0a4c4b5941fe982ccb3d3175e26d9d83f0025ffd.zip
Checkpoint: preserve all uncommitted rust-rewrite worktree work
Safety commit before reconciling this worktree with main, which has diverged with its own separate fixes today. Nothing here is reviewed or curated yet - this exists purely so none of this work can be lost to a git operation, disk issue, or worktree cleanup while that reconciliation happens.
Diffstat (limited to 'crates/config')
-rw-r--r--crates/config/src/engine/general.rs35
-rw-r--r--crates/config/src/engine/register.rs5
-rw-r--r--crates/config/src/engine/support.rs33
-rw-r--r--crates/config/src/engine/tests.rs52
4 files changed, 117 insertions, 8 deletions
diff --git a/crates/config/src/engine/general.rs b/crates/config/src/engine/general.rs
index 98cfebe..94b029b 100644
--- a/crates/config/src/engine/general.rs
+++ b/crates/config/src/engine/general.rs
@@ -194,6 +194,41 @@ impl Engine {
})?)
}
+ /// `srd.monitor.split(name, parts[, direction])` - divides connector
+ /// `name`'s real output into `parts` equal logical monitors for
+ /// placement/tiling purposes ("monitors inside monitors"), no new
+ /// `wl_output` involved - see `srdwm_core::monitor::MonitorSplit`'s
+ /// own doc comment for exactly what that does and doesn't give a
+ /// client. `direction` is `"columns"` (default, side-by-side) or
+ /// `"rows"` (stacked); any other value is treated as `"columns"`
+ /// rather than erroring, same "malformed value falls back to a
+ /// sensible default" stance other config setters already take.
+ /// `parts <= 1` clears an existing split for `name`.
+ pub(super) fn fn_monitor_split(&self) -> Result<mlua::Function<'_>> {
+ let state = self.state.clone();
+ Ok(self.lua.create_function(move |_, (name, parts, direction): (String, u32, Option<String>)| {
+ let rows = matches!(direction.as_deref(), Some("rows"));
+ state.borrow().wm.borrow_mut().set_monitor_split(name, parts, rows);
+ Ok(())
+ })?)
+ }
+
+ /// `srd.monitor.scale(name, factor)` - sets connector `name`'s
+ /// output scale, applied the next time a backend brings that head up
+ /// (startup, hotplug, or re-enable). A physically large monitor with
+ /// the same pixel count as a smaller one (a big low-DPI external
+ /// display next to a small high-DPI laptop panel, say) can run below
+ /// `1.0` to show more logical desktop space rather than just larger
+ /// text at the same resolution. `factor <= 0` clears an existing
+ /// override.
+ pub(super) fn fn_monitor_scale(&self) -> Result<mlua::Function<'_>> {
+ let state = self.state.clone();
+ Ok(self.lua.create_function(move |_, (name, factor): (String, f64)| {
+ state.borrow().wm.borrow_mut().set_monitor_scale(name, factor);
+ Ok(())
+ })?)
+ }
+
pub(super) fn fn_load(&self) -> Result<mlua::Function<'_>> {
let state = self.state.clone();
Ok(self.lua.create_function(move |lua, module: String| {
diff --git a/crates/config/src/engine/register.rs b/crates/config/src/engine/register.rs
index 0cf64bc..4ef6844 100644
--- a/crates/config/src/engine/register.rs
+++ b/crates/config/src/engine/register.rs
@@ -75,6 +75,11 @@ impl Engine {
workspace.set("move_window", self.fn_workspace_move_window()?)?;
srd.set("workspace", workspace)?;
+ let monitor = lua.create_table()?;
+ monitor.set("split", self.fn_monitor_split()?)?;
+ monitor.set("scale", self.fn_monitor_scale()?)?;
+ srd.set("monitor", monitor)?;
+
let theme = lua.create_table()?;
theme.set("set_colors", self.fn_theme_set("theme.colors")?)?;
theme.set("set_decorations", self.fn_theme_set("theme.decorations")?)?;
diff --git a/crates/config/src/engine/support.rs b/crates/config/src/engine/support.rs
index 0f83885..b67a688 100644
--- a/crates/config/src/engine/support.rs
+++ b/crates/config/src/engine/support.rs
@@ -80,13 +80,13 @@ pub(super) fn validate(s: &SharedState) -> Vec<String> {
check_range("layout.dynamic.gaps.outer", 0.0, 100.0);
check_range("layout.floating.gaps.inner", 0.0, 100.0);
check_range("layout.floating.gaps.outer", 0.0, 100.0);
- check_range("general.border_width", 0.0, 20.0);
check_range("theme.decorations.border.width", 0.0, 20.0);
check_range("theme.decorations.border.radius", 0.0, 100.0);
check_range("general.animation_duration", 0.0, 1000.0);
check_range("general.resize_margin", 1.0, 50.0);
check_range("performance.max_fps", 30.0, 240.0);
check_range("performance.window_cache_size", 10.0, 10000.0);
+ check_range("theme.decorations.border.inactive_dim", 0.0, 1.0);
let layouts: Vec<String> = s.wm.borrow().available_layouts().iter().map(|l| l.to_string()).collect();
for key in ["general.default_layout", "monitor.primary_layout", "monitor.secondary_layout"] {
@@ -110,6 +110,8 @@ pub(super) fn validate(s: &SharedState) -> Vec<String> {
"theme.decorations.border.inactive_color",
"theme.decorations.title_bar.background",
"theme.decorations.title_bar.foreground",
+ "theme.decorations.title_bar.foreground_focused",
+ "theme.decorations.title_bar.foreground_unfocused",
];
for key in color_keys {
if let Some(v) = s.values.get(key).and_then(ConfigValue::as_str) {
@@ -138,7 +140,6 @@ pub(super) fn default_config() -> HashMap<String, ConfigValue> {
set("general.default_layout", String("dynamic".into()));
set("general.smart_placement", Bool(true));
set("general.window_gap", Number(8.0));
- set("general.border_width", Number(2.0));
set("general.animations", Bool(true));
set("general.animation_duration", Number(200.0));
set("general.shadows", Bool(true));
@@ -163,12 +164,11 @@ pub(super) fn default_config() -> HashMap<String, ConfigValue> {
set("monitor.primary_layout", String("dynamic".into()));
set("monitor.secondary_layout", String("tiling".into()));
set("monitor.auto_detect", Bool(true));
- // Deliberately *not* seeded, unlike everything else here: srdwm has one
- // flat workspace list shared by every monitor (see WindowManager's
- // `current_workspace` doc comment), not Hyprland-style independent
- // per-monitor workspace sets - "this monitor's primary workspace" and
- // "this monitor's workspace count" describe a design that doesn't
- // exist. `workspace.count` is the one knob that actually does anything.
+ // "This monitor's workspace *count*" specifically is still deliberately
+ // not seeded/implemented - `workspace.count` is one flat number for
+ // the whole desktop, not per-monitor. Independent per-monitor
+ // workspace *sets* (which workspace each monitor is showing) is a
+ // different, now-real knob: see `workspace.per_monitor` below.
// The `window.*` namespace this codebase's own `docs/DEFAULTS.md`
// documented (focus_follows_mouse/mouse_follows_focus/auto_raise/
@@ -190,6 +190,13 @@ pub(super) fn default_config() -> HashMap<String, ConfigValue> {
// absence from this function, though that one differs by backend
// rather than being simply unbuilt.
set("workspace.auto_back_and_forth", Bool(false));
+ // `false`: srdwm's original single-shared-workspace design (switching
+ // workspace changes what's visible on every monitor at once) - `true`
+ // switches to Hyprland/niri-style independent per-monitor workspace
+ // sets, each monitor tracking and displaying its own current
+ // workspace. See `WindowManager::per_monitor_workspaces`'s own doc
+ // comment.
+ set("workspace.per_monitor", Bool(false));
set("performance.vsync", Bool(true));
set("performance.max_fps", Number(60.0));
@@ -246,6 +253,11 @@ pub(super) fn default_config() -> HashMap<String, ConfigValue> {
set("theme.decorations.border.radius", Number(6.0));
set("theme.decorations.border.active_color", String("#88c0d0".into()));
set("theme.decorations.border.inactive_color", String("#2e3440".into()));
+ // The actually-wired unfocused-border knob (`apply_general_settings`,
+ // `srdwm_core::ThemeConfig::border_inactive_dim`): a factor applied to
+ // `border.active_color`, not the unused absolute `inactive_color`
+ // above. `1.0` matches focused exactly; `0.0` fades to black.
+ set("theme.decorations.border.inactive_dim", Number(0.35));
set("theme.decorations.border.focused_style", String("solid".into()));
set("theme.decorations.border.unfocused_style", String("solid".into()));
set("theme.decorations.title_bar.height", Number(24.0));
@@ -253,6 +265,11 @@ pub(super) fn default_config() -> HashMap<String, ConfigValue> {
set("theme.decorations.title_bar.font", String("JetBrains Mono 10".into()));
set("theme.decorations.title_bar.background", String("#2e3440".into()));
set("theme.decorations.title_bar.foreground", String("#eceff4".into()));
+ // The actually-wired pair (`apply_general_settings`): titlebar text has
+ // always used two colours - brighter on the focused window, dimmer on
+ // every other one - never the single `foreground` key above.
+ set("theme.decorations.title_bar.foreground_focused", String("#88c0d0".into()));
+ set("theme.decorations.title_bar.foreground_unfocused", String("#4c566a".into()));
set("platform.backend", String("auto".into()));
set("platform.x11.use_ewmh", Bool(true));
diff --git a/crates/config/src/engine/tests.rs b/crates/config/src/engine/tests.rs
index 521dc5a..f170320 100644
--- a/crates/config/src/engine/tests.rs
+++ b/crates/config/src/engine/tests.rs
@@ -197,6 +197,58 @@
}
#[test]
+ fn srd_monitor_split_stores_a_split_request_by_connector_name() {
+ let dir = tempfile::tempdir().unwrap();
+ let wm = Rc::new(RefCell::new(WindowManager::new()));
+ let engine = Engine::new(wm.clone(), dir.path()).unwrap();
+ engine.lua.load(r#"srd.monitor.split("eDP-1", 2, "rows")"#).exec().unwrap();
+ let split = wm.borrow().monitor_split("eDP-1").unwrap();
+ assert_eq!(split.parts, 2);
+ assert!(split.rows);
+ }
+
+ #[test]
+ fn srd_monitor_split_direction_defaults_to_columns() {
+ let dir = tempfile::tempdir().unwrap();
+ let wm = Rc::new(RefCell::new(WindowManager::new()));
+ let engine = Engine::new(wm.clone(), dir.path()).unwrap();
+ engine.lua.load(r#"srd.monitor.split("HDMI-A-1", 3)"#).exec().unwrap();
+ let split = wm.borrow().monitor_split("HDMI-A-1").unwrap();
+ assert_eq!(split.parts, 3);
+ assert!(!split.rows);
+ }
+
+ #[test]
+ fn srd_monitor_scale_stores_a_factor_by_connector_name() {
+ let dir = tempfile::tempdir().unwrap();
+ let wm = Rc::new(RefCell::new(WindowManager::new()));
+ let engine = Engine::new(wm.clone(), dir.path()).unwrap();
+ engine.lua.load(r#"srd.monitor.scale("HDMI-A-1", 0.75)"#).exec().unwrap();
+ assert_eq!(wm.borrow().monitor_scale("HDMI-A-1"), Some(0.75));
+ }
+
+ #[test]
+ fn srd_monitor_scale_with_a_non_positive_factor_clears_it() {
+ let dir = tempfile::tempdir().unwrap();
+ let wm = Rc::new(RefCell::new(WindowManager::new()));
+ let engine = Engine::new(wm.clone(), dir.path()).unwrap();
+ engine.lua.load(r#"srd.monitor.scale("HDMI-A-1", 0.75)"#).exec().unwrap();
+ engine.lua.load(r#"srd.monitor.scale("HDMI-A-1", 0)"#).exec().unwrap();
+ assert_eq!(wm.borrow().monitor_scale("HDMI-A-1"), None);
+ }
+
+ #[test]
+ fn srd_monitor_split_with_one_part_clears_an_existing_split() {
+ let dir = tempfile::tempdir().unwrap();
+ let wm = Rc::new(RefCell::new(WindowManager::new()));
+ let engine = Engine::new(wm.clone(), dir.path()).unwrap();
+ engine.lua.load(r#"srd.monitor.split("eDP-1", 2)"#).exec().unwrap();
+ assert!(wm.borrow().monitor_split("eDP-1").is_some());
+ engine.lua.load(r#"srd.monitor.split("eDP-1", 1)"#).exec().unwrap();
+ assert!(wm.borrow().monitor_split("eDP-1").is_none());
+ }
+
+ #[test]
fn srd_window_scratchpad_hides_the_focused_window_and_show_brings_it_back() {
let dir = tempfile::tempdir().unwrap();
let wm = Rc::new(RefCell::new(WindowManager::new()));