Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions Configs/.local/lib/hyde/color.backend.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env bash
if ! source "$(which hyde-shell)"; then
echo "[$0] :: Error: hyde-shell not found."
echo "[$0] :: Is HyDE installed?"
exit 1
fi

# shellcheck disable=SC1091
source "${LIB_DIR}/hyde/shutils/argparse.sh"

VALID_BACKENDS=("imagemagick" "matugen")

fn_get() {
[ -f "$HYDE_STATE_HOME/staterc" ] && source "$HYDE_STATE_HOME/staterc"
local current="${DCOL_BACKEND:-imagemagick}"
echo "$current"
}

fn_set() {
local backend="$1"
if [[ -z "$backend" ]]; then
echo "Error: no backend specified"
echo "Available backends: ${VALID_BACKENDS[*]}"
exit 1
fi

# Validate
local valid=false
for b in "${VALID_BACKENDS[@]}"; do
if [[ "$backend" == "$b" ]]; then
valid=true
break
fi
done

if [[ "$valid" == false ]]; then
echo "Error: invalid backend '$backend'"
echo "Available backends: ${VALID_BACKENDS[*]}"
exit 1
fi

# Check matugen is installed if selecting it
if [[ "$backend" == "matugen" ]] && ! command -v matugen &>/dev/null; then
echo "Error: matugen not found. Install with: paru -S matugen-bin"
exit 1
fi

set_conf "DCOL_BACKEND" "$backend"
echo "Color backend set to: $backend"
notify-send -i "preferences-desktop-color" "HyDE Color Backend" "Switched to $backend"
}

fn_select() {
[ -f "$HYDE_STATE_HOME/staterc" ] && source "$HYDE_STATE_HOME/staterc"
local current="${DCOL_BACKEND:-imagemagick}"

font_scale="$ROFI_SCALE"
[[ $font_scale =~ ^[0-9]+$ ]] || font_scale=10
font_name=${ROFI_FONT:-$(get_hyprConf "MENU_FONT")}
font_name=${font_name:-$(get_hyprConf "FONT")}
font_override="* {font: \"${font_name:-"JetBrainsMono Nerd Font"} $font_scale\";}"
hypr_border=${hypr_border:-"$(hyprctl -j getoption decoration:rounding | jq '.int')"}
wind_border=$((hypr_border * 3 / 2))
elem_border=$((hypr_border == 0 ? 5 : hypr_border))
hypr_width=${hypr_width:-"$(hyprctl -j getoption general:border_size | jq '.int')"}
r_override="window{border:${hypr_width}px;border-radius:${wind_border}px;} wallbox{border-radius:${elem_border}px;} element{border-radius:${elem_border}px;}"

local options
options=$(printf '%s\n' "${VALID_BACKENDS[@]}")

local selected
selected=$(echo "$options" | rofi -dmenu -i -select "$current" \
-p "Color backend" \
-theme-str 'entry { placeholder: "Select color backend..."; }' \
-theme-str "$font_override" \
-theme-str "$r_override" \
-theme-str "$(get_rofi_pos)" \
-theme "clipboard")

if [[ -z "$selected" ]]; then
exit 0
fi

fn_set "$selected"
}

# Initialize argparse
argparse_init "$@"

argparse_program "hyde-shell color.backend"
argparse_header "HyDE Color Backend Selector"

argparse "--get,-g" "" "Print the current color backend"
argparse "--set,-s" "BACKEND" "Set the color backend (imagemagick or matugen)" "parameter"
argparse "--select,-S" "" "Select backend interactively via rofi"

argparse_finalize

case $ARGPARSE_ACTION in
get) fn_get ;;
set) fn_set "$BACKEND" ;;
select) fn_select ;;
*) argparse_help ;;
esac
12 changes: 10 additions & 2 deletions Configs/.local/lib/hyde/color.set.sh
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,19 @@ if [ -z "$wallbash_image" ] || [ ! -f "$wallbash_image" ]; then
exit 1
fi
dcol_file="$dcolDir/$(set_hash "$wallbash_image").dcol"
if [ ! -f "$dcol_file" ]; then
mcol_file="$dcolDir/$(set_hash "$wallbash_image").mcol"
if [ ! -f "$dcol_file" ] || { [[ "${DCOL_BACKEND}" == "matugen" ]] && [ ! -f "$mcol_file" ]; }; then
"$scrDir/wallpaper/cache.sh" commence -w "$wallbash_image" &> /dev/null
fi
# Source color file based on backend preference
# DCOL_BACKEND=matugen -> prefer .mcol, fallback to .dcol
# DCOL_BACKEND=imagemagick or unset -> use .dcol (default)
set -a
source "$dcol_file"
if [[ "${DCOL_BACKEND}" == "matugen" ]] && [[ -f "$mcol_file" ]]; then
source "$mcol_file"
else
source "$dcol_file"
fi
if [ -f "$HYDE_THEME_DIR/theme.dcol" ] && [ "$enableWallDcol" -eq 0 ]; then
source "$HYDE_THEME_DIR/theme.dcol"
print_log -sec "wallbash" -stat "override" "dominant colors from $HYDE_THEME theme"
Expand Down
168 changes: 168 additions & 0 deletions Configs/.local/lib/hyde/wallbash-matugen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#!/usr/bin/env python3
"""wallbash-matugen.py - Alternative color extraction using matugen (Material You)

Drop-in replacement for wallbash.sh when imagemagick fails or user prefers matugen.
Generates a dcol-compatible .mcol file (89 lines) from matugen's tonal palettes.

Usage: wallbash-matugen.py [OPTIONS] <image> [output_prefix]
Options are accepted for compatibility with wallbash.sh but most are ignored
since matugen handles its own color generation algorithm.
"""

