Add niri module

- An overridden `niri.service` is provided, so that systemd does not take
over autostart and try to start things *before* we have an XWayland server running.
- Somehow `wlsunset` is still not working, might need some investigation into why.
This commit is contained in:
Natsu Kagami 2025-01-16 04:23:55 +01:00
parent 704f1f1c79
commit 61382f4d32
Signed by: nki
GPG key ID: 55A032EB38B49ADB
7 changed files with 379 additions and 2 deletions

View file

@ -7,6 +7,7 @@
./modules/programs/my-broot.nix
./modules/programs/my-waybar.nix
./modules/programs/my-sway
./modules/programs/my-niri.nix
./modules/programs/my-kitty
./modules/programs/openconnect-epfl.nix
./common-linux.nix

View file

@ -13,6 +13,9 @@ let
if which sway &>/dev/null
set -a CHOICES "sway"
end
if which niri-session &>/dev/null
set -a CHOICES "Niri"
end
if which startplasma-wayland &>/dev/null
set -a CHOICES "KDE Plasma"
end
@ -22,6 +25,8 @@ let
case "sway"
systemctl --user unset-environment NIXOS_OZONE_WL
exec sway
case "Niri"
exec niri-session
case "KDE Plasma"
exec ${pkgs.kdePackages.plasma-workspace}/libexec/plasma-dbus-run-session-if-needed startplasma-wayland
case '*'

View file

@ -39,6 +39,7 @@
linux.graphical.wallpaper = ./images/pixiv_18776904.png;
linux.graphical.defaults.webBrowser.package = pkgs.zen-browser-bin;
linux.graphical.defaults.webBrowser.desktopFile = "zen.desktop";
programs.my-niri.enable = true;
programs.my-sway.enable = true;
programs.my-sway.fontSize = 15.0;
programs.my-sway.enableLaptop = true;

View file

