nix: home-manager: add config wrapper (wip)
@fufexan @viperML
This is a work-in-progress PR. Its intent is to introduce a complete wrapper module for Nix Home Manager for all configurations presented on the Wiki.
You can see a current example utilizing all implemented features:
https://github.com/spikespaz/dotfiles/tree/master/users/jacob/desktops/hyprland
Help is appreciated, if you can spare the time. Let me now and we can get organized.
Comments, questions, concerns? I am eager to discuss.
Checklist
- [ ] Ensure naming scheme is consistent, and renames are appropriate.
- [ ] Update
nix/README.mdfor an overview and guide. - [ ] Figure out how to generate nice Nix documentation, link it to the Wiki.
- [ ] Ensure every configuration is implemented
- [X] General
- [X] Decoration
- [X] Layouts
- [X] Dwindle Layout
- [ ] Master Layout
- [X] Animations
- [X] Bezier Curves
- [X] Input
- [X] Keyboard
- [X] Touchpad
- [ ] Touchdevice
- [X] Gestures
- [X] Misc
- [ ] Monitors
- [ ] Binds
- [ ] Dispatchers
- [X] Window Rules
- [X] Window Rule Groups
- [X] Debug
- [ ] Ensure every configuration is documented
- [X] General
- [ ] Decoration
- [ ] Layouts
- [ ] Dwindle Layout
- [ ] Master Layout
- [ ] Animations
- [ ] Bezier Curves
- [X] Input
- [ ] Keyboard
- [X] Touchpad
- [ ] Touchdevice
- [ ] Gestures
- [ ] Misc
- [ ] Monitors
- [ ] Binds
- [ ] Dispatchers
- [ ] Window Rules
- [ ] Window Rule Groups
- [ ] Debug
- [ ] Ensure options have correct types, new ones if needed
- [ ] General
- [ ] Decoration
- [ ] Layouts
- [ ] Dwindle Layout
- [ ] Master Layout
- [ ] Animations
- [ ] Bezier Curves
- [ ] Input
- [ ] Keyboard
- [ ] Touchpad
- [ ] Touchdevice
- [ ] Gestures
- [ ] Misc
- [ ] Monitors
- [ ] Binds
- [ ] Dispatchers
- [ ] Window Rules
- [ ] Window Rule Groups
- [ ] Debug
Layout
nix
├── default.nix
├── hm-module
│ ├── config
│ │ ├── animations.nix
│ │ ├── binds.nix
│ │ ├── debug.nix
│ │ ├── decoration.nix
│ │ ├── dwindle.nix
│ │ ├── general.nix
│ │ ├── gestures.nix
│ │ ├── input.nix
│ │ ├── keyboard.nix
│ │ ├── misc.nix
│ │ ├── touchpad.nix
│ │ └── windowrules.nix
│ ├── config.nix
│ └── default.nix
├── meson-build.patch
├── module.nix
├── pr.md
├── update-inputs.sh
├── wlroots.nix
├── xwayland-hidpi.patch
└── xwayland-vsync.patch
Introduced hm-module/ and all children. config.nix is the template for the config file to write, while config/ houses the options avaiable to the user. Also observe default.nix.
Module is exposed as a flake output, hyprland.homeManagerModules.default.
I don't maintain this project anymore, and even less hunders of lines of nix by you.
Oh my god that's huge. It's great, no doubt, but I think we can do better than this.
I'd rather spend the time implementing a TOML config in Hyprland itself, and then utilising Nix's formats.toToml over a freeform module to generate the config. I think it's way easier to maintain and also benefits non-Nix users.
lmfao
Oh my god that's huge. It's great, no doubt, but I think we can do better than this.
I'd rather spend the time implementing a TOML config in Hyprland itself, and then utilising Nix's
formats.toTomlover a freeform module to generate the config. I think it's way easier to maintain and also benefits non-Nix users.
It would be pretty peasy to switch it to toml
I think vaxry likes his shit config parser
I don't get it; why is this being worked on in upstream hyperland, instead of Home-Manager?
I don't get it; why is this being worked on in upstream hyperland, instead of Home-Manager?
- They move slow, and it wouldn't let us add/remove options as soon as they're added to the code.
- The (corresponding) Nixpkgs package has been outdated for a long time.
Basically we aim towards being autonomous, while following best practices from Nixpkgs and HM.
Yo this might be too trivial for this purpose, but i just do it with these 4 functions:
{
mkValueString = value:(
if builtins.isBool value then (
if value then "true" else "false"
)
else if (builtins.isFloat value || builtins.isInt value) then (
builtins.toString value
)
else if builtins.isString value then (
value
)
else if (
(builtins.isList value) &&
((builtins.length value) == 2) &&
((builtins.isFloat (builtins.elemAt value 0)) || (builtins.isFloat (builtins.elemAt value 0))) &&
((builtins.isFloat (builtins.elemAt value 1)) || (builtins.isFloat (builtins.elemAt value 1)))
) then (
builtins.toString (builtins.elemAt value 0) + " " + builtins.toString (builtins.elemAt value 1)
)
else abort "Unhandled value type ${builtins.typeOf value}"
);
concatAttrs = arg: func: (
assert builtins.isAttrs arg;
builtins.concatStringsSep "\n" (lib.attrsets.mapAttrsToList func arg)
);
mkHyprlandVariables = arg: (
concatAttrs arg (
name: value: name + (
if builtins.isAttrs value then (
" {\n" + (mkHyprlandVariables value) + "\n}"
)
else " = " + mkValueString value
)
)
);
mkHyprlandBinds = arg: (
concatAttrs arg (
name: value: (
if builtins.isList value then (
builtins.concatStringsSep "\n" (builtins.map (x: name + " = " + x) value)
)
else concatAttrs value (
name2: value2: name + " = " + name2 + "," + (assert builtins.isString value2; value2)
)
)
)
);
}
and then i use these to write my config like this:
home.file.".config/hypr/hyprland.conf".text = mkHyprlandVariables {
input = {
kb_layout = "de";
kb_variant = "bone";
follow_mouse = true;
touchpad = {
natural_scroll = true;
};
};
general = {
border_size = var.BORDERPX;
gaps_in = 5;
gaps_out = 10;
"col.active_border" = "rgb(${var.COLOR_ACCENT})";
"col.inactive_border" = "rgb(${var.COLOR_BG})";
cursor_inactive_timeout = 5;
layout = "master";
};
decoration = {
rounding = var.CORNERPX;
multisample_edges = true;
active_opacity = (lib.strings.toInt var.TRANSPARENCY * 0.01);
inactive_opacity = (lib.strings.toInt var.TRANSPARENCY * 0.01);
fullscreen_opacity = 1.0;
blur = false;
drop_shadow = false;
};
animations = {
enabled = true;
};
master = {
new_is_master = false;
new_on_top = false;
no_gaps_when_only = false;
};
gestures = {
workspace_swipe = true;
};
misc = {
disable_hyprland_logo = true;
disable_splash_rendering = true;
disable_autoreload = true;
enable_swallow = true;
swallow_regex = ".+${var.TERMINAL}.+";
};
} + "\n" +
mkHyprlandBinds {
bezier = {
linear = "0,0,1,1";
};
} + "\n" +
mkHyprlandBinds {
monitor = {
eDP-1 = "preferred,0x0,1";
DP-1 = "preferred,0x1080,1";
};
workspace = {
eDP-1 = "1";
DP-1 = "11";
};
animation = {
windows = "1,1,linear,slide";
windowsIn = "1,1,linear,popin";
windowsOut = "1,1,linear,popin";
fade = "1,3,linear";
workspaces = "1,1,linear,slide";
specialWorkspace = "1,2,linear,slidevert";
};
wsbind = {
# 1-10 ws to screen 1
"1" = "eDP-1"; "2" = "eDP-1"; "3" = "eDP-1"; "4" = "eDP-1"; "5" = "eDP-1";
"6" = "eDP-1"; "7" = "eDP-1"; "8" = "eDP-1"; "9" = "eDP-1"; "10" = "eDP-1";
# 21-41 to second screen
"11" = "DP-1"; "12" = "DP-1"; "13" = "DP-1"; "14" = "DP-1"; "15" = "DP-1";
"16" = "DP-1"; "17" = "DP-1"; "18" = "DP-1"; "19" = "DP-1"; "20" = "DP-1";
};
bind = {
## audio control
",XF86AudioRaiseVolume" = "exec,pamixer -i 1";
",XF86AudioLowerVolume" = "exec,pamixer -d 1";
",XF86AudioMute" = "exec,pamixer -t";
",XF86AudioPlay" = "exec,playerctl play-pause";
",XF86AudioNext" = "exec,playerctl next";
",XF86AudioPrev" = "exec,playerctl previous";
## brightness control
",XF86MonBrightnessUp" = "exec,brightnessctl set +75";
",XF86MonBrightnessDown" = "exec,brightnessctl set 75-";
## display control
#",XF86Display" = "exec,lock";
## killing application
",XF86RFKill" = "killactive,";
# STARTERS
"SUPER,T" = "exec,${var.TERMINAL}";
"SUPER,B" = "exec,${var.WEBBROWSER}";
"SUPER,d" = "exec,${var.FILEMANAGER}";
"SUPER,space" = "exec,rofi -modi drun,run -show drun";
"ALT,space" = "exec,rofi -modi drun,run -show drun";
"SUPER,k" = "togglespecialworkspace,";
"SUPER,o" = "toggleopaque,";
# SHORTCUT KEYS
"SUPER,C" = "killactive,";
"SUPER,F" = "fullscreen,";
"SUPER,V" = "togglefloating,e";
# "SUPER,L" = "exec,lock";
#############################
# there are 3 hand positions this configuration should work:
# - left hand on ctie (asdf)
# - right hand on nrsg (jkl;)
# or:
# - left hand on ctie (asdf)
# - right hand on mouse with all mouse button aviable
# or:
# - only touchscreen, and some buttons (doesn't work yet, maybe it will never fully work)
############################
# window changing
"ALT, ${right.right}" = "cyclenext,";
"ALT, ${right.left}" = "cyclenext, prev";
# move focused window
"ALT SHIFT, ${right.right}" = "swapnext,";
"ALT SHIFT, ${right.left}" = "swapnext, prev";
# resizing focused
"ALT CTRL, ${right.down}" = "resizeactive,0 100";
"ALT CTRL, ${right.up}" = "resizeactive,0 -100 ";
"ALT CTRL, ${right.right}" = "resizeactive,100 0";
"ALT CTRL, ${right.left}" = "resizeactive,-100 0";
# switch between workspaces by direction
"ALT, ${left.left}" = "workspace, -1";
"ALT, ${left.right}" = "workspace, +1";
"ALT, ${left.up}" = "workspace, previous";
# switch between workspaces directly
"ALT, 1" = "workspace, 1";
"ALT, 2" = "workspace, 2";
"ALT, 3" = "workspace, 3";
"ALT, 4" = "workspace, 4";
"ALT, 5" = "workspace, 5";
"ALT, 6" = "workspace, 6";
"ALT, 7" = "workspace, 7";
"ALT, 8" = "workspace, 8";
"ALT, 9" = "workspace, 9";
"ALT, 0" = "workspace, 10";
"ALT CTRL, 1" = "workspace, 11";
"ALT CTRL, 2" = "workspace, 12";
"ALT CTRL, 3" = "workspace, 13";
"ALT CTRL, 4" = "workspace, 14";
"ALT CTRL, 5" = "workspace, 15";
"ALT CTRL, 7" = "workspace, 17";
"ALT CTRL, 8" = "workspace, 18";
"ALT CTRL, 9" = "workspace, 19";
"ALT CTRL, 0" = "workspace, 20";
# move containers between workspaces by direction
"ALT SHIFT, ${left.left}" = "movetoworkspace, -1";
"ALT SHIFT, ${left.right}" = "movetoworkspace, +1";
# move containers between workspaces directly
"ALT SHIFT, 1" = "movetoworkspace, 1";
"ALT SHIFT, 2" = "movetoworkspace, 2";
"ALT SHIFT, 3" = "movetoworkspace, 3";
"ALT SHIFT, 4" = "movetoworkspace, 4";
"ALT SHIFT, 5" = "movetoworkspace, 5";
"ALT SHIFT, 6" = "movetoworkspace, 6";
"ALT SHIFT, 7" = "movetoworkspace, 7";
"ALT SHIFT, 8" = "movetoworkspace, 8";
"ALT SHIFT, 9" = "movetoworkspace, 9";
"ALT SHIFT, 0" = "movetoworkspace, 10";
"ALT CTRL SHIFT, 1" = "movetoworkspace, 11";
"ALT CTRL SHIFT, 2" = "movetoworkspace, 12";
"ALT CTRL SHIFT, 3" = "movetoworkspace, 13";
"ALT CTRL SHIFT, 4" = "movetoworkspace, 14";
"ALT CTRL SHIFT, 5" = "movetoworkspace, 15";
"ALT CTRL SHIFT, 6" = "movetoworkspace, 16";
"ALT CTRL SHIFT, 7" = "movetoworkspace, 17";
"ALT CTRL SHIFT, 8" = "movetoworkspace, 18";
"ALT CTRL SHIFT, 9" = "movetoworkspace, 19";
"ALT CTRL SHIFT, 0" = "movetoworkspace, 20";
"SUPER,mouse:274" = "killactive";
};
bindm = {
"ALT,mouse:272" = "movewindow";
"ALT,mouse:273" = "resizewindow";
};
bindl = {
#",switch:Lid Switch" = "exec,lock";
};
windowrulev2 = [
"float,class:Rofi"
"tile,class:PPSSPPSDL"
"noborder,class:Rofi"
"center,class:Rofi"
"monitor DP-1,class:xournalpp"
"workspace special,class:KeePassXC"
# no transparency for some windows
"opaque,class:KeePassXC"
"opaque,class:Rofi"
"opaque,class:Alacritty"
"opaque,class:PPSSPPSDL"
"opaque,class:osu!"
"opaque,class:xournalpp"
];
exec-once = [
"dunst"
"blueman-applet"
"${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"
"flameshot"
"nm-applet --indicator"
"xss-lock --transfer-sleep-lock -- swaylock -c 00000000"
"keepassxc"
"hyprpaper"
"waybar"
];
};
and yes submaps are not supported by this, i dont use them
If I wanted to test this, do I have to build your fork of Hyprland, @spikespaz ?
If I wanted to test this, do I have to build your fork of Hyprland, @spikespaz ?
You need to add my fork as a flake input, and merge the nix-hm-module branch with this upstream's main branch. You should not need to rebuild the derivation as long as the package manifest in my fork matches the one you were previously using. I recommend adding the cachix instance as a substituter in your system config.
I don't get it; why is this being worked on in upstream hyperland, instead of Home-Manager?
1. They move slow, and it wouldn't let us add/remove options as soon as they're added to the code. 2. The (corresponding) Nixpkgs package has been outdated for a long time.Basically we aim towards being autonomous, while following best practices from Nixpkgs and HM.
If you have any features for hyprland you would like to be merged in nixpkgs you can ping me directly on matrix. Just now it seems like hyprland patches many things which makes keeping it up-to-date also harder.
@Mic92 thank you for wanting to support us.
I'll get in touch with the current Hyprland maintainer in Nixpkgs and try to update the package, as well as add the unpackaged hyprland-protocols, hyprpicker, hyprpaper and xdg-desktop-portal-hyprland.
As for the Home Manager part, I will try to add the module there (and in NixOS as well) as soon as the packages are in.
I will ping you for reviews. Thanks again.
Deprecating and closing this PR in favor of: https://github.com/spikespaz/dotfiles/tree/master/hm-modules/hyprland
Use the module from my flake, because it is still WIP.
You can find basic instructions in the readme of the repository. There are no instructions specifically for the module itself because I still consider it "for advanced Nix users only", however if you fit that description, please DO USE IT AND PROVIDE FEEDBACK!
Check out the config.nix, default.nix, keybinds.nix windowrules.nix, and monitors.nix files in the following subdirectory: https://github.com/spikespaz/dotfiles/tree/master/users/jacob/desktops/hyprland
Cross-referencing: https://github.com/hyprwm/hyprland-wiki/pull/273#issuecomment-1647417182