Repository navigation
wallpaper theme and bar workflow
Related Files:
src/wall/mod.rssrc/wall/backend.rssrc/theme.rssrc/bar.rssrc/notifications.rssrc/state.rssrc/paths.rs
Related Pages:
- Installation, Configuration, and Usage
- Architecture and Data Flow
- Session Management and Hyprland Integration
Relevant source files
The following files were used as context for generating this wiki page:
The wallpaper, theme, and bar workflow in rice coordinates wallpaper changes with color theming and bar restarts, while persisting state and emitting desktop notifications. The workflow is driven by CLI subcommands and relies on configuration, filesystem paths, and external commands (hyprpaper, swww, wallust, waybar, pkill, hyprctl).
Sources: src/main.rs:1-24, src/cli.rs:1-39, src/wall/mod.rs:1-82, src/theme.rs:1-46, src/bar.rs:1-28, src/config.rs:1-40
At a high level, rice wall ... operations select a wallpaper from a configured directory, apply it through a backend, run a theme command (optionally restarting the bar), update persistent state, and finally show a success or error notification.
Sources: src/wall/mod.rs:1-82, src/wall/backend.rs:1-26, src/theme.rs:27-46, src/state.rs:1-58, src/notifications.rs:1-61, src/main.rs:25-60
The CLI entry point uses clap to parse subcommands: wall, bar, and session. Wallpaper-related actions are: next, prev, and start. The main function dispatches to the appropriate module, then wraps the result with success/error notifications.
Sources: src/cli.rs:1-39, src/main.rs:1-60
graph TD
Main["main()"] --> ParseCli["Cli::parse()"]
ParseCli["Cli::parse()"] --> MatchCmd["match Cli.command"]
MatchCmd["match Cli.command"] --> WallNext["WallAction::Next → wall::next()"]
MatchCmd["match Cli.command"] --> WallPrev["WallAction::Prev → wall::prev()"]
MatchCmd["match Cli.command"] --> WallStart["WallAction::Start → wall::start()"]
MatchCmd["match Cli.command"] --> BarRestart["BarAction::Restart → bar::restart()"]
MatchCmd["match Cli.command"] --> SessionStart["SessionAction::Start → session::start()"]
WallNext["WallAction::Next → wall::next()"] --> ResultMatch["match result"]
WallPrev["WallAction::Prev → wall::prev()"] --> ResultMatch["match result"]
WallStart["WallAction::Start → wall::start()"] --> ResultMatch["match result"]
BarRestart["BarAction::Restart → bar::restart()"] --> ResultMatch["match result"]
SessionStart["SessionAction::Start → session::start()"] --> ResultMatch["match result"]
ResultMatch["match result"] --> OnOk["Ok(()) → notifications::success()"]
ResultMatch["match result"] --> OnErr["Err(e) → notifications::error(), exit(1)"]
Sources: src/main.rs:1-60, src/cli.rs:1-39
| Component | Type | Responsibility |
|---|---|---|
wall module |
module | Select, step through, and start wallpapers; call backend and theme; persist state. |
backend |
module | Apply the selected wallpaper via hyprpaper, swww, or none. |
Theme |
struct | Theme configuration and logic to invoke theme command and restart bar. |
Bar |
struct | Bar configuration and logic to restart the bar process. |
State |
struct | Persist current wallpaper path and backend. |
Config |
struct | Load configuration including wallpaper, theme, bar, session, notifications. |
Notifications |
struct | Emit desktop notifications on success/error, with icon management. |
paths module |
module | Resolve and ensure config/cache/icon paths, expand ~. |
cmd module |
module | Execute external binaries with error handling. |
Sources: src/wall/mod.rs:1-82, src/wall/backend.rs:1-26, src/theme.rs:1-46, src/bar.rs:1-28, src/state.rs:1-58, src/config.rs:1-40, src/notifications.rs:1-61, src/paths.rs:1-66, src/cmd.rs:1-18
graph TD
Cli["CLI wall commands"] --> WallMod["wall module"]
WallMod["wall module"] --> Config["Config::load()"]
WallMod["wall module"] --> State["State::load()/save()"]
WallMod["wall module"] --> Backend["wall::backend::set()"]
WallMod["wall module"] --> ThemeApply["theme::apply()"]
ThemeApply["theme::apply()"] --> CmdRun["cmd::run()"]
ThemeApply["theme::apply()"] --> BarRestart["bar::restart()"]
BarRestart["bar::restart()"] --> CmdRun["cmd::run()"]
Config["Config::load()"] --> Paths["paths::*"]
State["State::load()/save()"] --> Paths["paths::*"]
Main["main()"] --> Notifications["notifications::success/error()"]
Notifications["notifications::success/error()"] --> Paths["paths::icon_file()"]
Sources: src/main.rs:1-60, src/wall/mod.rs:1-82, src/wall/backend.rs:1-26, src/theme.rs:1-46, src/bar.rs:1-28, src/state.rs:1-58, src/config.rs:1-40, src/notifications.rs:1-61, src/paths.rs:1-66, src/cmd.rs:1-18
The Config struct defines how wallpapers, backends, theming, bar, session, and notifications are configured. It is loaded from ~/.config/rice/config.toml, falling back to an embedded example on first run. wallpaper_dir is expanded for ~.
Sources: src/config.rs:1-40, src/paths.rs:24-42
#[derive(Deserialize, Debug)]
pub struct Config {
pub wallpaper_dir: PathBuf,
pub backend: String,
pub sort: String,
pub extensions: Vec<String>,
pub theme: Theme,
pub bar: Bar,
pub session: Session,
pub notifications: Notifications,
}Sources: src/config.rs:13-27
Config::load:
- Resolves
config_file()(e.g.,~/.config/rice/config.toml). - Writes default TOML if missing (from
examples/config.toml). - Reads and deserializes TOML into
Config. - Expands
wallpaper_dirusingexpand_tilde.
Sources: src/config.rs:1-40, src/paths.rs:24-42, src/paths.rs:49-66
The helper is_valid_img checks if an extension is allowed:
pub fn is_valid_img(&self, img_ext: &str) -> bool {
self.extensions.contains(&img_ext.to_string())
}Sources: src/config.rs:42-44
| Field | Type | Purpose |
|---|---|---|
wallpaper_dir |
PathBuf |
Directory where wallpapers are discovered. |
backend |
String |
Default backend ("hyprpaper", "swww", or "none"). |
sort |
String |
Sorting mode for images (e.g., "name"). |
extensions |
Vec<String> |
Allowed wallpaper file extensions. |
theme |
Theme |
Theming behavior for wallpaper changes. |
bar |
Bar |
Bar restart behavior on theme changes. |
notifications |
Notifications |
Whether to show success/error desktop notifications. |
Sources: src/config.rs:13-27, src/theme.rs:1-24, src/bar.rs:1-22, src/notifications.rs:11-25
The paths module centralizes XDG-like directories and file paths used by the workflow:
| Function | Return | Description |
|---|---|---|
project_dirs() |
ProjectDirs |
Base project dirs (config, cache) for rice. |
config_dir() |
PathBuf |
~/.config/rice. |
cache_dir() |
PathBuf |
~/.cache/rice. |
config_file() |
PathBuf |
~/.config/rice/config.toml. |
icon_file() |
PathBuf |
~/.config/rice/icon.png. |
state_file() |
PathBuf |
~/.cache/rice/state.json. |
ensure_dir() |
() |
mkdir -p for a directory path. |
expand_tilde() |
PathBuf |
Expand leading ~ or ~/... to $HOME if available. |
Sources: src/paths.rs:1-66
graph TD
ConfigLoad["Config::load()"] --> CfgPath["paths::config_file()"]
ConfigLoad["Config::load()"] --> ExpandWall["paths::expand_tilde(wallpaper_dir)"]
StateLoad["State::load()/save()"] --> StatePath["paths::state_file()"]
EnsureIcon["notifications::ensure_icon()"] --> IconPath["paths::icon_file()"]
EnsureIcon["notifications::ensure_icon()"] --> EnsureDir["paths::ensure_dir()"]
Sources: src/config.rs:29-39, src/state.rs:23-51, src/notifications.rs:27-41, src/paths.rs:24-66
Sources: src/wall/mod.rs:1-82, src/state.rs:1-58, src/config.rs:13-27
State persists the current wallpaper path and backend in JSON under ~/.cache/rice/state.json. It is optional to allow first-run behavior.
#[derive(Serialize, Deserialize, Debug)]
#[serde(default)]
pub struct State {
pub wallpaper: Option<PathBuf>,
pub backend: Option<String>,
}Sources: src/state.rs:7-14
State::load:
- Resolves
state_file(). - If missing, returns
State::default()(no wallpaper, no backend). - Otherwise reads and deserializes JSON.
Sources: src/state.rs:23-38
State::save:
- Resolves
state_file()and parent directory. - Ensures parent directory exists via
ensure_dir. - Serializes to pretty JSON and writes to file.
Sources: src/state.rs:40-58, src/paths.rs:57-66
list_images discovers wallpapers in the configured directory and filters them by extension and file-ness. If no images are found, it errors.
fn list_images(cfg: &Config) -> Result<Vec<PathBuf>> {
let dir = &cfg.wallpaper_dir;
let mut images: Vec<PathBuf> = fs::read_dir(dir)?
.filter_map(|res| res.ok())
.map(|entry| entry.path())
.filter(|path| {
path.is_file() && path.extension().map_or(false, |ext| cfg.is_valid_img(&ext.to_string_lossy().to_lowercase()))
})
.collect();
if images.is_empty() {
bail!("no wallpapers in {}", dir.display());
}
if cfg.sort == "name" {
images.sort();
}
Ok(images)
}Sources: src/wall/mod.rs:9-33, src/config.rs:42-44
Key behaviors:
- Only paths where
is_file()is true are kept. - File extension is lowercased and checked against
Config.extensions. - If
cfg.sort == "name", theVec<PathBuf>is sorted lexicographically. - An empty result yields an error via
bail!.
The core stepping logic is implemented in step(delta: isize) and used by next and prev.
fn step(delta: isize) -> Result<()> {
let cfg = Config::load()?;
let mut state = State::load()?;
let images = list_images(&cfg)?;
let n = images.len() as isize;
let idx = state.wallpaper
.as_ref()
.and_then(|cur| images.iter().position(|p| p == cur))
.unwrap_or(0) as isize;
let next = (idx + delta).rem_euclid(n) as usize;
let backend = state.backend
.as_deref()
.unwrap_or(cfg.backend.as_str());
backend::set(&images[next], backend)?;
theme::apply(&images[next], &cfg)?;
state.wallpaper = Some(images[next].clone());
state.backend = Some(backend.to_string());
state.save()?;
println!("{}", images[next].display());
Ok(())
}Sources: src/wall/mod.rs:35-61
Key points:
-
Loads
ConfigandState, lists images. -
Determines current index by matching
state.wallpaperagainstimages; defaults to0if not found or missing. -
Computes
nextindex using modular arithmetic withrem_euclidto wrap forward or backward. -
Chooses backend:
- First tries
state.backend. - Falls back to
cfg.backendif state has none.
- First tries
-
Applies wallpaper (
backend::set), then applies theme (theme::apply). -
Updates
state.wallpaperandstate.backend, saves state, and prints the chosen path to stdout.
next and prev are thin wrappers:
pub fn next() -> Result<()> {
step(1)
}
pub fn prev() -> Result<()> {
step(-1)
}Sources: src/wall/mod.rs:63-69
There are unit tests for the wrapping logic:
#[test]
fn wrap_next_from_last() {
let n: isize = 3;
let idx: isize = 2;
assert_eq!((idx + 1).rem_euclid(n) as usize, 0);
}
#[test]
fn wrap_prev_from_first() {
let n: isize = 3;
let idx: isize = 0;
assert_eq!((idx + -1).rem_euclid(n) as usize, 2);
}Sources: src/wall/mod.rs:96-108
graph TD
Step["step(delta)"] --> LoadCfg["Config::load()"]
Step["step(delta)"] --> LoadState["State::load()"]
Step["step(delta)"] --> ListImgs["list_images(cfg)"]
ListImgs["list_images(cfg)"] --> Images["Vec<PathBuf>"]
Step["step(delta)"] --> FindIdx["find current index"]
FindIdx["find current index"] --> WrapIdx["(idx + delta).rem_euclid(n)"]
WrapIdx["(idx + delta).rem_euclid(n)"] --> SelectImg["images[next]"]
Step["step(delta)"] --> ChooseBackend["state.backend or cfg.backend"]
ChooseBackend["state.backend or cfg.backend"] --> BackendSet["backend::set()"]
BackendSet["backend::set()"] --> ThemeApply["theme::apply()"]
ThemeApply["theme::apply()"] --> UpdateState["update State + save"]
Sources: src/wall/mod.rs:35-61
wall::start sets the wallpaper based on existing state, or defaults to the first image if state does not match any file.
pub fn start() -> Result<()> {
let cfg = Config::load()?;
let mut state = State::load()?;
let images = list_images(&cfg)?;
let path = state.wallpaper
.as_ref()
.and_then(|cur| images.iter().find(|p| *p == cur))
.unwrap_or(&images[0])
.clone();
let backend = state.backend
.as_deref()
.unwrap_or(cfg.backend.as_str())
.to_string();
backend::set(&path, &backend)?;
theme::apply(&path, &cfg)?;
state.wallpaper = Some(path.clone());
state.backend = Some(backend);
state.save()?;
println!("{}", path.display());
Ok(())
}Sources: src/wall/mod.rs:71-94
session::start launches configured applications and then calls wall::start to restore wallpaper and theme:
pub fn start() -> Result<()> {
let cfg = Config::load()?;
for exec in cfg.session.exec {
std::process::Command::new(&exec)
.spawn()
.with_context(|| format!("failed to start {exec}"))?;
};
crate::wall::start()?;
Ok(())
}Sources: src/session.rs:17-31
Sources: src/wall/backend.rs:1-26, src/cmd.rs:1-18
backend::set chooses how to apply the wallpaper based on the backend string. It converts the Path to UTF‑8 (erroring if invalid), then dispatches to a specific command.
pub fn set(path: &Path, backend: &str) -> Result<()> {
let img_path = path.to_str().context("wallpaper path is not valid utf-8")?;
match backend {
"hyprpaper" => cmd::run("hyprctl", &["hyprpaper", "wallpaper", &format!("eDP-1, {img_path}")]),
"swww" => cmd::run("swww", &["img", img_path]),
"none" => Ok(()),
_ => bail!("unkown backend {backend}"),
}
}Sources: src/wall/backend.rs:1-18
Behavior summary:
-
"hyprpaper"backend: runshyprctl hyprpaper wallpaper "eDP-1, <img_path>". -
"swww"backend: runsswww img <img_path>. -
"none"backend: does nothing (no command executed). - Any other backend string yields an error.
All external invocations go through cmd::run, which reports failures if the command cannot be started or exits with a non‑success status.
pub fn run(bin: &str, args: &[&str]) -> Result<()> {
let status = Command::new(bin)
.args(args)
.status()
.with_context(|| format!("failed to start {bin}"))?;
if !status.success() {
bail!("{bin} exited with {status}");
}
Ok(())
}Sources: src/cmd.rs:1-18
graph TD
WallStep["wall::step/start"] --> BackendSet["backend::set()"]
BackendSet["backend::set()"] --> Hyprpaper["\"hyprpaper\" case"]
BackendSet["backend::set()"] --> Swww["\"swww\" case"]
BackendSet["backend::set()"] --> NoneCase["\"none\" case"]
BackendSet["backend::set()"] --> Unknown["default: bail!"]
Hyprpaper["\"hyprpaper\" case"] --> CmdRun["cmd::run(\"hyprctl\", ...)"]
Swww["\"swww\" case"] --> CmdRun["cmd::run(\"swww\", ...)"]
NoneCase["\"none\" case"] --> NoOp["Ok(())"]
Sources: src/wall/backend.rs:1-18, src/cmd.rs:1-18
Sources: src/theme.rs:1-46, src/bar.rs:1-28, src/config.rs:13-27
The Theme struct holds configuration for how theming should be applied after a wallpaper change.
#[derive(Deserialize, Debug)]
#[serde(default)]
pub struct Theme {
enable: bool,
command: String,
args: Option<Vec<String>>
}
impl Default for Theme {
fn default() -> Self {
Self {
enable: true,
command: "wallust".into(),
args: None,
}
}
}Sources: src/theme.rs:5-23
Relevant fields:
| Field | Type | Behavior |
|---|---|---|
enable |
bool |
If false, theming is skipped entirely. |
command |
String |
Binary to run for theming (default "wallust"). |
args |
Option<Vec<String>> |
Extra arguments appended after default run <image>. |
Sources: src/theme.rs:5-23
theme::apply is called from wall::step and wall::start after the backend sets the wallpaper.
pub fn apply(path: & Path, cfg: &Config) -> Result<()> {
if !cfg.theme.enable {
return Ok(());
}
let p = paths::expand_tilde(path);
let img = p
.to_str()
.context("wallpaper path is not valid utf-8")?;
let mut args: Vec<String> = vec!["run".into(), img.to_string()];
if let Some(extra) = &cfg.theme.args {
args.extend(extra.iter().cloned());
}
let args_ref: Vec<&str> = args.iter().map(String::as_str).collect();
cmd::run(&cfg.theme.command, &args_ref)?;
if cfg.bar.restart_on_theme {
crate::bar::restart(cfg)?;
}
Ok(())
}Sources: src/theme.rs:25-46
Key points:
-
If
cfg.theme.enableisfalse, returns early without theming or bar restart. -
Expands
~in the wallpaper path again viapaths::expand_tilde. -
Fails if the path cannot be converted to UTF‑8.
-
Constructs arguments as:
- Prepend
"run"and<image path>. - Append any extra theme arguments from
cfg.theme.args.
- Prepend
-
Invokes
cmd::runoncfg.theme.command(e.g.,"wallust"). -
If
cfg.bar.restart_on_themeistrue, callsbar::restart.
Bar config:
#[derive(Deserialize, Debug)]
#[serde(default)]
pub struct Bar {
pub restart_on_theme: bool,
pub command: String,
}
impl Default for Bar {
fn default() -> Self {
Self {
restart_on_theme: true,
command: "waybar".into(),
}
}
}Sources: src/bar.rs:5-22
bar::restart first kills existing instances and then spawns a new bar process:
pub fn restart(cfg: &Config) -> Result<()> {
cmd::run("pkill", &[&cfg.bar.command])?;
std::process::Command::new(&cfg.bar.command)
.spawn()
.with_context(|| format!("failed to start {}", cfg.bar.command))?;
Ok(())
} Sources: src/bar.rs:24-28
graph TD
WallSet["wallpaper set"] --> ThemeApply["theme::apply()"]
ThemeApply["theme::apply()"] --> CheckEnable["if cfg.theme.enable"]
CheckEnable["if cfg.theme.enable"] --> BuildArgs["build args: run + image + extra"]
BuildArgs["build args: run + image + extra"] --> ThemeCmd["cmd::run(cfg.theme.command, ...)"]
ThemeCmd["cmd::run(cfg.theme.command, ...)"] --> CheckBar["if cfg.bar.restart_on_theme"]
CheckBar["if cfg.bar.restart_on_theme"] --> BarRestart["bar::restart(cfg)"]
BarRestart["bar::restart(cfg)"] --> KillBar["cmd::run(\"pkill\", [cfg.bar.command])"]
BarRestart["bar::restart(cfg)"] --> SpawnBar["Command::new(cfg.bar.command).spawn()"]
Sources: src/theme.rs:25-46, src/bar.rs:24-28
Sources: src/notifications.rs:1-61, src/paths.rs:24-42, src/main.rs:25-60, src/config.rs:13-27
The Notifications struct controls when desktop notifications are shown.
#[derive(Serialize, Deserialize, Debug)]
#[serde(default)]
pub struct Notifications {
pub on_success: bool,
pub on_error: bool,
}
impl Default for Notifications {
fn default() -> Self {
Self {
on_success: false,
on_error: true,
}
}
}Sources: src/notifications.rs:11-25
A PNG icon is embedded into the binary and written to ~/.config/rice/icon.png on first use.
const ICON: &[u8] = include_bytes!("../assets/rice-notify.png");Sources: src/notifications.rs:8
ensure_icon ensures the icon file exists:
fn ensure_icon() -> Result<PathBuf> {
let path = crate::paths::icon_file()?;
if !path.exists() {
if let Some(dir) = path.parent() {
crate::paths::ensure_dir(dir)?;
}
std::fs::write(&path, ICON)
.with_context(|| format!("failed to write {}", path.display()))?;
}
Ok(path)
}Sources: src/notifications.rs:27-41, src/paths.rs:36-42, src/paths.rs:57-62
show constructs and sends the notification using notify_rust, referencing the icon. Both success and error wrap show and check configuration flags.
fn show(body: &str, urgency: Urgency) {
let Ok(icon) = ensure_icon() else { return };
let Some(icon) = icon.to_str() else { return };
let _ = Notification::new()
.appname("rice")
.summary("rice")
.body(body)
.icon(icon)
.urgency(urgency)
.show();
}
pub fn success(cfg: &Config, body: &str) {
if cfg.notifications.on_success {
show(body, Urgency::Low);
}
}
pub fn error(cfg: &Config, body: &str) {
if cfg.notifications.on_error {
show(body, Urgency::Normal);
}
}Sources: src/notifications.rs:43-61
The main function uses these after executing commands:
- On
Ok(()): loadsConfigand callsnotifications::success(&cfg, "Wallpaper updated"). - On
Err(e): loadsConfig, callsnotifications::error(&cfg, &format!("Update failed: {e:#}")), prints error and exits with code 1.
Sources: src/main.rs:25-60
graph TD
CmdResult["Command result"] --> OnOk["Ok(())"]
CmdResult["Command result"] --> OnErr["Err(e)"]
OnOk["Ok(())"] --> LoadCfgOk["Config::load() (best-effort)"]
OnErr["Err(e)"] --> LoadCfgErr["Config::load() (best-effort)"]
LoadCfgOk["Config::load() (best-effort)"] --> SuccessFn["notifications::success(cfg, ...)"]
LoadCfgErr["Config::load() (best-effort)"] --> ErrorFn["notifications::error(cfg, ...)"]
SuccessFn["notifications::success(cfg, ...)"] --> ShowNotify["show(body, Urgency::Low)"]
ErrorFn["notifications::error(cfg, ...)"] --> ShowNotify["show(body, Urgency::Normal)"]
ShowNotify["show(body, Urgency::Low/Normal)"] --> EnsureIcon["ensure_icon()"]
EnsureIcon["ensure_icon()"] --> IconFile["paths::icon_file() + write if missing"]
Sources: src/main.rs:25-60, src/notifications.rs:27-61, src/paths.rs:36-42
This section shows a detailed sequence for a successful rice wall next invocation, focusing on wallpaper, theme, bar, and notifications.
sequenceDiagram
autonumber
participant U as User
participant M as main()
participant C as Config
participant S as State
participant W as wall::step
participant B as backend::set
participant T as theme::apply
participant BR as bar::restart
participant N as notifications
box System
participant P as paths
participant CMD as cmd::run
end
U->>+M: run "rice wall next"
M->>+W: wall::next() → step(1)
W->>+C: Config::load()
C-->>-W: Config
W->>+S: State::load()
S-->>-W: State
W->>+P: list_images() → cfg.wallpaper_dir
P-->>-W: Vec<PathBuf>
W->>W: compute current index + wrap with rem_euclid
W->>+B: backend::set(image, backend)
B->>+CMD: run backend command
CMD-->>-B: Ok
B-->>-W: Ok
W->>+T: theme::apply(image, cfg)
T->>+P: paths::expand_tilde(image)
P-->>-T: expanded Path
T->>+CMD: cmd::run(cfg.theme.command, args)
CMD-->>-T: Ok
T->>+BR: if cfg.bar.restart_on_theme → bar::restart(cfg)
BR->>+CMD: cmd::run("pkill", [cfg.bar.command])
CMD-->>-BR: Ok
BR->>CMD: spawn cfg.bar.command
CMD-->>BR: Ok (spawn)
BR-->>-T: Ok
T-->>-W: Ok
W->>+S: update and State::save()
S-->>-W: Ok
W-->>-M: Ok(())
M->>+C: Config::load() (best effort)
C-->>-M: Config or Err
M->>+N: notifications::success(cfg, "Wallpaper updated")
N-->>-M: (may no-op based on cfg)
M-->>-U: exit 0
Sources: src/main.rs:25-60, src/wall/mod.rs:35-61, src/wall/backend.rs:1-18, src/theme.rs:25-46, src/bar.rs:24-28, src/state.rs:23-58, src/paths.rs:49-66, src/notifications.rs:43-61, src/cmd.rs:1-18
The wallpaper, theme, and bar workflow in rice is built around three main ideas:
-
Deterministic wallpaper selection with persisted state: Wallpapers are discovered from a configured directory, filtered by extension, and optionally sorted by name; the current wallpaper and backend are stored in a JSON
Stateto enable consistent stepping and session restoration.
Sources: src/wall/mod.rs:9-33, src/wall/mod.rs:35-61, src/wall/mod.rs:71-94, src/state.rs:7-58 -
Backend-agnostic application with theming and bar restart: The system abstracts wallpaper backends (
hyprpaper,swww, ornone) viabackend::set, then applies a configurable theme command and optionally restarts the bar to reflect new colors.
Sources: src/wall/backend.rs:1-18, src/theme.rs:5-46, src/bar.rs:5-28, src/config.rs:13-27 -
User feedback and robustness: Every CLI action is wrapped with success/error handling, persistent state updates, and optional desktop notifications that use an embedded icon written to disk on demand, all driven by configuration.
Sources: src/main.rs:25-60, src/notifications.rs:8-61, src/paths.rs:36-42, src/config.rs:13-27
Together, these components provide a cohesive workflow where a single rice wall command updates the visual environment—wallpaper, theme, and bar—while preserving user state and giving immediate feedback on the operation’s outcome.