nixos-configuration/home-manager/modules/sway/waybar.nix
2023-06-11 15:30:27 +00:00

459 lines
13 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{ config, lib, nixosConfig, pkgs, ... }:
let
watchUserUnitState = unit: started: stopped: pkgs.writeShellScript "watch-user-unit-${unit}-state" ''
${pkgs.systemd}/bin/journalctl --user -u ${unit} -t systemd -o cat -f \
| ${pkgs.gnugrep}/bin/grep --line-buffered -Eo '^(Started|Stopped)' \
| ${pkgs.jq}/bin/jq --unbuffered -Rc 'if . == "Started" then ${builtins.toJSON started} else ${builtins.toJSON stopped} end'
'';
toggleUserUnitState = unit: pkgs.writeShellScript "toggle-user-unit-${unit}-state" ''
if ${pkgs.systemd}/bin/systemctl --user show ${unit} | ${pkgs.gnugrep}/bin/grep -q ActiveState=active; then
${pkgs.systemd}/bin/systemctl --user stop ${unit}
else
${pkgs.systemd}/bin/systemctl --user start ${unit}
fi
'';
# for fine-grained control over spacing
thinsp = " ";
solarized = import ../solarized.nix;
in
{
# home-managers waybar module performs additional checks that are overly strict
xdg.configFile."waybar/config".text = lib.generators.toJSON { } {
layer = "top";
position = "top";
height = 24;
modules-center = [ ];
modules-left = [
"sway/workspaces"
"sway/mode"
];
modules-right = [
"tray"
"custom/screencast"
"custom/redshift"
"idle_inhibitor"
"backlight"
"mpd"
"pulseaudio"
"network"
"custom/vpn"
"memory"
"cpu"
"temperature"
"battery"
"clock"
"custom/calendar"
];
"sway/workspaces" = {
disable-scroll = true;
};
"sway/mode" = {
format = "{}";
};
tray = {
spacing = 5;
};
"custom/redshift" = {
exec = watchUserUnitState
"gammastep"
{ class = "active"; }
{ class = "inactive"; };
on-click = toggleUserUnitState "gammastep";
return-type = "json";
format = "󰌵";
tooltip = false;
};
idle_inhibitor = {
format = "{icon}";
format-icons = {
activated = "󰈈 ";
deactivated = "󰈉 ";
};
};
"custom/screencast" = {
exec = pkgs.writeScript "screencast-monitor" /* python */ ''
#!${pkgs.python3}/bin/python3
import subprocess
import sys
active_outputs = 0
with subprocess.Popen(
["${pkgs.coreutils}/bin/stdbuf", "-o0", "${nixosConfig.services.pipewire.package}/bin/pw-link", "-m", "-o", "xdg-desktop-portal-wlr"],
stdout=subprocess.PIPE,
text=True,
) as proc:
for line in proc.stdout:
action = line.split(" ")[0]
if action == "=" or action == "+":
active_outputs += 1
elif action == "-":
active_outputs -= 1
else:
print(f"Invalid action {action} (in line {line})", file=sys.stderr)
if active_outputs > 0:
print("󱒃")
else:
print()
sys.stdout.flush()
'';
format = "{}";
tooltip = false;
};
backlight = {
format = "{percent}% {icon}";
format-icons = [ "󰛩" "󱩎" "󱩏" "󱩐" "󱩑" "󱩒" "󱩓" "󱩔" "󱩕" "󱩖" "󰛨" ];
on-scroll-up = "${pkgs.brightnessctl}/bin/brightnessctl -q set +5%";
on-scroll-down = "${pkgs.brightnessctl}/bin/brightnessctl -q set 5%-";
};
mpd = {
server = config.services.mpd.network.listenAddress;
format = "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) 󰎈";
format-disconnected = "Disconnected 󰎈";
format-stopped = "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped 󰎈";
unknown-tag = "N/A";
interval = 2;
tooltip-format = "MPD (connected)";
tooltip-format-disconnected = "MPD (disconnected)";
on-scroll-up = "${pkgs.mpc_cli}/bin/mpc -q -h ${config.services.mpd.network.listenAddress} volume +2";
on-scroll-down = "${pkgs.mpc_cli}/bin/mpc -q -h ${config.services.mpd.network.listenAddress} volume -2";
title-len = 48;
artist-len = 24;
consume-icons = {
on = "󰩫 ";
};
random-icons = {
off = "󰒞 ";
on = "󰒝 ";
};
repeat-icons = {
on = "󰑖 ";
};
single-icons = {
on = "󰑘 ";
};
state-icons = {
paused = "󰏤 ";
playing = "󰐊 ";
};
};
pulseaudio = {
format = "{volume}% {icon} {format_source}";
format-bluetooth = "{volume}% {icon}󰗾{format_source}";
format-bluetooth-muted = "{icon}󰗿{format_source}";
format-muted = "󰝟 {format_source}";
format-source = "{volume}% ${thinsp}";
format-source-muted = "${thinsp}";
format-icons = {
car = "󰄋 ";
default = [ "󰕿" "󰖀" "󰕾" ];
hands-free = "󰋎";
headphone = "󰋋";
headset = "󰋎";
phone = "󰏲";
portable = "󰏲";
};
on-click-right = "${pkgs.pavucontrol}/bin/pavucontrol";
};
network = {
format-wifi = "{essid} ({signalStrength}%) 󰖩 ";
format-ethernet = "{ipaddr}/{cidr} 󰈀 ";
format-linked = "{ifname} (No IP) 󰈀 ";
format-disconnected = "Disconnected ";
format-alt = "{ifname}: {ipaddr}/{cidr}";
tooltip = false;
on-click-right = "${config.programs.alacritty.package}/bin/alacritty -e ${pkgs.networkmanager}/bin/nmtui";
};
"custom/vpn" = {
interval = 10;
exec = pkgs.writeShellScript "vpn-state" ''
${pkgs.iproute}/bin/ip -j link \
| ${pkgs.jq}/bin/jq --unbuffered --compact-output '
[[.[].ifname | select(. | startswith("mullvad"))][] | split("-")[1] + " 󰌾${thinsp}"] as $conns
| { text: ($conns[0] // ""), class: (if $conns | length > 0 then "connected" else "disconnected" end) }'
'';
return-type = "json";
format = "{}";
tooltip = false;
};
memory = {
interval = 2;
format = "{:2}% 󰍛 ";
};
cpu = {
interval = 2;
format = "{usage:2}% ";
tooltip = false;
};
temperature = {
critical-threshold = 80;
format = "{temperatureC}°C {icon}";
format-icons = [ "" "" "" "" "" ];
} // (lib.optionalAttrs (nixosConfig.networking.hostName == "mayushii") {
hwmon-path = "/sys/class/hwmon/hwmon3/temp1_input";
});
battery = {
interval = 5;
format = "{capacity}% {icon}";
format-charging = "{capacity}% ";
format-plugged = "{capacity}% x";
format-alt = "{time} {icon}";
format-icons = [ "󰂎" "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹" ];
states = {
critical = 15;
good = 95;
warning = 30;
};
};
clock = {
format = "{:%H:%M %Z}";
format-alt = "{:%Y-%m-%d (%a)}";
tooltip-format = "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>";
};
"custom/calendar" = {
interval = 300;
exec = pkgs.writeScript "calendar" /* python */ ''
#!${pkgs.python3}/bin/python3
import json
import subprocess
def khal(args):
completed = subprocess.run(["${pkgs.khal}/bin/khal"] + args, capture_output=True)
assert completed.returncode == 0
return completed.stdout.decode("utf-8")
events_today = khal(["list", "today", "today", "-df", "", "-f", "{title}"]).rstrip().split("\n")
events_2d = khal(["list", "today", "tomorrow", "-df", "<b>{name}, {date}</b>"]).rstrip()
if len(events_today) == 1 and events_today[0] == "No events":
events_today = []
if len(events_today) == 0:
text = "󰃮 "
else:
text = f"{len(events_today)} 󰃶 "
print(
json.dumps(
{
"class": "active" if len(events_today) > 0 else "",
"text": text,
"tooltip": events_2d,
}
)
)
'';
return-type = "json";
format = "{}";
};
};
xdg.configFile."waybar/style.css".text = ''
* {
border-radius: 0;
border: none;
font-family: "Iosevka Nerd Font";
font-size: 14px;
min-height: 0;
transition-property: none;
}
window#waybar {
background-color: ${solarized.base03.hex};
color: ${solarized.base0.hex};
}
#workspaces button {
padding: 0 5px;
background-color: ${solarized.base03.hex};
color: inherit;
border-bottom: 2px solid transparent;
}
#workspaces button:hover {
background: ${solarized.base02.hex};
box-shadow: inherit;
text-shadow: inherit;
}
#workspaces button.focused {
border-bottom: 2px solid ${solarized.green.hex};
}
#workspaces button.urgent {
background-color: ${solarized.red.hex};
}
#mode {
background-color: ${solarized.base02.hex};
font-style: italic;
}
/* all modules on the right */
#waybar > box > box:nth-child(3) > widget > label {
padding: 0 10px;
}
#battery.charging {
color: ${solarized.base02.hex};
background-color: ${solarized.green.hex};
}
@keyframes blink {
to {
background-color: ${solarized.base3.hex};
color: ${solarized.base00.hex};
}
}
#battery.critical:not(.charging),
#temperature.critical {
background-color: ${solarized.red.hex};
animation-name: blink;
animation-duration: 0.5s;
/* FIXME use nearest neighbor interpolation if possible */
animation-timing-function: cubic-bezier(1, 0, 0, 1);
animation-iteration-count: infinite;
animation-direction: alternate;
}
#cpu {
background-color: ${solarized.cyan.hex};
color: ${solarized.base02.hex}
}
#memory {
background-color: ${solarized.yellow.hex};
color: ${solarized.base02.hex}
}
#backlight {
background-color: ${solarized.base3.hex};
color: ${solarized.base00.hex};
}
#network {
background-color: ${solarized.violet.hex};
color: ${solarized.base02.hex}
}
#custom-vpn {
background-color: ${solarized.blue.hex};
color: ${solarized.base02.hex}
}
#network.disconnected {
background-color: ${solarized.red.hex};
}
#pulseaudio {
background-color: ${solarized.base3.hex};
color: ${solarized.base00.hex};
}
#pulseaudio.muted {
background-color: ${solarized.base03.hex};
color: ${solarized.base0.hex};
}
#temperature {
background-color: ${solarized.magenta.hex};
color: ${solarized.base02.hex};
}
#idle_inhibitor.activated {
background-color: ${solarized.base3.hex};
color: ${solarized.base03.hex};
}
#mpd {
background-color: ${solarized.green.hex};
color: ${solarized.base02.hex};
}
#mpd.disconnected {
background-color: ${solarized.red.hex};
}
#mpd.stopped {
background-color: ${solarized.orange.hex};
}
#mpd.paused {
background-color: ${solarized.yellow.hex};
}
#custom-redshift {
color: ${solarized.base02.hex};
}
#custom-redshift.active {
background-color: ${solarized.red.hex};
}
#custom-redshift.inactive {
background-color: ${solarized.blue.hex};
}
#tray {
padding: 0 5px;
}
#custom-notification_inhibitor.active {
background-color: ${solarized.base3.hex};
color: ${solarized.base03.hex};
}
#custom-screencast {
background-color: ${solarized.red.hex};
color: ${solarized.base03.hex};
animation-name: blink;
animation-duration: 0.5s;
animation-timing-function: cubic-bezier(1, 0, 0, 1);
animation-iteration-count: infinite;
animation-direction: alternate;
}
#custom-calendar.active {
background-color: ${solarized.base3.hex};
color: ${solarized.base00.hex};
}
'';
systemd.user.services.waybar = {
Unit = {
Description = "Highly customizable Wayland bar for Sway and Wlroots based compositors.";
Documentation = "https://github.com/Alexays/Waybar/wiki/";
PartOf = [ "sway-session.target" ];
};
Install.WantedBy = [ "sway-session.target" ];
Service = {
# ensure sway is already started, otherwise workspaces will not work
ExecStartPre = "${config.wayland.windowManager.sway.package}/bin/swaymsg";
ExecStart = "${pkgs.waybar}/bin/waybar";
ExecReload = "${pkgs.utillinux}/bin/kill -SIGUSR2 $MAINPID";
Restart = "on-failure";
RestartSec = "1s";
};
};
# TODO: remove when https://github.com/nix-community/home-manager/issues/2064
# is resolved
systemd.user.targets.tray = {
Unit = {
Description = "Home Manager System Tray";
Requires = [ "graphical-session-pre.target" ];
};
};
}