1use crate::animation::{Effect, EffectOverrides, apply_effect_overrides, effect_from_name};
2use crate::config::{ScalingMode, ThemeProvider};
3use clap::{Args, Parser, Subcommand};
4use std::path::PathBuf;
5
6#[derive(Args, Debug, Clone, Default)]
8pub struct EffectArgs {
9 #[arg(long, value_name = "NAME", value_parser = parse_effect_name)]
13 pub effect: Option<String>,
14
15 #[arg(long, value_name = "DURATION")]
17 pub duration: Option<String>,
18
19 #[arg(long, value_name = "PRESET|X,Y")]
22 pub origin: Option<String>,
23
24 #[arg(long, value_name = "DEG")]
26 pub angle: Option<f32>,
27
28 #[arg(long, value_name = "X,Y")]
30 pub direction: Option<String>,
31
32 #[arg(long, value_enum)]
34 pub easing: Option<crate::animation::Easing>,
35
36 #[arg(long, value_name = "VALUE")]
38 pub from: Option<f32>,
39
40 #[arg(long, value_name = "VALUE")]
42 pub to: Option<f32>,
43
44 #[arg(long, value_name = "HZ")]
46 pub frequency: Option<f32>,
47
48 #[arg(long, value_name = "VALUE")]
50 pub amplitude: Option<f32>,
51
52 #[arg(long, value_name = "VALUE")]
54 pub speed: Option<f32>,
55
56 #[arg(long, value_name = "VALUE")]
58 pub softness: Option<f32>,
59
60 #[arg(long, value_name = "VALUE")]
62 pub scale: Option<f32>,
63}
64
65impl EffectArgs {
66 pub fn to_effect(&self, fallback: Effect) -> Effect {
69 let mut effect = self
70 .effect
71 .as_deref()
72 .and_then(effect_from_name)
73 .unwrap_or(fallback);
74 apply_effect_overrides(&mut effect, &self.to_overrides());
75 effect
76 }
77
78 pub fn to_overrides(&self) -> EffectOverrides {
80 let origin = self.origin.as_deref().and_then(parse_origin);
81 EffectOverrides {
82 origin,
83 origin_preset: if origin.is_none() {
84 self.origin.clone()
85 } else {
86 None
87 },
88 direction: self.direction.as_deref().and_then(parse_vec2),
89 angle: self.angle,
90 easing: self.easing,
91 from: self.from,
92 to: self.to,
93 frequency: self.frequency,
94 amplitude: self.amplitude,
95 speed: self.speed,
96 softness: self.softness,
97 scale: self.scale,
98 }
99 }
100}
101
102fn parse_effect_name(s: &str) -> Result<String, String> {
104 if crate::animation::effect_names().contains(&s) {
105 Ok(s.to_string())
106 } else {
107 Err(format!(
108 "unknown effect '{}' — expected one of: {}",
109 s,
110 crate::animation::effect_names().join(", ")
111 ))
112 }
113}
114
115fn parse_origin(s: &str) -> Option<(f32, f32)> {
117 let mut parts = s.split(',');
118 let x = parts.next()?.trim().parse::<f32>().ok()?;
119 let y = parts.next()?.trim().parse::<f32>().ok()?;
120 Some((x.clamp(0.0, 1.0), y.clamp(0.0, 1.0)))
121}
122
123fn parse_vec2(s: &str) -> Option<[f32; 2]> {
125 let mut parts = s.split(',');
126 let x = parts.next()?.trim().parse::<f32>().ok()?;
127 let y = parts.next()?.trim().parse::<f32>().ok()?;
128 Some([x, y])
129}
130
131#[derive(Parser, Debug)]
133#[command(
134 name = "wallr",
135 about = "Wayland wallpaper engine with animation and theme pipelines",
136 version
137)]
138pub struct WallrCli {
139 #[command(subcommand)]
140 pub command: Commands,
141
142 #[arg(global = true, long)]
144 pub config: Option<PathBuf>,
145
146 #[arg(global = true, short = 'v', long, action = clap::ArgAction::Count)]
148 pub verbose: u8,
149
150 #[arg(global = true, short = 'q', long)]
152 pub quiet: bool,
153}
154
155#[derive(Subcommand, Debug)]
156pub enum Commands {
157 Img {
159 path: PathBuf,
161 #[arg(long)]
163 no_theme: bool,
164 #[arg(long, value_enum)]
166 theme: Option<ThemeProvider>,
167 #[arg(long)]
169 monitor: Option<String>,
170 #[arg(long)]
172 animation: Option<String>,
173 #[arg(long, value_enum)]
175 mode: Option<ScalingMode>,
176 #[command(flatten)]
178 effect_args: EffectArgs,
179 },
180 Set {
182 path: PathBuf,
184 #[arg(long)]
186 no_theme: bool,
187 #[arg(long, value_enum)]
189 theme: Option<ThemeProvider>,
190 #[arg(long)]
192 monitor: Option<String>,
193 #[arg(long)]
195 animation: Option<String>,
196 #[arg(long, value_enum)]
198 mode: Option<ScalingMode>,
199 #[command(flatten)]
201 effect_args: EffectArgs,
202 },
203 Doctor,
205 Validate {
207 path: PathBuf,
209 },
210 Config {
212 #[command(subcommand)]
213 subcommand: ConfigCommands,
214 },
215 Cache {
217 #[command(subcommand)]
218 subcommand: CacheCommands,
219 },
220 Reload,
222 Monitor {
224 #[command(subcommand)]
225 subcommand: MonitorCommands,
226 },
227
228 Daemon,
231 New {
233 name: String,
235 #[arg(long)]
237 shader: bool,
238 },
239 Watch {
241 dir: PathBuf,
243 },
244 Preview {
246 path: PathBuf,
248 #[arg(long)]
250 watch: bool,
251 #[arg(long, value_name = "PACKAGE|FILE")]
253 animation: Option<String>,
254 #[command(flatten)]
256 effect_args: EffectArgs,
257 },
258 Install {
260 package: String,
262 },
263 Publish,
265 Search {
267 query: String,
269 },
270 Ipc {
272 #[command(subcommand)]
273 subcommand: IpcCommands,
274 },
275}
276
277#[derive(Subcommand, Debug)]
278pub enum ConfigCommands {
279 Get { key: String },
281 Set { key: String, value: String },
283 Path,
285}
286
287#[derive(Subcommand, Debug)]
288pub enum CacheCommands {
289 Clear,
291 Info,
293}
294
295#[derive(Subcommand, Debug)]
296pub enum MonitorCommands {
297 List,
299 Current,
301}
302
303#[derive(Subcommand, Debug)]
304pub enum IpcCommands {
305 Pause,
307 Resume,
309 Reload,
311 Preview,
313 Stop,
315 Status,
317}