Change behaviour of --update

- Bool option to overwrite config file
- Set scaling mode, too
This commit is contained in:
Candifloss 2025-12-20 15:07:28 +05:30
parent 4da3fe8358
commit 736e258d25
4 changed files with 82 additions and 85 deletions

View File

@ -7,51 +7,64 @@ Configuration is optional, and the app exits immediately after setting the wallp
## Features
* Simple CLI usage
* Optional config file
* Two image scaling modes: **Fill** and **Stretch**
* Minimal dependencies
* X11 only (Wayland not supported)
- Simple CLI usage
- Optional config file
- Two image scaling modes: **Fill** and **Stretch**
- Minimal dependencies
- X11 only (Wayland not supported)
---
## Usage
### Set wallpaper once (no config change)
`icing` can be used in two ways.
1. Specify a wallpaper explicitly as an argument
```sh
icing --set /path/to/image.png
```
Sets the wallpaper immediately but does **not** update the config file.
### Update config and set wallpaper
Sets the wallpaper immediately without modifying the config file.
```sh
icing --update /path/to/image.png
```
* Writes the image path to the config file
* Sets the wallpaper immediately
* Future runs of `icing` will reuse this image
### Use config only
2. Use the config file
```sh
icing
```
Uses the image path and settings from the config file.
Run without arguments to apply the wallpaper defined in the config file.
Fails if no config exists.
### Optional arguments
See below for config instructions.
### Additional options
#### `--mode`
Use the `--mode` option to specify the image scaling mode.
```sh
--mode fill|stretch
icing --set /path/to/image.png --mode fill
```
Overrides the default or configured scaling mode (see below).
#### `--update`
Use the `--update` flag to persist the new settings to the config file.
```sh
icing --set /path/to/image.png --update
icing --set /path/to/image.png --mode stretch --update
```
- Sets the wallpaper
- Writes the settings to the config file
- Future runs of `icing` will reuse these settings
## Configuration
Config file location:
@ -110,31 +123,31 @@ Optionally move it to your `$PATH` (for example `/usr/bin/`) if desired.
## Requirements
* X11 (Xorg)
* A window manager
* Rust (for building)
- X11 (Xorg)
- A window manager
- Rust (for building)
## Future plans
* Multi-monitor support via `--monitor <MONITOR>`
- Multi-monitor support via `--monitor <MONITOR>`
```sh
icing --monitor HDMI-1 --set image.png
icing --monitor HDMI-2 --set image2.jpg --mode stretch
```
* Probably not:
- Probably not:
* Additional scaling modes (unless they make sense)
* Wayland support
- Additional scaling modes (unless they make sense)
- Wayland support
## Notes
* `icing` does not run in the background
* No daemon, no config watcher
* Intended to be called explicitly (startup scripts, keybindings, etc.)
- `icing` does not run in the background
- No daemon, no config watcher
- Intended to be called explicitly (startup scripts, keybindings, etc.)
---

View File

@ -6,8 +6,8 @@ use std::path::PathBuf;
#[derive(Debug)]
pub struct Args {
pub set: Option<PathBuf>,
pub update: Option<PathBuf>,
pub mode: Option<String>, // parsed now, used later
pub update: bool,
pub mode: Option<String>,
}
impl Args {
@ -15,11 +15,12 @@ impl Args {
let mut args = Arguments::from_env();
let set = args.opt_value_from_str("--set")?;
let update = args.opt_value_from_str("--update")?;
let mode = args.opt_value_from_str("--mode")?;
let update = args.contains("--update");
if set.is_some() && update.is_some() {
bail!("--set and --update cannot be used together");
// --update is only meaningful if --set is provided
if update && set.is_none() {
bail!("--update requires --set");
}
// Fail if unknown arguments are present

View File

@ -24,18 +24,19 @@ impl Config {
Ok(config)
}
/// Update config with image specifeid by CLI arg
pub fn update_background_image(&self, new_path: &str) -> Result<()> {
/// Persist wallpaper settings to config.
pub fn write(background_image: &str, mode: Option<&str>) -> Result<()> {
let path = config_path()?;
let updated = toml::to_string(&Config {
background_image: new_path.to_string(),
mode: self.mode.clone(),
})?;
let config = Config {
background_image: background_image.to_string(),
mode: mode.map(str::to_string),
};
let serialized = toml::to_string(&config)?;
fs::create_dir_all(path.parent().expect("config path has parent"))?;
fs::write(&path, updated)
fs::write(&path, serialized)
.with_context(|| format!("Failed to write config file: {}", path.display()))?;
Ok(())

View File

@ -17,50 +17,22 @@ pub fn resolve_wallpaper(
// Avoids moving `config`, allows to:
// - read values multiple times
// - decide later whether we need to write a new config
let cfg_ref = config.as_ref();
let cfg = config.as_ref();
// Resolve wallpaper image path by precedence:
// 1. args (`--set`, `--update`): Use path from args.
// 2. config: Use path from config.
let path = match (&args.set, &args.update) {
let path = if let Some(path) = &args.set {
// Case: arg `--set`: One-shot wallpaper change. Not persistent.
(Some(path), None) => path.clone(),
// Case: arg `--update`. Persist wallpaper path to config (create if missing), then apply it.
(None, Some(path)) => {
// Use existing config if present.
// Otherwise, create a minimal temporary config only for writing.
// Avoids moving or cloning the original config, keeps ownership simple.
let cfg = match cfg_ref {
Some(cfg) => cfg,
None => &config::Config {
background_image: String::new(),
mode: None,
},
};
// Write to config.
cfg.update_background_image(
path.to_str()
// Unsupported path format.
.ok_or_else(|| anyhow::anyhow!("Non-UTF8 path"))?,
)?;
path.clone()
}
path.clone()
} else {
// Case: No args. Fallback to config file.
(None, None) => {
let cfg = cfg_ref.ok_or_else(|| {
// No CLI args and no valid config.
anyhow::anyhow!("No or invalid image path specified in config or args.")
})?;
let cfg = cfg.ok_or_else(|| {
// No CLI args and no valid config.
anyhow::anyhow!("No or invalid image settings specified in config or args.")
})?;
// Successfully loaded.
cfg.background_image.clone().into()
}
// Case: Both args `--set` & `--update`. This case is already rejected during argument parsing.
_ => unreachable!(),
cfg.background_image.clone().into()
};
// Resolve scaling mode by precedence:
@ -72,7 +44,7 @@ pub fn resolve_wallpaper(
mode_arg
.parse::<wallpaper::ScalingMode>()
.with_context(|| "Invalid wallpaper mode in CLI")?
} else if let Some(cfg) = cfg_ref {
} else if let Some(cfg) = cfg {
if let Some(mode_str) = &cfg.mode {
// Config-specified mode
mode_str
@ -83,9 +55,19 @@ pub fn resolve_wallpaper(
wallpaper::ScalingMode::Fill
}
} else {
// No CLI and no config default
// No CLI and no config -> default
wallpaper::ScalingMode::Fill
};
// Persist if requested
if args.update {
config::Config::write(
path.to_str()
// Unsupported path format.
.ok_or_else(|| anyhow::anyhow!("Non-UTF8 path"))?,
args.mode.as_deref().or(cfg.and_then(|c| c.mode.as_deref())),
)?;
}
Ok(WallpaperSettings { path, mode })
}