452 lines
13 KiB
Nix
452 lines
13 KiB
Nix
{ 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-manager’s 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" ];
|
||
};
|
||
};
|
||
}
|