nix/graphical/desktop/wm.nix
2026-04-08 11:51:33 +10:00

143 lines
3.6 KiB
Nix

{
inputs,
getFlakePkg',
lib,
pkgs,
config,
...
}:
let
inherit (config) rice;
inherit (inputs.niri.lib.kdl) leaf node flag serialize;
niri = getFlakePkg' inputs.niri "niri-unstable";
xwayland-satellite = getFlakePkg' inputs.niri "xwayland-satellite-unstable";
ui = rice.roles rice.palette.shortHex;
# serialize binds attrset to KDL
serializeBind = key: bind:
if bind ? action then
leaf key { action = bind.action; }
else if bind ? spawn then
leaf key bind.spawn
else
flag key;
bindsKdl = serialize.nodes [
(node "binds" [] (
lib.mapAttrsToList serializeBind config.niri.binds
))
];
niriConfig =
let
template = builtins.readFile ./niri.kdl;
baseConfig =
with rice;
lib.replaceStrings
[
"%CURSOR%"
"%ROUNDING%"
"%GAPS%"
"%BORDER%"
"%ACTIVE%"
"%INACTIVE%"
"%SATELLITE%"
]
[
cursor.name
(toString borders.rounding)
(toString borders.gaps)
(toString borders.thickness)
ui.highlight
ui.muted
(lib.getExe xwayland-satellite)
]
template;
in
baseConfig + "\n" + bindsKdl + "\n" + config.niri.extraConfig;
niriConfigFile = pkgs.writeText "niri-config.kdl" niriConfig;
niri-session-direct = pkgs.writeShellScript "niri-session-direct" ''
export NIRI_CONFIG="${niriConfigFile}"
systemctl --user import-environment
dbus-update-activation-environment --all
systemctl --user start niri-session-bridge.service &
exec ${lib.getExe niri} --session
'';
in
{
imports = [
inputs.niri-tag.nixosModules.niri-tag
inputs.niri-s76.nixosModules.default
];
options.niri = {
binds = lib.mkOption {
type = lib.types.attrsOf lib.types.anything;
default = { };
description = "Niri keybindings";
};
extraConfig = lib.mkOption {
type = lib.types.lines;
default = "";
description = "Extra KDL config appended to niri config";
};
};
config.environment.systemPackages = [
niri
xwayland-satellite
];
config.environment.etc."greetd/wayland-sessions/niri.desktop".text = ''
[Desktop Entry]
Name=Niri
Comment=A scrollable-tiling Wayland compositor
Exec=${niri-session-direct}
Type=Application
DesktopNames=niri
'';
config.programs.niri = {
enable = true;
package = niri;
};
config.services.niri-tag = {
enable = true;
prepopulate = 10;
strict = true;
scratchpads = {
"chrome-listen.lobotomise.me__-Default" = 99;
"Bitwarden" = 101;
};
};
config.services.niri-s76-bridge.enable = true;
# niri runs directly from greetd (not as a user service),
# so that it stays inside the logind session scope for proper polkit/dbus access.
config.systemd.user.services.niri.wantedBy = lib.mkForce [ ];
config.systemd.user.services.niri.enable = lib.mkForce false;
# bridge service to activate graphical-session.target for the direct-launched niri.
# waits for niri IPC readiness before pulling in the target.
config.systemd.user.services.niri-session-bridge = {
unitConfig = {
Description = "Activate graphical-session.target for direct-launched niri";
BindsTo = [ "graphical-session.target" ];
Before = [ "graphical-session.target" ];
Wants = [ "graphical-session-pre.target" ];
After = [ "graphical-session-pre.target" ];
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${lib.getExe niri} msg version";
};
};
}