diff --git a/.config/hypr/hyprland/keybinds.conf b/.config/hypr/hyprland/keybinds.conf index 278d5ba2e..806db1523 100644 --- a/.config/hypr/hyprland/keybinds.conf +++ b/.config/hypr/hyprland/keybinds.conf @@ -43,7 +43,7 @@ bindld = Super+Shift,M, Toggle mute, exec, wpctl set-mute @DEFAULT_SINK@ toggle bindl = Alt ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden] bindl = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden] bindld = Super+Alt,M, Toggle mic, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden] -bindd = Ctrl+Super, T, Change wallpaper, exec, ~/.config/quickshell/scripts/switchwall.sh # Change wallpaper +bindd = Ctrl+Super, T, Change wallpaper, exec, ~/.config/quickshell/scripts/colors/switchwall.sh # Change wallpaper bind = Ctrl+Super, R, exec, killall ags agsv1 gjs ydotool qs quickshell; qs & # Restart widgets ##! Utilities diff --git a/.config/quickshell/modules/common/ConfigOptions.qml b/.config/quickshell/modules/common/ConfigOptions.qml index d0425cadb..04607104d 100644 --- a/.config/quickshell/modules/common/ConfigOptions.qml +++ b/.config/quickshell/modules/common/ConfigOptions.qml @@ -16,6 +16,9 @@ Singleton { property QtObject appearance: QtObject { property int fakeScreenRounding: 2 // 0: None | 1: Always | 2: When not fullscreen property bool transparency: false + property QtObject palette: QtObject { + property string type: "auto" // Allowed: auto, scheme-content, scheme-expressive, scheme-fidelity, scheme-fruit-salad, scheme-monochrome, scheme-neutral, scheme-rainbow, scheme-tonal-spot + } } property QtObject audio: QtObject { // Values in % diff --git a/.config/quickshell/modules/common/Directories.qml b/.config/quickshell/modules/common/Directories.qml index d2c570234..32c905621 100644 --- a/.config/quickshell/modules/common/Directories.qml +++ b/.config/quickshell/modules/common/Directories.qml @@ -29,7 +29,7 @@ Singleton { property string notificationsPath: FileUtils.trimFileProtocol(`${Directories.cache}/notifications/notifications.json`) property string generatedMaterialThemePath: FileUtils.trimFileProtocol(`${Directories.state}/user/generated/colors.json`) property string cliphistDecode: FileUtils.trimFileProtocol(`/tmp/quickshell/media/cliphist`) - property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.config}/quickshell/scripts/switchwall.sh`) + property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.config}/quickshell/scripts/colors/switchwall.sh`) // Cleanup on init Component.onCompleted: { Hyprland.dispatch(`exec mkdir -p '${shellConfig}'`) diff --git a/.config/quickshell/scripts/applycolor.sh b/.config/quickshell/scripts/colors/applycolor.sh similarity index 92% rename from .config/quickshell/scripts/applycolor.sh rename to .config/quickshell/scripts/colors/applycolor.sh index 58fad4ce3..ab30ca223 100755 --- a/.config/quickshell/scripts/applycolor.sh +++ b/.config/quickshell/scripts/colors/applycolor.sh @@ -28,13 +28,13 @@ colorvalues=($colorstrings) # Array of color values apply_term() { # Check if terminal escape sequence template exists - if [ ! -f "$SCRIPT_DIR"/terminal/sequences.txt ]; then + if [ ! -f "$CONFIG_DIR"/scripts/terminal/sequences.txt ]; then echo "Template file not found for Terminal. Skipping that." return fi # Copy template mkdir -p "$STATE_DIR"/user/generated/terminal - cp "$SCRIPT_DIR"/terminal/sequences.txt "$STATE_DIR"/user/generated/terminal/sequences.txt + cp "$CONFIG_DIR"/scripts/terminal/sequences.txt "$STATE_DIR"/user/generated/terminal/sequences.txt # Apply colors for i in "${!colorlist[@]}"; do sed -i "s/${colorlist[$i]} #/${colorvalues[$i]#\#}/g" "$STATE_DIR"/user/generated/terminal/sequences.txt diff --git a/.config/quickshell/scripts/generate_colors_material.py b/.config/quickshell/scripts/colors/generate_colors_material.py similarity index 100% rename from .config/quickshell/scripts/generate_colors_material.py rename to .config/quickshell/scripts/colors/generate_colors_material.py diff --git a/.config/quickshell/scripts/colors/scheme_for_image.py b/.config/quickshell/scripts/colors/scheme_for_image.py new file mode 100755 index 000000000..74ead1e65 --- /dev/null +++ b/.config/quickshell/scripts/colors/scheme_for_image.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +import sys +import cv2 +import numpy as np + +# Allowed scheme types +SCHEMES = [ + "scheme-content", + "scheme-expressive", + "scheme-fidelity", + "scheme-fruit-salad", + "scheme-monochrome", + "scheme-neutral", + "scheme-rainbow", + "scheme-tonal-spot" +] + +def image_colorfulness(image): + # Based on Hasler and Süsstrunk's colorfulness metric + (B, G, R) = cv2.split(image.astype("float")) + rg = np.absolute(R - G) + yb = np.absolute(0.5 * (R + G) - B) + std_rg = np.std(rg) + std_yb = np.std(yb) + mean_rg = np.mean(rg) + mean_yb = np.mean(yb) + colorfulness = np.sqrt(std_rg ** 2 + std_yb ** 2) + (0.3 * np.sqrt(mean_rg ** 2 + mean_yb ** 2)) + return colorfulness + +def pick_scheme(colorfulness): + if colorfulness < 10: + return "scheme-monochrome" + elif colorfulness < 50: + return "scheme-neutral" + else: + return "scheme-tonal-spot" + +def main(): + colorfulness_mode = False + args = sys.argv[1:] + if '--colorfulness' in args: + colorfulness_mode = True + args.remove('--colorfulness') + if len(args) < 1: + print("scheme-tonal-spot") + sys.exit(1) + img_path = args[0] + img = cv2.imread(img_path) + if img is None: + print("scheme-tonal-spot") + sys.exit(1) + colorfulness = image_colorfulness(img) + if colorfulness_mode: + print(f"{colorfulness}") + else: + scheme = pick_scheme(colorfulness) + print(scheme) + +if __name__ == "__main__": + main() diff --git a/.config/quickshell/scripts/switchwall.sh b/.config/quickshell/scripts/colors/switchwall.sh similarity index 83% rename from .config/quickshell/scripts/switchwall.sh rename to .config/quickshell/scripts/colors/switchwall.sh index dffd9bd61..6918e5af0 100755 --- a/.config/quickshell/scripts/switchwall.sh +++ b/.config/quickshell/scripts/colors/switchwall.sh @@ -227,7 +227,7 @@ switch() { matugen "${matugen_args[@]}" source "$(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate" - python "$SCRIPT_DIR/generate_colors_material.py" "${generate_colors_material_args[@]}" \ + python3 "$SCRIPT_DIR/generate_colors_material.py" "${generate_colors_material_args[@]}" \ > "$STATE_DIR"/user/generated/material_colors.scss "$SCRIPT_DIR"/applycolor.sh deactivate @@ -246,6 +246,15 @@ main() { color="" noswitch_flag="" + get_type_from_config() { + jq -r '.appearance.palette.type' "$XDG_CONFIG_HOME/illogical-impulse/config.json" 2>/dev/null || echo "auto" + } + + detect_scheme_type_from_image() { + local img="$1" + "$SCRIPT_DIR"/scheme_for_image.py "$img" 2>/dev/null | tr -d '\n' + } + while [[ $# -gt 0 ]]; do case "$1" in --mode) @@ -280,12 +289,55 @@ main() { esac done + # If type_flag is not set, get it from config + if [[ -z "$type_flag" ]]; then + type_flag="$(get_type_from_config)" + fi + + # Validate type_flag (allow 'auto' as well) + allowed_types=(scheme-content scheme-expressive scheme-fidelity scheme-fruit-salad scheme-monochrome scheme-neutral scheme-rainbow scheme-tonal-spot auto) + valid_type=0 + for t in "${allowed_types[@]}"; do + if [[ "$type_flag" == "$t" ]]; then + valid_type=1 + break + fi + done + if [[ $valid_type -eq 0 ]]; then + echo "[switchwall.sh] Warning: Invalid type '$type_flag', defaulting to 'auto'" >&2 + type_flag="auto" + fi + # Only prompt for wallpaper if not using --color and not using --noswitch and no imgpath set if [[ -z "$imgpath" && -z "$color_flag" && -z "$noswitch_flag" ]]; then cd "$(xdg-user-dir PICTURES)/Wallpapers" 2>/dev/null || cd "$(xdg-user-dir PICTURES)" || return 1 imgpath="$(kdialog --getopenfilename . --title 'Choose wallpaper')" fi + # If type_flag is 'auto', detect scheme type from image (after imgpath is set) + if [[ "$type_flag" == "auto" ]]; then + if [[ -n "$imgpath" && -f "$imgpath" ]]; then + detected_type="$(detect_scheme_type_from_image "$imgpath")" + # Only use detected_type if it's valid + valid_detected=0 + for t in "${allowed_types[@]}"; do + if [[ "$detected_type" == "$t" && "$detected_type" != "auto" ]]; then + valid_detected=1 + break + fi + done + if [[ $valid_detected -eq 1 ]]; then + type_flag="$detected_type" + else + echo "[switchwall] Warning: Could not auto-detect a valid scheme, defaulting to 'scheme-tonal-spot'" >&2 + type_flag="scheme-tonal-spot" + fi + else + echo "[switchwall] Warning: No image to auto-detect scheme from, defaulting to 'scheme-tonal-spot'" >&2 + type_flag="scheme-tonal-spot" + fi + fi + switch "$imgpath" "$mode_flag" "$type_flag" "$color_flag" "$color" }