mod backend;
mod config;
#[cfg(target_os = "macos")]
mod macos_live_lockscreen;
mod scale;
mod wallpaper;
#[cfg(target_os = "macos")]
use std::path::PathBuf;
#[cfg(target_os = "linux")]
use anyhow::Context;
use backend::{Backend, PauseMode, RunOptions};
use clap::Parser;
#[cfg(target_os = "macos")]
use clap::Subcommand;
use scale::ScaleMode;
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
#[cfg_attr(target_os = "macos", command(subcommand_negates_reqs = true))]
struct Args {
#[cfg(target_os = "macos")]
#[command(subcommand)]
command: Option<Command>,
#[arg(required_unless_present = "rand", conflicts_with = "rand")]
path: Option<String>,
#[arg(long, conflicts_with = "path")]
rand: bool,
#[arg(long, value_enum, default_value_t = ScaleMode::Fill)]
scale: ScaleMode,
#[cfg(target_os = "linux")]
#[arg(long, value_enum, default_value_t = backend::wayland::LayerMode::Background)]
layer: backend::wayland::LayerMode,
#[cfg(target_os = "linux")]
#[arg(long, value_name = "PATH")]
shader: Option<String>,
#[arg(long, conflicts_with = "pause_below")]
pause_on_battery: bool,
#[arg(long, value_name = "PERCENT", value_parser = clap::value_parser!(u8).range(1..=100))]
pause_below: Option<u8>,
}
#[cfg(target_os = "macos")]
#[derive(Subcommand, Debug)]
enum Command {
InstallLiveLockscreen {
video: PathBuf,
#[arg(long)]
name: Option<String>,
#[arg(long)]
remove: bool,
},
}
fn main() -> anyhow::Result<()> {
env_logger::init();
let args = Args::parse();
#[cfg(target_os = "macos")]
if let Some(cmd) = args.command {
match cmd {
Command::InstallLiveLockscreen {
video,
name,
remove,
} => return macos_live_lockscreen::install::run(video, name, remove),
}
}
let config = config::load()?;
let path = if args.rand {
wallpaper::pick_random(&config.search_paths)
.ok_or_else(|| anyhow::anyhow!("no wallpapers found in configured search paths"))?
.to_string_lossy()
.into_owned()
} else {
args.path
.expect("clap ensures path is set when --rand is not used")
};
if let Ok(home) = std::env::var("HOME") {
let cache_dir = std::path::Path::new(&home).join(".cache/phonto");
if std::fs::create_dir_all(&cache_dir).is_ok() {
let _ = std::fs::write(cache_dir.join("current"), &path);
}
}
let pause = match (args.pause_on_battery, args.pause_below) {
(true, _) => PauseMode::OnBattery,
(false, Some(pct)) => PauseMode::BelowPercent(pct),
(false, None) => PauseMode::Never,
};
let options = RunOptions {
pause,
scale: args.scale,
};
#[cfg(target_os = "linux")]
{
let shader = args
.shader
.as_deref()
.map(|p| {
std::fs::read_to_string(p)
.with_context(|| format!("failed to read shader file: {p}"))
})
.transpose()?;
backend::wayland::WaylandBackend::new(args.layer, shader)?.run(path, options)
}
#[cfg(target_os = "macos")]
return backend::macos::MacosBackend::new()?.run(path, options);
}