Compare commits

..

1 Commits

Author SHA1 Message Date
c053846adb fix(waybar): restore workspace clicks 2026-10-11 21:43:20 -04:00
7 changed files with 120 additions and 131 deletions

View File

@@ -30,32 +30,6 @@ in
home-manager.users.${config.user.name}.programs.waybar = { home-manager.users.${config.user.name}.programs.waybar = {
enable = true; enable = true;
# Waybar 0.15.0 sends old Hyprland workspace dispatch commands.
# Use the fix commit until a release includes upstream PR #5013.
package = pkgs.waybar.overrideAttrs {
src = pkgs.fetchFromGitHub {
owner = "Alexays";
repo = "Waybar";
rev = "05945748dccce28bf96d26d8f64a9e69a8dd49ba";
hash = "sha256-51R3mIt8cLNvh/X5qe9vOqeJCj0U9KRyemVE5y+OhiU=";
};
postUnpack =
let
libcava = pkgs.fetchFromGitHub {
owner = "LukashonakV";
repo = "cava";
tag = "0.10.7";
hash = "sha256-zkyj1vBzHtoypX4Bxdh1Vmwh967DKKxN751v79hzmgQ=";
};
in
''
pushd "$sourceRoot"
cp -R --no-preserve=mode,ownership ${libcava} subprojects/cava-0.10.7
patchShebangs .
popd
'';
};
# A user service bound to graphical-session.target, which uwsm activates, # A user service bound to graphical-session.target, which uwsm activates,
# so the bar comes up with the session and no compositor exec-once. # so the bar comes up with the session and no compositor exec-once.

View File

@@ -7,15 +7,6 @@
}: }:
# PipeWire as the desktop audio server. # PipeWire as the desktop audio server.
let let
volumeKey = keys: command: {
inherit keys;
repeating = true;
action = {
kind = "exec";
command = "${lib.getExe' pkgs.swayosd "swayosd-client"} --output-volume ${command}";
};
};
mediaKey = keys: command: { mediaKey = keys: command: {
inherit keys; inherit keys;
locked = true; locked = true;
@@ -39,17 +30,6 @@ in
}; };
registry.keybind.${config.networking.hostName} = { registry.keybind.${config.networking.hostName} = {
volume-up = volumeKey "XF86AudioRaiseVolume" "raise --max-volume 100";
volume-down = volumeKey "XF86AudioLowerVolume" "lower";
mute = {
keys = "XF86AudioMute";
locked = true;
action = {
kind = "exec";
command = "${lib.getExe' pkgs.swayosd "swayosd-client"} --output-volume mute-toggle";
};
};
media-play = mediaKey "XF86AudioPlay" "play-pause"; media-play = mediaKey "XF86AudioPlay" "play-pause";
media-next = mediaKey "XF86AudioNext" "next"; media-next = mediaKey "XF86AudioNext" "next";
media-previous = mediaKey "XF86AudioPrev" "previous"; media-previous = mediaKey "XF86AudioPrev" "previous";

View File

@@ -5,41 +5,39 @@
... ...
}: }:
# SwayOSD: a transient on-screen popup for volume and brightness changes. # SwayOSD: a transient on-screen popup for volume and brightness changes.
let
user = config.user.name;
in
{ {
options.modules.desktop.utilities.osd.enable = options.modules.desktop.utilities.osd.enable =
lib.mkEnableOption "the SwayOSD volume and brightness popup"; lib.mkEnableOption "the SwayOSD volume and brightness popup";
config = lib.mkIf config.modules.desktop.utilities.osd.enable { config = lib.mkIf config.modules.desktop.utilities.osd.enable {
environment.systemPackages = [ pkgs.swayosd ];
# The udev rule chgrps each backlight's brightness node to `video` and adds
# group write, so the server dims the panel without root.
# Membership below grants the running session that access.
services.udev.packages = [ pkgs.swayosd ];
users.users.${config.user.name}.extraGroups = [ "video" ];
# The server draws the popups, so it runs for the whole graphical session.
# It binds to the target uwsm activates.
home-manager.users.${config.user.name}.systemd.user.services.swayosd = {
Unit = {
Description = "SwayOSD on-screen display server";
PartOf = [ "graphical-session.target" ];
After = [ "graphical-session.target" ];
};
Service = {
ExecStart = lib.getExe' pkgs.swayosd "swayosd-server";
Restart = "on-failure";
};
Install.WantedBy = [ "graphical-session.target" ];
};
registry.keybind.${config.networking.hostName} = { registry.keybind.${config.networking.hostName} = {
volume-up = {
keys = "XF86AudioRaiseVolume";
repeating = true;
action = {
kind = "exec";
command = "${pkgs.swayosd}/bin/swayosd-client --output-volume raise --max-volume 100";
};
};
volume-down = {
keys = "XF86AudioLowerVolume";
repeating = true;
action = {
kind = "exec";
command = "${pkgs.swayosd}/bin/swayosd-client --output-volume lower";
};
};
brightness-up = { brightness-up = {
keys = "XF86MonBrightnessUp"; keys = "XF86MonBrightnessUp";
repeating = true; repeating = true;
action = { action = {
kind = "exec"; kind = "exec";
command = "${lib.getExe' pkgs.swayosd "swayosd-client"} --brightness raise"; command = "${pkgs.swayosd}/bin/swayosd-client --brightness raise";
}; };
}; };
@@ -48,9 +46,41 @@
repeating = true; repeating = true;
action = { action = {
kind = "exec"; kind = "exec";
command = "${lib.getExe' pkgs.swayosd "swayosd-client"} --brightness lower"; command = "${pkgs.swayosd}/bin/swayosd-client --brightness lower";
};
};
mute = {
keys = "XF86AudioMute";
locked = true;
action = {
kind = "exec";
command = "${pkgs.swayosd}/bin/swayosd-client --output-volume mute-toggle";
}; };
}; };
}; };
environment.systemPackages = [ pkgs.swayosd ];
# The udev rule chgrps each backlight's brightness node to `video` and adds
# group write, so the server dims the panel without root.
# Membership below grants the running session that access.
services.udev.packages = [ pkgs.swayosd ];
users.users.${user}.extraGroups = [ "video" ];
# The server draws the popups, so it runs for the whole graphical session.
# It binds to the target uwsm activates.
home-manager.users.${user}.systemd.user.services.swayosd = {
Unit = {
Description = "SwayOSD on-screen display server";
PartOf = [ "graphical-session.target" ];
After = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${pkgs.swayosd}/bin/swayosd-server";
Restart = "on-failure";
};
Install.WantedBy = [ "graphical-session.target" ];
};
}; };
} }