import json
import os
import subprocess
import sys

# Mapping strategy:
# dcol group 1 <- neutral palette (dark bg/surface, used as pry1 background)
# dcol group 2 <- primary palette (main accent color)
# dcol group 3 <- secondary palette (muted accent)
# dcol group 4 <- tertiary palette (complementary accent)
PALETTE_MAP = ["neutral", "primary", "secondary", "tertiary"]

# Tone selections for accents
# Available tones: 0, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 90, 95, 98, 99, 100
DARK_MODE = {
"accent_tones": [10, 20, 25, 30, 35, 40, 50, 60, 80], # xa1=dark, xa9=bright
"pry_tone": 10, # Very dark primary (matches wallbash: darkest dominant color)
"txt_tone": 95, # Very light text for contrast
}
LIGHT_MODE = {
"accent_tones": [95, 80, 70, 60, 50, 40, 35, 30, 20], # xa1=bright, xa9=dark
"pry_tone": 90, # Very light primary
"txt_tone": 10, # Very dark text
}


def hex_to_rgba(hex_str: str) -> str:
r = int(hex_str[0:2], 16)
g = int(hex_str[2:4], 16)
b = int(hex_str[4:6], 16)
return f"rgba({r},{g},{b},\\1)"


def run_matugen(image_path: str) -> dict:
"""Run matugen and return parsed JSON output."""
try:
result = subprocess.run(
[
"matugen", "image",
"--json", "strip",
"--dry-run",
"--prefer", "saturation",
image_path,
],
capture_output=True,
text=True,
)
except FileNotFoundError:
print("Error: matugen not found. Install with: paru -S matugen-bin", file=sys.stderr)
sys.exit(1)

if result.returncode != 0 or not result.stdout.strip():
print(f"Error: matugen failed to process image: {result.stderr.strip()}", file=sys.stderr)
sys.exit(1)

try:
return json.loads(result.stdout)
except json.JSONDecodeError as e:
print(f"Error: failed to parse matugen output: {e}", file=sys.stderr)
sys.exit(1)


def generate_mcol(palettes: dict, mode: str) -> list[str]:
"""Generate 89 dcol-compatible lines from matugen palettes."""
config = DARK_MODE if mode == "dark" else LIGHT_MODE
pry_tone = config["pry_tone"]
txt_tone = config["txt_tone"]
accent_tones = config["accent_tones"]

lines = [f'dcol_mode="{mode}"']

for group_idx, palette_name in enumerate(PALETTE_MAP, 1):
palette = palettes[palette_name]
tone_lookup = {int(k): v["color"].upper() for k, v in palette.items()}

# Primary color
pry = tone_lookup[pry_tone]
lines.append(f'dcol_pry{group_idx}="{pry}"')
lines.append(f'dcol_pry{group_idx}_rgba="{hex_to_rgba(pry)}"')

# Text color
txt = tone_lookup[txt_tone]
lines.append(f'dcol_txt{group_idx}="{txt}"')
lines.append(f'dcol_txt{group_idx}_rgba="{hex_to_rgba(txt)}"')

# Accent colors xa1-xa9
for acnt_idx, tone in enumerate(accent_tones, 1):
acol = tone_lookup[tone]
lines.append(f'dcol_{group_idx}xa{acnt_idx}="{acol}"')
lines.append(f'dcol_{group_idx}xa{acnt_idx}_rgba="{hex_to_rgba(acol)}"')

return lines


def parse_args(argv: list[str]) -> tuple[str, str, str]:
"""Parse CLI args, return (sort_mode, image_path, output_prefix)."""
sort_mode = "auto"
args = argv[1:]
positional = []

i = 0
while i < len(args):
arg = args[i]
if arg in ("-v", "--vibrant", "-p", "--pastel", "-m", "--mono"):
pass # Accept but ignore
elif arg in ("-c", "--custom"):
i += 1 # Skip the curve argument
elif arg in ("-d", "--dark"):
sort_mode = "dark"
elif arg in ("-l", "--light"):
sort_mode = "light"
else:
positional.append(arg)
i += 1

if not positional:
print("Error: Input file not provided!", file=sys.stderr)
sys.exit(1)

image_path = positional[0]
output_prefix = positional[1] if len(positional) > 1 else image_path

return sort_mode, image_path, output_prefix


def main():
sort_mode, image_path, output_prefix = parse_args(sys.argv)
output_file = f"{output_prefix}.mcol"

if not os.path.isfile(image_path):
print(f"Error: Input file not found: {image_path}", file=sys.stderr)
sys.exit(1)

