{ config, lib, ... }: # The Hyprland compositor. let directionalKeybinds = lib.listToAttrs ( lib.concatLists ( lib.mapAttrsToList (key: direction: [ { name = "hyprland-focus-${direction}"; value = { keys = [ "SUPER" key ]; action = { type = "focus"; inherit direction; }; }; } { name = "hyprland-move-window-${direction}"; value = { keys = [ "SUPER" "SHIFT" key ]; action = { type = "move-window"; inherit direction; }; }; } ]) { H = "left"; J = "down"; K = "up"; L = "right"; } ) ); resizeKeybinds = lib.listToAttrs ( lib.mapAttrsToList (key: delta: { name = "hyprland-resize-${delta.name}"; value = { keys = [ "SUPER" "ALT" key ]; action = { type = "resize-window"; inherit (delta) x y; }; }; }) { H = { name = "left"; x = -40; y = 0; }; J = { name = "down"; x = 0; y = 40; }; K = { name = "up"; x = 0; y = -40; }; L = { name = "right"; x = 40; y = 0; }; } ); workspaceKeybinds = lib.listToAttrs ( lib.concatMap ( workspace: let workspaceName = toString workspace; in [ { name = "hyprland-focus-workspace-${workspaceName}"; value = { keys = [ "SUPER" workspaceName ]; action = { type = "focus-workspace"; inherit workspace; }; }; } { name = "hyprland-move-to-workspace-${workspaceName}"; value = { keys = [ "SUPER" "SHIFT" workspaceName ]; action = { type = "move-to-workspace"; inherit workspace; }; }; } ] ) (lib.range 1 9) ); mkBind = keybind: { _args = [ (lib.concatStringsSep " + " keybind.keys) (mkDispatcher keybind.action) ]; }; mkDispatcher = action: lib.generators.mkLuaInline ( { exec = "hl.dsp.exec_cmd(${toLua action.command})"; focus = "hl.dsp.focus(${toLua { inherit (action) direction; }})"; "move-window" = "hl.dsp.window.move(${toLua { inherit (action) direction; }})"; "resize-window" = "hl.dsp.window.resize(${ toLua ({ inherit (action) x y; relative = true; }) })"; "toggle-floating" = "hl.dsp.window.float(${toLua { action = "toggle"; }})"; fullscreen = "hl.dsp.window.fullscreen()"; layout = "hl.dsp.layout(${toLua action.message})"; "close-window" = "hl.dsp.window.close()"; "kill-window" = "hl.dsp.window.kill()"; "focus-workspace" = "hl.dsp.focus(${toLua { inherit (action) workspace; }})"; "move-to-workspace" = "hl.dsp.window.move(${toLua { inherit (action) workspace; }})"; } .${action.type} ); keybinds = lib.attrValues ( lib.attrByPath [ config.networking.hostName ] { } config.registry.desktop-keybind ); toLua = lib.generators.toLua { }; in { options.modules.desktop.session.hyprland.enable = lib.mkEnableOption "the Hyprland compositor"; config = lib.mkIf config.modules.desktop.session.hyprland.enable { registry.desktop-keybind.${config.networking.hostName} = { hyprland-terminal = { keys = [ "SUPER" "Return" ]; action = { type = "exec"; command = "ghostty"; }; }; hyprland-toggle-floating = { keys = [ "SUPER" "Space" ]; action.type = "toggle-floating"; }; hyprland-fullscreen = { keys = [ "SUPER" "F" ]; action.type = "fullscreen"; }; hyprland-toggle-split = { keys = [ "SUPER" "T" ]; action = { type = "layout"; message = "togglesplit"; }; }; hyprland-close-window = { keys = [ "SUPER" "SHIFT" "Q" ]; action.type = "close-window"; }; hyprland-kill-window = { keys = [ "SUPER" "CTRL" "Q" ]; action.type = "kill-window"; }; } // directionalKeybinds // resizeKeybinds // workspaceKeybinds; # This program integration owns the session, portals, and polkit, launched # through the universal Wayland session manager. programs.hyprland = { enable = true; withUWSM = true; }; home-manager.users.${config.user.name}.wayland.windowManager.hyprland = { enable = true; # One package drives the whole session, so there is never a version split. # The program integration above installs it and the portal, leaving home- # manager to write only the config. package = null; portalPackage = null; # uwsm owns the systemd graphical-session targets. systemd.enable = false; # Write the native Lua Hyprland config. Home Manager renders the Nix # settings below as hl.* calls. configType = "lua"; settings = { config.general.layout = "dwindle"; bind = map mkBind keybinds; }; }; }; }