1{pkgs, lib, config, ...}:
2
3with lib;
4let
5 inherit (lib) mkOption mkIf optionals literalExample;
6 cfg = config.services.xserver.windowManager.xmonad;
7
8 ghcWithPackages = cfg.haskellPackages.ghcWithPackages;
9 packages = self: cfg.extraPackages self ++
10 optionals cfg.enableContribAndExtras
11 [ self.xmonad-contrib self.xmonad-extras ];
12
13 xmonad-vanilla = pkgs.xmonad-with-packages.override {
14 inherit ghcWithPackages packages;
15 };
16
17 xmonad-config =
18 let
19 xmonadAndPackages = self: [ self.xmonad ] ++ packages self;
20 xmonadEnv = ghcWithPackages xmonadAndPackages;
21 configured = pkgs.writers.writeHaskellBin "xmonad" {
22 ghc = cfg.haskellPackages.ghc;
23 libraries = xmonadAndPackages cfg.haskellPackages;
24 inherit (cfg) ghcArgs;
25 } cfg.config;
26 in
27 pkgs.runCommandLocal "xmonad" {
28 nativeBuildInputs = [ pkgs.makeWrapper ];
29 } ''
30 install -D ${xmonadEnv}/share/man/man1/xmonad.1.gz $out/share/man/man1/xmonad.1.gz
31 makeWrapper ${configured}/bin/xmonad $out/bin/xmonad \
32 --set NIX_GHC "${xmonadEnv}/bin/ghc" \
33 --set XMONAD_XMESSAGE "${pkgs.xorg.xmessage}/bin/xmessage"
34 '';
35
36 xmonad = if (cfg.config != null) then xmonad-config else xmonad-vanilla;
37in {
38 meta.maintainers = with maintainers; [ lassulus xaverdh ivanbrennan ];
39
40 options = {
41 services.xserver.windowManager.xmonad = {
42 enable = mkEnableOption "xmonad";
43 haskellPackages = mkOption {
44 default = pkgs.haskellPackages;
45 defaultText = "pkgs.haskellPackages";
46 example = literalExample "pkgs.haskell.packages.ghc784";
47 description = ''
48 haskellPackages used to build Xmonad and other packages.
49 This can be used to change the GHC version used to build
50 Xmonad and the packages listed in
51 <varname>extraPackages</varname>.
52 '';
53 };
54
55 extraPackages = mkOption {
56 type = types.functionTo (types.listOf types.package);
57 default = self: [];
58 defaultText = "self: []";
59 example = literalExample ''
60 haskellPackages: [
61 haskellPackages.xmonad-contrib
62 haskellPackages.monad-logger
63 ]
64 '';
65 description = ''
66 Extra packages available to ghc when rebuilding Xmonad. The
67 value must be a function which receives the attrset defined
68 in <varname>haskellPackages</varname> as the sole argument.
69 '';
70 };
71
72 enableContribAndExtras = mkOption {
73 default = false;
74 type = lib.types.bool;
75 description = "Enable xmonad-{contrib,extras} in Xmonad.";
76 };
77
78 config = mkOption {
79 default = null;
80 type = with lib.types; nullOr (either path str);
81 description = ''
82 Configuration from which XMonad gets compiled. If no value is
83 specified, a vanilla xmonad binary is put in PATH, which will
84 attempt to recompile and exec your xmonad config from $HOME/.xmonad.
85 This setup is then analogous to other (non-NixOS) linux distributions.
86
87 If you do set this option, you likely want to use "launch" as your
88 entry point for xmonad (as in the example), to avoid xmonad's
89 recompilation logic on startup. Doing so will render the default
90 "mod+q" restart key binding dysfunctional though, because that attempts
91 to call your binary with the "--restart" command line option, unless
92 you implement that yourself. You way mant to bind "mod+q" to
93 <literal>(restart "xmonad" True)</literal> instead, which will just restart
94 xmonad from PATH. This allows e.g. switching to the new xmonad binary
95 after rebuilding your system with nixos-rebuild.
96
97 If you actually want to run xmonad with a config specified here, but
98 also be able to recompile and restart it from a copy of that source in
99 $HOME/.xmonad on the fly, you will have to implement that yourself
100 using something like "compileRestart" from the example.
101 This should allow you to switch at will between the local xmonad and
102 the one NixOS puts in your PATH.
103 '';
104 example = ''
105 import XMonad
106 import XMonad.Util.EZConfig (additionalKeys)
107 import Control.Monad (when)
108 import Text.Printf (printf)
109 import System.Posix.Process (executeFile)
110 import System.Info (arch,os)
111 import System.Environment (getArgs)
112 import System.FilePath ((</>))
113
114 compiledConfig = printf "xmonad-%s-%s" arch os
115
116 compileRestart resume =
117 whenX (recompile True) $
118 when resume writeStateToFile
119 *> catchIO
120 ( do
121 dir <- getXMonadDataDir
122 args <- getArgs
123 executeFile (dir </> compiledConfig) False args Nothing
124 )
125
126 main = launch defaultConfig
127 { modMask = mod4Mask -- Use Super instead of Alt
128 , terminal = "urxvt" }
129 `additionalKeys`
130 [ ( (mod4Mask,xK_r), compileRestart True)
131 , ( (mod4Mask,xK_q), restart "xmonad" True ) ]
132 '';
133 };
134
135 xmonadCliArgs = mkOption {
136 default = [];
137 type = with lib.types; listOf str;
138 description = ''
139 Command line arguments passed to the xmonad binary.
140 '';
141 };
142
143 ghcArgs = mkOption {
144 default = [];
145 type = with lib.types; listOf str;
146 description = ''
147 Command line arguments passed to the compiler (ghc)
148 invocation when xmonad.config is set.
149 '';
150 };
151
152 };
153 };
154 config = mkIf cfg.enable {
155 services.xserver.windowManager = {
156 session = [{
157 name = "xmonad";
158 start = ''
159 systemd-cat -t xmonad -- ${xmonad}/bin/xmonad ${lib.escapeShellArgs cfg.xmonadCliArgs} &
160 waitPID=$!
161 '';
162 }];
163 };
164
165 environment.systemPackages = [ xmonad ];
166 };
167}