# Run matugen
data = run_matugen(image_path)

# Determine mode
if sort_mode == "auto":
sort_mode = "dark" if data.get("is_dark_mode", True) else "light"

# Generate color lines
lines = generate_mcol(data["palettes"], sort_mode)

# Validate
if len(lines) != 89:
print(f"Warning: Generated {len(lines)} lines (expected 89)", file=sys.stderr)
sys.exit(1)

# Write output
with open(output_file, "w") as f:
f.write("\n".join(lines) + "\n")

print(f"wallbash-matugen :: {sort_mode} :: \"{output_file}\" ({len(lines)} lines)")


if __name__ == "__main__":
main()
23 changes: 20 additions & 3 deletions Configs/.local/lib/hyde/wallpaper/cache.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,23 @@ wallpaper_cache_init() {
fi
}

fn_generate_dcol() {
# Generate color cache files for wallbash
# Always generates .dcol (imagemagick dominant colors)
# Also generates .mcol (matugen material colors) if matugen is available
local img="$1"
local out="$2"

# Always generate .dcol via imagemagick (primary/default)
"$scrDir/wallbash.sh" --custom "$wallbashCustomCurve" "$img" "$out" &>/dev/null

# Also generate .mcol via matugen if available (coexist)
if command -v matugen &>/dev/null; then
{ [ ! -e "${out}.mcol" ] || [ "$(wc -l < "${out}.mcol")" -ne 89 ]; } && \
"$scrDir/wallbash-matugen.py" --custom "$wallbashCustomCurve" "$img" "$out" &>/dev/null
fi
}

fn_wallcache() {
local x_hash="$1"
local x_wall="$2"
Expand All @@ -54,7 +71,7 @@ fn_wallcache() {
[ ! -e "$thmbDir/$x_hash.quad" ] && magick "$thmbDir/$x_hash.sqre" \( -size 500x500 xc:white -fill "rgba(0,0,0,0.7)" -draw "polygon 400,500 500,500 500,0 450,0" -fill black -draw "polygon 500,500 500,0 450,500" \) -alpha Off -compose CopyOpacity -composite "$thmbDir/$x_hash.quad.png" && mv "$thmbDir/$x_hash.quad.png" "$thmbDir/$x_hash.quad"
{
[ ! -e "$dcolDir/$x_hash.dcol" ] || [ "$(wc -l < "$dcolDir/$x_hash.dcol")" -ne 89 ]
} && "$scrDir/wallbash.sh" --custom "$wallbashCustomCurve" "$thmbDir/$x_hash.thmb" "$dcolDir/$x_hash" &> /dev/null
} && fn_generate_dcol "$thmbDir/$x_hash.thmb" "$dcolDir/$x_hash"
if [ "$is_video" -eq 1 ]; then
rm -f "$temp_image"
fi
Expand All @@ -74,7 +91,7 @@ fn_wallcache_force() {
magick "$x_wall"[0] -strip -thumbnail 500x500^ -gravity center -extent 500x500 "$thmbDir/$x_hash.sqre.png" && mv "$thmbDir/$x_hash.sqre.png" "$thmbDir/$x_hash.sqre"
magick "$x_wall"[0] -strip -scale 10% -blur 0x3 -resize 100% "$thmbDir/$x_hash.blur"
magick "$thmbDir/$x_hash.sqre" \( -size 500x500 xc:white -fill "rgba(0,0,0,0.7)" -draw "polygon 400,500 500,500 500,0 450,0" -fill black -draw "polygon 500,500 500,0 450,500" \) -alpha Off -compose CopyOpacity -composite "$thmbDir/$x_hash.quad.png" && mv "$thmbDir/$x_hash.quad.png" "$thmbDir/$x_hash.quad"
"$scrDir/wallbash.sh" --custom "$wallbashCustomCurve" "$thmbDir/$x_hash.thmb" "$dcolDir/$x_hash" &> /dev/null
fn_generate_dcol "$thmbDir/$x_hash.thmb" "$dcolDir/$x_hash"
if [ "$is_video" -eq 1 ]; then
rm -f "$temp_image"
fi
Expand Down Expand Up @@ -150,7 +167,7 @@ wallpaper_cache_commence() {
parallel --bar --link "fn_wallcache$mode" ::: "${wallHash[@]}" ::: "${wallList[@]}"
}

export -f fn_wallcache fn_wallcache_force fn_envar_cache wallpaper_cache_bootstrap wallpaper_cache_init wallpaper_cache_commence extract_thumbnail
export -f fn_generate_dcol fn_wallcache fn_wallcache_force fn_envar_cache wallpaper_cache_bootstrap wallpaper_cache_init wallpaper_cache_commence extract_thumbnail

if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
subcommand="$1"
Expand Down
1 change: 1 addition & 0 deletions Scripts/pkg_extra.lst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ duf|fish # prettier version of d

# --------------------------------------------------- // Backends
#electron # electron backend
# matugen-bin # Material You color generation (optional alternative to imagemagick for dcol)

# --------------------------------------------------- // Lockscreen
#swaylock-effects-git # swaylock with effects // very minimal lockscreen
Expand Down
Loading