iwaku config changes, load changes, hypr support only IF passed the same as zshell
This commit is contained in:
@@ -9,10 +9,3 @@ target/
|
|||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||||
*.pdb
|
*.pdb
|
||||||
|
|
||||||
# RustRover
|
|
||||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
||||||
#.idea/
|
|
||||||
|
|||||||
Generated
+11
-11
@@ -405,6 +405,17 @@ version = "1.0.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iwaku"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"image",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tiny-skia",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
version = "0.1.34"
|
version = "0.1.34"
|
||||||
@@ -790,17 +801,6 @@ version = "0.8.53"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
|
checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rs-pictures"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"image",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"tiny-skia",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.22"
|
version = "1.0.22"
|
||||||
|
|||||||
+7
-19
@@ -1,39 +1,27 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rs-pictures"
|
name = "iwaku"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "rs-pictures"
|
name = "iwaku"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Image loading and encoding
|
image = { version = "0.25", features = ["png"] }
|
||||||
image = { version = "0.25", features = ["png", "jpeg"] }
|
|
||||||
|
|
||||||
# 2D rendering for effects (rounded corners, drop shadow)
|
|
||||||
tiny-skia = "0.11"
|
tiny-skia = "0.11"
|
||||||
|
|
||||||
# Config serialization
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
|
||||||
# Error handling
|
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.149"
|
||||||
|
|
||||||
# ── Build profiles ────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
lto = "thin" # link-time optimisation across crates
|
lto = "thin"
|
||||||
codegen-units = 1 # better inlining at the cost of compile time
|
codegen-units = 1
|
||||||
strip = true # strip debug symbols → smaller binary
|
strip = true
|
||||||
|
|
||||||
# Dev builds are slow for pixel-processing code. This gives opt-level 2
|
|
||||||
# to our own crate only while keeping dependencies at their default (opt=3
|
|
||||||
# they already compiled with), so incremental rebuilds stay fast.
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 0
|
opt-level = 0
|
||||||
|
|
||||||
[profile.dev.package."*"]
|
[profile.dev.package."*"]
|
||||||
opt-level = 3 # all deps at full optimisation even in dev mode
|
opt-level = 3
|
||||||
|
|||||||
+6
-25
@@ -5,11 +5,12 @@ use std::path::PathBuf;
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[serde(rename = "screenshot")]
|
#[serde(rename = "screenshot")]
|
||||||
pub effects: EffectsConfig,
|
pub screenshot: EffectsConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct EffectsConfig {
|
pub struct EffectsConfig {
|
||||||
|
pub mode: String,
|
||||||
pub rounded_corners: bool,
|
pub rounded_corners: bool,
|
||||||
pub corner_radius: f32,
|
pub corner_radius: f32,
|
||||||
pub drop_shadow: bool,
|
pub drop_shadow: bool,
|
||||||
@@ -19,28 +20,6 @@ pub struct EffectsConfig {
|
|||||||
pub shadow_color: [u8; 4],
|
pub shadow_color: [u8; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EffectsConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
rounded_corners: false,
|
|
||||||
corner_radius: 12.0,
|
|
||||||
drop_shadow: false,
|
|
||||||
shadow_blur_radius: 20.0,
|
|
||||||
shadow_offset_x: 5.0,
|
|
||||||
shadow_offset_y: 8.0,
|
|
||||||
shadow_color: [0, 0, 0, 160],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
effects: EffectsConfig::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn config_path() -> Option<PathBuf> {
|
pub fn config_path() -> Option<PathBuf> {
|
||||||
let home = std::env::var("HOME").ok()?;
|
let home = std::env::var("HOME").ok()?;
|
||||||
@@ -54,10 +33,12 @@ impl Config {
|
|||||||
|
|
||||||
pub fn load() -> Result<Self> {
|
pub fn load() -> Result<Self> {
|
||||||
let path = Self::config_path().context("Could not determine HOME directory")?;
|
let path = Self::config_path().context("Could not determine HOME directory")?;
|
||||||
|
Self::load_from(&path)
|
||||||
|
}
|
||||||
|
|
||||||
let raw = std::fs::read_to_string(&path)
|
pub fn load_from(path: &PathBuf) -> Result<Self> {
|
||||||
|
let raw = std::fs::read_to_string(path)
|
||||||
.with_context(|| format!("Failed to read config at {}", path.display()))?;
|
.with_context(|| format!("Failed to read config at {}", path.display()))?;
|
||||||
|
|
||||||
serde_json::from_str(&raw)
|
serde_json::from_str(&raw)
|
||||||
.with_context(|| format!("Failed to parse JSON config at {}", path.display()))
|
.with_context(|| format!("Failed to parse JSON config at {}", path.display()))
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-12
@@ -1,36 +1,70 @@
|
|||||||
mod config;
|
mod config;
|
||||||
mod effects;
|
mod effects;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use std::io::Write as _;
|
use std::io::Write as _;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let paths: Vec<String> = std::env::args().skip(1).collect();
|
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||||
|
|
||||||
if paths.is_empty() {
|
let mut image_path: Option<String> = None;
|
||||||
eprintln!("Usage: rs-pictures <image> [image2 ...]");
|
let mut hypr_path: Option<String> = None;
|
||||||
eprintln!("No image paths provided.");
|
|
||||||
std::process::exit(1);
|
let mut i = 0;
|
||||||
|
while i < args.len() {
|
||||||
|
match args[i].as_str() {
|
||||||
|
"-image" => {
|
||||||
|
i += 1;
|
||||||
|
image_path = Some(
|
||||||
|
args.get(i)
|
||||||
|
.cloned()
|
||||||
|
.context("Expected a path after -image")?,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
"-hypr" => {
|
||||||
|
i += 1;
|
||||||
|
hypr_path = Some(
|
||||||
|
args.get(i)
|
||||||
|
.cloned()
|
||||||
|
.context("Expected a path after -hypr")?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
unknown => bail!("Unknown argument: {unknown}"),
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let image_path =
|
||||||
|
image_path.context("Usage: iwaku -image <path> [-hypr <config_path>]\nMissing -image")?;
|
||||||
|
|
||||||
let config = config::Config::load().context("Failed to load config")?;
|
let config = config::Config::load().context("Failed to load config")?;
|
||||||
|
|
||||||
for path in &paths {
|
let effects = if config.screenshot.mode == "auto" {
|
||||||
if let Err(e) = process_image(path, &config) {
|
let hypr_config_path = hypr_path
|
||||||
eprintln!("Error processing '{}': {e:#}", path);
|
.map(PathBuf::from)
|
||||||
}
|
.context("Mode is 'auto' but -hypr <config_path> was not provided")?;
|
||||||
|
let hypr_config =
|
||||||
|
config::Config::load_from(&hypr_config_path).context("Failed to load hypr config")?;
|
||||||
|
hypr_config.screenshot
|
||||||
|
} else {
|
||||||
|
config.screenshot
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = process_image(&image_path, &effects) {
|
||||||
|
eprintln!("Error processing '{}': {e:#}", image_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_image(path: &str, config: &config::Config) -> Result<()> {
|
fn process_image(path: &str, effects: &config::EffectsConfig) -> Result<()> {
|
||||||
let img = image::open(path)
|
let img = image::open(path)
|
||||||
.with_context(|| format!("Failed to open image '{path}'"))?
|
.with_context(|| format!("Failed to open image '{path}'"))?
|
||||||
.into_rgba8();
|
.into_rgba8();
|
||||||
|
|
||||||
let processed = effects::apply_effects(img, &config.effects);
|
let processed = effects::apply_effects(img, effects);
|
||||||
|
|
||||||
let mut png_bytes: Vec<u8> = Vec::new();
|
let mut png_bytes: Vec<u8> = Vec::new();
|
||||||
image::DynamicImage::ImageRgba8(processed)
|
image::DynamicImage::ImageRgba8(processed)
|
||||||
|
|||||||
Reference in New Issue
Block a user