1use crate::effect::{Effect, EffectOverrides, apply_effect_overrides, effect_from_name};
2use crate::types::{ScalingMode, ThemeProvider};
3use clap::{Args, Parser, Subcommand};
4use std::path::PathBuf;
5
6#[derive(Args, Debug, Clone, Default)]
8pub struct EffectArgs {
9 #[arg(short = 'e', long, value_name = "NAME", value_parser = parse_effect_name)]
12 pub effect: Option<String>,
13
14 #[arg(short = 'd', long, value_name = "TIME")]
16 pub duration: Option<String>,
17
18 #[arg(short = 'o', long, value_name = "PRESET|X,Y")]
20 pub origin: Option<String>,
21
22 #[arg(short = 'a', long, value_name = "DEG")]
24 pub angle: Option<f32>,
25
26 #[arg(long, value_name = "X,Y")]
28 pub direction: Option<String>,
29
30 #[arg(long, value_enum)]
32 pub easing: Option<crate::effect::Easing>,
33
34 #[arg(long, value_name = "VAL")]
36 pub from: Option<f32>,
37
38 #[arg(long, value_name = "VAL")]
40 pub to: Option<f32>,
41
42 #[arg(long, value_name = "HZ")]
44 pub frequency: Option<f32>,
45
46 #[arg(long, value_name = "VAL")]
48 pub amplitude: Option<f32>,
49
50 #[arg(long, value_name = "VAL")]
52 pub softness: Option<f32>,
53}
54
55impl EffectArgs {
56 pub fn to_effect(&self, fallback: Effect) -> Effect {
59 let mut effect = self
60 .effect
61 .as_deref()
62 .and_then(effect_from_name)
63 .unwrap_or(fallback);
64 apply_effect_overrides(&mut effect, &self.to_overrides());
65 effect
66 }
67
68 pub fn to_overrides(&self) -> EffectOverrides {
69 let origin = self.origin.as_deref().and_then(parse_origin);
70 EffectOverrides {
71 origin,
72 origin_preset: if origin.is_none() {
73 self.origin.clone()
74 } else {
75 None
76 },
77 direction: self.direction.as_deref().and_then(parse_vec2),
78 angle: self.angle,
79 easing: self.easing,
80 from: self.from,
81 to: self.to,
82 frequency: self.frequency,
83 amplitude: self.amplitude,
84 softness: self.softness,
85 }
86 }
87}
88
89fn parse_effect_name(s: &str) -> Result<String, String> {
93 if crate::effect::effect_from_name(s).is_some() {
94 Ok(s.to_string())
95 } else {
96 Err(format!(
97 "unknown effect '{}' - expected one of: {}",
98 s,
99 crate::effect::effect_names().join(", ")
100 ))
101 }
102}
103
104fn parse_origin(s: &str) -> Option<(f32, f32)> {
106 let mut parts = s.split(',');
107 let x = parts.next()?.trim().parse::<f32>().ok()?;
108 let y = parts.next()?.trim().parse::<f32>().ok()?;
109 Some((x.clamp(0.0, 1.0), y.clamp(0.0, 1.0)))
110}
111
112fn parse_vec2(s: &str) -> Option<[f32; 2]> {
114 let mut parts = s.split(',');
115 let x = parts.next()?.trim().parse::<f32>().ok()?;
116 let y = parts.next()?.trim().parse::<f32>().ok()?;
117 Some([x, y])
118}
119
120#[derive(Parser, Debug)]
121#[command(
122 name = "wallr",
123 about = "Wayland wallpaper engine with GPU animations and theme pipelines",
124 version
125)]
126pub struct WallrCli {
127 #[command(subcommand)]
128 pub command: Commands,
129
130 #[arg(global = true, short = 'c', long)]
132 pub config: Option<PathBuf>,
133
134 #[arg(global = true, short = 'v', long, action = clap::ArgAction::Count)]
136 pub verbose: u8,
137
138 #[arg(global = true, short = 'q', long)]
140 pub quiet: bool,
141}
142
143#[derive(Subcommand, Debug)]
144pub enum Commands {
145 #[command(alias = "img")]
147 Set {
148 path: PathBuf,
150
151 #[arg(short = 'm', long, value_name = "OUTPUT")]
153 monitor: Option<String>,
154
155 #[arg(long, value_enum)]
157 mode: Option<ScalingMode>,
158
159 #[arg(long)]
161 no_theme: bool,
162
163 #[arg(short = 't', long, value_enum)]
165 theme: Option<ThemeProvider>,
166
167 #[command(flatten)]
169 effect_args: EffectArgs,
170 },
171
172 Doctor,
174
175 Config {
177 #[command(subcommand)]
178 subcommand: ConfigCommands,
179 },
180
181 Reload,
183
184 Monitor {
186 #[command(subcommand)]
187 subcommand: MonitorCommands,
188 },
189
190 Daemon {
192 #[arg(long)]
194 max_fps: Option<u32>,
195 },
196
197 Ipc {
199 #[command(subcommand)]
200 subcommand: IpcCommands,
201 },
202
203 Quit,
205}
206
207#[derive(Subcommand, Debug)]
208pub enum ConfigCommands {
209 Get { key: String },
211 Set { key: String, value: String },
213 Path,
215}
216
217#[derive(Subcommand, Debug)]
218pub enum MonitorCommands {
219 List,
221 Current,
223}
224
225#[derive(Subcommand, Debug)]
226pub enum IpcCommands {
227 Pause {
229 #[arg(short = 'm', long)]
231 monitor: Option<String>,
232 },
233 Resume {
235 #[arg(short = 'm', long)]
237 monitor: Option<String>,
238 },
239 Reload,
241 Preview,
243 Stop,
245 Status,
247 Info {
249 #[arg(short = 'm', long)]
251 monitor: Option<String>,
252 },
253 Seek {
255 timestamp: String,
257 #[arg(short = 'm', long)]
259 monitor: Option<String>,
260 },
261 Blank {
263 #[arg(short = 'm', long)]
265 monitor: Option<String>,
266 #[command(flatten)]
267 effect_args: EffectArgs,
268 },
269 Restore {
271 #[arg(short = 'm', long)]
273 monitor: Option<String>,
274 #[command(flatten)]
275 effect_args: EffectArgs,
276 },
277}