diff --git a/config/config.nix b/config/config.nix index ef86b27..b2f0592 100644 --- a/config/config.nix +++ b/config/config.nix @@ -64,10 +64,8 @@ in fonts = { enableDefaultPackages = true; packages = with pkgs; [ + # Monospace victor-mono - noto-fonts - noto-fonts-cjk - noto-fonts-emoji jetbrains-mono (nerdfonts.override { fonts = [ @@ -78,7 +76,18 @@ in }) font-awesome emacs-all-the-icons-fonts + + # Regular + noto-fonts + noto-fonts-cjk + noto-fonts-emoji source-sans-pro + + # Display + quicksand + + # Unicode + symbola ]; fontconfig = { diff --git a/config/packages.nix b/config/packages.nix index a5dd0e5..5415814 100644 --- a/config/packages.nix +++ b/config/packages.nix @@ -5,9 +5,11 @@ ffmpeg openssl jaq + socat git wget libnotify + inotify-tools ripgrep unzip tldr diff --git a/home-manager/wayland/eww/eww.scss b/home-manager/wayland/eww/eww.scss new file mode 100644 index 0000000..4cd454f --- /dev/null +++ b/home-manager/wayland/eww/eww.scss @@ -0,0 +1,247 @@ +$background: #1a1626; +$background-alt: #252630; +$background-scale: #414868; +$foreground: #c0caf5; +$foreground-alt: #8189af; +$foreground-light: #343b58; +$disabled: #565f89; +$accent: #f6a8cf; + +* { + all: unset; +} + +@mixin segment { + background-color: $background-alt; + border-radius: 20px; + margin: 0px 2px; +} + +.bar { + background-color: $background; + font-family: "Quicksand Medium"; + color: $foreground; + font-size: 13; + + padding: 3px 10px; +} + + +tooltip { + background-color: rgba(0,0,0,0.8); + font-family: "JetBrains Mono"; + font-size: 12; +} + +.detail { + font-size: 10; + color: $foreground-alt; +} + +.bar-sep { + margin: 0px 5px; + color: $background-alt; + font-size: 22; +} + +.bar-window { + margin-left: 4px; + + .detail { + margin-top: 1px; + } + + :not(.detail) { + margin-top: -2px; + margin-left: 1px; + } +} + +.bar-time { + @include segment; + font-size: 15; + padding: 0px 10px; + + .bar-date { + margin-top: 1px; + font-size: 11; + color: #a9b1d6; + } +} + +.bar-music { + @include segment; + padding-left: 3px; + padding-right: 10px; + + .progress { + margin-right: 5px; + + .back { + color: $background-scale; + } + + .front { + color: $accent; + } + } + + .symbol.paused { + font-size: 12; + margin-left: 2px; + } + + .symbol.playing { + font-size: 15; + margin-top: 1px; + } + + .bar-artist { + margin-top: 1px; + margin-bottom: -2px; + } + + .bar-title { + margin-bottom: -3px; + } +} + + +.bar-workspaces { + @include segment; + padding: 3px 5px; + + .workspace-button { + padding: 0px 5px; + border-radius: 20px; + color: #51587a; + + &.occupied { + background-color: $background-scale; + color: $accent; + } + + &.current + box { + padding: 0px 7px; + border-radius: 20px; + background-color: $accent; + color: $foreground-light; + } + + &.occupied.previous-occupied { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + &.occupied.next-occupied { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + } +} + +scale trough { + background-color: $background-scale; + border-radius: 5px; + min-height: 6px; + min-width: 60px; + margin: 0px 5px; + + highlight { + border-radius: 5px; + } +} + +.bar-scale { + margin: 0px 5px; + + .percent { + margin-top: -1px; + margin-left: 3px; + } +} + +@mixin scale-color($color) { + color: $color; + + scale highlight { + background-color: $color; + } +} + +.bar-volume { + @include scale-color(#7dcfff); + &.muted { @include scale-color(#f7768e); } + + .symbol { + margin-right: 6px; + font-size: 17; + } +} + +.bar-brightness { + @include scale-color(#e0af68); + + .symbol { + margin-right: 7px; + font-size: 14; + } +} + +.bar-internet { + margin-left: 7px; + margin-right: 10px; + font-family: "JetBrains Mono"; + color: #bb9af7; + font-size: 12; + + &.disabled { + color: $disabled; + } + + .symbol { + font-size: 15; + } +} + +.bar-bluetooth { + margin: 0px 5px; + font-size: 15; + color: #7aa2f7; + + &.disabled { + color: $disabled; + } +} + + +.bar-circular { + margin: 0px 4px; + font-size: 15; + + .back { + color: $background-scale; + } + + &.critical { + color: #f7768e; + } +} + +.bar-battery { + color: #8ece6a; +} + +.bar-ram { + color: #b4f9f8; + .symbol { + margin-right: 5px; + } +} + +.bar-storage { + color: #cfc9c2; + .symbol { + margin-right: 1px; + } +} diff --git a/home-manager/wayland/eww/eww.yuck b/home-manager/wayland/eww/eww.yuck new file mode 100644 index 0000000..70728d7 --- /dev/null +++ b/home-manager/wayland/eww/eww.yuck @@ -0,0 +1,339 @@ + +;; Window Info + +(deflisten window :initial "{}" + "~/.config/eww/scripts/active-window") + +(defwidget bar_window [] + (box :class "bar-window" + :orientation "v" + :space-evenly false + (label :class "detail" + :halign "start" + :valign "end" + :limit-width 60 + :text {window.class ?: "Desktop"}) + (label :halign "start" + :valign "end" + :limit-width 55 + :text {window.title ?: "Workspace ${current_workspace}"}))) + +;; Workspaces + +(deflisten workspaces :initial "[]" + "~/.config/eww/scripts/get-workspaces") +(deflisten current_workspace :initial "1" + "~/.config/eww/scripts/get-active-workspace") + +(defwidget bar_workspace_button [workspace] + (eventbox :onclick "hyprctl dispatch workspace ${workspace.id}" + (overlay + (box :class "segment workspace-button ${workspace.id == current_workspace ? "current" : "" +} ${workspace.windows > 0 ? "occupied" : "empty" +} ${workspace.previous > 0 ? "previous-occupied" : "previous-empty" +} ${workspace.next > 0 ? "next-occupied" : "next-empty"}" + (label :text "${workspace.id}")) + (box :visible {workspace.id == current_workspace} + (label :text "${workspace.id}"))))) + +(defwidget bar_workspaces [] + (eventbox :onscroll "~/.config/eww/scripts/change-active-workspace {} ${current_workspace}" + (box :class "bar-workspaces" + :orientation "h" + :space-evenly true + (for workspace in workspaces + (bar_workspace_button :workspace workspace))))) + +;; Time + +(defwidget bar_time [] + (box :class "bar-time segment" + :orientation "h" + :width 195 + :space-evenly false + :spacing 6 + (label :halign "start" + :yalign 0.5 + :text {formattime(EWW_TIME, "%H:%M:%S")}) + (label :class "bar-date" + :halign "end" + :hexpand true + :yalign 0.5 + :text {formattime(EWW_TIME, "%A, %Y-%m-%d")}))) + +;; Playerctl + +(deflisten mpris :initial "{}" + "~/.config/eww/scripts/mpris-metadata") + +(defwidget bar_music [] + (eventbox :onclick "playerctl play-pause" + (box :class "bar-music segment" + :orientation "h" + :width 195 + :space-evenly false + (box :class "progress" + (overlay + (circular-progress :class "back" + :value 100 + :width 25 + :thickness 2 + :start-at 75 + :clockwise true) + (circular-progress :class "front" + :value {(mpris.position ?: 0) == 0 || + (mpris.duration ?: 0) == 0 ? 0 : + mpris.position > mpris.duration ? 100 : + mpris.position / mpris.duration * 100} + :width 25 + :thickness 2 + :start-at 75 + :clockwise true) + (label :class "symbol ${mpris.status == "Playing" ? "playing" : "paused"}" + :text {mpris.status == "Playing" ? "󰏤" : ""}))) + (box :orientation "v" + :space-evenly false + :visible {(mpris.title ?: "") != "" && (mpris.status ?: "Stopped") != "Stopped"} + (label :class "bar-artist detail" + :halign "start" + :limit-width 30 + :text {mpris.artist ?: ""}) + (label :halign "bar-title" + :limit-width 25 + :text {mpris.title ?: ""}))))) + +;; Scales (Volume + Brightness) + + +(defwidget bar_scale [?class reveal_name reveal value symbol onchange ?onclick] + (eventbox :onhover "${EWW_CMD} update ${reveal_name}=true" + :onhoverlost "${EWW_CMD} update ${reveal_name}=false" + (box :class "bar-scale ${class}" + :space-evenly false + (eventbox :onclick onclick + (label :class "symbol" + :text symbol)) + (revealer :transition "slideright" + :reveal reveal + :duration "400ms" + (scale :value value + :orientation "h" + :width "100%" + :min 0 + :max 101 + :onchange onchange)) + (label :class "percent" + :width 29 + :xalign 0 + :text "${value}%")))) + + +(defvar volume_reveal false) +(deflisten volume :initial "0" + "~/.config/eww/scripts/volume") + +(defwidget bar_volume [] + (bar_scale :class "bar-volume ${volume.muted ? "muted" : ""}" + :reveal_name "volume_reveal" + :reveal volume_reveal + :value {volume.value} + :symbol {volume.muted ? "󰝟" : + volume.value < 5 ? "󰖁" : + volume.value < 35 ? "󰕿" : + volume.value < 66 ? "󰖀" : "󰕾"} + :onchange "pamixer --set-volume {}" + :onclick "pamixer -t")) + + +(defvar brightness_reveal false) +(deflisten brightness :initial "0" + "~/.config/eww/scripts/brightness") + +(defwidget bar_brightness [] + (bar_scale :class "bar-brightness" + :reveal_name "brightness_reveal" + :reveal brightness_reveal + :value brightness + :symbol "󰃠" + :onchange "brightnessctl -e s {}%")) + + +;; Circular Indicators + +(defwidget bar_circular [?class ?critical ?tooltip value symbol] + (box :class "bar-circular ${class} ${(critical ?: false) ? "critical" : ""}" + :orientation "h" + :tooltip tooltip + (overlay + (circular-progress :class "back" + :value 100 + :width 25 + :thickness 2 + :start-at 75 + :clockwise true) + (circular-progress :class "front" + :value value + :width 25 + :thickness 2 + :start-at 75 + :clockwise true) + (label :class "symbol" + :text symbol)))) + +(defwidget bar_battery [] + (bar_circular :class "bar-battery" + :critical {EWW_BATTERY["BAT1"].capacity <= 15} + :tooltip "Battery: ${EWW_BATTERY["BAT1"].capacity}%" + :value {EWW_BATTERY["BAT1"].capacity} + :symbol {EWW_BATTERY["BAT1"].status == "Charging" ? "󰂄" : "󰁹"})) + +(defwidget bar_ram [] + (bar_circular :class "bar-ram" + :critical {EWW_RAM.used_mem_perc >= 95} + :tooltip "RAM: ${round(EWW_RAM.used_mem / 1073741824, 2) +} / ${round(EWW_RAM.total_mem / 1073741824, 2)} GB (${round(EWW_RAM.used_mem_perc, 2)}%)" + :value {EWW_RAM.used_mem_perc} + :symbol "")) + +(defwidget bar_storage [] + (bar_circular :class "bar-storage" + :tooltip "Storage: ${round(EWW_DISK["/"].used / 1073741824, 2) +} / ${round(EWW_DISK["/"].total / 1073741824, 0)} GB (${round(EWW_DISK["/"].used_perc, 2)}%)" + :value {EWW_DISK["/"].used_perc} + :symbol "󰆼")) + +;; Network + +(defpoll wifi :interval "2s" :initial "{}" + "~/.config/eww/scripts/network 802-11-wireless") +(defpoll ethernet :interval "2s" :initial "{}" + "~/.config/eww/scripts/network 802-3-ethernet") +(defpoll bluetooth :interval "2s" :initial "{}" + "~/.config/eww/scripts/network bluetooth") + +(defvar internet_reveal false) + +(defwidget bar_internet [] + (box :class "bar-internet ${wifi == "{}" && ethernet == "{}" ? "disabled" : ""}" + :space-evenly false + :tooltip {ethernet != "{}" ? "${ethernet.name} (${ethernet.device}, ${EWW_NET[ethernet.device].up}B up, ${EWW_NET[ethernet.device].down}B down)" : + wifi != "{}" ? "${wifi.name} (${wifi.device}, ${EWW_NET[wifi.device].NET_UP}B up, ${EWW_NET[wifi.device].NET_DOWN}B down)" : ""} + (label :class "symbol" + :text {wifi != "{}" ? "󰤨" : ethernet != "{}" ? "󰈁" : "󰤮"}))) + +(defwidget bar_bluetooth [] + (box :class "bar-bluetooth ${bluetooth == "{}" ? "disabled" : ""}" + (label :limit-width 15 + :text {bluetooth != "{}" ? "󰂯" : "󰂲"}))) + +;; Separator + +(defwidget bar_sep [] + (box :class "bar-sep" + :hexpand false + :vexpand false + (label :text "|"))) + + +;; Bar Layout + +(defwidget bar_left_edge [] + (box :orientation "h" + :space-evenly false + :halign "start" + :hexpand true + (bar_window))) + +(defwidget bar_left_wing [] + (box :orientation "h" + :space-evenly false + :halign "end" + :hexpand true + (bar_time))) + +(defwidget bar_center [] + (bar_workspaces)) + +(defwidget bar_right_wing [] + (box :orientation "h" + :space-evenly false + :hexpand true + (bar_music))) + +(defwidget laptop_bar_right_edge [] + (box :orientation "h" + :space-evenly false + :halign "end" + :hexpand true + (bar_volume) + (bar_brightness) + (bar_sep) + (bar_bluetooth) + (bar_internet) + (bar_sep) + (bar_storage) + (bar_ram) + (bar_battery))) + +(defwidget laptop_bar_layout [] + (centerbox :class "bar" + :orientation "h" + (box :orientation "h" + :space-evenly false + :hexpand true + (bar_left_edge) + (bar_left_wing)) + (bar_center) + (box :orientation "h" + :space-evenly false + (bar_right_wing) + (laptop_bar_right_edge)))) + + +(defwidget desktop_bar_right_edge [] + (box :orientation "h" + :space-evenly false + :halign "end" + :hexpand true + (bar_volume) + (bar_sep) + (bar_bluetooth) + (bar_internet) + (bar_sep) + (bar_storage) + (bar_ram))) + +(defwidget desktop_bar_layout [] + (centerbox :class "bar" + :orientation "h" + (box :orientation "h" + :space-evenly false + :hexpand true + (bar_left_edge) + (bar_left_wing)) + (bar_center) + (box :orientation "h" + :space-evenly false + (bar_right_wing) + (laptop_bar_right_edge)))) + + +(defwindow laptop_bar + :geometry (geometry :width "100%" + :height "3%" + :anchor "top center") + :namespace "eww-bar" + :stacking "fg" + :exclusive true + :focusable false + (laptop_bar_layout)) + +(defwindow desktop_bar + :geometry (geometry :width "100%" + :height "3%" + :anchor "top center") + :namespace "eww-bar" + :stacking "fg" + :exclusive true + :focusable false + (desktop_bar_layout)) diff --git a/home-manager/wayland/eww/scripts/active-window b/home-manager/wayland/eww/scripts/active-window new file mode 100755 index 0000000..07ace63 --- /dev/null +++ b/home-manager/wayland/eww/scripts/active-window @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +hyprctl activewindow -j | jaq -c . +socat -u UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | + stdbuf -o0 grep -ose '^activewindow>>' | while read -r line; do + hyprctl activewindow -j | jaq -c . +done diff --git a/home-manager/wayland/eww/scripts/brightness b/home-manager/wayland/eww/scripts/brightness new file mode 100755 index 0000000..db5358b --- /dev/null +++ b/home-manager/wayland/eww/scripts/brightness @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +brightnessctl -me | cut -d, -f4 | tr -d '%' +device=$(brightnessctl -m | head -n 1 | cut -d, -f1) +while true; do + inotifywait /sys/class/backlight/"$device"/brightness &>/dev/null + brightnessctl -me | cut -d, -f4 | tr -d '%' +done diff --git a/home-manager/wayland/eww/scripts/change-active-workspace b/home-manager/wayland/eww/scripts/change-active-workspace new file mode 100755 index 0000000..5c3ee89 --- /dev/null +++ b/home-manager/wayland/eww/scripts/change-active-workspace @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +direction=$1 +current=$2 + +min=1 +max=10 + +function clamp { + if test $1 -lt $min; then + echo $min + elif test $1 -gt $max; then + echo $max + else + echo $1 + fi +} + +if test "$direction" = "down" +then + target=$(clamp $(($current+1))) + echo "jumping to $target" + hyprctl dispatch workspace $target +elif test "$direction" = "up" +then + target=$(clamp $(($current-1))) + echo "jumping to $target" + hyprctl dispatch workspace $target +fi diff --git a/home-manager/wayland/eww/scripts/get-active-workspace b/home-manager/wayland/eww/scripts/get-active-workspace new file mode 100755 index 0000000..048bb63 --- /dev/null +++ b/home-manager/wayland/eww/scripts/get-active-workspace @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +hyprctl monitors -j | jaq '.[] | select(.focused) | .activeWorkspace.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/home-manager/wayland/eww/scripts/get-workspaces b/home-manager/wayland/eww/scripts/get-workspaces new file mode 100755 index 0000000..9e3a82e --- /dev/null +++ b/home-manager/wayland/eww/scripts/get-workspaces @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +workspaces() { + hyprctl workspaces -j | jaq -c 'map({key: .id | tostring, value: .windows}) | from_entries + | . as $windows | [range(1;11) | {id: tostring, windows: $windows[tostring] // 0, + previous: $windows[.-1|tostring] // 0, next: $windows[.+1|tostring] // 0 }]' +} + +workspaces +socat -u UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | while read -r line; do + workspaces +done diff --git a/home-manager/wayland/eww/scripts/mpris-metadata b/home-manager/wayland/eww/scripts/mpris-metadata new file mode 100755 index 0000000..c4593dc --- /dev/null +++ b/home-manager/wayland/eww/scripts/mpris-metadata @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +playerctl metadata --follow --format \ +'{"playerName": "{{ playerName }}",'\ +'"status": "{{ status }}",'\ +'"artist": "{{ artist }}",'\ +'"title": "{{ title }}",'\ +'"album": "{{ album }}",'\ +'"position": "{{ position }}",'\ +'"position_d": "{{ duration(position) }}",'\ +'"duration": "{{ mpris:length }}",'\ +'"duration_d": "{{ duration(mpris:length) }}"}' diff --git a/home-manager/wayland/eww/scripts/network b/home-manager/wayland/eww/scripts/network new file mode 100755 index 0000000..42715ff --- /dev/null +++ b/home-manager/wayland/eww/scripts/network @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +nmcli -t c show --active | awk -F ':' -e \ + '// { if ($3 == "'$1'") { + print "{\"name\":\""$1"\",\"device\":\""$4"\"}"; + found = 1; + exit; + }} + END { if (!found) print "{}" }' diff --git a/home-manager/wayland/eww/scripts/volume b/home-manager/wayland/eww/scripts/volume new file mode 100755 index 0000000..783efa6 --- /dev/null +++ b/home-manager/wayland/eww/scripts/volume @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +volume() { + echo '{"value":'$(pamixer --get-volume)',"muted":"'$(pamixer --get-mute)'"}' +} + +volume +pw-mon -oa | stdbuf -o0 grep -os "changed:" | while read -r line; do + volume +done diff --git a/home-manager/wayland/hyprland.nix b/home-manager/wayland/hyprland.nix index eb8a359..591239c 100644 --- a/home-manager/wayland/hyprland.nix +++ b/home-manager/wayland/hyprland.nix @@ -1,8 +1,9 @@ { config, pkgs, lib, ... }: let -scripts = ../../scripts; -modifier = "SUPER"; -terminal = "alacritty"; + inherit (config) platform; + scripts = ../../scripts; + modifier = "SUPER"; + terminal = "alacritty"; in { home.packages = with pkgs; [ swaybg @@ -11,7 +12,7 @@ in { wayland.windowManager.hyprland = { enable = true; - enableNvidiaPatches = config.platform == "desktop"; + enableNvidiaPatches = platform == "desktop"; systemd.enable = true; xwayland.enable = true; @@ -21,7 +22,7 @@ in { "$menu" = "rofi -show drun"; exec-once = [ - "background=${../../assets/background.png} ${scripts}/autostart" + "background=${../../assets/background.png} platform=${platform} ${scripts}/autostart" ]; general = { @@ -149,7 +150,7 @@ in { audio-disp = "${scripts}/multimedia Volume pamixer $(pamixer --get-volume)"; audio = cmd: "pamixer ${cmd} && ${audio-disp}"; # Brightness using brightnessctl - brightness-disp = ''${scripts}/multimedia Brightness brightnessctl $(brightnessctl -e -m | cut -d "," -f4 | tr -d "%")''; + brightness-disp = ''${scripts}/multimedia Brightness brightnessctl $(brightnessctl -e -m | cut -d, -f4 | tr -d "%")''; brightness = x: "brightnessctl -e set ${x} && ${brightness-disp}"; in [ # XF86 key bindings diff --git a/home-manager/wayland/wltools.nix b/home-manager/wayland/wltools.nix index bc49278..83dae62 100644 --- a/home-manager/wayland/wltools.nix +++ b/home-manager/wayland/wltools.nix @@ -57,6 +57,12 @@ xdg.configFile.rofi.source = ./rofi; + # EWW + + programs.eww.enable = true; + programs.eww.package = pkgs.eww-wayland; + programs.eww.configDir = ./eww; + # Mako services.mako = { diff --git a/scripts/autostart b/scripts/autostart index f3b1942..3917f4b 100755 --- a/scripts/autostart +++ b/scripts/autostart @@ -1,9 +1,12 @@ #!/usr/bin/env bash +# Background swaybg -o '*' -i "$background" -m fill & -waybar & +# Status Bar +eww open "$platform"_bar --screen 0 +# Idle Handler swayidle timeout 120 'swaylock -f --grace=180' \ timeout 600 'systemctl suspend' \ before-sleep 'swaylock -f' &