diff --git a/.config/eww/scripts/get-activeworkspace-id b/.config/eww/scripts/get-activeworkspace-id new file mode 100755 index 0000000..48c589d --- /dev/null +++ b/.config/eww/scripts/get-activeworkspace-id @@ -0,0 +1,6 @@ +#!/bin/bash + +hyprctl activeworkspace -j | jq '.id' + +socat -u UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | + stdbuf -o0 awk -F '>>|,' -e '/^workspace>>/ {print $2}' -e '/^focusedmon>>/ {print $3}' diff --git a/.config/eww/scripts/get-workspaces b/.config/eww/scripts/get-workspaces new file mode 100755 index 0000000..74789de --- /dev/null +++ b/.config/eww/scripts/get-workspaces @@ -0,0 +1,12 @@ +#!/bin/bash + +monitor="${1}" + +spaces (){ + hyprctl workspaces -j | tr -d '\n' | jq -c "[.[] | select(.monitor | contains(\"${monitor}\"))]" +} + +spaces +socat -u UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | while read -r line; do + spaces +done diff --git a/.config/eww/scripts/launch.py b/.config/eww/scripts/launch.py new file mode 100755 index 0000000..2afaa87 --- /dev/null +++ b/.config/eww/scripts/launch.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +import subprocess +import json +from string import Template +import os + + +def panic(err): + import traceback + print(traceback.format_exc()) + print(f"error {err}") + os._exit(1) + + +class Monitor: + id: int + name: str + width: int + height: int + def __init__(self, id: int, name: str, width: int, height: int): + self.id = id + self.name = name + self.width = width + self.height = height + + +def get_monitors() -> list[Monitor]: + ws = subprocess.run( + ["hyprctl", "monitors", "-j"], + capture_output=True + ) + _monitors = json.loads(ws.stdout) + + ret = [] + for m in _monitors: + ret.append(Monitor( + id=m["id"], + name=m["name"], + width=m["width"], + height=m["height"], + )) + return ret + + +def temlate(source: str, monitor: Monitor, bar_name: str, home: str, base_dir: str) -> str: + height = int(monitor.height * .02) + return Template(source) \ + .safe_substitute({ + "monitor": monitor.id, + "monitor_name": monitor.name, + "bar_name": bar_name, + "home": home, + "base_dir": base_dir, + "height": height, + }) + + +def generate_bar_config(monitor: Monitor, bar_name: str, home: str, base_dir: str, source_dir: str, dest_dir: str): + items = os.listdir(source_dir) + for item in items: + if os.path.isdir(f"{source_dir}/{item}"): + os.makedirs(f"{dest_dir}/{item}") + generate_bar_config(monitor, bar_name, home, base_dir, f"{source_dir}/{item}", f"{dest_dir}/{item}") + elif os.path.isfile(f"{source_dir}/{item}"): + data = "" + try: + with open(f"{source_dir}/{item}", "r") as fp: + data = fp.read() + except Exception as e: + panic(e) + + data = temlate( + data, + monitor, + bar_name, + home, + base_dir + ) + + try: + with open(f"{dest_dir}/{item}", "w") as fp: + fp.write(data) + except Exception as e: + panic(e) + + +def launch_bar(monitor: Monitor, wayland_display: str): + bar_name = f"bar-{wayland_display}-{monitor.id}" + dir = f"/tmp/vbar/{wayland_display}/{monitor.id}" + + home = os.getenv("HOME", default=None) + if home == None: + panic("HOME environment variable is unset") + + try: + os.makedirs(dir) + except FileExistsError: + try: + subprocess.run(["rm", "-rf", dir]) + os.makedirs(dir) + except Exception as e: + panic(e) + + config = generate_bar_config(monitor, bar_name, home, dir, f"{home}/.config/eww/templates/bar", dir) + + try: + subprocess.run(["eww", "-c", dir, "reload"]) + subprocess.run(["eww", "-c", dir, "open", bar_name]) + except Exception as e: + panic(e) + + +def main(): + wayland_display = os.getenv("WAYLAND_DISPLAY", default=None) + if wayland_display is None: + os.exit(1) + monitors = get_monitors() + for monitor in monitors: + launch_bar( + monitor=monitor, + wayland_display=wayland_display, + ) + + +if __name__ == '__main__': + main() + diff --git a/.config/eww/scripts/lock b/.config/eww/scripts/lock new file mode 100755 index 0000000..4d18b0d --- /dev/null +++ b/.config/eww/scripts/lock @@ -0,0 +1,17 @@ +#!/bin/bash + +swaylock \ + --screenshots \ + --clock \ + --indicator \ + --indicator-radius 100 \ + --indicator-thickness 7 \ + --effect-blur 7x5 \ + --effect-vignette 0.5:0.5 \ + --ring-color bb00cc \ + --key-hl-color 880033 \ + --line-color 00000000 \ + --inside-color 00000088 \ + --separator-color 00000000 \ + --grace 2 \ + --fade-in 0.2 diff --git a/.config/eww/templates/bar/eww.scss b/.config/eww/templates/bar/eww.scss new file mode 100644 index 0000000..633298b --- /dev/null +++ b/.config/eww/templates/bar/eww.scss @@ -0,0 +1,20 @@ +@import "$base_dir/modules/workspaces.scss"; +@import "$base_dir/modules/clock.scss"; +@import "$base_dir/modules/weather.scss"; +@import "$base_dir/modules/widget.scss"; +@import "$base_dir/modules/management.scss"; +@import "$base_dir/variables.scss"; + +*{ + all: unset; +} + +.vbar { + padding: .4rem; + font-family: "FiraCode Nerd Font"; + font-size: 1.2rem; + background-color: rgba($background, 1); + color: rgba($foreground, 1); + border-radius: 10px; + border: 2px solid $b_cyan; +} diff --git a/.config/eww/templates/bar/eww.yuck b/.config/eww/templates/bar/eww.yuck new file mode 100644 index 0000000..4dca6c8 --- /dev/null +++ b/.config/eww/templates/bar/eww.yuck @@ -0,0 +1,15 @@ +; Args: +; home: The user home directory +; base_dir: The installation directory +; bar_name: The bar name (should be unique) +; monitor: The Hyprland monitor id +; monitor_name: The Hyprland monitor name +(include "$base_dir/modules/bar.yuck") + +(defwindow $bar_name + :geometry (geometry :x "0" :y "15" + :anchor "bottom center" + :height "$height" :width "60%") + :exclusive true + :monitor $monitor + (bar )) diff --git a/.config/eww/templates/bar/modules/activeworkspaceid.yuck b/.config/eww/templates/bar/modules/activeworkspaceid.yuck new file mode 100644 index 0000000..258c130 --- /dev/null +++ b/.config/eww/templates/bar/modules/activeworkspaceid.yuck @@ -0,0 +1,3 @@ +(deflisten activeworkspaceid + :initial "2" + "$home/.config/eww/scripts/get-activeworkspace-id") diff --git a/.config/eww/templates/bar/modules/bar.yuck b/.config/eww/templates/bar/modules/bar.yuck new file mode 100644 index 0000000..f7a59d9 --- /dev/null +++ b/.config/eww/templates/bar/modules/bar.yuck @@ -0,0 +1,16 @@ +(include "$base_dir/modules/workspaces.yuck") +(include "$base_dir/modules/activeworkspaceid.yuck") +(include "$base_dir/modules/widgets-panel.yuck") +(include "$base_dir/modules/management.yuck") + +(defwidget bar[] + (box + :vexpand false + :hexpand false + :class "vbar" + (workspaces-widget) + (box + :halign "end" + :space-evenly false + (widgets-panel) + (management)))) diff --git a/.config/eww/templates/bar/modules/clock.scss b/.config/eww/templates/bar/modules/clock.scss new file mode 100644 index 0000000..5d208b6 --- /dev/null +++ b/.config/eww/templates/bar/modules/clock.scss @@ -0,0 +1,8 @@ +@import "$base_dir/variables.scss"; + +.clock { + padding: .3rem; + margin: .3rem; + background-color: $black; + border-radius: 4px; +} diff --git a/.config/eww/templates/bar/modules/clock.yuck b/.config/eww/templates/bar/modules/clock.yuck new file mode 100644 index 0000000..21199a2 --- /dev/null +++ b/.config/eww/templates/bar/modules/clock.yuck @@ -0,0 +1,18 @@ +(defpoll time + :interval "1s" + `date +'{"hour":"%H","min":"%M","sec":"%S"}'`) +(defwidget clock[] + (clock-h)) + +(defwidget clock-v[] + (box + :class "clock widget" + :orientation "vertical" + (label :text "${time.hour}") + (label :text "${time.min}"))) + +(defwidget clock-h[] + (box + :class "clock" + :orientation "horizontal" + (label :text "${time.hour}:${time.min}"))) diff --git a/.config/eww/templates/bar/modules/management.scss b/.config/eww/templates/bar/modules/management.scss new file mode 100644 index 0000000..049a868 --- /dev/null +++ b/.config/eww/templates/bar/modules/management.scss @@ -0,0 +1,29 @@ +@import "$base_dir/variables.scss"; + +.management { + padding: .3rem; + margin: .3rem; + background-color: $black; + border-radius: 4px; +} + +.btn { + padding: .3rem; +} + +.btn:hover { + background-color: $b_black; + border-radius: 4px; +} + +.lock-btn { + color: $blue; +} + +.reboot-btn { + color: $yellow; +} + +.shutdown-btn { + color: $red; +} diff --git a/.config/eww/templates/bar/modules/management.yuck b/.config/eww/templates/bar/modules/management.yuck new file mode 100644 index 0000000..f184911 --- /dev/null +++ b/.config/eww/templates/bar/modules/management.yuck @@ -0,0 +1,26 @@ +(defwidget management[] + (box + :halign "end" + :space-evenly false + :class "management" + (lock) + (reboot) + (shutdown))) + +(defwidget lock[] + (button + :class "lock-btn btn" + :onclick "$home/.config/eww/scripts/lock" + "󰌾")) + +(defwidget reboot[] + (button + :class "reboot-btn btn" + :onclick "reboot" + "󰜉")) + +(defwidget shutdown[] + (button + :class "shutdown-btn btn" + :onclick "poweroff" + "󰐥")) diff --git a/.config/eww/templates/bar/modules/weather.scss b/.config/eww/templates/bar/modules/weather.scss new file mode 100644 index 0000000..fc3a964 --- /dev/null +++ b/.config/eww/templates/bar/modules/weather.scss @@ -0,0 +1,12 @@ +@import "$base_dir/variables.scss"; + +.weather { + padding: .3rem; + margin: .3rem; + background-color: $black; + border-radius: 4px; +} + +.weather-icon { + padding-right: .8rem; +} diff --git a/.config/eww/templates/bar/modules/weather.yuck b/.config/eww/templates/bar/modules/weather.yuck new file mode 100644 index 0000000..3bf9783 --- /dev/null +++ b/.config/eww/templates/bar/modules/weather.yuck @@ -0,0 +1,59 @@ +(defpoll weather + :interval "30m" + :default `curl 'https://api.open-meteo.com/v1/forecast?latitude=37.98&longitude=23.73¤t_weather=true'` + `curl 'https://api.open-meteo.com/v1/forecast?latitude=37.98&longitude=23.73¤t_weather=true'`) + +(defwidget weather-widget[] + (weather-widget-h)) + +(defwidget weather-widget-v[] + (box + :class "weather widget" + :orientation "vertical" + (weather-type) + (temp))) + +(defwidget weather-widget-h[] + (box + :class "weather" + :orientation "horizontal" + :space-evenly false + (weather-type) + (temp))) + +(defwidget temp[] + (label :text "${weather.current_weather.temperature}°C")) + +;; https://open-meteo.com/en/docs +;; 0 Clear sky +;; 1, 2, 3 Mainly clear, partly cloudy, and overcast +;; 45, 48 Fog and depositing rime fog +;; 51, 53, 55 Drizzle: Light, moderate, and dense intensity +;; 56, 57 Freezing Drizzle: Light and dense intensity +;; 61, 63, 65 Rain: Slight, moderate and heavy intensity +;; 66, 67 Freezing Rain: Light and heavy intensity +;; 71, 73, 75 Snow fall: Slight, moderate, and heavy intensity +;; 77 Snow grains +;; 80, 81, 82 Rain showers: Slight, moderate, and violent +;; 85, 86 Snow showers slight and heavy +;; 95 * Thunderstorm: Slight or moderate +;; 96, 99 * Thunderstorm with slight and heavy hail +(defwidget weather-type[] + (label + :class "weather-icon" + :text {weather.current_weather.weathercode == 1 ? "󰖙" : + weather.current_weather.weathercode < 4 ? "󰖕" : + weather.current_weather.weathercode == 45 || weather.current_weather.weathercode == 48 ? "󰖑" : + weather.current_weather.weathercode == 51 || weather.current_weather.weathercode == 53 || + weather.current_weather.weathercode == 55 || weather.current_weather.weathercode == 56 || + weather.current_weather.weathercode == 57 ? "󰼳" : + weather.current_weather.weathercode == 61 || weather.current_weather.weathercode == 63 || + weather.current_weather.weathercode == 65 || weather.current_weather.weathercode == 66 || + weather.current_weather.weathercode == 67 ? "󰖗" : + weather.current_weather.weathercode == 71 || weather.current_weather.weathercode == 73 || + weather.current_weather.weathercode == 75 || weather.current_weather.weathercode == 77 ? "󰖘" : + weather.current_weather.weathercode == 80 || weather.current_weather.weathercode == 81 || + weather.current_weather.weathercode == 82 ? "󰖖" : + weather.current_weather.weathercode == 85 || weather.current_weather.weathercode == 86 ? "󰼶": + weather.current_weather.weathercode == 95 || weather.current_weather.weathercode == 96 || + weather.current_weather.weathercode == 99 ? "󰖓" : "err: ${weather.current_weather.weathercode}"})) diff --git a/.config/eww/templates/bar/modules/widget.scss b/.config/eww/templates/bar/modules/widget.scss new file mode 100644 index 0000000..133b8e7 --- /dev/null +++ b/.config/eww/templates/bar/modules/widget.scss @@ -0,0 +1,11 @@ +@import "$base_dir/variables.scss"; + +.widget { + font-size: 1rem; + padding: .2rem; + margin-left: .4rem; + margin-right: .4rem; + background-color: rgba($black, 1); + border: 1px solid $magenta; + border-radius: 4px; +} diff --git a/.config/eww/templates/bar/modules/widgets-panel.yuck b/.config/eww/templates/bar/modules/widgets-panel.yuck new file mode 100644 index 0000000..03005e6 --- /dev/null +++ b/.config/eww/templates/bar/modules/widgets-panel.yuck @@ -0,0 +1,9 @@ +(include "$base_dir/modules/clock.yuck") +(include "$base_dir/modules/weather.yuck") + +(defwidget widgets-panel[] + (box + :halign "end" + :space-evenly false + (weather-widget) + (clock))) diff --git a/.config/eww/templates/bar/modules/workspaces.scss b/.config/eww/templates/bar/modules/workspaces.scss new file mode 100644 index 0000000..3842913 --- /dev/null +++ b/.config/eww/templates/bar/modules/workspaces.scss @@ -0,0 +1,28 @@ +@import "$base_dir/variables.scss"; + +.workspaces-widget { + margin-left: .4rem; +} + +.workspace-entry { + padding-left: .6rem; + padding-right: .6rem; + background-color: rgba($black, 1); + border: 1px solid $magenta; + border-radius: 4px; +} + +.workspace-entry:hover { + background-color: rgba($b_black, 1); +} + +.workspace-current { + background-color: rgba($yellow, 1); + color: rgba($b_black, 1); + border: 0px none; +} + +.workspace-current:hover { + background-color: rgba($yellow, 1); +} + diff --git a/.config/eww/templates/bar/modules/workspaces.yuck b/.config/eww/templates/bar/modules/workspaces.yuck new file mode 100644 index 0000000..7246d56 --- /dev/null +++ b/.config/eww/templates/bar/modules/workspaces.yuck @@ -0,0 +1,18 @@ +(deflisten workspaces + :initial "[]" + "$home/.config/eww/scripts/get-workspaces $monitor_name") + +(defwidget workspaces-widget[] + (box + :space-evenly false + :class "wk-${activeworkspaceid} workspaces-widget" + :halign "start" + :hexpand true + :spacing 8 + (for workspace in workspaces + (workspace-entry :workspace_id "${workspace.id}")))) + +(defwidget workspace-entry[workspace_id] + (button :onclick "hyprctl dispatch workspace ${workspace_id}" + :class "workspace-entry ${workspace_id == activeworkspaceid ? "workspace-current" : ""}" + (label :text "${workspace_id}"))) diff --git a/.config/eww/templates/bar/variables.scss b/.config/eww/templates/bar/variables.scss new file mode 100644 index 0000000..e970d00 --- /dev/null +++ b/.config/eww/templates/bar/variables.scss @@ -0,0 +1,20 @@ +$background: #1a1b26; +$foreground: #c0caf5; + +$black: #15161E; +$red: #f7768e; +$green: #9ece6a; +$yellow: #e0af68; +$blue: #7aa2f7; +$magenta: #bb9af7; +$cyan: #7dcfff; +$white: #a9b1d6; + +$b_black: #414868; +$b_red: #f7768e; +$b_green: #9ece6a; +$b_yellow: #e0af68; +$b_blue: #7aa2f7; +$b_magenta: #bb9af7; +$b_cyan: #7dcfff; +$b_white: #c0caf5;