diff options
| author | Fuwn <[email protected]> | 2024-09-11 22:39:56 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2024-09-11 22:39:56 -0700 |
| commit | 17f5ecf98375c92bb07ef0587defd16cff925979 (patch) | |
| tree | 376f64305aad1d050ab2b5e6a93d2c01313b5b3b /lib | |
| parent | home: yazi (diff) | |
| download | nixos-config-17f5ecf98375c92bb07ef0587defd16cff925979.tar.xz nixos-config-17f5ecf98375c92bb07ef0587defd16cff925979.zip | |
home: pywal
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/pywal/default.nix | 20 | ||||
| -rw-r--r-- | lib/pywal/wal.py | 356 |
2 files changed, 376 insertions, 0 deletions
diff --git a/lib/pywal/default.nix b/lib/pywal/default.nix new file mode 100644 index 0000000..59ed30c --- /dev/null +++ b/lib/pywal/default.nix @@ -0,0 +1,20 @@ +{ pkgs, wallpaper }: +let + colourScheme = builtins.fromJSON ( + builtins.readFile "${ + pkgs.runCommand "colourscheme" + { + buildInputs = with pkgs; [ + imagemagick + jq + ]; + } + '' + mkdir -p $out + + ${pkgs.python3}/bin/python3 ${./wal.py} ${wallpaper} | sed "s/'/\"/g" | jq 'to_entries | map({"colour\(.key)": .value}) | add' > $out/colourscheme + '' + }/colourscheme" + ); +in +colourScheme diff --git a/lib/pywal/wal.py b/lib/pywal/wal.py new file mode 100644 index 0000000..d247b3f --- /dev/null +++ b/lib/pywal/wal.py @@ -0,0 +1,356 @@ +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +# https://github.com/dylanaraps/pywal +# https://github.com/dylanaraps/pywal/blob/master/pywal/backends/wal.py +# https://github.com/dylanaraps/pywal/blob/master/pywal/util.py + + +""" +Misc helper functions. +""" + +import colorsys +import json +import logging +import os +import platform +import re +import shutil +import subprocess +import sys + + +class Color: + """Color formats.""" + + alpha_num = "100" + + def __init__(self, hex_color): + self.hex_color = hex_color + + def __str__(self): + return self.hex_color + + @property + def rgb(self): + """Convert a hex color to rgb.""" + return "%s,%s,%s" % (*hex_to_rgb(self.hex_color),) + + @property + def xrgba(self): + """Convert a hex color to xrdb rgba.""" + return hex_to_xrgba(self.hex_color) + + @property + def rgba(self): + """Convert a hex color to rgba.""" + return "rgba(%s,%s,%s,%s)" % (*hex_to_rgb(self.hex_color), self.alpha_dec) + + @property + def alpha(self): + """Add URxvt alpha value to color.""" + return "[%s]%s" % (self.alpha_num, self.hex_color) + + @property + def alpha_dec(self): + """Export the alpha value as a decimal number in [0, 1].""" + return int(self.alpha_num) / 100 + + @property + def decimal(self): + """Export color in decimal.""" + return "%s%s" % ("#", int(self.hex_color[1:], 16)) + + @property + def decimal_strip(self): + """Strip '#' from decimal color.""" + return int(self.hex_color[1:], 16) + + @property + def octal(self): + """Export color in octal.""" + return "%s%s" % ("#", oct(int(self.hex_color[1:], 16))[2:]) + + @property + def octal_strip(self): + """Strip '#' from octal color.""" + return oct(int(self.hex_color[1:], 16))[2:] + + @property + def strip(self): + """Strip '#' from color.""" + return self.hex_color[1:] + + @property + def red(self): + """Red value as float between 0 and 1.""" + return "%.3f" % (hex_to_rgb(self.hex_color)[0] / 255.0) + + @property + def green(self): + """Green value as float between 0 and 1.""" + return "%.3f" % (hex_to_rgb(self.hex_color)[1] / 255.0) + + @property + def blue(self): + """Blue value as float between 0 and 1.""" + return "%.3f" % (hex_to_rgb(self.hex_color)[2] / 255.0) + + def lighten(self, percent): + """Lighten color by percent.""" + percent = float(re.sub(r"[\D\.]", "", str(percent))) + return Color(lighten_color(self.hex_color, percent / 100)) + + def darken(self, percent): + """Darken color by percent.""" + percent = float(re.sub(r"[\D\.]", "", str(percent))) + return Color(darken_color(self.hex_color, percent / 100)) + + def saturate(self, percent): + """Saturate a color.""" + percent = float(re.sub(r"[\D\.]", "", str(percent))) + return Color(saturate_color(self.hex_color, percent / 100)) + + +def read_file(input_file): + """Read data from a file and trim newlines.""" + with open(input_file, "r") as file: + return file.read().splitlines() + + +def read_file_json(input_file): + """Read data from a json file.""" + with open(input_file, "r") as json_file: + return json.load(json_file) + + +def read_file_raw(input_file): + """Read data from a file as is, don't strip + newlines or other special characters.""" + with open(input_file, "r") as file: + return file.readlines() + + +def save_file(data, export_file): + """Write data to a file.""" + create_dir(os.path.dirname(export_file)) + + try: + with open(export_file, "w") as file: + file.write(data) + except PermissionError: + logging.warning("Couldn't write to %s.", export_file) + + +def save_file_json(data, export_file): + """Write data to a json file.""" + create_dir(os.path.dirname(export_file)) + + with open(export_file, "w") as file: + json.dump(data, file, indent=4) + + +def create_dir(directory): + """Alias to create the cache dir.""" + os.makedirs(directory, exist_ok=True) + + +def setup_logging(): + """Logging config.""" + logging.basicConfig( + format=( + "[%(levelname)s\033[0m] " "\033[1;31m%(module)s\033[0m: " "%(message)s" + ), + level=logging.INFO, + stream=sys.stdout, + ) + logging.addLevelName(logging.ERROR, "\033[1;31mE") + logging.addLevelName(logging.INFO, "\033[1;32mI") + logging.addLevelName(logging.WARNING, "\033[1;33mW") + + +def hex_to_rgb(color): + """Convert a hex color to rgb.""" + return tuple(bytes.fromhex(color.strip("#"))) + + +def hex_to_xrgba(color): + """Convert a hex color to xrdb rgba.""" + col = color.lower().strip("#") + return "%s%s/%s%s/%s%s/ff" % (*col,) + + +def rgb_to_hex(color): + """Convert an rgb color to hex.""" + return "#%02x%02x%02x" % (*color,) + + +def darken_color(color, amount): + """Darken a hex color.""" + color = [int(col * (1 - amount)) for col in hex_to_rgb(color)] + return rgb_to_hex(color) + + +def lighten_color(color, amount): + """Lighten a hex color.""" + color = [int(col + (255 - col) * amount) for col in hex_to_rgb(color)] + return rgb_to_hex(color) + + +def blend_color(color, color2): + """Blend two colors together.""" + r1, g1, b1 = hex_to_rgb(color) + r2, g2, b2 = hex_to_rgb(color2) + + r3 = int(0.5 * r1 + 0.5 * r2) + g3 = int(0.5 * g1 + 0.5 * g2) + b3 = int(0.5 * b1 + 0.5 * b2) + + return rgb_to_hex((r3, g3, b3)) + + +def saturate_color(color, amount): + """Saturate a hex color.""" + r, g, b = hex_to_rgb(color) + r, g, b = [x / 255.0 for x in (r, g, b)] + h, l, s = colorsys.rgb_to_hls(r, g, b) + s = amount + r, g, b = colorsys.hls_to_rgb(h, l, s) + r, g, b = [x * 255.0 for x in (r, g, b)] + + return rgb_to_hex((int(r), int(g), int(b))) + + +def rgb_to_yiq(color): + """Sort a list of colors.""" + return colorsys.rgb_to_yiq(*hex_to_rgb(color)) + + +def disown(cmd): + """Call a system command in the background, + disown it and hide it's output.""" + subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + +def get_pid(name): + """Check if process is running by name.""" + if not shutil.which("pidof"): + return False + + try: + if platform.system() != "Darwin": + subprocess.check_output(["pidof", "-s", name]) + else: + subprocess.check_output(["pidof", name]) + + except subprocess.CalledProcessError: + return False + + return True + + +""" +Generate a colorscheme using imagemagick. +""" + +import logging +import re +import shutil +import subprocess +import sys + + +def imagemagick(color_count, img, magick_command): + """Call Imagemagick to generate a scheme.""" + flags = ["-resize", "25%", "-colors", str(color_count), "-unique-colors", "txt:-"] + img += "[0]" + + return subprocess.check_output([*magick_command, img, *flags]).splitlines() + + +def has_im(): + """Check to see if the user has im installed.""" + if shutil.which("magick"): + return ["magick"] + + if shutil.which("convert"): + return ["convert"] + + logging.error("Imagemagick wasn't found on your system.") + logging.error("Try another backend. (wal --backend)") + sys.exit(1) + + +def gen_colors(img): + """Format the output from imagemagick into a list + of hex colors.""" + magick_command = has_im() + + for i in range(0, 20, 1): + raw_colors = imagemagick(16 + i, img, magick_command) + + if len(raw_colors) > 16: + break + + if i == 19: + logging.error("Imagemagick couldn't generate a suitable palette.") + sys.exit(1) + + else: + logging.warning("Imagemagick couldn't generate a palette.") + logging.warning("Trying a larger palette size %s", 16 + i) + + return [re.search("#.{6}", str(col)).group(0) for col in raw_colors[1:]] + + +def adjust(colors, light): + """Adjust the generated colors and store them in a dict that + we will later save in json format.""" + raw_colors = colors[:1] + colors[8:16] + colors[8:-1] + + # Manually adjust colors. + if light: + for color in raw_colors: + color = saturate_color(color, 0.5) + + raw_colors[0] = lighten_color(colors[-1], 0.85) + raw_colors[7] = colors[0] + raw_colors[8] = darken_color(colors[-1], 0.4) + raw_colors[15] = colors[0] + + else: + # Darken the background color slightly. + if raw_colors[0][1] != "0": + raw_colors[0] = darken_color(raw_colors[0], 0.40) + + raw_colors[7] = blend_color(raw_colors[7], "#EEEEEE") + raw_colors[8] = darken_color(raw_colors[7], 0.30) + raw_colors[15] = blend_color(raw_colors[15], "#EEEEEE") + + return raw_colors + + +def get(img, light=False): + """Get colorscheme.""" + colors = gen_colors(img) + return adjust(colors, light) + + +if __name__ == "__main__" and len(sys.argv) > 1: + print(get(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else False)) |