diff --git a/nixos/modules/services/x11/window-managers/xmonad.nix b/nixos/modules/services/x11/window-managers/xmonad.nix index ecad411ff683..68f97c2f504b 100644 --- a/nixos/modules/services/x11/window-managers/xmonad.nix +++ b/nixos/modules/services/x11/window-managers/xmonad.nix @@ -2,7 +2,7 @@ with lib; let - inherit (lib) mkOption mkIf optionals literalExpression; + inherit (lib) mkOption mkIf optionals literalExpression optionalString; cfg = config.services.xserver.windowManager.xmonad; ghcWithPackages = cfg.haskellPackages.ghcWithPackages; @@ -26,11 +26,14 @@ let 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 \ + '' + optionalString cfg.enableConfiguredRecompile '' + --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 { @@ -95,12 +98,14 @@ in { xmonad from PATH. This allows e.g. switching to the new xmonad binary after rebuilding your system with nixos-rebuild. For the same reason, ghc is not added to the environment when this - option is set. + option is set, unless is + set to true. If you actually want to run xmonad with a config specified here, but also be able to recompile and restart it from a copy of that source in - $HOME/.xmonad on the fly, you will have to implement that yourself - using something like "compileRestart" from the example. + $HOME/.xmonad on the fly, set + to true and implement something like "compileRestart" + from the example. This should allow you to switch at will between the local xmonad and the one NixOS puts in your PATH. ''; @@ -116,6 +121,29 @@ in { compiledConfig = printf "xmonad-%s-%s" arch os + myConfig = defaultConfig + { modMask = mod4Mask -- Use Super instead of Alt + , terminal = "urxvt" } + `additionalKeys` + [ ( (mod4Mask,xK_r), compileRestart True) + , ( (mod4Mask,xK_q), restart "xmonad" True ) ] + + -------------------------------------------- + {- version 0.17.0 -} + -------------------------------------------- + -- compileRestart resume = + -- dirs <- io getDirectories + -- whenX (recompile dirs True) $ + -- when resume writeStateToFile + -- *> catchIO + -- ( do + -- args <- getArgs + -- executeFile (cacheDir dirs compiledConfig) False args Nothing + -- ) + -- + -- main = getDirectories >>= launch myConfig + -------------------------------------------- + compileRestart resume = whenX (recompile True) $ when resume writeStateToFile @@ -126,12 +154,17 @@ in { executeFile (dir compiledConfig) False args Nothing ) - main = launch defaultConfig - { modMask = mod4Mask -- Use Super instead of Alt - , terminal = "urxvt" } - `additionalKeys` - [ ( (mod4Mask,xK_r), compileRestart True) - , ( (mod4Mask,xK_q), restart "xmonad" True ) ] + main = launch myConfig + ''; + }; + + enableConfiguredRecompile = mkOption { + default = false; + type = lib.types.bool; + description = '' + Enable recompilation even if is set to a + non-null value. This adds the necessary Haskell dependencies (GHC with + packages) to the xmonad binary's environment. ''; }; diff --git a/nixos/tests/xmonad.nix b/nixos/tests/xmonad.nix index 078cd2118107..a14d4b819eb6 100644 --- a/nixos/tests/xmonad.nix +++ b/nixos/tests/xmonad.nix @@ -1,4 +1,55 @@ -import ./make-test-python.nix ({ pkgs, ...} : { +import ./make-test-python.nix ({ pkgs, ...}: + +let + mkConfig = name: keys: '' + import XMonad + import XMonad.Operations (restart) + import XMonad.Util.EZConfig + import XMonad.Util.SessionStart + import Control.Monad (when) + import Text.Printf (printf) + import System.Posix.Process (executeFile) + import System.Info (arch,os) + import System.Environment (getArgs) + import System.FilePath (()) + + main = launch $ def { startupHook = startup } `additionalKeysP` myKeys + + startup = isSessionStart >>= \sessInit -> + spawn "touch /tmp/${name}" + >> if sessInit then setSessionStarted else spawn "xterm" + + myKeys = [${builtins.concatStringsSep ", " keys}] + + compiledConfig = printf "xmonad-%s-%s" arch os + + compileRestart resume = + whenX (recompile True) $ + when resume writeStateToFile + *> catchIO + ( do + dir <- getXMonadDataDir + args <- getArgs + executeFile (dir compiledConfig) False args Nothing + ) + ''; + + oldKeys = + [ ''("M-C-x", spawn "xterm")'' + ''("M-q", restart "xmonad" True)'' + ''("M-C-q", compileRestart True)'' + ''("M-C-t", spawn "touch /tmp/somefile")'' # create somefile + ]; + + newKeys = + [ ''("M-C-x", spawn "xterm")'' + ''("M-q", restart "xmonad" True)'' + ''("M-C-q", compileRestart True)'' + ''("M-C-r", spawn "rm /tmp/somefile")'' # delete somefile + ]; + + newConfig = pkgs.writeText "xmonad.hs" (mkConfig "newXMonad" newKeys); +in { name = "xmonad"; meta = with pkgs.lib.maintainers; { maintainers = [ nequissimus ]; @@ -10,21 +61,10 @@ import ./make-test-python.nix ({ pkgs, ...} : { services.xserver.displayManager.defaultSession = "none+xmonad"; services.xserver.windowManager.xmonad = { enable = true; + enableConfiguredRecompile = true; enableContribAndExtras = true; extraPackages = with pkgs.haskellPackages; haskellPackages: [ xmobar ]; - config = '' - import XMonad - import XMonad.Operations (restart) - import XMonad.Util.EZConfig - import XMonad.Util.SessionStart - - main = launch $ def { startupHook = startup } `additionalKeysP` myKeys - - startup = isSessionStart >>= \sessInit -> - if sessInit then setSessionStarted else spawn "xterm" - - myKeys = [ ("M-C-x", spawn "xterm"), ("M-q", restart "xmonad" True) ] - ''; + config = mkConfig "oldXMonad" oldKeys; }; }; @@ -38,10 +78,40 @@ import ./make-test-python.nix ({ pkgs, ...} : { machine.wait_for_window("${user.name}.*machine") machine.sleep(1) machine.screenshot("terminal1") + machine.succeed("rm /tmp/oldXMonad") machine.send_key("alt-q") - machine.sleep(3) + machine.wait_for_file("/tmp/oldXMonad") machine.wait_for_window("${user.name}.*machine") machine.sleep(1) machine.screenshot("terminal2") + + # /tmp/somefile should not exist yet + machine.fail("stat /tmp/somefile") + + # original config has a keybinding that creates somefile + machine.send_key("alt-ctrl-t") + machine.sleep(1) + machine.succeed("stat /tmp/somefile") + + # set up the new config + machine.succeed("mkdir -p ${user.home}/.xmonad") + machine.copy_from_host("${newConfig}", "${user.home}/.xmonad/xmonad.hs") + + # recompile xmonad using the new config + machine.send_key("alt-ctrl-q") + machine.wait_for_file("/tmp/newXMonad") + + # new config has a keybinding that deletes somefile + machine.send_key("alt-ctrl-r") + machine.sleep(1) + machine.fail("stat /tmp/somefile") + + # restart with the old config, and confirm the old keybinding is back + machine.succeed("rm /tmp/oldXMonad") + machine.send_key("alt-q") + machine.wait_for_file("/tmp/oldXMonad") + machine.send_key("alt-ctrl-t") + machine.sleep(1) + machine.succeed("stat /tmp/somefile") ''; })