blob: e60161021371f1bca00a19e68755ca1bc32d130a [file] [log] [blame]
{
pkgs,
config,
inputs,
system,
lib,
...
}:
let
inherit (inputs) nixpkgs-stable;
in {
options.chimera.niri = {
enable = lib.mkEnableOption "Use Niri as your window manager";
xwayland.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable xwayland-satellite to run X apps in niri";
};
monitors = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule {
options = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable this monitor";
};
mode = lib.mkOption {
type = lib.types.nullOr (
lib.types.submodule {
options = {
width = lib.mkOption { type = lib.types.int; };
height = lib.mkOption { type = lib.types.int; };
refresh = lib.mkOption {
type = lib.types.nullOr lib.types.float;
default = null;
};
};
}
);
default = null;
};
position = lib.mkOption {
type = lib.types.nullOr (
lib.types.submodule {
options = {
x = lib.mkOption { type = lib.types.int; };
y = lib.mkOption { type = lib.types.int; };
};
}
);
default = null;
};
scale = lib.mkOption {
type = lib.types.float;
default = 1.0;
};
transform = {
flipped = lib.mkOption {
type = lib.types.bool;
default = false;
};
rotation = lib.mkOption {
type = lib.types.enum [
0
90
180
270
];
default = 0;
};
};
variable-refresh-rate = lib.mkEnableOption "Enable Variable Refresh Rate (AMD FreeSync / Nvidia G-Sync)";
};
}
);
description = "Atribute set of monitors";
default = { };
};
startupCommands = lib.mkOption {
type = lib.types.listOf (lib.types.submodule {
options = {
command = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "Command to run";
};
};
});
description = "List of commands to run at startup";
default = [ ];
};
};
config = lib.mkIf config.chimera.niri.enable {
chimera.wayland.enable = true;
programs.bash.profileExtra = lib.mkIf config.chimera.shell.bash.enable (
lib.mkBefore ''
if [ -z $WAYLAND_DISPLAY ] && [ "$(tty)" = "/dev/tty1" ]; then
exec ${pkgs.systemd}/bin/systemd-cat -t niri ${pkgs.dbus}/bin/dbus-run-session ${config.programs.niri.package}/bin/niri --session
fi
''
);
programs.zsh.profileExtra = lib.mkIf config.chimera.shell.zsh.enable (
lib.mkBefore ''
if [ -z $WAYLAND_DISPLAY ] && [ "$(tty)" = "/dev/tty1" ]; then
exec ${pkgs.systemd}/bin/systemd-cat -t niri ${pkgs.dbus}/bin/dbus-run-session ${config.programs.niri.package}/bin/niri --session
fi
''
);
programs.niri =
let
mod = "Super";
mod1 = "Alt";
terminal = "${pkgs.kitty}/bin/kitty";
menu = (
if config.chimera.runner.anyrun.enable then
"${inputs.anyrun.packages.${system}.anyrun}/bin/anyrun"
else
""
);
lock = ''${pkgs.swaylock}/bin/swaylock -i ${config.chimera.theme.wallpaper} -s fill'';
in
{
enable = true;
package = pkgs.niri-stable;
settings = {
environment = {
NIXOS_OZONE_WL = "1";
DISPLAY = lib.mkIf config.chimera.niri.xwayland.enable ":0";
};
input.keyboard = {
track-layout = "window";
repeat-delay = 200;
repeat-rate = 25;
xkb = {
layout = config.chimera.input.keyboard.layout;
variant = config.chimera.input.keyboard.variant;
options = lib.mkIf (config.chimera.input.keyboard.appleMagic) "apple:alupckeys";
};
};
input.mouse.natural-scroll = config.chimera.input.mouse.scrolling.natural;
input.touchpad.natural-scroll = config.chimera.input.touchpad.scrolling.natural;
input.warp-mouse-to-focus = true;
input.focus-follows-mouse = {
enable = true;
max-scroll-amount = "0%";
};
input.power-key-handling.enable = false;
binds = let
inherit (config.lib.niri) actions;
generateWorkspaceBindings = workspaceNumber: {
"${mod}+${builtins.toString (lib.mod workspaceNumber 10)}".action.focus-workspace = [workspaceNumber];
"${mod}+Shift+${builtins.toString (lib.mod workspaceNumber 10)}".action.move-column-to-workspace = [workspaceNumber];
};
joinAttrsetList = listOfAttrsets: lib.fold (a: b: a // b) {} listOfAttrsets;
in { # General Keybinds
"${mod}+Q".action.close-window = [];
"${mod}+Shift+Q".action.quit = [];
"${mod}+Return".action.spawn = "${terminal}";
"${mod}+L".action.spawn = ["sh" "-c" "${config.programs.niri.package}/bin/niri msg action do-screen-transition && ${lock}"];
"${mod}+P".action.power-off-monitors = [];
"${mod}+R".action.screenshot = [];
"${mod}+Ctrl+R".action.screenshot-screen = [];
"${mod}+Shift+R".action.screenshot-window = [];
"Print".action.screenshot = [];
"Ctrl+Print".action.screenshot-screen = [];
"Shift+Print".action.screenshot-window = [];
"${mod}+Alt+R".action.spawn = ["sh" "-c" ''([ -d Videos ] || mkdir Videos) && ${nixpkgs-stable.legacyPackages.${system}.wl-screenrec}/bin/wl-screenrec --geometry "$(${pkgs.slurp}/bin/slurp)" --filename "Videos/$(date +'%Y-%m-%d-%H-%M-%S').mp4"''];
"${mod}+Alt+Shift+R".action.spawn = ["sh" "-c" "pkill wl-screenrec && cat \"$(${pkgs.coreutils}/bin/ls -t Videos/*.mp4 | ${pkgs.coreutils}/bin/head -n 1)\" | ${pkgs.wl-clipboard}/bin/wl-copy"];
"${mod}+Space".action.switch-layout = ["next"];
"${mod}+Shift+Space".action.switch-layout = ["prev"];
"${mod}+D" = lib.mkIf config.chimera.runner.enable { action.spawn = "${menu}"; };
"Alt+Space" = lib.mkIf (
config.chimera.runner.enable
&& config.chimera.input.keybinds.alternativeSearch.enable
) { action.spawn = "${menu}"; };
"${mod}+Shift+Slash".action.show-hotkey-overlay = [];
} // ( # Workspace Keybinds
lib.pipe (lib.range 1 10) [
(map generateWorkspaceBindings)
joinAttrsetList
]
) // ( # Window Manipulation Bindings
{
"${mod}+BracketLeft".action.consume-or-expel-window-left = [];
"${mod}+BracketRight".action.consume-or-expel-window-right = [];
"${mod}+Shift+BracketLeft".action.consume-window-into-column = [];
"${mod}+Shift+BracketRight".action.expel-window-from-column = [];
"${mod}+Slash".action.switch-preset-column-width = [];
"${mod}+${mod1}+F".action.fullscreen-window = [];
# Focus
"${mod}+Up".action.focus-window-or-workspace-up = [];
"${mod}+Down".action.focus-window-or-workspace-down = [];
# Non Jump Movement
"${mod}+Shift+Up".action.move-window-up-or-to-workspace-up = [];
"${mod}+Shift+Down".action.move-window-down-or-to-workspace-down = [];
"${mod}+Shift+Left".action.consume-or-expel-window-left = [];
"${mod}+Shift+Right".action.consume-or-expel-window-right = [];
# To Monitor
"${mod}+Shift+Ctrl+Up".action.move-window-to-monitor-up = [];
"${mod}+Shift+Ctrl+Down".action.move-window-to-monitor-down = [];
"${mod}+Shift+Ctrl+Left".action.move-window-to-monitor-left = [];
"${mod}+Shift+Ctrl+Right".action.move-window-to-monitor-right = [];
# To Workspace
"${mod}+Ctrl+Up".action.move-window-to-workspace-up = [];
"${mod}+Ctrl+Down".action.move-window-to-workspace-down = [];
# Sizing
"${mod}+Equal".action.set-window-height = ["+5%"];
"${mod}+Minus".action.set-window-height = ["-5%"];
}) // ( # Column Manipulation Bindings
{
# Focus
"${mod}+Left".action.focus-column-left = [];
"${mod}+Right".action.focus-column-right = [];
"${mod}+${mod1}+C".action.center-column = [];
"${mod}+F".action.maximize-column = [];
# Non Monitor Movement
"${mod}+${mod1}+Shift+Up".action.move-column-to-workspace-up = [];
"${mod}+${mod1}+Shift+Down".action.move-column-to-workspace-down = [];
"${mod}+${mod1}+Shift+Left".action.move-column-left = [];
"${mod}+${mod1}+Shift+Right".action.move-column-right = [];
# To Monitor
"${mod}+${mod1}+Shift+Ctrl+Up".action.move-column-to-monitor-up = [];
"${mod}+${mod1}+Shift+Ctrl+Down".action.move-column-to-monitor-down = [];
"${mod}+${mod1}+Shift+Ctrl+Left".action.move-column-to-monitor-left = [];
"${mod}+${mod1}+Shift+Ctrl+Right".action.move-column-to-monitor-right = [];
# Sizing
"${mod}+${mod1}+Equal".action.set-column-width = ["+5%"];
"${mod}+${mod1}+Minus".action.set-column-width = ["-5%"];
}) // ( # Workspace Manipulation Bindings
{
# Focus
"${mod}+Page_Up".action.focus-workspace-up = [];
"${mod}+Page_Down".action.focus-workspace-down = [];
# Within Itself
"${mod}+Shift+Page_Up".action.move-workspace-up = [];
"${mod}+Shift+Page_Down".action.move-workspace-down = [];
# To Monitor
"${mod}+Shift+Ctrl+Page_Up".action.move-workspace-to-monitor-up = [];
"${mod}+Shift+Ctrl+Page_Down".action.move-workspace-to-monitor-down = [];
"${mod}+Shift+Ctrl+Home".action.move-workspace-to-monitor-left = [];
"${mod}+Shift+Ctrl+End".action.move-workspace-to-monitor-right = [];
}) // { # Audio
"XF86AudioRaiseVolume" = {
allow-when-locked = true;
action.spawn = ["${pkgs.wireplumber}/bin/wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.05+"];
};
"XF86AudioLowerVolume" = {
allow-when-locked = true;
action.spawn = ["${pkgs.wireplumber}/bin/wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.05-"];
};
"XF86AudioMute" = {
allow-when-locked = true;
action.spawn = ["${pkgs.wireplumber}/bin/wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"];
};
"XF86AudioMicMute" = {
allow-when-locked = true;
action.spawn = ["${pkgs.wireplumber}/bin/wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"];
};
};
outputs = config.chimera.niri.monitors;
cursor = {
size = config.chimera.theme.cursor.size;
theme = config.chimera.theme.cursor.name;
};
layout = {
gaps = 16;
center-focused-column = "on-overflow";
preset-column-widths = [
{ proportion = 1. / 4.; }
{ proportion = 1. / 3.; }
{ proportion = 1. / 2.; }
{ proportion = 2. / 3.; }
{ proportion = 9. / 10.; }
]; # TODO: clicks to PR a docs update for niri-flake
};
prefer-no-csd = true; # No "client-side-decorations" (i.e. client-side window open/close buttons)
hotkey-overlay.skip-at-startup = true;
screenshot-path = null;
spawn-at-startup = if config.chimera.waybar.enable then [{
command = [ "${pkgs.waybar}/bin/waybar" ];
}] else [] ++ [
{
command = [ "${pkgs.swaybg}/bin/swaybg" "-i" "${config.chimera.theme.wallpaper}" "-m" "fill" ];
}
{
command = [ "${pkgs.xwayland-satellite-unstable}/bin/xwayland-satellite" ]; # todo: use xwayland-satellite-nixpkgs in the future...
}
] ++ config.chimera.niri.startupCommands;
};
};
};
}