I wanted extend my xmonad.hs
by moving it into its own sandboxed project environment via stack
. It turns out that such sandboxing is possible via embedding your xmonad main
function in a parent main
that utilizes xmonad-entryhelper. Following the instructions in the projects README, converted xmonad.hs
from this:
main :: IO ()
main = do
statBar <- spawnPipe myXMobar
xmonad def
{ terminal = myTerminal
, focusFollowsMouse = myFocusFollowsMouse
, borderWidth = myBorderWidth
, modMask = myModMask
, workspaces = myWorkspaces
, normalBorderColor = myNormalBorderColor
, focusedBorderColor = myFocusedBorderColor
-- key bindings
, keys = myKeys
--, mouseBindings = myMouseBindings
-- hooks, layouts
, layoutHook = myLayoutHook
, manageHook = manageHook def <+> myManageHook
--, handleEventHook = myEventHook
, logHook = myLogHook statBar >> setWMName "LG3D"
--, startupHook = myStartupHook
}
To...
kaleidoscope :: IO ()
kaleidoscope = do
statBar <- spawnPipe myXMobar
xmonad def
{ terminal = myTerminal
, focusFollowsMouse = myFocusFollowsMouse
, borderWidth = myBorderWidth
, modMask = myModMask
, workspaces = myWorkspaces
, normalBorderColor = myNormalBorderColor
, focusedBorderColor = myFocusedBorderColor
-- key bindings
, keys = myKeys
--, mouseBindings = myMouseBindings
-- hooks, layouts
, layoutHook = myLayoutHook
, manageHook = manageHook def <+> myManageHook
--, handleEventHook = myEventHook
, logHook = myLogHook statBar >> setWMName "LG3D"
--, startupHook = myStartupHook
}
main :: IO ()
main = EH.withCustomHelper kaleidoscopeConfig
where
kaleidoscopeConfig = EH.defaultConfig
{ EH.run = kaleidoscope
, EH.compile = \force -> EH.withLock ExitSuccess $ do
let cmd =
if force
then "cd /home/oldmanmike/src/github.com/oldmanmike/kaleidoscope && stack clean && stack build"
else "cd /home/oldmanmike/src/github.com/oldmanmike/kaleidoscope && stack build"
EH.compileUsingShell cmd
, EH.postCompile = EH.defaultPostCompile
}
So, I can now compile my project using xmonad --recompile
and the binary found in .xmonad
is soft linked to the binary produced by my sandboxed project.
But for some reason, xmonad --restart
doesn't work anymore. What could be tripping it up?
The command itself doesn't seem to produce any errors from X - it just returns successfully with no sign anything even happened. I trying both the xmonad --restart
command at the shell and binding mod-q
in xmonad to either spawnPipe "xmonad --restart"
or io sendRestart
- neither seem to work. When I shut down my Xserver and restart it manually, any and all updates show up, but right now hot swapping changes doesn't seem to work. Does it need to see two separate binaries at once - the current one and new one?
EDIT: I've been playing around with the problem via reading the source code for xmonad
, xmonad-entryhelper
, and X11
. I'm currently picking apart this:
sendRestart :: IO ()
sendRestart = do
dpy <- openDisplay ""
rw <- rootWindow dpy $ defaultScreen dpy
xmonad_restart <- internAtom dpy "XMONAD_RESTART" False
allocaXEvent $ \e -> do
setEventType e clientMessage
setClientMessageEvent e rw xmonad_restart 32 0 currentTime
sendEvent dpy rw False structureNotifyMask e
sync dpy False
I'm not familiar with the X11 API yet, but what weirds me out is the internAtom
function and what the purpose of XMONAD_RESTART serves in the client message. There's also this handler:
handle e@ClientMessageEvent { ev_message_type = mt } = do
a <- getAtom "XMONAD_RESTART"
if (mt == a)
then restart "xmonad" True
else broadcastMessage e
handle e = broadcastMessage e -- trace (eventName e) -- ignoring
So I'm guessing atoms are just ad-hoc strings used to ID message events? I don't get any error messages when I run a restart so it would seem as if it makes it all the way to the operation handle:
restart :: String -> Bool -> X ()
restart prog resume = do
broadcastMessage ReleaseResources
io . flush =<< asks display
let wsData = show . W.mapLayout show . windowset
maybeShow (t, Right (PersistentExtension ext)) = Just (t, show ext)
maybeShow (t, Left str) = Just (t, str)
maybeShow _ = Nothing
extState = return . show . catMaybes . map maybeShow . M.toList . extensibleState
args <- if resume then gets (\s -> "--resume":wsData s:extState s) else return []
catchIO (executeFile prog True args Nothing)
I'm suspecting there's a permission issue here that xmonad-x86_64-linux
had back when it was compiled and launched by a system installation of xmonad rather than my sandboxed one (which is currently symlinked to the binary produced by stack).
It turns out the problem was that I couldn't use the xmonad
binary produced by stack even though it was in my path and ~/.xmonad/xmonad-x86_64-linux
was symlinked to it. Instead, I got restarting working by binding my mod-q to the following:
restart "/home/oldmanmike/.xmonad/xmonad-x86_64-linux" True
It will work provided I give it the absolute path.
This makes some sense given an observation earlier that putting the following in my .xinitrc would cause X to crash:
exec xmonad
Instead, I had to use the following for the initial launch:
exec ~/.xmonad/xmonad-x86_64-linux
So for now, it would seem as if calling xmonad
is a lot less reliable of a command and I should just alias the path to xmonad-x86_64-linux
and call that from now on.