Skip to content

wallpaper theme and bar workflow

Cody-will edited this page Sep 20, 2026 · 1 revision

Wallpaper, Theme, and Bar Workflow

Related Files:

  • src/wall/mod.rs
  • src/wall/backend.rs
  • src/theme.rs
  • src/bar.rs
  • src/notifications.rs
  • src/state.rs
  • src/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:

Wallpaper, Theme, and Bar Workflow

Introduction

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


High-Level Workflow Overview

CLI Entry and Overall Flow

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)"]
Loading

Sources: src/main.rs:1-60, src/cli.rs:1-39

Core Components Involved

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()"]
Loading

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


Configuration and Paths

Config Structure

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:

  1. Resolves config_file() (e.g., ~/.config/rice/config.toml).
  2. Writes default TOML if missing (from examples/config.toml).
  3. Reads and deserializes TOML into Config.
  4. Expands wallpaper_dir using expand_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

Key Config Fields (Relevant to Workflow)

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

Path Management

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()"]
Loading

Sources: src/config.rs:29-39, src/state.rs:23-51, src/notifications.rs:27-41, src/paths.rs:24-66


Wallpaper Selection and State Management

Sources: src/wall/mod.rs:1-82, src/state.rs:1-58, src/config.rs:13-27

State Model

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:

  1. Resolves state_file().
  2. If missing, returns State::default() (no wallpaper, no backend).
  3. Otherwise reads and deserializes JSON.
    Sources: src/state.rs:23-38

State::save:

  1. Resolves state_file() and parent directory.
  2. Ensures parent directory exists via ensure_dir.
  3. Serializes to pretty JSON and writes to file.
    Sources: src/state.rs:40-58, src/paths.rs:57-66

Listing and Sorting Wallpapers

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", the Vec<PathBuf> is sorted lexicographically.
  • An empty result yields an error via bail!.

Stepping Through Wallpapers (next / prev)

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 Config and State, lists images.

  • Determines current index by matching state.wallpaper against images; defaults to 0 if not found or missing.

  • Computes next index using modular arithmetic with rem_euclid to wrap forward or backward.

  • Chooses backend:

    • First tries state.backend.
    • Falls back to cfg.backend if state has none.
  • Applies wallpaper (backend::set), then applies theme (theme::apply).

  • Updates state.wallpaper and state.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"]
Loading

Sources: src/wall/mod.rs:35-61

Starting Session Wallpaper (wall start and session start)

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


Wallpaper Backend Integration

Sources: src/wall/backend.rs:1-26, src/cmd.rs:1-18

Backend Selection and Commands

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: runs hyprctl hyprpaper wallpaper "eDP-1, <img_path>".
  • "swww" backend: runs swww img <img_path>.
  • "none" backend: does nothing (no command executed).
  • Any other backend string yields an error.

Command Execution Layer

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(())"]
Loading

Sources: src/wall/backend.rs:1-18, src/cmd.rs:1-18


Theme Application and Bar Restart

Sources: src/theme.rs:1-46, src/bar.rs:1-28, src/config.rs:13-27

Theme Configuration

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 Application Flow

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.enable is false, returns early without theming or bar restart.

  • Expands ~ in the wallpaper path again via paths::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.
  • Invokes cmd::run on cfg.theme.command (e.g., "wallust").

  • If cfg.bar.restart_on_theme is true, calls bar::restart.

Bar Configuration and Restart Logic

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()"]
Loading

Sources: src/theme.rs:25-46, src/bar.rs:24-28


Notifications and Icon Handling

Sources: src/notifications.rs:1-61, src/paths.rs:24-42, src/main.rs:25-60, src/config.rs:13-27

Notifications Configuration

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

Icon Management

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

Notification Emission

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(()): loads Config and calls notifications::success(&cfg, "Wallpaper updated").
  • On Err(e): loads Config, calls notifications::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"]
Loading

Sources: src/main.rs:25-60, src/notifications.rs:27-61, src/paths.rs:36-42


End-to-End Sequence: rice wall next

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
Loading

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


Summary

The wallpaper, theme, and bar workflow in rice is built around three main ideas:

  1. 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 State to 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

  2. Backend-agnostic application with theming and bar restart: The system abstracts wallpaper backends (hyprpaper, swww, or none) via backend::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

  3. 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.

Clone this wiki locally