srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2025-01-17 23:01:00 +0200
committersrdusr <[email protected]>2025-01-17 23:01:00 +0200
commit6c1dfe191843ad56aaa0381e5645ad9e74c38b8b (patch)
tree427d72a55eba93b2f0da299342c12bd3c61df1d6 /crates
parent3d73ed0f057a555ded67ad58dbe02b36cd4d2d1f (diff)
downloadsrdwm-6c1dfe191843ad56aaa0381e5645ad9e74c38b8b.tar.gz
srdwm-6c1dfe191843ad56aaa0381e5645ad9e74c38b8b.zip
Accumulate config engine, build metadata, and doc updates
Remaining files from the backlog: Lua config engine additions (general/register/support/window), workspace Cargo.toml/ Cargo.lock churn from the new dependencies added elsewhere in this sweep, srdwm/main.rs wiring, and docs (DEFAULTS.md plus a new SESSION_HANDOFF.md written mid-session for continuity across a restart - see that file's own header for what it is and isn't).
Diffstat (limited to 'crates')
-rw-r--r--crates/config/src/engine/general.rs20
-rw-r--r--crates/config/src/engine/register.rs2
-rw-r--r--crates/config/src/engine/support.rs2
-rw-r--r--crates/config/src/engine/window.rs14
-rw-r--r--crates/srdwm/Cargo.toml2
-rw-r--r--crates/srdwm/src/main.rs84
6 files changed, 121 insertions, 3 deletions
diff --git a/crates/config/src/engine/general.rs b/crates/config/src/engine/general.rs
index 96ea4d3..98cfebe 100644
--- a/crates/config/src/engine/general.rs
+++ b/crates/config/src/engine/general.rs
@@ -128,7 +128,8 @@ impl Engine {
/// class_regex = "...", instance = "..." }, { floating = true,
/// workspace = 2, x = .., y = .., width = .., height = ..,
/// decorated = false, border_color = {r,g,b}, border_width = 2,
- /// maximized = true, opacity = 0.9 })`. At least one matcher field is
+ /// corner_radius = 10, maximized = true, opacity = 0.9 })`. At least
+ /// one matcher field is
/// required; unmatched rules apply nothing.
///
/// `title`/`class` are plain substring/exact match, cheap and cover
@@ -182,6 +183,7 @@ impl Engine {
decorated: actions.get("decorated")?,
border_color,
border_width: actions.get("border_width")?,
+ corner_radius: actions.get("corner_radius")?,
pinned: actions.get("pinned")?,
opacity: actions.get("opacity")?,
resize_margin: actions.get("resize_margin")?,
@@ -217,6 +219,22 @@ impl Engine {
})?)
}
+ /// Sets an environment variable in srdwm's own process - `std::process::
+ /// Command` inherits the parent's environment by default (`fn_spawn`
+ /// above never overrides that), so anything set here is visible to
+ /// every process `srd.spawn` starts from this point on, and to their own
+ /// children in turn. Hyprland's `env = NAME,VALUE` works the same way
+ /// (sets it in its own process before forking anything), which is the
+ /// mechanism a ported `env.conf` needs - there is no per-spawn
+ /// equivalent that would let one client see a variable no other client
+ /// does, so this is deliberately global and process-wide, not scoped.
+ pub(super) fn fn_setenv(&self) -> Result<mlua::Function<'_>> {
+ Ok(self.lua.create_function(move |_, (name, value): (String, String)| {
+ std::env::set_var(&name, &value);
+ Ok(())
+ })?)
+ }
+
pub(super) fn fn_quit(&self) -> Result<mlua::Function<'_>> {
let state = self.state.clone();
Ok(self.lua.create_function(move |_, ()| {
diff --git a/crates/config/src/engine/register.rs b/crates/config/src/engine/register.rs
index abe919b..0cf64bc 100644
--- a/crates/config/src/engine/register.rs
+++ b/crates/config/src/engine/register.rs
@@ -17,6 +17,7 @@ impl Engine {
srd.set("rule", self.fn_rule()?)?;
srd.set("load", self.fn_load()?)?;
srd.set("spawn", self.fn_spawn()?)?;
+ srd.set("setenv", self.fn_setenv()?)?;
srd.set("notify", self.fn_notify()?)?;
srd.set("quit", self.fn_quit()?)?;
srd.set("reload", self.fn_reload()?)?;
@@ -44,6 +45,7 @@ impl Engine {
window.set("set_decorations", self.fn_window_set_decorations()?)?;
window.set("set_border_color", self.fn_window_set_border_color()?)?;
window.set("set_border_width", self.fn_window_set_border_width()?)?;
+ window.set("set_corner_radius", self.fn_window_set_corner_radius()?)?;
window.set("set_opacity", self.fn_window_set_opacity()?)?;
window.set("set_resize_margin", self.fn_window_set_resize_margin()?)?;
window.set("set_floating", self.fn_window_set_floating()?)?;
diff --git a/crates/config/src/engine/support.rs b/crates/config/src/engine/support.rs
index 9346499..0f83885 100644
--- a/crates/config/src/engine/support.rs
+++ b/crates/config/src/engine/support.rs
@@ -82,6 +82,7 @@ pub(super) fn validate(s: &SharedState) -> Vec<String> {
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);
@@ -242,6 +243,7 @@ pub(super) fn default_config() -> HashMap<String, ConfigValue> {
set("theme.colors.success", String("#a3be8c".into()));
set("theme.decorations.border.width", Number(2.0));
+ 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()));
set("theme.decorations.border.focused_style", String("solid".into()));
diff --git a/crates/config/src/engine/window.rs b/crates/config/src/engine/window.rs
index a7e6bf3..61e7b89 100644
--- a/crates/config/src/engine/window.rs
+++ b/crates/config/src/engine/window.rs
@@ -139,6 +139,20 @@ impl Engine {
})?)
}
+ pub(super) fn fn_window_set_corner_radius(&self) -> Result<mlua::Function<'_>> {
+ let state = self.state.clone();
+ Ok(self.lua.create_function(move |_, radius: u32| {
+ let wm = state.borrow().wm.clone();
+ let mut wm = wm.borrow_mut();
+ if let Some(id) = wm.focused_id() {
+ if let Some(w) = wm.window_mut(id) {
+ w.corner_radius = radius;
+ }
+ }
+ Ok(())
+ })?)
+ }
+
pub(super) fn fn_window_set_opacity(&self) -> Result<mlua::Function<'_>> {
let state = self.state.clone();
Ok(self.lua.create_function(move |_, opacity: f32| {
diff --git a/crates/srdwm/Cargo.toml b/crates/srdwm/Cargo.toml
index 0d1ffaf..a8ecd34 100644
--- a/crates/srdwm/Cargo.toml
+++ b/crates/srdwm/Cargo.toml
@@ -14,7 +14,7 @@ srdwm-core.workspace = true
srdwm-platform.workspace = true
srdwm-config.workspace = true
log.workspace = true
-env_logger.workspace = true
+tracing-subscriber.workspace = true
[target.'cfg(unix)'.dependencies]
# SIGTERM/SIGINT handling only - see `main.rs`'s `install_signal_handlers`
diff --git a/crates/srdwm/src/main.rs b/crates/srdwm/src/main.rs
index 5c2bfea..7e25fec 100644
--- a/crates/srdwm/src/main.rs
+++ b/crates/srdwm/src/main.rs
@@ -175,9 +175,49 @@ fn apply_general_settings(engine: &Engine, wm: &Rc<RefCell<WindowManager>>) {
}
let border_width = engine.get_f64("theme.decorations.border.width", 2.0).max(0.0) as u32;
theme.default_border_width = border_width;
+ let corner_radius = engine.get_f64("theme.decorations.border.radius", 6.0).max(0.0) as u32;
+ theme.default_corner_radius = corner_radius;
if let Some(rgb) = srdwm_core::parse_hex_color(&engine.get_string("theme.decorations.border.active_color", "#88c0d0")) {
theme.default_border_color = rgb;
}
+ // "server" (srdwm draws the titlebar, Windows/macOS-style) or "client"
+ // (srdwm steps back for anything with no decoration opinion of its
+ // own, GNOME-style) - see `ThemeConfig::default_decorated`'s own doc
+ // comment. Anything other than exactly "client" is treated as
+ // "server", the existing default, rather than erroring on a typo.
+ theme.default_decorated = engine.get_string("theme.decorations.default_mode", "server") != "client";
+
+ // `theme.lock.*` - srdwm's own session-lock UI (`crates/wayland/src/
+ // native_lock.rs`), configured the same "start from ThemeConfig-style
+ // Nord defaults, override per key" way as everything above.
+ let mut lock = srdwm_core::LockConfig::default();
+ if let Some(rgb) = srdwm_core::parse_hex_color(&engine.get_string("theme.lock.box_bg", "#2e3440")) {
+ lock.box_bg = rgb;
+ }
+ if let Some(rgb) = srdwm_core::parse_hex_color(&engine.get_string("theme.lock.box_border", "#88c0d0")) {
+ lock.box_border = rgb;
+ }
+ if let Some(rgb) = srdwm_core::parse_hex_color(&engine.get_string("theme.lock.text_color", "#eceff4")) {
+ lock.text_color = rgb;
+ }
+ if let Some(rgb) = srdwm_core::parse_hex_color(&engine.get_string("theme.lock.error_color", "#bf616a")) {
+ lock.error_color = rgb;
+ }
+ lock.corner_radius = engine.get_f64("theme.lock.corner_radius", lock.corner_radius as f64).max(0.0) as u32;
+ lock.blur_radius = engine.get_f64("theme.lock.blur_radius", lock.blur_radius as f64).max(0.0) as u32;
+ lock.show_caps_lock = engine.get_bool("theme.lock.show_caps_lock", lock.show_caps_lock);
+ lock.show_failed_attempts = engine.get_bool("theme.lock.show_failed_attempts", lock.show_failed_attempts);
+ let fail_message = engine.get_string("theme.lock.fail_message", &lock.fail_message);
+ lock.fail_message = fail_message;
+ // A single character, not a string - silently keeping the default
+ // rather than erroring is deliberate here (same "malformed value falls
+ // back to default rather than erroring a second time" stance `parse_
+ // hex_color` callers already take above), since a multi-character
+ // "dot" would misalign the whole password row's width calculations,
+ // which assume one glyph per typed character.
+ if let Some(ch) = engine.get_string("theme.lock.dot_char", &lock.dot_char.to_string()).chars().next() {
+ lock.dot_char = ch;
+ }
let mut wm = wm.borrow_mut();
wm.tiling.gap_inner = gap;
@@ -190,6 +230,7 @@ fn apply_general_settings(engine: &Engine, wm: &Rc<RefCell<WindowManager>>) {
wm.focus_follows_mouse = focus_follows_mouse;
wm.auto_raise = auto_raise;
wm.theme = theme;
+ wm.lock = lock;
wm.auto_back_and_forth = engine.get_bool("workspace.auto_back_and_forth", false);
}
@@ -305,6 +346,18 @@ fn sync(wm: &Rc<RefCell<WindowManager>>, platform: &mut dyn Platform) {
}
}
+ // Same "core records the intent, this loop forwards it to the platform
+ // that can actually act on it" shape as `close_requests` above - a
+ // count rather than a single flag, so more than one `srd dispatch
+ // cycle_keyboard_layout` in one tick isn't silently collapsed into one
+ // real cycle.
+ for _ in 0..wm.borrow_mut().take_keyboard_layout_cycle_requests() {
+ match platform.cycle_keyboard_layout() {
+ Ok(name) => wm.borrow_mut().set_keyboard_layout(name),
+ Err(e) => log::warn!("cycle_keyboard_layout() failed: {e}"),
+ }
+ }
+
let ws = wm.borrow().current_workspace();
wm.borrow_mut().arrange_workspace(ws);
@@ -388,7 +441,25 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
};
- env_logger::init();
+ // `log::*` throughout this codebase and `tracing::*` inside smithay
+ // (its own buffer-import failures, e.g. "Failed to import surface" /
+ // "Unknown buffer format for", are emitted via `tracing::warn!`/
+ // `error!`, not `log::`) are two independent facades - plain
+ // `env_logger::init()` only ever installed a `log` backend, so every
+ // smithay-side diagnostic was silently dropped with no subscriber to
+ // receive it. `tracing_subscriber::fmt()` bridges `log::*` calls into
+ // `tracing` itself (its default `tracing-log` feature installs that
+ // bridge as part of `init()`) and then prints both, still filtered by
+ // the same `RUST_LOG` directive syntax `env_logger` used - a separate,
+ // explicit `tracing_log::LogTracer::init()` call here crashed srdwm on
+ // every single startup (`SetLoggerError` - `log::set_boxed_logger` can
+ // only ever succeed once process-wide, and `init()` below already
+ // claims it) instead of merely being redundant with it, since `init()`
+ // internally unwraps that install rather than tolerating it being taken
+ // already. Confirmed live: this is what put srdwm into a boot loop.
+ tracing_subscriber::fmt()
+ .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
+ .init();
log::info!("srdwm starting");
// Decided before the config loads (it needs no engine, just argv/the
@@ -443,6 +514,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
log::info!("detected {} monitor(s)", monitors.len());
wm.borrow_mut().set_monitors(monitors);
+ // Read once so `WindowManager::keyboard_layout` (surfaced over `srd`,
+ // for an AGS peer session's keyboard-layout badge) has a real value
+ // from the start rather than an empty string until the first real
+ // cycle. `Err` here just means this backend has no real XKB-backed
+ // seat to ask (X11, Windows, macOS) - not worth failing startup over,
+ // same as any other platform capability that's honestly unsupported.
+ match platform.keyboard_layout() {
+ Ok(name) => wm.borrow_mut().set_keyboard_layout(name),
+ Err(e) => log::debug!("keyboard_layout() unavailable on this backend: {e}"),
+ }
+
// The platform is fully connected now (for Wayland, `WAYLAND_DISPLAY`
// was just set by `srdwm_wayland::connect` - see `udev`/`winit`
// - and for X11, `DISPLAY` was already set by whatever started the X