View File

@@ -7,13 +7,13 @@
}: }:
# Keybound screen recording: pick a region, then toggle a video-only capture. # Keybound screen recording: pick a region, then toggle a video-only capture.
let let
# A single key starts and stops recording. # A single key both starts and stops.
# Waybar refreshes on state changes instead of polling. # A running wf-recorder is stopped with SIGINT so it finalises the file.
recordingToggle = pkgs.writeShellScript "screen-record-toggle" '' # Otherwise slurp picks a region and wf-recorder runs in the foreground until
refresh_waybar() { # that stop arrives, so this instance lives for the whole recording and then
${lib.getExe' pkgs.procps "pkill"} -RTMIN+10 waybar >/dev/null 2>&1 || true # reports it saved.
} # No -a means no audio is captured.
toggle = pkgs.writeShellScript "screen-record-toggle" ''
if ${lib.getExe' pkgs.procps "pgrep"} -x wf-recorder >/dev/null; then if ${lib.getExe' pkgs.procps "pgrep"} -x wf-recorder >/dev/null; then
${lib.getExe' pkgs.procps "pkill"} -INT -x wf-recorder ${lib.getExe' pkgs.procps "pkill"} -INT -x wf-recorder
exit 0 exit 0
@@ -25,15 +25,12 @@ let
file="$dir/recording-$(${lib.getExe' pkgs.coreutils "date"} +%Y%m%d-%H%M%S).mp4" file="$dir/recording-$(${lib.getExe' pkgs.coreutils "date"} +%Y%m%d-%H%M%S).mp4"
${lib.getExe' pkgs.libnotify "notify-send"} -a "Screen recording" "Recording started" "Region capture, no audio." ${lib.getExe' pkgs.libnotify "notify-send"} -a "Screen recording" "Recording started" "Region capture, no audio."
${lib.getExe pkgs.wf-recorder} -g "$region" -f "$file" & ${lib.getExe pkgs.wf-recorder} -g "$region" -f "$file"
recorder=$!
refresh_waybar
wait "$recorder" || true
refresh_waybar
${lib.getExe' pkgs.libnotify "notify-send"} -a "Screen recording" "Recording saved" "$file" ${lib.getExe' pkgs.libnotify "notify-send"} -a "Screen recording" "Recording saved" "$file"
''; '';
# Empty text collapses the widget when recording is idle. # Lit while a wf-recorder capture is running.
# Empty text collapses the widget when idle, so it shows nothing until then.
recordingStatus = pkgs.writeShellScript "waybar-recording-status" '' recordingStatus = pkgs.writeShellScript "waybar-recording-status" ''
if ${lib.getExe' pkgs.procps "pgrep"} -x wf-recorder >/dev/null; then if ${lib.getExe' pkgs.procps "pgrep"} -x wf-recorder >/dev/null; then
printf '{"text":"${util.glyph "f03d"}","tooltip":"Recording","class":"recording"}\n' printf '{"text":"${util.glyph "f03d"}","tooltip":"Recording","class":"recording"}\n'
@@ -51,7 +48,7 @@ in
keys = "SUPER + SHIFT + R"; keys = "SUPER + SHIFT + R";
action = { action = {
kind = "exec"; kind = "exec";
command = "${recordingToggle}"; command = "${toggle}";
}; };
}; };
@@ -60,8 +57,7 @@ in
settings = { settings = {
return-type = "json"; return-type = "json";
exec = "${recordingStatus}"; exec = "${recordingStatus}";
interval = "once"; interval = 1;
signal = 10;
}; };
}; };
}; };

