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.
Fails if no config exists.
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 })
}