complete-computing-environment/xmonad.org

13 KiB

Trying out XMonad in Home Manager

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 roam: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>>

myMod = mod4Mask

main = xmonad myConfig

myConfig = kde4Config
  { modMask    = myMod
  , layoutHook = desktopLayoutModifiers $ myLayoutHook 
  , manageHook = manageHook kde4Config <+> myManageHook
  , workspaces = myWorkspaces
  <<kde4ConfigDecl>>
  } `additionalKeys`
  [ ((myMod, xK_Tab), toggleRecentWS) -- (ref:recentWS)
  <<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:

, focusedBorderColor = "#ebbe7b"
, normalBorderColor  = "#707231"
, borderWidth        = 4

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.

, ((myMod, xK_a), spawn "org-agenda")
, ((myMod, xK_f), spawn "emacsclient -c -n")
, ((myMod, xK_g), spawn "org-journal")
, ((myMod, xK_x), spawn "meta-x")
, ((myMod, xK_z), spawn "org-todos")
, ((myMod, xK_c), spawn "emacsclient -c -e '(srs)' -n")
, ((myMod, xK_p), spawn "bash -c 'pkill krunner; krunner'")

There is also /rrix/complete-computing-environment/src/branch/main/(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.

myWorkspaces = ["front", "im", "music", "misc1", "misc2", "fullscreen"]

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.
myLayoutHook = imw $ fullscr $ tiled ||| Mirror tiled ||| full ||| threeCol 
  where
    tiled    = BW.boringWindows $ smartSpacingWithEdge 4 $ Tall nmaster delta ratio
    full     = BW.boringWindows $ noBorders $ Full
    threeCol = BW.boringWindows $ smartSpacingWithEdge 4 $ ThreeColMid nmaster delta ratio
    imw      = onWorkspace "im" $ threeCol ||| tiled ||| Mirror tiled ||| full
    fullscr  = onWorkspace "fullscreen" full
    nmaster  = 1      -- Default number of windows in the master pane
    ratio    = 2/3    -- Default proportion of screen occupied by master pane
    delta    = 3/100  -- Percent of screen to increment by when resizing panes

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 and focusDown 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 /rrix/complete-computing-environment/src/branch/main/additionalKeys above:

, ((myMod, xK_j), BW.focusDown)
, ((myMod, xK_k), BW.focusUp)
, ((myMod, xK_m), BW.focusMaster)
, ((myMod, xK_s), BW.markBoringEverywhere)
, ((myMod .|. shiftMask, xK_s), BW.clearBoring)

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 and myTitleFloats define window classes and titles which will be floated above the tiling layout. So things like my pass master password dialog (provided by GPG pinentry), the KDE logout screen… this is not so confusing.
  • prodApps, commApps, and mediaApps 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 /rrix/complete-computing-environment/src/branch/main/(checkSkips) are used to signal to XMonad to not manage the Plasma desktop elements. These should be included in the default kde4Config IMO but alas it seems like most of the folks who run XMonad swap out for XMobar
import qualified XMonad.StackSet as W
import XMonad.Hooks.ManageHelpers (isInProperty)

These are the /rrix/complete-computing-environment/src/branch/main/(checkSkips) helpers..:

checkSkipTaskbar :: Query Bool
checkSkipTaskbar = isInProperty "_NET_WM_STATE" "_NET_WM_STATE_SKIP_TASKBAR"

checkIsDesktop :: Query Bool
checkIsDesktop = isInProperty "_NET_WM_WINDOW_TYPE" "_NET_WM_WINDOW_TYPE_DESKTOP"

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 roam:SystemD to start up the desktop. This means that KDEWM is ignored roam:because reasons. Well. Here's a work around. "just write a user unit and do a bunch of BS"

systemd.user.services.plasma-xmonad = {
  Install.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";
};
home.activation.plasma-xmonad = ''
  # 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 roam: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?