View File

@@ -7,26 +7,26 @@
# Keyboard-driven screenshots copy to the clipboard before Satty opens for # Keyboard-driven screenshots copy to the clipboard before Satty opens for
# annotation. Satty copies annotated screenshots when it saves them. # annotation. Satty copies annotated screenshots when it saves them.
let let
screenshotKey = keys: target: { user = config.user.name;
inherit keys;
action = { grimblast = "${pkgs.grimblast}/bin/grimblast";
kind = "exec"; satty = "${pkgs.satty}/bin/satty";
command = "${capture target}"; tee = "${pkgs.coreutils}/bin/tee";
}; wl-copy = "${pkgs.wl-clipboard}/bin/wl-copy";
}; xdgUserDir = "${pkgs.xdg-user-dirs}/bin/xdg-user-dir";
# Copy the original screenshot before Satty opens. # Copy the original screenshot before Satty opens.
# Satty copies and saves annotated screenshots when the user presses Enter. # Satty copies and saves annotated screenshots when the user presses Enter.
capture = capture =
target: target:
pkgs.writeShellScript "screenshot-${target}" '' pkgs.writeShellScript "screenshot-${target}" ''
dir="$(${lib.getExe' pkgs.xdg-user-dirs "xdg-user-dir"} PICTURES)/Screenshots" dir="$(${xdgUserDir} PICTURES)/Screenshots"
${lib.getExe' pkgs.coreutils "mkdir"} -p "$dir" mkdir -p "$dir"
${lib.getExe pkgs.grimblast} save ${target} - \ ${grimblast} save ${target} - \
| ${lib.getExe' pkgs.coreutils "tee"} >(${lib.getExe' pkgs.wl-clipboard "wl-copy"}) \ | ${tee} >(${wl-copy}) \
| ${lib.getExe pkgs.satty} --filename - \ | ${satty} --filename - \
--output-filename "$dir/screenshot-%Y%m%d-%H%M%S.png" \ --output-filename "$dir/screenshot-%Y%m%d-%H%M%S.png" \
--copy-command ${lib.getExe' pkgs.wl-clipboard "wl-copy"} \ --copy-command ${wl-copy} \
--save-after-copy \ --save-after-copy \
--early-exit \ --early-exit \
--actions-on-enter save-to-clipboard --actions-on-enter save-to-clipboard
@@ -37,7 +37,33 @@ in
lib.mkEnableOption "screenshots via grimblast and satty"; lib.mkEnableOption "screenshots via grimblast and satty";
config = lib.mkIf config.modules.desktop.utilities.screenshot.enable { config = lib.mkIf config.modules.desktop.utilities.screenshot.enable {
home-manager.users.${config.user.name} = { registry.keybind.${config.networking.hostName} = {
screenshot-area = {
keys = "Print";
action = {
kind = "exec";
command = "${capture "area"}";
};
};
screenshot-active = {
keys = "SHIFT + Print";
action = {
kind = "exec";
command = "${capture "active"}";
};
};
screenshot-screen = {
keys = "CTRL + Print";
action = {
kind = "exec";
command = "${capture "screen"}";
};
};
};
home-manager.users.${user} = {
# satty is a one-shot annotation surface, so float it rather than letting # satty is a one-shot annotation surface, so float it rather than letting
# it claim a tile in the layout. # it claim a tile in the layout.
wayland.windowManager.hyprland.settings.window_rule = [ wayland.windowManager.hyprland.settings.window_rule = [
@@ -46,12 +72,7 @@ in
float = true; float = true;
} }
]; ];
};
registry.keybind.${config.networking.hostName} = {
screenshot-area = screenshotKey "Print" "area";
screenshot-active = screenshotKey "SHIFT + Print" "active";
screenshot-screen = screenshotKey "CTRL + Print" "screen";
}; };
}; };
} }

