nixos-configuration/users/jalr/modules/sway/waybar.nix
2025-04-16 22:54:28 +02:00

452 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;
solarizedColors = as: lib.strings.concatLines (lib.attrsets.mapAttrsToList (name: value: let color = solarized."${value}".hex; in "@define-color ${name} ${color};") as);
in
{
# home-managers waybar module performs additional checks that are overly strict
xdg.configFile."waybar/config".text =
let
makoctl = "${pkgs.mako}/bin/makoctl";
in
lib.generators.toJSON { } {
layer = "top";
output = [
"!HEADLESS-1"
"*"
];
position = "top";
height = 24;
modules-center = [ ];
modules-left = [
"sway/workspaces"
"sway/mode"
];
modules-right = [
"tray"
"custom/screencast"
"custom/redshift"
"idle_inhibitor"
"custom/pomodoro"
"backlight"
"pulseaudio"
"network"
"memory"
"cpu"
"temperature"
"battery"
"clock"
"custom/dnd"
];
"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;
};
"custom/pomodoro" = let uairctl = "${pkgs.uair}/bin/uairctl"; in {
# We need to remove nul-characters
# See https://github.com/metent/uair/issues/15
exec = pkgs.writeShellScript "uairctl-without-null-characters" ''
${uairctl} listen -o waybar | ${pkgs.gnused}/bin/sed --unbuffered 's/\x0//g'
'';
on-click = "${uairctl} toggle";
on-scroll-up = "${uairctl} next";
on-scroll-down = "${uairctl} prev";
on-click-middle = "${uairctl} finish";
menu = "on-click-right";
/*
menu-actions = {
"work 1" = "${uairctl} jump 0";
"break 1" = "${uairctl} jump 1";
"work 2" = "${uairctl} jump 2";
"break 2" = "${uairctl} jump 3";
"work 3" = "${uairctl} jump 4";
"break 3" = "${uairctl} jump 5";
"work 4" = "${uairctl} jump 6";
"long break" = "${uairctl} jump 7";
};
*/
return-type = "json";
};
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("󱒃")
subprocess.run(["${makoctl}", "mode", "-a" "screencast"], stdout=subprocess.DEVNULL)
else:
print()
subprocess.run(["${makoctl}", "mode", "-r" "screencast"], stdout=subprocess.DEVNULL)
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%-";
};
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";
};
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/dnd" = {
exec = pkgs.writeShellScript "waybar-dnd-widget" ''
export PATH=${pkgs.lib.makeBinPath [pkgs.gnugrep]}
if ${makoctl} mode | grep -Fxq 'dnd'; then
echo '{"text": "󰂛", "class": "active"}'
else
echo '{"text": "󰂚", "class": "inactive"}'
fi
'';
on-click = "/etc/profiles/per-user/jalr/bin/dnd on";
on-click-right = "/etc/profiles/per-user/jalr/bin/dnd off";
interval = 10;
return-type = "json";
};
};
xdg.configFile = {
"waybar/theme-light.css".text = solarizedColors {
base00 = "base3";
base01 = "base2";
base02 = "base1";
base03 = "base0";
base04 = "base00";
base05 = "base01";
base06 = "base02";
base07 = "base03";
base08 = "red";
base09 = "orange";
base0A = "yellow";
base0B = "green";
base0C = "cyan";
base0D = "blue";
base0E = "violet";
base0F = "magenta";
};
"waybar/theme-dark.css".text = solarizedColors {
base00 = "base03";
base01 = "base02";
base02 = "base01";
base03 = "base00";
base04 = "base0";
base05 = "base1";
base06 = "base2";
base07 = "base3";
base08 = "red";
base09 = "orange";
base0A = "yellow";
base0B = "green";
base0C = "cyan";
base0D = "blue";
base0E = "violet";
base0F = "magenta";
};
"waybar/style.css".text = ''
@import "theme.css";
* {
border-radius: 0;
border: none;
font-family: "Iosevka Nerd Font";
font-size: 14px;
min-height: 0;
transition-property: none;
}
window#waybar {
background-color: @base00;
color: @base04;
}
#workspaces button {
padding: 0 5px;
background-color: @base00;
color: inherit;
border-bottom: 2px solid transparent;
}
#workspaces button:hover {
background: @base01;
box-shadow: inherit;
text-shadow: inherit;
}
#workspaces button.focused {
border-bottom: 2px solid @base0B;
}
#workspaces button.urgent {
background-color: @base08;
}
#mode {
background-color: @base01;
font-style: italic;
}
/* all modules on the right */
#waybar > box > box:nth-child(3) > widget > label {
padding: 0 10px;
}
#battery.charging {
color: @base01;
background-color: @base0B;
}
@keyframes blink {
to {
background-color: @base07;
color: @base03;
}
}
#battery.critical:not(.charging),
#temperature.critical {
background-color: @base08;
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: @base0C;
color: @base01
}
#memory {
background-color: @base0A;
color: @base01
}
#backlight {
background-color: @base07;
color: @base03;
}
#network {
background-color: @base0E;
color: @base01
}
#network.disconnected {
background-color: @base08;
}
#pulseaudio {
background-color: @base07;
color: @base03;
}
#pulseaudio.muted {
background-color: @base00;
color: @base04;
}
#temperature {
background-color: @base0F;
color: @base01;
}
#idle_inhibitor.activated {
background-color: @base07;
color: @base00;
}
#custom-redshift {
color: @base01;
}
#custom-redshift.active {
background-color: @base08;
}
#custom-redshift.inactive {
background-color: @base0D;
}
#tray {
padding: 0 5px;
}
#custom-notification_inhibitor.active {
background-color: @base07;
color: @base00;
}
#custom-screencast {
background-color: @base08;
color: @base00;
animation-name: blink;
animation-duration: 1s;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
animation-direction: alternate;
}
#custom-pomodoro.resumed {
color: @base01;
background-color: @base0B;
}
#custom-dnd.active {
color: @base01;
background-color: @base0B;
}
'';
};
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" ];
};
};
}