xmonad-entryhelper makes your compiled XMonad config a standalone binary.
It simulates the XMonad's argument handling and supports customized compliation.
Please check
README
for details.
[
Skip to Readme]
xmonad-entryhelper
xmonad-entryhelper makes your compiled XMonad config a standalone binary.
It simulates the XMonad's argument handling
and supports customized compliation.
Table of Contents
Introduction
xmonad-entryhelper frees you from keeping a xmonad library as a system- or user- level dependency.
Instead, you can keep your XMonad configurations either as a local
cabal project using
cabal sandbox or within
a protected environment like those created by hsenv
Simple setup
-
After installation, modify your xmonad.hs
accordingly:
Your xmonad config might look like:
-- some imports here
import ...
...
-- your main entry point
main :: IO ()
main = _
Rename your main
to something else, import withHelper
from XMonad.Util.EntryHelper
and use it to call your old main
:
-- some imports here
import ...
import XMonad.Util.EntryHelper (withHelper)
...
-- your old main entry point
oldMain :: IO ()
oldMain = _
-- your new main entry point
main :: IO ()
main = withHelper oldMain
It is recommended to set the "restart xmonad" action (typically mod-q
in your keybinding)
to just invoke xmonad --restart
. Although the default action,
essentially xmonad --recompile && xmonad --restart
should work properly,
argument --recompile
forces the compilation (which might involve
removing all binaries and compiling everything).
If you are using a build system like make
or cabal
,
forcing a compilation might not be a desired behavior as build systems are in general designed
to prevent recompilation.
-
Finally you need to have a writable local PATH
directory.
For example you can make directory $HOME/bin
:
mkdir -p ~/bin
And append the following lines in your shell profile file
(it's usually your ~/.bash_profile
file):
...
# my local executable files
export PATH="${HOME}/bin:${PATH}"
Create soft link to your compiled xmonad
binary:
# the binary name varies depending on your OS and architecture,
# check your ~/.xmonad/ directory to find out
$ ln -s ~/.xmonad/xmonad-x86_64-linux ~/bin/xmonad
And verify if xmonad
is now leading to your compiled xmonad config:
$ source ~/.profile
$ which xmonad
/home/username/bin/xmonad
If this doesn't work, check articles like
Zsh/Bash startup files loading order
for troubleshooting.
-
Now you are free to remove XMonad from your system- or user- level packages.
Because your compiled XMonad will work on its own:
$ xmonad --help
xmonad-entryhelper - XMonad config entry point wrapper
Usage: xmonad [OPTION]
Options:
--help Print this message
--version Print XMonad's version number
--recompile Recompile XMonad
--replace Replace the running window manager with XMonad
--restart Request a running XMonad process to restart
Argument handling
Although this projects tries to resemble the argument handling behavior of XMonad,
there are not exact the same. The differences are:
-
When invoked without argument or with --replace
or --resume
(this argument is not documented,
assumably intended for internal use only):
-
XMonad always does up-to-date checking internally and compile source codes as needed before
invocations
-
EntryHelper doesn't do up-to-date checking.
(TL;DR) This is because XMonad is using the following routing when executed:
- XMonad started
- Check for source files and recompile as needed
- Execute the compiled binary
- If any goes wrong, start xmonad using default configuration
This routing only works if XMonad binary and the binary compiled by XMonad
are two different programs. But as EntryHelper wants to make your compiled
binary and the XMonad program the same file, the same routing will cause
an infinite loop (start => check => execute => start ...
).
To solve this problem, in EntryHelper the up-to-date checking
is considered one part of the compilation. And the compilation
will not be executed unless --recompile
or --restart
is given.
Additionally, if you are using a build system like make
or cabal
to handle compilation,
leaving the job of up-to-date checking
to the build system would be the simplest approach.
-
When invoked with --restart
:
- EntryHelper will try to recompile (without forcing) before sending the request
- both XMonad and EntryHelper send the restart request
Advanced features
-
Customized compilation and post-compilation handling
By passing a Config
(from XMonad.Util.EntryHelper.Config
) to withCustomHelper
,
it is possible to customize the compilation and post-compilation actions.
Read document of Config
for detail.
-
Customized shell command compilation
You can invoke an arbitrary shell command to do the compilation using
compileUsingShell
from XMonad.Util.EntryHelper.Compile
, the working
directory for this shell command will be ~/.xmonad
and its stdout
and stderr
outputs will be redirected into ~/.xmonad/xmonad.errors
.
Assuming you have set your environment variable ${XMONAD_HOME}
to point to the project home directory,
and you are using Makefile
to handle the compilation,
the following example should work for you:
import qualified XMonad.Util.EntryHelper as EH
main :: IO ()
main = EH.withCustomHelper mhConf
where
mhConf = EH.defaultConfig
{ EH.run = oldMain
, EH.compile = \force -> do
let cmd = if force
then "cd ${XMONAD_HOME} && make clean && make all"
else "cd ${XMONAD_HOME} && make all"
EH.compileUsingShell cmd
}
-
Parallel compilation protection
You might find withLock
from XMonad.Util.EntryHelper.Compile
useful
to prevent yourself from parallel compilation
(this is usually caused by hitting mod-q
rapidly multiple times...).
It creates a temprary file (typically /tmp/xmonad.{username}.lock
) before compiling
and deletes it after the compilation is done. When this temprary file exists,
no other protected action with the same
file lock is allowed to proceed and will return with a default value.
To protect an action
from parallel execution, all you have to do is to
replace it with withLock def action
, with def
being a default value to return
when it hits a file lock.
Continue from the previous example:
import qualified XMonad.Util.EntryHelper as EH
main :: IO ()
main = EH.withCustomHelper mhConf
where
mhConf = EH.defaultConfig
{ EH.run = oldMain
-- adding "EH.withLock ExitSuccess $"
, EH.compile = \force -> EH.withLock ExitSuccess $ do
let cmd = if force
then "cd ${XMONAD_HOME} && make clean && make all"
else "cd ${XMONAD_HOME} && make all"
EH.compileUsingShell cmd
}
Be careful not to protect an action more than once.
-
Sending restart request to current xmonad instance
sendRestart
from XMonad.Util.EntryHelper.Util
is an exact copy of the same
function found in XMonad (unfortunately XMonad doesn't export it).
Simply calling this function will sent a restart request to the running XMonad
instance.
Note that XMonad restarts by looking for the compiled binary to replace it,
which means the binary file (e.g. ~/.xmonad/xmonad-x86_64-linux
) has to exist
or otherwise your window manager session will crash.
Feedback
Feel free to open issues for either bug report, enhancement or discussion.