I haven't been particularly pleased with i3wm's container strategy and basically just miss some of the simplicity of operating with XMonad. I don't miss all the complexity of it though, but maybe I'm in a better position to write Haskell now… Easy enough to fuck around and find out.
Minimal Haskell support in Emacs
Of course this will be included in Arroyo Emacs to make developing this configuration viable.
use-package haskell-mode)
(provide 'cce/haskell) (
xomand.hs
configuration file
Because KDE is a
Base for my Emacs Desktop, I don't fuck with things like xmobar
or R&R support in this layer, let my
desktop environment deal with all of that instead of me. It has built in
integration
to configure it to manage the Plasma windows and whatnot reasonably.
import XMonad
import XMonad.Config.Kde
<<imports>>
= mod4Mask
myMod
= xmonad myConfig
main
= kde4Config
myConfig = myMod
{ modMask = desktopLayoutModifiers $ myLayoutHook
, layoutHook = manageHook kde4Config <+> myManageHook
, manageHook = myWorkspaces
, workspaces <<kde4ConfigDecl>>
`additionalKeys`
} -- (ref:recentWS)
[ ((myMod, xK_Tab), toggleRecentWS) <<additionalKeys>>
]
<<myLayoutHook>>
<<myManageHook>>
<<myWorkspaces>>
<<helperFns>>
de-tangling this a bit is probably a fool's errand, but here we go…
XMonad's docs on configuring as well as the quick start guide will make a lot more sense of the hooks but broadly:
The basic kde4Config
"but rrix don't you run Plasma 5?" yes, yes I do, but unlike in the
3->4 transition the KDE developers were able to maintain a large
amount of backwards compatibility. I think it would behoove the XMonad
developers to recognize this and make, say, a kde3Config
for the 30 Trinity users still left
but Sir, This is a
Wendy's. So all this is broadly defined in XMonad.Config.Kde
and XMonad.Config.Desktop
except it doesn't talk about Plasma 5 at all.
Make sure if you do make your own kde4Config
that it pulls the default manageHook
and invokes desktopLayoutModifiers
out of the default
configuration.
The only really notable things other than wiring up the hooks defined below are some simple appearance changes:
= "#ebbe7b"
, focusedBorderColor = "#707231"
, normalBorderColor = 4 , borderWidth
EZConfig
makes it eazy to define new
keybindings
There's not a lot of "there" there additionalKeys is used to add or override keybindings.
import XMonad.Util.EZConfig (additionalKeys)
import XMonad.Actions.CycleRecentWS (toggleRecentWS)
That's right, we're nesting noweb
functions!
I have a bunch of little helpers in i3wm and A World Without EXWM which I should
collate and expand on for example to add my org-fc function… but
anyways, S-a
opens my agenda, S-f
opens a new Emacs
frame, S-g
opens my Journal, S-x
opens a
new frame with the M-x
execute-interactive-command
open on it. S-z
opens my todo list. S-c
opens my Flash Cards.
"org-agenda")
, ((myMod, xK_a), spawn "emacsclient -c -n")
, ((myMod, xK_f), spawn "org-journal")
, ((myMod, xK_g), spawn "meta-x")
, ((myMod, xK_x), spawn "org-todos")
, ((myMod, xK_z), spawn "emacsclient -c -e '(srs)' -n")
, ((myMod, xK_c), spawn "bash -c 'pkill krunner; krunner'") , ((myMod, xK_p), spawn
There is also (recentWS) above which toggles
between the … most recent workspace … on S-Tab
. Useful for jumping between work terminals
spread on the high-numbered workspaces when I am working on a
high-context activity on my laptop display only. I provide it up there
so that the first element of that list doesn't start with a comma,
basically. All the other parts which go in to additionalKeys
should start with a commas as
though they're (rightfully) being spliced in to a list.
myWorkspaces
culls down my workspace list a
bit
I don't need nine workspaces. If I am working on that many different
things it's a sign I should close stuff out. myWorkspaces
is included in the kde4Config
declaration above. This pattern is
lifted from an example
xmonad configuration.
= ["front", "im", "music", "misc1", "misc2", "fullscreen"] myWorkspaces
myLayoutHook
captures Layout Customization
myLayoutHook
customizes each of the
layouts you can swap between (by default bound to S-SPC
)
- "main" window with everything tiled to the right
- "main" window with everything tiled below
- "main" window as full screen
- "three column" layout with the main window in the middle
- my IM windows are set to "three column" by default
- my fullscreen workspace is set to
fullscreen
by default.
= imw $ fullscr $ tiled ||| Mirror tiled ||| full ||| threeCol
myLayoutHook where
= BW.boringWindows $ smartSpacingWithEdge 4 $ Tall nmaster delta ratio
tiled = BW.boringWindows $ noBorders $ Full
full = BW.boringWindows $ smartSpacingWithEdge 4 $ ThreeColMid nmaster delta ratio
threeCol = onWorkspace "im" $ threeCol ||| tiled ||| Mirror tiled ||| full
imw = onWorkspace "fullscreen" full
fullscr = 1 -- Default number of windows in the master pane
nmaster = 2/3 -- Default proportion of screen occupied by master pane
ratio = 3/100 -- Percent of screen to increment by when resizing panes delta
This code block I think was ultimately what I left i3wm for. The "do it yourself"
container design made it a real pain in the ass especially when swapping
between my 3:2 laptop display and 21:9 widescreen desktop. Now it's just
a Super-SPACE
away.
I modify my layouts with two modules:
- XMonad.Layout.BoringWindows
lets a user mark a window as "boring". (lol) that is, it'll still
manage, tile, apply rules to the windows, but if you cycle between them
with the
focusUp
andfocusDown
commands that are provided in the module, the "boring" windows will be skipped. Great for something you want to keep an eye on like a terminal running a log tail while you're working. - XMonad.Layout.Spacing
provides
smartSpacingWithEdge
which pushes my windows in a few pixels so that I can differentiate them easier, and so that I can see some of my beautiful art of rally desktop backgrounds 😉
The Boring Window commands are spliced in to additionalKeys above:
, ((myMod, xK_j), BW.focusDown)
, ((myMod, xK_k), BW.focusUp)
, ((myMod, xK_m), BW.focusMaster)
, ((myMod, xK_s), BW.markBoringEverywhere).|. shiftMask, xK_s), BW.clearBoring) , ((myMod
This relies on these libraries:
import XMonad.Layout.ThreeColumns
import qualified XMonad.Layout.BoringWindows as BW
import XMonad.Layout.Spacing
import XMonad.Layout.NoBorders
import XMonad.Layout.PerWorkspace
myManageHook
provides rules that apply to
windows when they are opened
This does a fair bit at once, and is based on some examples taken out of the xmonad docs. Seriously go read them and do your best to make sense of all these new and interesting operators. I'm sorry about all the Haskell and all the line-noise operators, I really am. I don't understand them either, they're mostly lifted from folks who took too many math courses at Uni.
myFloats = ["pinentry", "ksmserver-logout-greeter", "krunner"]
myTitleFloats = []
prodApps = ["firefox", "emacs"]
commApps = ["discord", "Signal", "Element", "telegram-desktop"]
mediaApps = ["cantata"]
myManageHook = composeAll . concat $
[ [ className =? c --> doFloat | c <- myFloats]
, [ title =? t --> doFloat | t <- myTitleFloats]
, [ className =? c --> doF (W.shift "1") | c <- prodApps]
, [ className =? c --> doF (W.shift "2") | c <- commApps]
, [ className =? c --> doF (W.shift "3") | c <- mediaApps]
, [ className =? "plasmashell" <&&> checkSkipTaskbar --> doIgnore] -- (ref:checkSkips)
, [ className =? "plasmashell" <&&> checkIsDesktop --> doIgnore]
]
myFloats
andmyTitleFloats
define window classes and titles which will be floated above the tiling layout. So things like my pass master password dialog (provided byGPG
pinentry), the KDE logout screen… this is not so confusing.prodApps
,commApps
, andmediaApps
are classes of windows which will be stuffed in to workspaces for productivity, communication, and media. workspaces 4-9 are basically overflow areas and places to stuff my "do-nothing" Firefox window.- the two rules in (checkSkips) are used to signal to
XMonad
to not manage the Plasma desktop elements. These should be included in the defaultkde4Config
IMO but alas it seems like most of the folks who runXMonad
swap out forXMobar
…
import qualified XMonad.StackSet as W
import XMonad.Hooks.ManageHelpers (isInProperty)
These are the (checkSkips) helpers..:
checkSkipTaskbar :: Query Bool
= isInProperty "_NET_WM_STATE" "_NET_WM_STATE_SKIP_TASKBAR"
checkSkipTaskbar
checkIsDesktop :: Query Bool
= isInProperty "_NET_WM_WINDOW_TYPE" "_NET_WM_WINDOW_TYPE_DESKTOP" checkIsDesktop
home-manager configuration
{ pkgs, config, ... }:
rec {
home.sessionVariables = {
KDEWM="${pkgs.xmonad-with-packages}/bin/xmonad";
};
xsession.windowManager.xmonad = {
enable = true;
enableContribAndExtras = true;
config = ../files/xmonad.hs;
};
# <<plasma-xmonad>>
}
XMonad Auto Start on KDE >= 5.25
After KDE 5.25, Plasma now uses SystemD to start up the desktop. This means
that KDEWM
is ignored because reasons. Well. Here's
a work around. "just write a user unit and do a bunch of BS"
-xmonad = {
systemd.user.services.plasmaInstall.WantedBy = ["plasma-plasmashell.service"];
Unit.Description = "Start XMonad instead of KWin";
Unit.Before = "plasma-plasmashell.service";
Service.ExecStart = "${config.home.homeDirectory}/.xmonad/xmonad-${pkgs.stdenv.hostPlatform.system}";
Service.Slice="session.slice";
Service.Restart="on-failure";
};
-xmonad = ''
home.activation.plasma # systemctl --user mask plasma-kwin_x11.service
# systemctl --user daemon-reload
# back to kwin ...
systemctl --user unmask plasma-kwin_x11.service
systemctl --user disable plasma-xmonad.service
'';
Thinking about this Thread from Geoffrey Litt….
Along these lines, one of my favorite findings in malleable software research:
Wendy Mackay found in 1991 that a very common reason people customized their Unix setups was to keep things the way they used to be
Kinda ironic, right?