summary refs log tree commit diff
diff options
context:
space:
mode:
authorIvan <ivanbrennan@users.noreply.github.com>2020-12-28 11:27:36 -0500
committerGitHub <noreply@github.com>2020-12-28 17:27:36 +0100
commitb90c5cb703da946f53097bc8c7fb1a5acd09c701 (patch)
tree574d59b3e9de80e4b667dbe6063e661fd85e71ca
parent80e3ca6f4afd7f0ce89c12363b4120480eed7891 (diff)
downloadnixpkgs-b90c5cb703da946f53097bc8c7fb1a5acd09c701.tar
nixpkgs-b90c5cb703da946f53097bc8c7fb1a5acd09c701.tar.gz
nixpkgs-b90c5cb703da946f53097bc8c7fb1a5acd09c701.tar.bz2
nixpkgs-b90c5cb703da946f53097bc8c7fb1a5acd09c701.tar.lz
nixpkgs-b90c5cb703da946f53097bc8c7fb1a5acd09c701.tar.xz
nixpkgs-b90c5cb703da946f53097bc8c7fb1a5acd09c701.tar.zst
nixpkgs-b90c5cb703da946f53097bc8c7fb1a5acd09c701.zip
XMonad: configured recompile (#107696)
* nixos/xmonad: xmonad config w/ghc+xmessage

When the "config" option isn't set, we use xmonad-with-packages to
provide xmonad with runtime access to an isolated ghc, ensuring it can
recompile and exec a user's local config (e.g. $HOME/.xmonad/xmonad.hs)
regardless of which ghc (if any) is on PATH.

When the "config" option is set, however, we compile a configured xmonad
executable upfront (during nixos-rebuild), and prior to this commit, it
was not provided with runtime access to an isolated ghc.

As a result, with the "config" option set, it was not possible
to recompile and exec a user's local config unless there was a
compatible version of ghc on PATH with the necessary packages (xmonad,
xmonad-contrib, etc.) in its package database. Adding such a ghc to
environment.systemPackages, e.g.

  (haskellPackages.ghcWithPackages (ps: with ps; [xmonad xmonad-contrib]))

is problematic because it adds both ghc and an unconfigured xmonad to
PATH, e.g.

  $ ls -l $(which xmonad ghc)
  lrwxrwxrwx ... /run/current-system/sw/bin/ghc -> /nix/store/...-ghc-8.10.2-with-packages/bin/ghc
  lrwxrwxrwx ... /run/current-system/sw/bin/xmonad -> /nix/store/...-ghc-8.10.2-with-packages/bin/xmonad

Having the unconfigured xmonad on PATH is particularly bad because
restarting xmonad will dump the user into the unconfigured version, and
if no local config exists (e.g. in $HOME/.xmonad/xmonad.hs), they'll be
left in this unconfigured state.

In this commmit, we give the configured xmonad runtime access to ghc
like xmonad-with-packages does for the unconfigured version. The aim
is to allow the user to switch between the nixos module's config and a
local config (e.g. $HOME/.xmonad/xmonad.hs) at will, so they can try out
config changes without performing a nixos-rebuild.

Since the xmonad on PATH is the configured executable, there's no
danger a user could unwittingly restart into the unconfigured version,
and because xmonad will refuse to recompile when no local config
exists, there's no danger a user could unwittingly recompile into an
unconfigured version.

Given that a local config exists, the recompile/restart behavior depends
on two factors:
- which entry point is used
  * 'XMonad.xmonad' (default)
  * 'XMonad.launch' (recommended in "config" option description)
- what operation is triggered (i.e. via mod+q)
  * `spawn "xmonad --recompile && xmonad --restart"` (default)
  * `restart "xmonad" True`
  * custom function

If the default 'XMonad.xmonad' entrypoint and default mod+q operation
are used, hitting mod+q will compile and exec the local config, which
will remain in use until next time the display manager is restarted.

If the entrypoint is changed to 'XMonad.launch' but mod+q left with its
default operation, hitting mod+q will have no visible effect. The logs
(as seen by running `journalctl --identifier xmonad --follow`) will show
an error,
  X Error of failed request:  BadAccess (attempt to access private resource denied)
which indicates that the shell was unable to start xmonad because
another window manager is already running (namely, the nixos-configured
xmonad).
https://wiki.haskell.org/Xmonad/Frequently_asked_questions#X_Error_of_failed_request:_BadAccess_.28attempt_to_access_private_resource_denied.29

Changing the mod+q operation to `restart "xmonad" True` (as recommended
in the "config" option's description) will allow a restart of the
nixos-configured xmonad to be triggeredy by hitting mod+q.

Finally, if the entrypoint is 'XMonad.launch', mod+q has been
bound to `restart "xmonad" True` and another key bound to a custom
recompile/restart function (e.g. `compileRestart` as shown in the
"config" option example), the user can switch between the nixos module's
config and their local config, with the custom key switching to the
local config and mod+q switching back.

* nixos/xmonad: refactor let binding

* nixos/xmonad: refactor (eliminate duplicate code)

* nixos/xmonad: install man pages

Prior to this commit, man pages were not installed if the "config"
option was set.

* nixos/xmonad: comment grammar fixups

* nixos/xmonad: writeStateToFile in example config

Calling writeStateToFile prior to recompiling and restarting allows
state (workspaces, etc.) to be preserved across the restart.

* nixos/xmonad: add ivanbrennan to maintainers

* nixos/xmonad: adjust compileRestart example

* nixos/xmonad: add missing import to example config
-rw-r--r--nixos/modules/services/x11/window-managers/xmonad.nix58
1 files changed, 38 insertions, 20 deletions
diff --git a/nixos/modules/services/x11/window-managers/xmonad.nix b/nixos/modules/services/x11/window-managers/xmonad.nix
index b9013ca1ff9..2bb4827be9d 100644
--- a/nixos/modules/services/x11/window-managers/xmonad.nix
+++ b/nixos/modules/services/x11/window-managers/xmonad.nix
@@ -5,25 +5,37 @@ let
   inherit (lib) mkOption mkIf optionals literalExample;
   cfg = config.services.xserver.windowManager.xmonad;
 
+  ghcWithPackages = cfg.haskellPackages.ghcWithPackages;
+  packages = self: cfg.extraPackages self ++
+                   optionals cfg.enableContribAndExtras
+                   [ self.xmonad-contrib self.xmonad-extras ];
+
   xmonad-vanilla = pkgs.xmonad-with-packages.override {
-    ghcWithPackages = cfg.haskellPackages.ghcWithPackages;
-    packages = self: cfg.extraPackages self ++
-                     optionals cfg.enableContribAndExtras
-                     [ self.xmonad-contrib self.xmonad-extras ];
+    inherit ghcWithPackages packages;
   };
 
-  xmonad-config = pkgs.writers.writeHaskellBin "xmonad" {
-    ghc = cfg.haskellPackages.ghc;
-    libraries = [ cfg.haskellPackages.xmonad ] ++
-                cfg.extraPackages cfg.haskellPackages ++
-                optionals cfg.enableContribAndExtras
-                (with cfg.haskellPackages; [ xmonad-contrib xmonad-extras ]);
-    inherit (cfg) ghcArgs;
-  } cfg.config;
+  xmonad-config =
+    let
+      xmonadAndPackages = self: [ self.xmonad ] ++ packages self;
+      xmonadEnv = ghcWithPackages xmonadAndPackages;
+      configured = pkgs.writers.writeHaskellBin "xmonad" {
+        ghc = cfg.haskellPackages.ghc;
+        libraries = xmonadAndPackages cfg.haskellPackages;
+        inherit (cfg) ghcArgs;
+      } cfg.config;
+    in
+      pkgs.runCommandLocal "xmonad" {
+        nativeBuildInputs = [ pkgs.makeWrapper ];
+      } ''
+        install -D ${xmonadEnv}/share/man/man1/xmonad.1.gz $out/share/man/man1/xmonad.1.gz
+        makeWrapper ${configured}/bin/xmonad $out/bin/xmonad \
+          --set NIX_GHC "${xmonadEnv}/bin/ghc" \
+          --set XMONAD_XMESSAGE "${pkgs.xorg.xmessage}/bin/xmessage"
+      '';
 
   xmonad = if (cfg.config != null) then xmonad-config else xmonad-vanilla;
 in {
-  meta.maintainers = with maintainers; [ lassulus xaverdh ];
+  meta.maintainers = with maintainers; [ lassulus xaverdh ivanbrennan ];
 
   options = {
     services.xserver.windowManager.xmonad = {
@@ -72,13 +84,13 @@ in {
           This setup is then analogous to other (non-NixOS) linux distributions.
 
           If you do set this option, you likely want to use "launch" as your
-          entry point for xmonad (as in the example), to avoid xmonads
+          entry point for xmonad (as in the example), to avoid xmonad's
           recompilation logic on startup. Doing so will render the default
           "mod+q" restart key binding dysfunctional though, because that attempts
           to call your binary with the "--restart" command line option, unless
           you implement that yourself. You way mant to bind "mod+q" to
           <literal>(restart "xmonad" True)</literal> instead, which will just restart
-          xmonad from PATH. This allows e.g. switching to the new xmonad binary,
+          xmonad from PATH. This allows e.g. switching to the new xmonad binary
           after rebuilding your system with nixos-rebuild.
 
           If you actually want to run xmonad with a config specified here, but
@@ -91,6 +103,7 @@ in {
         example = ''
           import XMonad
           import XMonad.Util.EZConfig (additionalKeys)
+          import Control.Monad (when)
           import Text.Printf (printf)
           import System.Posix.Process (executeFile)
           import System.Info (arch,os)
@@ -99,16 +112,21 @@ in {
 
           compiledConfig = printf "xmonad-%s-%s" arch os
 
-          compileRestart = whenX (recompile True) . catchIO $ do
-              dir  <- getXMonadDataDir
-              args <- getArgs
-              executeFile (dir </> compiledConfig) False args Nothing
+          compileRestart resume =
+            whenX (recompile True) $
+              when resume writeStateToFile
+                *> catchIO
+                  ( do
+                      dir <- getXMonadDataDir
+                      args <- getArgs
+                      executeFile (dir </> compiledConfig) False args Nothing
+                  )
 
           main = launch defaultConfig
               { modMask = mod4Mask -- Use Super instead of Alt
               , terminal = "urxvt" }
               `additionalKeys`
-              [ ( (mod4Mask,xK_r), compileRestart )
+              [ ( (mod4Mask,xK_r), compileRestart True)
               , ( (mod4Mask,xK_q), restart "xmonad" True ) ]
         '';
       };