@ -74,7 +74,7 @@ with lib;
# Background color and transparency
background =
if isNull cfg.background then {
background_opacity = "0.85";
background_opacity = "0.93";
dynamic_background_opacity = true;
} else {
background_image = "${cfg.background}";

View file

@ -0,0 +1,328 @@
{ config, osConfig, lib, pkgs, ... }:
let
cfg = config.programs.my-niri;
sh = config.lib.niri.actions.spawn "sh" "-c";
playerctl = lib.getExe pkgs.playerctl;
amixer = lib.getExe' pkgs.alsa-utils "amixer";
brightnessctl = lib.getExe pkgs.brightnessctl;
app-menu = "${pkgs.dmenu}/bin/dmenu_path | ${pkgs.bemenu}/bin/bemenu | ${pkgs.findutils}/bin/xargs swaymsg exec --";
wallpaper = config.linux.graphical.wallpaper;
workspaces = {
"01" = { name = "🌏 web"; };
"02" = { name = "💬 chat"; };
"03" = { name = " code"; };
"04" = { name = "🎶 music"; };
"05" = { name = "🔧 extra"; };
"06" = { name = "🧰 6"; };
"07" = { name = "🔩 7"; };
"08" = { name = "🛠 8"; };
"09" = { name = "🔨 9"; };
"10" = { name = "🎲 misc"; };
"99" = { name = "📧 Email"; };
};
xwayland-display = ":12";
in
{
options.programs.my-niri = {
enable = lib.mkEnableOption "My own niri configuration";
enableLaptop = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable laptop options";
};
lock-command = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "The command to lock the screen";
default = [ "${pkgs.swaylock}/bin/swaylock" ]
++ (if wallpaper == "" then [ "" ] else [ "-i" "${wallpaper}" "-s" "fill" ])
++ [ "-l" "-k" ];
};
};
config = lib.mkIf cfg.enable {
programs.niri.settings = {
environment = {
QT_QPA_PLATFORM = "wayland";
QT_WAYLAND_DISABLE_WINDOWDECORATION = "1";
QT_IM_MODULE = "fcitx";
GTK_IM_MODULE = "fcitx"; # Til text-input is merged
# export NIXOS_OZONE_WL=1 # Until text-input is merged
DISPLAY = xwayland-display;
} // lib.optionalAttrs osConfig.services.desktopManager.plasma6.enable {
XDG_MENU_PREFIX = "plasma-";
};
input.keyboard.xkb = { layout = "jp"; };
input.touchpad = lib.mkIf cfg.enableLaptop {
tap = true;
dwt = true;
natural-scroll = true;
middle-emulation = true;
};
input.mouse = {
accel-profile = "flat";
};
input.warp-mouse-to-focus = true;
input.focus-follows-mouse = {
enable = true;
max-scroll-amount = "0%";
};
outputs =
let
eachMonitor = _: monitor: {
name = monitor.meta.niriName or monitor.name; # Niri might not find the monitor by name
value = {
mode = monitor.meta.mode;
position = monitor.meta.fixedPosition or null;
scale = monitor.scale or 1;
variable-refresh-rate = (monitor.adaptive_sync or "off") == "on";
};
};
in
lib.mapAttrs' eachMonitor config.common.monitors;
spawn-at-startup = [
# Wallpaper
{ command = [ (lib.getExe pkgs.swaybg) "-i" "${wallpaper}" "-m" "fill" ]; }
# IME
{ command = [ "fcitx5" ]; }
# XWayland
{ command = [ (lib.getExe pkgs.xwayland-satellite) xwayland-display ]; }
# Waybar
{ command = [ "systemctl" "--user" "start" "swaync.service" ]; }
{ command = [ "systemctl" "--user" "start" "xdg-desktop-portal-gtk.service" "xdg-desktop-portal.service" ]; }
{ command = [ "systemctl" "--user" "reset-failed" "waybar.service" "wlsunset.service" ]; }
# Startup
{ command = [ (lib.getExe pkgs.dex) "-ae" "niri" ]; }
];
layout = {
gaps = 16;
preset-column-widths = [
{ proportion = 1. / 3.; }
{ proportion = 1. / 2.; }
{ proportion = 2. / 3.; }
];
default-column-width.proportion = 1. / 2.;
focus-ring = {
width = 4;
active.gradient = { from = "#00447AFF"; to = "#71C4FFAA"; angle = 45; };
inactive.color = "#505050";
};
border.enable = false;
struts = let v = 8; in { left = v; right = v; bottom = v; top = v; };
};
prefer-no-csd = true;
inherit workspaces;
window-rules = [
# Rounded Corners
{
geometry-corner-radius = let v = 8.0; in { bottom-left = v; bottom-right = v; top-left = v; top-right = v; };
clip-to-geometry = true;
}
# Workspace assignments
{
open-on-workspace = workspaces."01".name;
open-maximized = true;
matches = [
{ at-startup = true; app-id = "^firefox$"; }
{ at-startup = true; app-id = "^librewolf$"; }
{ at-startup = true; app-id = "^zen$"; }
];
}
{
open-on-workspace = workspaces."02".name;
open-maximized = true;
matches = [
{ title = "^((d|D)iscord|((A|a)rm(c|C)ord))$"; }
{ title = "VencordDesktop"; }
{ app-id = "VencordDesktop"; }
{ title = "vesktop"; }
{ app-id = "vesktop"; }
{ title = "Slack"; }
];
}
{
open-on-workspace = workspaces."99".name;
open-maximized = true;
matches = [
{ app-id = "thunderbird"; }
{ app-id = "evolution"; }
];
}
# Floating
{
open-floating = true;
matches = [
{ app-id = ".*float.*"; }
{ app-id = "org\\.freedesktop\\.impl\\.portal\\.desktop\\..*"; }
{ title = ".*float.*"; }
{ title = "Extension: .*Bitwarden.*"; }
{ app-id = "Rofi"; }
];
}
# Kitty dimming
{
matches = [{ app-id = "kitty"; }];
excludes = [{ is-focused = true; }];
opacity = 0.95;
}
];
layer-rules = [
{
matches = [{ namespace = "^swaync-.*"; }];
block-out-from = "screen-capture";
}
];
binds = with config.lib.niri.actions; {
# Mod-Shift-/, which is usually the same as Mod-?,
# shows a list of important hotkeys.
"Mod+Shift+Slash".action = show-hotkey-overlay;
# Some basic spawns
"Mod+Return".action = spawn (lib.getExe config.linux.graphical.defaults.terminal.package);
"Mod+Space".action = spawn (lib.getExe pkgs.rofi) "-show" "drun";
"Mod+R".action = sh app-menu;
"Mod+Semicolon".action = spawn cfg.lock-command;
# Audio and Volume
"XF86AudioPrev" = { action = spawn playerctl "previous"; allow-when-locked = true; };
"XF86AudioPlay" = { action = spawn playerctl "play-pause"; allow-when-locked = true; };
"Shift+XF86AudioPlay" = { action = spawn playerctl "stop"; allow-when-locked = true; };
"XF86AudioNext" = { action = spawn playerctl "next"; allow-when-locked = true; };
"XF86AudioRecord" = { action = spawn amixer "-q" "set" "Capture" "toggle"; allow-when-locked = true; };
"XF86AudioMute" = { action = spawn amixer "-q" "set" "Master" "toggle"; allow-when-locked = true; };
"XF86AudioLowerVolume" = { action = spawn amixer "-q" "set" "Master" "3%-"; allow-when-locked = true; };
"XF86AudioRaiseVolume" = { action = spawn amixer "-q" "set" "Master" "3%+"; allow-when-locked = true; };
# Backlight
"XF86MonBrightnessDown".action = spawn brightnessctl "s" "10%-";
"XF86MonBrightnessUp".action = spawn brightnessctl "s" "10%+";
"Shift+XF86MonBrightnessDown".action = spawn brightnessctl "-d" "kbd_backlight" "s" "25%-";
"Shift+XF86MonBrightnessUp".action = spawn brightnessctl "-d" "kbd_backlight" "s" "25%+";
"Mod+Shift+Q".action = close-window;
"Mod+Left".action = focus-column-or-monitor-left;
"Mod+Right".action = focus-column-or-monitor-right;
"Mod+Up".action = focus-window-or-workspace-up;
"Mod+Down".action = focus-window-or-workspace-down;
"Mod+H".action = focus-column-or-monitor-left;
"Mod+L".action = focus-column-or-monitor-right;
"Mod+K".action = focus-window-or-workspace-up;
"Mod+J".action = focus-window-or-workspace-down;
"Mod+Shift+Left".action = move-column-left-or-to-monitor-left;
"Mod+Shift+Right".action = move-column-right-or-to-monitor-right;
"Mod+Shift+Up".action = move-window-up-or-to-workspace-up;
"Mod+Shift+Down".action = move-window-down-or-to-workspace-down;
"Mod+Shift+H".action = move-column-left-or-to-monitor-left;
"Mod+Shift+L".action = move-column-right-or-to-monitor-right;
"Mod+Shift+K".action = move-window-up-or-to-workspace-up;
"Mod+Shift+J".action = move-window-down-or-to-workspace-down;
"Mod+Bracketleft".action = focus-column-first;
"Mod+Bracketright".action = focus-column-last;
"Mod+Shift+Bracketleft".action = move-column-to-first;
"Mod+Shift+Bracketright".action = move-column-to-last;
# For compat with my current sway
"Mod+Ctrl+H".action = move-workspace-to-monitor-left;
"Mod+Ctrl+L".action = move-workspace-to-monitor-right;
"Mod+I".action = focus-workspace-down;
"Mod+O".action = focus-workspace-up;
"Mod+Shift+I".action = move-column-to-workspace-down;
"Mod+Shift+O".action = move-column-to-workspace-up;
"Mod+Ctrl+I".action = move-workspace-down;
"Mod+Ctrl+O".action = move-workspace-up;
# Mouse bindings
"Mod+WheelScrollDown" = { action = focus-workspace-down; cooldown-ms = 150; };
"Mod+WheelScrollUp" = { action = focus-workspace-up; cooldown-ms = 150; };
"Mod+Ctrl+WheelScrollDown" = { action = move-column-to-workspace-down; cooldown-ms = 150; };
"Mod+Ctrl+WheelScrollUp" = { action = move-column-to-workspace-up; cooldown-ms = 150; };
"Mod+WheelScrollRight".action = focus-column-right;
"Mod+WheelScrollLeft".action = focus-column-left;
"Mod+Ctrl+WheelScrollRight".action = move-column-right;
"Mod+Ctrl+WheelScrollLeft".action = move-column-left;
# You can refer to workspaces by index. However, keep in mind that
# niri is a dynamic workspace system, so these commands are kind of
# "best effort". Trying to refer to a workspace index bigger than
# the current workspace count will instead refer to the bottommost
# (empty) workspace.
#
# For example, with 2 workspaces + 1 empty, indices 3, 4, 5 and so on
# will all refer to the 3rd workspace.
"Mod+1".action = focus-workspace (workspaces."01".name);
"Mod+2".action = focus-workspace (workspaces."02".name);
"Mod+3".action = focus-workspace (workspaces."03".name);
"Mod+4".action = focus-workspace (workspaces."04".name);
"Mod+5".action = focus-workspace (workspaces."05".name);
"Mod+6".action = focus-workspace (workspaces."06".name);
"Mod+7".action = focus-workspace (workspaces."07".name);
"Mod+8".action = focus-workspace (workspaces."08".name);
"Mod+9".action = focus-workspace (workspaces."09".name);
"Mod+0".action = focus-workspace (workspaces."10".name);
"Mod+Ctrl+1".action = move-column-to-workspace (workspaces."01".name);
"Mod+Ctrl+2".action = move-column-to-workspace (workspaces."02".name);
"Mod+Ctrl+3".action = move-column-to-workspace (workspaces."03".name);
"Mod+Ctrl+4".action = move-column-to-workspace (workspaces."04".name);
"Mod+Ctrl+5".action = move-column-to-workspace (workspaces."05".name);
"Mod+Ctrl+6".action = move-column-to-workspace (workspaces."06".name);
"Mod+Ctrl+7".action = move-column-to-workspace (workspaces."07".name);
"Mod+Ctrl+8".action = move-column-to-workspace (workspaces."08".name);
"Mod+Ctrl+9".action = move-column-to-workspace (workspaces."09".name);
"Mod+Ctrl+0".action = move-column-to-workspace (workspaces."10".name);
"Mod+asciicircum".action = focus-workspace (workspaces."99".name);
"Mod+Shift+asciicircum".action = move-column-to-workspace (workspaces."99".name);
"Mod+Tab".action = focus-workspace-previous;
"Mod+Comma".action = consume-or-expel-window-left;
"Mod+Period".action = consume-or-expel-window-right;
"Mod+W".action = switch-preset-column-width;
"Mod+Shift+W".action = switch-preset-window-height;
"Mod+Ctrl+W".action = reset-window-height;
"Mod+F".action = maximize-column;
"Mod+Shift+F".action = fullscreen-window;
"Mod+E".action = center-column;
"Mod+Minus".action = set-column-width "-10%";
"Mod+At".action = set-column-width "+10%";
"Mod+Shift+Minus".action = set-window-height "-10%";
"Mod+Shift+At".action = set-window-height "+10%";
"Mod+V".action = switch-focus-between-floating-and-tiling;
"Mod+Shift+V".action = toggle-window-floating;
"Mod+Shift+Space".action = toggle-window-floating; # Sway compat
"Print".action = screenshot;
"Ctrl+Print".action = screenshot-screen;
"Shift+Print".action = screenshot-window;
"Mod+Shift+E".action = quit;
};
};
};
}

