glass: blur strength as a config value

shell.conf blur (1 to 30, default 3; Hyprland's default is 8) sets how
much Hyprland frosts what is behind the glass, applied the moment
shell.conf is saved. Hyprland's blur strength is global (a layer rule can
only switch blur on), so the shell sets decoration.blur.size through
hyprctl eval, and again on every configreloaded>> event, since a config
reload puts Hyprland's own value back. leaving blur out keeps Hyprland's
This commit is contained in:
server2
2026-10-10 00:24:48 +02:00
parent ca884d0d31
commit 65c62734e7
3 changed files with 69 additions and 0 deletions
+3
View File
@@ -30,6 +30,7 @@ The Material You scheme type is Skwd-wall's setting (`matugen.schemeType`).
cover_colors = false # true: the open media card and equalizer take the song cover's colours as background
transition_ms = 400 # how long a colour change cross-fades; 0 switches instantly
glass = 1.0 # surface opacity, 0.05 to 1; below 1 the desktop shows through
blur = 3 # Hyprland's blur strength behind the glass, 1 to 30 (Hyprland default 8)
```
## Glass
@@ -48,6 +49,8 @@ amane_glass = hl.layer_rule({
The look of the surfaces (fill, rim, highlight, shadow) lives in `src/surface.rs`.
`blur` in `shell.conf` sets how much the glass frosts: low values leave the background nearly readable. Hyprland's blur strength is one global setting (a layer rule can only turn blur on), so the shell sets `decoration.blur.size` for everything Hyprland blurs, and sets it again after every Hyprland config reload. Leave `blur` out to keep Hyprland's own value.
## Layout
```
+5
View File
@@ -8,6 +8,11 @@ cover_colors = false
# how long a colour change cross-fades, in milliseconds; 0 switches instantly
transition_ms = 400
# how strongly Hyprland blurs what is behind the glass: 1 (barely, nearly readable) to 30;
# Hyprland's own default is 8. its blur strength is global, so this also changes the blur
# of any other translucent window. leave it out to keep Hyprland's value
blur = 3
# how opaque the shell's surfaces are, 0.05 to 1 (1 = solid). below 1 the desktop shows
# through; to frost it, Hyprland has to blur behind the shell (README: Glass)
glass = 0.4
+61
View File
@@ -1,5 +1,8 @@
use std::cell::Cell;
use std::io::{BufRead, BufReader};
use std::os::unix::net::UnixStream;
use std::path::PathBuf;
use std::process::Command;
use std::sync::{Mutex, PoisonError};
use std::time::Duration;
use std::{env, fs, thread};
@@ -97,6 +100,9 @@ impl Service for Theme {
thread::spawn(|| follow("shell.conf"));
// a hyprland config reload puts its own blur back: ask for ours again
thread::spawn(follow_hyprland);
follow("theme");
}
}
@@ -130,6 +136,59 @@ fn reload() {
*last = Some(read);
}
/*
* shell.conf `blur`: how strongly Hyprland blurs behind the glass. hyprland's blur
* strength is global (a layer rule can only switch blur on), so this sets
* decoration.blur.size for everything it blurs. none leaves hyprland's own value
*/
static BLUR: Mutex<Option<u32>> = Mutex::new(None);
fn set_blur(size: Option<u32>) {
let changed = {
let mut blur = BLUR.lock().unwrap_or_else(PoisonError::into_inner);
let changed = *blur != size;
*blur = size;
changed
};
if changed {
send_blur();
}
}
// on its own thread, so hyprctl never holds up the theme
fn send_blur() {
let Some(size) = *BLUR.lock().unwrap_or_else(PoisonError::into_inner) else {
return;
};
thread::spawn(move || {
let lua = format!("hl.config({{ [\"decoration.blur.size\"] = {size} }})");
let _ = Command::new("hyprctl").arg("eval").arg(lua).output();
});
}
// hyprland's event socket says configreloaded>> after every reload, which resets the blur
fn follow_hyprland() {
let (Some(runtime), Some(instance)) = (env::var_os("XDG_RUNTIME_DIR"), env::var_os("HYPRLAND_INSTANCE_SIGNATURE")) else {
return;
};
let Ok(events) = UnixStream::connect(PathBuf::from(runtime).join("hypr").join(instance).join(".socket2.sock")) else {
return;
};
for line in BufReader::new(events).lines().map_while(Result::ok) {
if line.starts_with("configreloaded") {
send_blur();
}
}
}
fn read(name: &str) -> String {
fs::read_to_string(config_file(name)).unwrap_or_default()
}
@@ -180,6 +239,8 @@ fn apply(theme: &str, config: &str, startup: bool) {
theme.cover_colors = setting("cover_colors") == Some("true");
theme.glass = setting("glass").and_then(|glass| glass.parse::<f32>().ok()).map_or(1.0, |glass| glass.clamp(0.05, 1.0));
set_blur(setting("blur").and_then(|size| size.parse::<u32>().ok()).map(|size| size.clamp(1, 30)));
// a change in the middle of a fade starts from the colours on screen now
let now = theme.colors.value();