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