View file

@ -45,11 +45,14 @@ in
config.programs.waybar =
let
barWith = { showMedia ? true, showConnectivity ? true, extraSettings ? { }, ... }: lib.mkMerge ([{
layer = "top";
position = "top";
modules-left = [
"sway/workspaces"
"sway/mode"
"sway/window"
"niri/workspaces"
"niri/window"
];
modules-center = [
];
@ -91,6 +94,19 @@ in
"\\((\\d+)\\) Discord \\| (.*)" = "[🗨] {$1} $2";
};
};
"niri/window" = {
format = "{title}";
"rewrite" = {
"(.*) Mozilla Firefox" = "[🌎] $1";
"(.*) - Mozilla Thunderbird" = "[📧] $1";
"(.*) - Kakoune" = "[] $1";
"(.*) - fish" = "[>_] $1";
"(.*) - Discord" = "[🗨] $1";
# ArmCord thing
" Discord \\| (.*)" = "[🗨] $1";
"\\((\\d+)\\) Discord \\| (.*)" = "[🗨] {$1} $2";
};
};
"tray" = {
icon-size = 21;
spacing = 10;

View file

@ -45,12 +45,31 @@ let
# services.gnome.evolution-data-server.plugins = with pkgs; [ evolution-ews ];
};
wlr = { ... }: mkIf config.common.linux.enable {
wlr = { lib, config, ... }: mkIf config.common.linux.enable {
# swaync disable notifications on screencast
xdg.portal.wlr.settings.screencast = {
exec_before = ''which swaync-client && swaync-client --inhibitor-add "xdg-desktop-portal-wlr" || true'';
exec_after = ''which swaync-client && swaync-client --inhibitor-remove "xdg-desktop-portal-wlr" || true'';
};
# Niri stuff
# https://github.com/sodiboo/niri-flake/blob/main/docs.md
programs.niri.enable = true;
programs.niri.package = pkgs.niri-stable;
systemd.user.services.niri = {
description = "A scrollable-tiling Wayland compositor";
bindsTo = [ "graphical-session.target" ];
before = [ "graphical-session.target" ];
wants = [ "graphical-session-pre.target" ];
after = [ "graphical-session-pre.target" ];
serviceConfig.Slice = "session.slice";
serviceConfig.Type = "notify";
serviceConfig.ExecStart = "${lib.getExe config.programs.niri.package} --session";
path = mkForce [ ];
};
# Override gnome-keyring disabling
services.gnome.gnome-keyring.enable = lib.mkForce false;
};
logitech = { pkgs, ... }: mkIf cfg.enable {
@ -343,6 +362,13 @@ in
extraPortals = [ pkgs.kdePackages.xdg-desktop-portal-kde pkgs.xdg-desktop-portal-gtk ];
config.sway.default = [ "wlr" "kde" "kwallet" ];
config.niri = {
default = [ "gnome" "gtk" ];
"org.freedesktop.impl.portal.Access" = "gtk";
"org.freedesktop.impl.portal.Notification" = "gtk";
"org.freedesktop.impl.portal.Secret" = "kwallet";
"org.freedesktop.impl.portal.FileChooser" = "kde";
};
};
# D-Bus
services.dbus.packages = with pkgs; [ gcr ];