View File

@@ -6,13 +6,15 @@
}: }:
# Nord theming for the graphical layer, driven from one base16 scheme by Stylix. # Nord theming for the graphical layer, driven from one base16 scheme by Stylix.
let let
# A single static Nord wallpaper, drawn as a vertical gradient across the # A single static Nord wallpaper, drawn as a vertical gradient across the
# Polar Night shades so it needs no committed binary and no network fetch. # Polar Night shades so it needs no committed binary and no network fetch.
wallpaper = pkgs.runCommand "nord-wallpaper.png" { } '' wallpaper = pkgs.runCommand "nord-wallpaper.png" { } ''
${lib.getExe pkgs.imagemagick} \ ${pkgs.imagemagick}/bin/magick \
-size 2560x1440 gradient:'#2E3440'-'#3B4252' \ -size 2560x1440 gradient:'#2E3440'-'#3B4252' \
"$out" "$out"
''; '';
user = config.user.name;
in in
{ {
options.modules.desktop.utilities.theming.enable = options.modules.desktop.utilities.theming.enable =
@@ -56,8 +58,12 @@ in
}; };
}; };
}; };
registry.env.${config.networking.hostName} = {
HYPRCURSOR_THEME = config.stylix.cursor.name;
HYPRCURSOR_SIZE = toString config.stylix.cursor.size;
};
home-manager.users.${config.user.name} = { home-manager.users.${user} = {
# Stylix drives the cursor through home-manager's pointer-cursor config, # Stylix drives the cursor through home-manager's pointer-cursor config,
# whose generation must be switched on explicitly. # whose generation must be switched on explicitly.
home.pointerCursor.enable = true; home.pointerCursor.enable = true;
@@ -66,10 +72,5 @@ in
# base16 mapping Stylix would apply, so its target stays off. # base16 mapping Stylix would apply, so its target stays off.
stylix.targets.nixvim.enable = false; stylix.targets.nixvim.enable = false;
}; };
registry.env.${config.networking.hostName} = {
HYPRCURSOR_THEME = config.stylix.cursor.name;
HYPRCURSOR_SIZE = toString config.stylix.cursor.size;
};
}; };
} }

View File

@@ -1,27 +1,14 @@
{ config, lib, ... }: { config, lib, ... }:
# Declares XDG user directories, so tools that query them use predictable # Declares the XDG user directories, so a tool that queries them lands in a
# folders a host can relocate from one place. # well-known folder a host can relocate from one place.
let let
homeDirectory = "/home/${config.user.name}"; user = config.user.name;
in in
{ {
options.modules.desktop.utilities.userdirs.enable = lib.mkEnableOption "XDG user directories"; options.modules.desktop.utilities.userdirs.enable = lib.mkEnableOption "XDG user directories";
config = lib.mkIf config.modules.desktop.utilities.userdirs.enable { config = lib.mkIf config.modules.desktop.utilities.userdirs.enable {
home-manager.users.${config.user.name}.xdg.userDirs = { # Write ~/.config/user-dirs.dirs from the option defaults.
enable = true; home-manager.users.${user}.xdg.userDirs.enable = true;
createDirectories = true;
setSessionVariables = true;
desktop = "${homeDirectory}/.ignoreme";
documents = "${homeDirectory}/docs";
download = "${homeDirectory}/dwn";
music = "${homeDirectory}/mus";
pictures = "${homeDirectory}/pic";
publicShare = "${homeDirectory}/.ignoreme";
templates = "${homeDirectory}/.ignoreme";
videos = "${homeDirectory}/vid";
projects = "${homeDirectory}/.ignoreme";
};
}; };
} }