From 15653d4669fe89d4070bc953a87109a0910fb299 Mon Sep 17 00:00:00 2001 From: slotThe Date: Thu, 15 Apr 2021 14:37:00 +0200 Subject: [PATCH 1/4] Add INSTALL.md --- INSTALL.md | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 + 2 files changed, 182 insertions(+) create mode 100644 INSTALL.md diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..9f2606e --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,180 @@ +# Install XMonad + +## Stack + +### Preparation + +We'll use the [XDG] directory specifications here, meaning our +configuration will reside within `$XDG_CONFIG_HOME`, which is +`~/.config` on most systems. Let's create this directory and move to +it: + +``` shell + $ mkdir -p ~/.config/xmonad && cd ~/.config/xmonad +``` + +If you already have an `xmonad.hs` configuration, you can copy it over +now. If not, you can use the defaults: create a file called `xmonad.hs` +with the following content: + +``` haskell + import XMonad + + main :: IO () + main = xmonad def +``` + +### Install Stack + +The easiest way to get [stack] is probably via your system's package +manager. For example, on Debian: + +``` shell + $ apt install haskell-stack +``` + +If your distribution does not package stack, you can also easily install +it via the following command (this is the recommended way to install +stack via its [documentation][stack]): + +``` shell + $ curl -sSL https://get.haskellstack.org/ | sh +``` + +Yet another way would be via [ghcup]; this is similar to installers like +`rustup`, in case you prefer that. + +### Create a New Project + +Let's create a stack project. Since we're already in the correct +directory (`~/.config/xmonad`), we can start by cloning the `xmonad` and +the `xmonad-contrib` repositories: + +``` shell + $ git clone https://github.com/xmonad/xmonad + $ git clone https://github.com/xmonad/xmonad-contrib +``` + +This will give you the latest `$HEAD`; if you want you can also check +out a tagged release, e.g.: + +``` shell + $ git clone --branch v0.16 https://github.com/xmonad/xmonad + $ git clone --branch v0.17 https://github.com/xmonad/xmonad-contrib +``` + +Starting a new stack project is as simple as running `stack init`. +Stack should now inform you that it will use the relevant `stack` and +`cabal` files from `xmonad` and `xmonad-contrib` to generate its +`stack.yaml` file. At the time of writing, this looks a little bit like +this: + +``` + $ stack init + Looking for .cabal or package.yaml files to use to init the project. + Using cabal packages: + - xmonad-contrib/ + - xmonad/ + + Selecting the best among 19 snapshots... + + * Matches https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/9.yaml + + Selected resolver: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/9.yaml + Initialising configuration using resolver: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/9.yaml + Total number of user packages considered: 2 + Writing configuration to file: stack.yaml + All done. +``` + +If you look into your current directory now, you should see a freshly +generated `stack.yaml` file: + +``` + $ ls + xmonad xmonad-contrib stack.yaml xmonad.hs +``` + +The meat of that file (comments start with `#`, we've omitted them here) +will look a little bit like + +``` yaml + resolver: + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/9.yaml + + packages: + - xmonad + - xmonad-contrib +``` + +### Install Everything + +Installing things is as easy as typing `stack install`. This will +install the correct version of GHC, as well as build all of the required +packages (`stack build`) and then copy the relevant executables +(`xmonad`, in our case) to `~/.local/bin`. Make sure to add that +directory to your `$PATH`! + +If you're getting build failures while building the `X11` package it may +be that you don't have the required C libraries installed. Depending on +your system, this may be `libX11-devel`, or `libxss`. + +### Tell XMonad How to Recompile Itself + +In order to tell xmonad to invoke `stack build` when we issue `xmonad +--recompile` (bound to `M-q` by default), we need to create a so-called +`build` file. This is quite literally just a shell script called +`build` in your xmonad directory (which is `~/.config/xmonad` for us) +that tells xmonad how it should build its executable. + +A good starting point (this is essentially [what xmonad would do] +without a build file, with the exception that we are invoking `stack +ghc` instead of plain `ghc`) would be + +``` shell + #!/bin/sh + + exec stack ghc -- \ + --make xmonad.hs \ + -i \ + -ilib \ + -fforce-recomp \ + -main-is main \ + -v0 \ + -o "$1" +``` + +Don't forget to mark the file as `+x`: `chmod +x build`! + +And that's it! Recompilation should work normally now, though you will +potentially need to restart your computer, or at least the running X +session, first. + +### Don't Recompile on Every Startup + +By default, xmonad always recompiles itself when a build script is used +(because the build script could contain arbitrary code, so a simple +check whether the `xmonad.hs` file changed is not enough). If you find +that too annoying, then you can use the `xmonad-ARCH` executable that +`xmonad --recompile` generates instead of `xmonad` in your startup. For +example, instead of writing + +``` shell + exec xmonad +``` + +in your `~/.xinitrc`, you would write + +``` shell + exec $HOME/.local/share/xmonad/xmonad-x86_64-linux +``` + +The `~/.local/share` prefix is the `$XDG_DATA_DIR` directory. Note that +if your xmonad configuration resides within `~/.xmonad`, then the +executable will also be within that directory and not in +`$XDG_DATA_DIR`. + +[XDG]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html +[stack]: https://docs.haskellstack.org/en/stable/README/ +[ghcup]: https://www.haskell.org/ghcup/ +[what xmonad would do]: https://github.com/xmonad/xmonad/blob/master/src/XMonad/Core.hs#L657-L665 diff --git a/README.md b/README.md index eaccf7b..b796433 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ package system (e.g. on Debian or Gentoo). If at all possible, use this in preference to a source build, as the dependency resolution will be simpler. +For tool-specific guides see [INSTALL.md](./INSTALL.md). + We'll now walk through the complete list of toolchain dependencies. * GHC: the Glasgow Haskell Compiler From 7199d953a7fe702a50cdb6426516dc13e6d3f7c5 Mon Sep 17 00:00:00 2001 From: slotThe Date: Thu, 18 Mar 2021 16:34:43 +0100 Subject: [PATCH 2/4] Add tutorial.md --- tutorial.md | 1203 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1203 insertions(+) create mode 100644 tutorial.md diff --git a/tutorial.md b/tutorial.md new file mode 100644 index 0000000..8e2ec04 --- /dev/null +++ b/tutorial.md @@ -0,0 +1,1203 @@ +NOTE: `xmonad 0.16` and `xmonad-contrib 0.17` are obviously not released +yet. But don't worry, if you're reading this then you're already a beta +tester :) Since this guide is supposed to work on older versions of +xmonad as well this is no problem however; please report any +incompatibilities you notice and state the version of xmonad and +xmonad-contrib that you tried to use. If you use the git versions of +xmonad and xmonad-contrib, you should be able to follow everything just +fine. + +# XMonad Configuration Tutorial + +We're going to take you, step-by-step, through the process of +configuring xmonad, setting up [xmobar] as a status bar, configuring +[trayer-srg] as a tray, and making it all play nicely together. + +We assume that you have read the [xmonad guided tour] already. It is a +bit dated at this point but, because xmonad is stable, the guide should +still give you a good overview of the most basic functionality. + +Before we begin, here are two screenshots of the kind of desktop we will +be creating together. In particular, a useful layout we'll conjure into +being—the three columns layout from [XMonad.Layout.ThreeColumns] with +the ability to magnify stack windows via [XMonad.Layout.Magnifier]: + +

+blank desktop +blank desktop +

+ +So let's get started! + +## Preliminaries + +First you'll want to install xmonad. You can either do this with your +system's package manager or via `stack`/`cabal`. The latter may be +necessary if your distribution does not package `xmonad` and +`xmonad-contrib` (or packages a version that's very old) or you want to +use the git versions instead of a tagged release. You can find +instructions for how to do this in [INSTALL.md]. + +One more word of warning: if you are on a distribution that installs +every Haskell package dynamically linked—thus essentially breaking +Haskell—(Arch Linux being a prominent example) then we would highly +recommend installing via `stack` or `cabal` as instructed in +[INSTALL.md]. If you still want to install xmonad via your system's +package manager, you will need to `xmonad --recompile` _every time_ a +Haskell dependency is updated—else xmonad may fail to start when you +want to log in! + +We're going to assume xmonad version `0.16` and xmonad-contrib version +`0.17` here, though most of these steps should work with older versions +as well. When we get to the relevant parts, will point you to +alternatives that work with at least xmonad version `0.15` and +xmonad-contrib version `0.16`. This will usually be accompanied by a +big "_IF YOU ARE ON A VERSION `< 0.17`_", so don't worry about missing +it! + +Throughout the tutorial we will use, for keybindings, a syntax very akin +to the [GNU Emacs conventions] for the same thing—so `C-x` means "hold +down the control key and then press the `x` key". One exception is that +in our case `M` will not necessarily mean Alt (also called `Meta`), but +"your modifier key"; this is Alt by default, although many people map it +to Super instead (I will show you how to do this below). + +This guide should work for any GNU/Linux distribution and even for BSD +folks. Because debian-based distributions are still rather popular, we +will give you the `apt` commands when it comes to installing software. +If you use another distribution, just substitute the appropriate +commands for your system. + +To install xmonad, as well as some utilities, via `apt` you can just run + +``` shell + apt-get install xmonad libghc-xmonad-contrib-dev libghc-xmonad-dev suckless-tools +``` + +This installs xmonad itself, everything you need to configure it, and +`suckless-tools`, which provides the application launcher `dmenu`. This +program is used as the default application launcher on `M-p`. + +If you are installing xmonad with `stack` or `cabal`, remember to _not_ +install `xmonad` and its libraries here! + +For the remainder of this document, we will assume that you are running +a live xmonad session in some capacity. If you have set up your +`~/.xinitrc` as directed in the xmonad guided tour, you should be good +to go! If not, just smack an `exec xmonad` at the bottom of that file. + +## Installing Xmobar + +What we need to do now—provided we want to use a bar at all—is to +install [xmobar]. If you visit [xmobar's `Installation` section] you +will find everything from how to install it with your system's package +manager all the way to how to compile it yourself. + +We will show you how we make these programs talk to each other a bit +later on. For now, let's start to explore how we can customize this +window manager of ours! + +## Customizing XMonad + +Xmonad's configuration file is written in [Haskell]—but don't worry, we +won't assume that you know the language for the purposes of this +tutorial. The configuration file can either reside within +`$XDG_CONFIG_HOME/xmonad`, `~/.xmonad`, or `$XMONAD_CONFIG_DIR`; see +`man 1 xmonad` for further details (the likes of `$XDG_CONFIG_HOME` is +called a [shell variable]). Let's use `$XDG_CONFIG_HOME/xmonad` for the +purposes of this tutorial, which resolves to `~/.config/xmonad` on most +systems—the `~/.config` directory is also the place where things will +default to should `$XDG_CONFIG_HOME` not be set. + +First, we need to create `~/.config/xmonad` and, in this directory, a +file called `xmonad.hs`. We'll start off with importing some of the +utility modules we will use. At the very top of the file, write + +``` haskell + import XMonad + + import XMonad.Util.EZConfig + import XMonad.Util.Ungrab +``` + +All of these imports are _unqualified_, meaning we are importing all of +the functions in the respective modules. For configuration files this +is what most people want. If you prefer to import things explicitly +you can do so by adding the necessary function to the `import` statement +in parentheses. For example + +``` haskell + import XMonad.Util.EZConfig (additionalKeysP) +``` + +For the purposes of this tutorial, we will be importing everything +coming from xmonad directly unqualified. + +Next, a basic configuration—which is the same as the default config—is +this: + +``` haskell + main :: IO () + main = xmonad def +``` + +In case you're interested in what this default configuration actually +looks like, you can find it under [XMonad.Config]. Do note that it is +_not_ advised to copy that file and use it as the basis of your +configuration, as you won't notice when a default changes! + +You should be able to save the above file, with the import lines plus +the other two and then press `M-q` to load it up. Another way to +validate your `xmonad.hs` is to simply run `xmonad --recompile` in a +terminal. You'll see errors (in an `xmessage` popup, so make sure that +is installed on your system) if it's bad, and nothing if it's good. +It's not the end of the world if you restart xmonad and get errors, as +you will still be on your old, working, configuration and have all the +time in the world to fix your errors before trying again! + +Let's add a few additional things. The default modifier key is Alt, +which is also used in Emacs. Sometimes Emacs and xmonad want to use the +same key for different actions. Rather than remap every common key, +many people just change Mod to be the Super key—the one between Ctrl and +Alt on most keyboards. We can do this by changing the above `main` +function in the following way: + +``` haskell + main :: IO () + main = xmonad $ def + { modMask = mod4Mask -- Rebind Mod to the Super key + } +``` + +The two dashes signify a comment to the end of the line. Notice the +curly braces; these stand for a [record update] in Haskell (records are +sometimes called "structs" in C-like languages). What it means is "take +`def` and change its `modMask` field to the thing **I** want". Taking a +record that already has some defaults set and modifying only the fields +one cares about is a pattern that is often used within xmonad, so it's +worth pausing for a bit here to really take this new syntax in. + +Don't mind the dollar sign too much; it essentially serves to split +apart the `xmonad` function and the `def { .. }` record update visually. +Haskell people really don't like writing parentheses, so they sometimes +use a dollar sign instead. For us this is particularly nice, because +now we don't have to awkwardly write + +``` haskell + main :: IO () + main = xmonad (def + { modMask = mod4Mask -- Rebind Mod to the Super key + }) +``` + +This will be especially handy when adding more combinators; something we +will talk about later on. The dollar sign is superfluous in this +example, but that will change soon enough so it's worth introducing it +here as well. + +What if we wanted to add other keybindings? Say you also want to bind +`M-S-z` to lock your screen with the screensaver, `M-S-=` to take a +snapshot of one window, and `M-]` to spawn Firefox. This can be +achieved with the `additionalKeysP` function from the +[XMonad.Util.EZConfig] module—luckily we already have it imported! Our +config file, starting with `main`, now looks like: + +``` haskell + main :: IO () + main = xmonad $ def + { modMask = mod4Mask -- Rebind Mod to the Super key + } + `additionalKeysP` + [ ("M-S-z", spawn "xscreensaver-command -lock") + , ("M-S-=", unGrab *> spawn "scrot -s" ) + , ("M-]" , spawn "firefox" ) + ] +``` + +That syntax look familiar? + +You can find the names for special keys, like `Print` or the `F`-keys, +in the [XMonad.Util.EZConfig] documentation. + +We will cover setting up the screensaver later in this tutorial. + +The `unGrab` before running the `scrot -s` command tells xmonad to +release its keyboard grab before `scrot -s` tries to grab the keyboard +itself. The little `*>` operator essentially just sequences two +functions, i.e. `f *> g` says + + > first do `f` and then, discarding any result that `f` may have given + > me, do `g`. + +Do note that you may need to install `scrot` if you don't have it on +your system already. + +What if we wanted to augment our xmonad experience just a little more? +We already have `xmonad-contrib`, which means endless possibilities! +Say we want to add a three column layout to our layouts and also magnify +focused stack windows, making it more useful on smaller screens. + +We start by visiting the documentation for [XMonad.Layout.ThreeColumns]. +The very first sentence of the `Usage` section tells us exactly what we +want to start with: + + > You can use this module with the following in your `~/.xmonad/xmonad.hs`: + > + > `import XMonad.Layout.ThreeColumns` + +Ignoring the part about where exactly our `xmonad.hs` is (we have opted +to put it into `~/.config/xmonad/xmonad.hs`, remember?) we can follow +that documentation word for word. Let's add + +``` haskell + import XMonad.Layout.ThreeColumns +``` + +to the top of our configuration file. Most modules have a lot of +accompanying text and usage examples in them—so while the type +signatures may seem scary, don't be afraid to look up the +[xmonad-contrib documentation] on hackage! + +Next we just need to tell xmonad that we want to use that particular +layout. To do this, there is the `layoutHook`. Let's use the default +layout as a base: + +``` haskell + myLayout = tiled ||| Mirror tiled ||| Full + where + tiled = Tall nmaster delta ratio + nmaster = 1 -- Default number of windows in the master pane + ratio = 1/2 -- Default proportion of screen occupied by master pane + delta = 3/100 -- Percent of screen to increment by when resizing panes +``` + +The so-called `where`-clause above simply consists of local declarations +that might clutter things up where they all declared at the top-level +like this + +``` haskell + myLayout = Tall 1 (3/100) (1/2) ||| Mirror (Tall 1 (3/100) (1/2)) ||| Full +``` + +It also gives us the chance of documenting what the individual numbers +mean! + +Now we can add the layout according to the [XMonad.Layout.ThreeColumns] +documentation. At this point, we would encourage you to try this +yourself with just the docs guiding you. If you can't do it, don't +worry; it'll come with time! + +We can, for example, add the additional layout like this: + +``` haskell + myLayout = tiled ||| Mirror tiled ||| Full ||| ThreeColMid 1 (3/100) (1/2) + where + tiled = Tall nmaster delta ratio + nmaster = 1 -- Default number of windows in the master pane + ratio = 1/2 -- Default proportion of screen occupied by master pane + delta = 3/100 -- Percent of screen to increment by when resizing panes +``` + +or even, because the numbers happen to line up, like this: + +``` haskell + myLayout = tiled ||| Mirror tiled ||| Full ||| threeCol + where + threeCol = ThreeColMid nmaster delta ratio + tiled = Tall nmaster delta ratio + nmaster = 1 -- Default number of windows in the master pane + ratio = 1/2 -- Default proportion of screen occupied by master pane + delta = 3/100 -- Percent of screen to increment by when resizing panes +``` + +Now we just need to tell xmonad that we want to use this modified +`layoutHook` instead of the default. Again, try to reason this out for +yourself by just looking at the documentation. Ready? Here we go: + +``` haskell + main :: IO () + main = xmonad $ def + { modMask = mod4Mask -- Rebind Mod to the Super key + , layoutHook = myLayout -- Use custom layouts + } + `additionalKeysP` + [ ("M-S-z", spawn "xscreensaver-command -lock") + , ("M-S-=", unGrab *> spawn "scrot -s" ) + , ("M-]" , spawn "firefox" ) + ] +``` + +But we also wanted to add magnification, right? Luckily for us, there's +a module for that as well! It's called [XMonad.Layout.Magnifier]. +Again, take a look at the documentation before reading on—see if you can +reason out what to do by yourself. Let's pick the `magnifiercz'` +modifier from the library; it magnifies a window by a given amount, but +only if it's a stack window. Add it to your three column layout thusly: + +``` haskell + myLayout = tiled ||| Mirror tiled ||| Full ||| threeCol + where + threeCol = magnifiercz' 1.3 $ ThreeColMid nmaster delta ratio + tiled = Tall nmaster delta ratio + nmaster = 1 -- Default number of windows in the master pane + ratio = 1/2 -- Default proportion of screen occupied by master pane + delta = 3/100 -- Percent of screen to increment by when resizing panes +``` + +Don't forget to import the module! + +You can think of the `$` here as putting everything into parentheses +from the dollar to the end of the line. If you don't like that you can +also write + +``` haskell + threeCol = magnifiercz' 1.3 (ThreeColMid nmaster delta ratio) +``` + +instead. + +That's it! Now we have a perfectly functioning three column layout with +a magnified stack. If you compare this with the starting screenshots, +you will see that that's exactly the behaviour we wanted to achieve! + +A last thing that's worth knowing about are so-called "combinators"—at +least we call them that, we can't tell you what to do. These are +functions that compose with the `xmonad` function and add a lot of hooks +and other things for you (trying to achieve a specific goal), so you +don't have to do all the manual work yourself. For example, xmonad—by +default—is only [ICCCM] compliant. Nowadays, however, a lot of programs +(including many compositors) expect the window manager to _also_ be +[EWMH] compliant. So let's save ourselves a lot of future trouble and +add that to xmonad straight away! + +This functionality is to be found in the [XMonad.Hooks.EwmhDesktops] +module, so let's import it: + +``` haskell + import XMonad.Hooks.EwmhDesktops +``` + +We might also consider using the `ewmhFullscreen` combinator. By +default, a "fullscreened" application is still bound by its window +dimensions; this means that if the window occupies half of the screen +before it was fullscreened, it will also do so afterwards. Some people +really like this behaviour, as applications thinking they're in +fullscreen mode tend to remove a lot of clutter (looking at you, +Firefox). However, because a lot of people explicitly do not want this +effect (and some applications, like chromium, will misbehave and need +some [Hacks] to make this work), we will also add the relevant function +to get "proper" fullscreen behaviour here. + +_IF YOU ARE ON A VERSION `< 0.17`_: The `ewmhFullscreen` function does + not exist in these versions. Instead of it, you can try to add + `fullscreenEventHook` to your `handleEventHook` to achieve similar + functionality (how to do this is explained in the documentation of + [XMonad.Hooks.EwmhDesktops]). + +To use the two combinators, we compose them with the `xmonad` function +in the following way: + +``` haskell + main :: IO () + main = xmonad $ ewmhFullscreen $ ewmh $ def + { modMask = mod4Mask -- Rebind Mod to the Super key + , layoutHook = myLayout -- Use custom layouts + } + `additionalKeysP` + [ ("M-S-z", spawn "xscreensaver-command -lock") + , ("M-S-=", unGrab *> spawn "scrot -s" ) + , ("M-]" , spawn "firefox" ) + ] +``` + +Do mind the order of the two combinators—by a particularly awkward set +of circumstances, they do not commute! + +This `main` function is getting pretty crowded now, so let's refactor it +a little bit. A good way to do this is to split the config part into +one function and the "main and all the combinators" part into another. +Let's call the config part `myConfig` for... obvious reasons. It would +look like this: + +``` haskell + main :: IO () + main = xmonad $ ewmhFullscreen $ ewmh $ myConfig + + myConfig = def + { modMask = mod4Mask -- Rebind Mod to the Super key + , layoutHook = myLayout -- Use custom layouts + } + `additionalKeysP` + [ ("M-S-z", spawn "xscreensaver-command -lock") + , ("M-S-=", unGrab *> spawn "scrot -s" ) + , ("M-]" , spawn "firefox" ) + ] +``` + +Much better! + +## Make XMonad and Xmobar Talk to Each Other + +Onto the main dish. First, we have to import the necessary modules. +Add the following to your list of imports: + +``` haskell + import XMonad.Hooks.DynamicLog + import XMonad.Hooks.StatusBar + import XMonad.Hooks.StatusBar.PP +``` + +_IF YOU ARE ON A VERSION `< 0.17`_: The `XMonad.Hooks.StatusBar` and + `XMonad.Hooks.StatusBar.PP` modules don't exist yet. You can find + everything you need in the `XMonad.Hooks.DynamicLog` module, so remove + these two imports. + +Replace your `main` function above with: + +``` haskell + main :: IO () + main = xmonad $ ewmhFullscreen $ ewmh $ xmobarProp $ myConfig +``` + +_IF YOU ARE ON A VERSION `< 0.17`_: The `xmobarProp` function does not + exist in these versions. Instead of it, use `xmobar` via + `main = xmonad . ewmhFullscreen . ewmh =<< xmobar myConfig` + and carefully read the part about pipes later on (`xmobar` uses pipes + to make xmobar talk to xmonad). + +As a quick side-note, we could have also written + +``` haskell + main :: IO () + main = xmonad . ewmhFullscreen . ewmh . xmobarProp $ myConfig +``` + +Notice how `$` became `.`! The dot operator `(.)` in Haskell means +function composition and is read from right to left. What this means in +this specific case is essentially the following: + + > take the four functions `xmonad`, `ewmhFullscreen`, `ewmh`, and + > `xmobarProp` and give me the big new function + > `xmonad . ewmhFullscreen . ewmh . xmobarProp` that first executes + > `xmobarProp`, then `ewmh`, then `ewmhFullscreen`, and finally + > `xmonad`. Then give it `myConfig` as its argument so it can do its + > thing. + +This should strike you as nothing more than a syntactical quirk, at +least in this case. And indeed, since `($)` is just function +application there is a very nice relationship between `(.)` and `($)`. +This may be more obvious if we write everything with parentheses and +apply the `(.)` operator (because we do have an argument): + +``` haskell + -- ($) version + main = xmonad $ ewmhFullscreen $ ewmh $ xmobarProp $ myConfig + -- ($) version with parentheses + main = xmonad (ewmhFullscreen (ewmh (xmobarProp (myConfig)))) + + -- (.) version with parentheses + main = (xmonad . ewmhFullscreen . ewmh . xmobarProp) (myConfig) + -- xmobarProp applied + main = (xmonad . ewmhFullscreen . ewmh) (xmobarProp (myConfig)) + -- ewmh applied + main = (xmonad . ewmhFullscreen) (ewmh (xmobarProp (myConfig))) + -- xmonad and ewmhFullscreen applied + main = (xmonad (ewmhFullscreen (ewmh (xmobarProp (myConfig)))) +``` + +It's the same! This is special to the interplay with `(.)` and `($)` +though; if you're on an older version of xmonad and xmonad-contrib and +use `xmobar` instead of `xmobarProp`, then you _have_ to write + +``` haskell + main = xmonad . ewmhFullscreen . ewmh =<< xmobar myConfig +``` + +and this is _not_ equivalent to + +``` haskell + main = xmonad (ewmhFullscreen (ewmh =<< xmobar myConfig)) +``` + +Consult a Haskell book of your choice for why this is the case. + +Back to our actual goal: customizing xmonad. What the code we've +written does is take our tweaked default configuration `myConfig` and +add the support we need to make xmobar our status bar. Do note that you +will also need to add the `XMonadLog` plugin to your xmobar +configuration; we will do this together below, so don't sweat it for +now. + +To understand why this is necessary, let's talk a little bit about how +xmonad and xmobar fit together. You can make them talk to each other in +several different ways. + +By default, xmobar accepts input on its stdin, which it can display at +an arbitrary position on the screen. We want xmonad to send xmobar the +stuff that you can see at the upper left of the starting screenshots: +information about available workspaces, current layout, and open +windows. Naïvely, we can achieve this by spawning a pipe and letting +xmonad feed the relevant information to that pipe. The problem with +that approach is that when the pipe is not being read and gets full, +xmonad will freeze! + +It is thus much better to switch over to property based logging, where +we are writing to an X11 property and having xmobar read that; no danger +when things are not being read! For this reason we have to use +`XMonadLog` instead of `StdinReader` in our xmobar. There's also an +`UnsafeXMonadLog` available, should you want to send actions to xmobar +(this is useful, for example, for [XMonad.Util.ClickableWorkspaces], +which is a new feature in `0.17`). + +_IF YOU ARE ON A VERSION `< 0.17`_: As discussed above, the `xmobar` + function uses pipes, so you actually do want to use the `StdinReader`. + Simply replace _all_ occurences of `XMonadLog` with `StdinReader` + below (don't forget the template!) + +## Configuring Xmobar + +Now, before this will work, we have to configure xmobar. Here's a nice +starting point. Be aware that, while Haskell syntax highlighting is +used here to make this pretty, xmobar's config is _not_ a Haskell file +and thus can't execute arbitrary code—at least not by default. If you +do want to configure xmobar in Haskell there is a note about that at the +end of this section. + +``` haskell + Config { overrideRedirect = False + , font = "xft:iosevka-9" + , bgColor = "#5f5f5f" + , fgColor = "#f8f8f2" + , position = TopW L 90 + , commands = [ Run Weather "EGPF" + [ "--template", " °C" + , "-L", "0" + , "-H", "25" + , "--low" , "lightblue" + , "--normal", "#f8f8f2" + , "--high" , "red" + ] 36000 + , Run Cpu + [ "-L", "3" + , "-H", "50" + , "--high" , "red" + , "--normal", "green" + ] 10 + , Run Alsa "default" "Master" + [ "--template", "" + , "--suffix" , "True" + , "--" + , "--on", "" + ] + , Run Memory ["--template", "Mem: %"] 10 + , Run Swap [] 10 + , Run Date "%a %Y-%m-%d %H:%M" "date" 10 + , Run XMonadLog + ] + , sepChar = "%" + , alignSep = "}{" + , template = "%XMonadLog% }{ %alsa:default:Master% | %cpu% | %memory% * %swap% | %EGPF% | %date% " + } +``` + +First, we set the font to use for the bar, as well as the colors. The +position options are documented well on the [xmobar home page] or, +alternatively, in the [quick-start.org] on GitHub. The particular +option of `TopW L 90` says to put the bar in the upper left of the +screen, and make it consume 90% of the width of the screen (we need to +leave a little bit of space for `trayer-srg`). If you're up for it—and +this really requires more shell-scripting than Haskell knowledge—you can +also try to seamlessly embed trayer into xmobar by using +[trayer-padding-icon.sh] and following the advice given in that thread. + +In the commands list you, well, define commands. Commands are the +pieces that generate the content to be displayed in your bar. These +will later be combined together in the `template`. Here, we have +defined a weather widget, a CPU widget, memory and swap widgets, a date, +a volume indicator, and of course the data from xmonad via `XMonadLog`. + +The `EGPF` in the weather command is a particular station. Replace both +(!) occurences of it with your choice of ICAO weather stations. For a +list of ICAO codes you can visit the relevant [Wikipedia page]. You can +of course monitor more than one if you like; see xmobar's [weather +monitor] documentation for further details. + +The `template` then combines everything together. The `alignSep` +variable controls the alignment of all of the monitors. Stuff to be +left-justified goes before the `}` character, things to be centered +after it, and things to be right justified after `{`. We have nothing +centered so there is nothing in-between them. + +Save the file to `~/.xmobarrc`. Now press `M-q` to reload xmonad; you +should now see xmobar with your new configuration! Please note that, at +this point, the config _has_ to reside in `~/.xmobarrc`. We will, +however, discuss how to change this soon. + +It is also possible to completely configure xmobar in Haskell, just like +xmonad. If you want to know more about that, you can check out the +[xmobar.hs] example in the official documentation. For a more +complicated example, you can also check out [jao's xmobar.hs] (he's the +current maintainer of xmobar). + +## Changing What XMonad Sends to Xmobar + +Now that the xmobar side of the picture looks nice, what about the stuff +that xmonad sends to xmobar? It would be nice to visually match these +two. Sadly, this is not quite possible with our `xmobarProp` function; +however, looking at the implementation of the function (or, indeed, the +top-level documentation of the module!) should give us some ideas for +how to proceed: + +``` haskell + xmobarProp config = + withEasySB (statusBarProp "xmobar" (pure xmobarPP)) toggleStrutsKey config +``` + +This means that `xmobarProp` just calls the functions `withEasySB` and +`statusBarProp` with some arguments; crucially for us, notice the +`xmobarPP`. In this context "PP" stands for "pretty-printer"—exactly +what we want to modify! + +Let's copy the implementation over into our main function: + +``` haskell + main :: IO () + main = xmonad + . ewmhFullscreen + . ewmh + . withEasySB (statusBarProp "xmobar" (pure def)) defToggleStrutsKey + $ myConfig +``` + +_IF YOU ARE ON A VERSION `< 0.17`_: `xmobar` has a similar definition, + relying on `statusBar` alone: `xmobar = statusBar "xmobar" xmobarPP + toggleStrutsKey`. Sadly, the `defToggleStrutsKey` function is not yet + exported, so you will have to define it yourself: + + ``` haskell + main :: IO () + main = xmonad + . ewmhFullscreen + . ewmh + =<< statusBar "xmobar" def toggleStrutsKey myConfig + where + toggleStrutsKey :: XConfig Layout -> (KeyMask, KeySym) + toggleStrutsKey XConfig{ modMask = m } = (m, xK_b) + ``` + +The `defToggleStrutsKey` here is just the key with which you can toggle +the bar; it is bound to `M-b`. If you want to change this, you can also +define your own: + +``` haskell + main :: IO () + main = xmonad + . ewmhFullscreen + . ewmh + . withEasySB (statusBarProp "xmobar" (pure def)) toggleStrutsKey + $ myConfig + where + toggleStrutsKey :: XConfig Layout -> (KeyMask, KeySym) + toggleStrutsKey XConfig{ modMask = m } = (m, xK_b) +``` + +Feel free to change the binding by modifying the `(m, xK_b)` tuple to +your liking. + +If you want your xmobar configuration file to reside somewhere else than +`~/.xmobarrc`, you can now simply give the file to xmobar as a +positional argument! For example: + +``` haskell + main :: IO () + main = xmonad + . ewmhFullscreen + . ewmh + . withEasySB (statusBarProp "xmobar ~/.config/xmobar/xmobarrc" (pure def)) defToggleStrutsKey + $ myConfig +``` + +Back to controlling what exactly we send to xmobar. The `def` +pretty-printer just gives us the same result that the internal +`xmobarPP` would have given us. Let's try to build something on top of +this. To prepare, we can first create a new function `myXmobarPP` with +the default configuration: + +``` haskell + myXmobarPP :: PP + myXmobarPP = def +``` + +and then plug that into our main function: + +``` haskell + main :: IO () + main = xmonad + . ewmhFullscreen + . ewmh + . withEasySB (statusBarProp "xmobar" (pure myXmobarPP)) defToggleStrutsKey + $ myConfig +``` + +As before, we now change things by modifying that `def` record until we +find something that we like. There are _a lot_ of options for the [PP +record]; I'd advise you to read through all of them now, so you don't +get lost! + +``` haskell + myXmobarPP :: PP + myXmobarPP = def + { ppSep = magenta " • " + , ppTitle = wrap (white "[") (white "]") . magenta . ppWindow + , ppTitleUnfocused = wrap (lowWhite "[") (lowWhite "]") . blue . ppWindow + , ppTitleSanitize = xmobarStrip + , ppCurrent = wrap " " "" . xmobarBorder "Top" "#8be9fd" 2 + , ppHidden = white . wrap " " "" + , ppHiddenNoWindows = lowWhite . wrap " " "" + , ppUrgent = red . wrap (yellow "!") (yellow "!") + } + where + -- | Windows should have *some* title, which should not not exceed a + -- sane length. + ppWindow :: String -> String + ppWindow = xmobarRaw . (\w -> if null w then "untitled" else w) . shorten 30 + + blue, lowWhite, magenta, red, white, yellow :: String -> String + magenta = xmobarColor "#ff79c6" "" + blue = xmobarColor "#bd93f9" "" + white = xmobarColor "#f8f8f2" "" + yellow = xmobarColor "#f1fa8c" "" + red = xmobarColor "#ff5555" "" + lowWhite = xmobarColor "#bbbbbb" "" +``` + +_IF YOU ARE ON A VERSION `< 0.17`_: Both `ppTitleUnfocused` and + `xmobarBorder` are not available yet, so you will have to remove them. + As an alternative to `xmobarBorder`, a common way to "mark" the + currently focused workspace is by using brackets; you can try + something like `ppCurrent = wrap (blue "[") (blue "]")` and see if you + like it. + +That's a lot! But don't worry, take a deep breath and remind yourself +of what you read above in the documentation of the [PP record]. Even if +you haven't read the documentation yet, most of the fields should be +pretty self-explanatory; `ppTitle` formats the title of the currently +focused window, `ppCurrent` formats the currently focused workspace, +`ppHidden` is for the hidden workspaces that have windows on them, etc. +The rest is just deciding on some pretty colours and formatting things +just how we like it. + +If this is too much for you, you can also really just start with the +blank + +``` haskell + myXmobarPP :: PP + myXmobarPP = def +``` + +then add something, reload xmonad, see how things change and whether you +like them. If not, remove that part and try something else. If you do, +try to understand how that particular piece of code works. You'll have +something approaching the above that you fully understand in no time! + +## Configuring Related Utilities + +So now you've got a status bar and xmonad. We still need a few more +things: a screensaver, a tray for our apps that have tray icons, a way +to set our desktop background, and the like. + +For this, we will need a few pieces of software. + +``` shell + apt-get install trayer xscreensaver +``` + +If you want a network applet, something to set your desktop background, +and a power-manager: + +``` shell + apt-get install nm-applet feh xfce4-power-manager +``` + +First, configure xscreensaver how you like it with the +`xscreensaver-demo` command. Now, we will set these things up in +`~/.xinitrc` (we could also do most of this in xmonad's `startupHook`, +but `~/.xinitrc` is perhaps more standard). If you want to use xmonad +with a desktop environment, see [Basic Desktop Environment Integration] +for how to do this. + +Your `~/.xinitrc` may wind up looking like this: + +``` shell + #!/bin/sh + + # [... default stuff that your distro may throw in here ...] # + + # Set up an icon tray + trayer --edge top --align right --SetDockType true --SetPartialStrut true \ + --expand true --width 10 --transparent true --tint 0x5f5f5f --height 18 & + + # Set the default X cursor to the usual pointer + xsetroot -cursor_name left_ptr + + # Set a nice background + feh --bg-fill --no-fehbg ~/.wallpapers/haskell-red-noise.png + + # Fire up screensaver + xscreensaver -no-splash & + + # Power Management + xfce4-power-manager & + + if [ -x /usr/bin/nm-applet ] ; then + nm-applet --sm-disable & + fi + + exec xmonad +``` + +Notice the call to `trayer` above. The options tell it to go on the top +right, with a default width of 10% of the screen (to nicely match up +with xmobar, which we set to a width of 90% of the screen). We give it +a color and a height. + +Then we fire up the rest of the programs that interest us. + +Finally, we start xmonad. + +blank desktop + +Mission accomplished! + +Of course substitute the wallpaper for one of your own. If you like the +one used above, you can find it [here](https://i.imgur.com/9MQHuZx.png). + +## Final Touches + +There may be some programs that you don't want xmonad to tile. The +classic example here is the [GNU Image Manipulation Program]; it pops up +all sorts of new windows all the time, and they work best at defined +sizes. It makes sense for xmonad to float these kinds of windows by +default. + +This kind of behaviour can be achieved via the `manageHook`, which runs +when windows are created. There are several functions to help you match +on a certain window in [XMonad.ManageHook]. For example, suppose we'd +want to match on the class name of the application. With the +application open, open another terminal and invoke the `xprop` command. +Then click on the application that you would like to know the properties +of. In our case you should see (among other things) + +``` shell + WM_CLASS(STRING) = "gimp", "Gimp" +``` + +The second string in `WM_CLASS` is the class name, which we can access +with `className` from [XMonad.ManageHook]. The first one is usually +called the instance name and is matched-on via `appName` from the same +module. + +Let's use the class name for now. We can tell all windows with that +class name to float by defining the following manageHook: + +``` haskell + myManageHook = (className =? "Gimp" --> doFloat) +``` + +Say we also want to float all dialogs. This is easy with the `isDialog` +function from [XMonad.Hooks.ManageHelpers] (which you should import) and +a little modification to the `myManageHook` function: + +``` haskell + myManageHook :: ManageHook + myManageHook = composeAll + [ className =? "Gimp" --> doFloat + , isDialog --> doFloat + ] +``` + +Now we just need to tell xmonad to actually use our manageHook. This is +as easy as overriding the `manageHook` field in `myConfig`. You can do +it like this: + +``` haskell + myConfig = def + { modMask = mod4Mask -- Rebind Mod to the Super key + , layoutHook = myLayout -- Use custom layouts + , manageHook = myManageHook -- Match on certain windows + } + `additionalKeysP` + [ ("M-S-z", spawn "xscreensaver-command -lock") + , ("M-S-=", unGrab *> spawn "scrot -s" ) + , ("M-]" , spawn "firefox" ) + ] +``` + +## The Whole Thing + +The full `~/.config/xmonad/xmonad.hs`, in all its glory, now looks like +this: + +``` haskell + import XMonad + + import XMonad.Hooks.DynamicLog + import XMonad.Hooks.ManageDocks + import XMonad.Hooks.ManageHelpers + import XMonad.Hooks.StatusBar + import XMonad.Hooks.StatusBar.PP + + import XMonad.Util.EZConfig + import XMonad.Util.Ungrab + + import XMonad.Layout.Magnifier + import XMonad.Layout.ThreeColumns + + import XMonad.Hooks.EwmhDesktops + + + main :: IO () + main = xmonad + . ewmhFullscreen + . ewmh + . withEasySB (statusBarProp "xmobar" (pure myXmobarPP)) defToggleStrutsKey + $ myConfig + + myConfig = def + { modMask = mod4Mask -- Rebind Mod to the Super key + , layoutHook = myLayout -- Use custom layouts + , manageHook = myManageHook -- Match on certain windows + } + `additionalKeysP` + [ ("M-S-z", spawn "xscreensaver-command -lock") + , ("M-S-=", unGrab *> spawn "scrot -s" ) + , ("M-]" , spawn "firefox" ) + ] + + myManageHook :: ManageHook + myManageHook = composeAll + [ className =? "Gimp" --> doFloat + , isDialog --> doFloat + ] + + myLayout = tiled ||| Mirror tiled ||| Full ||| threeCol + where + threeCol = magnifiercz' 1.3 $ ThreeColMid nmaster delta ratio + tiled = Tall nmaster delta ratio + nmaster = 1 -- Default number of windows in the master pane + ratio = 1/2 -- Default proportion of screen occupied by master pane + delta = 3/100 -- Percent of screen to increment by when resizing panes + + myXmobarPP :: PP + myXmobarPP = def + { ppSep = magenta " • " + , ppTitle = wrap (white "[") (white "]") . magenta . ppWindow + , ppTitleUnfocused = wrap (lowWhite "[") (lowWhite "]") . blue . ppWindow + , ppTitleSanitize = xmobarStrip + , ppCurrent = wrap " " "" . xmobarBorder "Top" "#8be9fd" 2 + , ppHidden = white . wrap " " "" + , ppHiddenNoWindows = lowWhite . wrap " " "" + , ppUrgent = red . wrap (yellow "!") (yellow "!") + } + where + -- | Windows should have *some* title, which should not not exceed a + -- sane length. + ppWindow :: String -> String + ppWindow = xmobarRaw . (\w -> if null w then "untitled" else w) . shorten 30 + + blue, lowWhite, magenta, red, white, yellow :: String -> String + magenta = xmobarColor "#ff79c6" "" + blue = xmobarColor "#bd93f9" "" + white = xmobarColor "#f8f8f2" "" + yellow = xmobarColor "#f1fa8c" "" + red = xmobarColor "#ff5555" "" + lowWhite = xmobarColor "#bbbbbb" "" +``` + +## Further Customizations + +The following section mostly consists of extra bits—feel free to skip +this and jump directly to [Get in Touch](#get-in-touch). We've covered +a lot of ground here and sometimes it's really better to let things +settle a bit before going further; much more so if you're happy with how +things are looking and feeling right now! + +### Beautifying Xmobar + +A usual starting point for beautifying xmobar is either to use `xpm` +icons, or a font like `font-awesome` to add icons to the rest of the +text. We will show you how to do the latter. Xmobar, thankfully, makes +this very easy; just put a font down under `additionalFonts` and wrap +your Icons with `` tags and the respective index of the font +(starting from `1`). As an example, consider how we would extend our +configuration above with this new functionality: + +``` haskell + Config { overrideRedirect = False + , font = "xft:iosevka-9" + , additionalFonts = ["xft:FontAwesome-9"] + ... + , Run Battery + [ ... + , "--lows" , "\62020 " + , "--mediums", "\62018 " + , "--highs" , "\62016 " + ... + ] + ... + } +``` + +For an explanation of the battery commands used above, see xmobars +[battery] documentation. + +You can also specify workspaces in the same way and feed them to xmobar +via the property (e.g. have `"\xf120"` as one of your +workspace names). + +As an example how this would look like in a real configuration, you can +look at [Liskin's], [slotThe's], or [TheMC47's] xmobar configuration. +Do note that the last two are Haskell-based and thus may be a little +hard to understand for newcomers. + +[Liskin's]: https://github.com/liskin/dotfiles/blob/home/.xmobarrc +[TheMC47's]: https://github.com/TheMC47/dotfiles/tree/master/xmobar/xmobarrc +[slotThe's]: https://gitlab.com/slotThe/dotfiles/-/blob/master/xmobar/.config/xmobarrc/src/xmobarrc.hs + +### Renaming Layouts + +`Magnifier NoMaster ThreeCol` is quite a mouthful to show in your bar, +right? Thankfully there is the nifty [XMonad.Layout.Renamed], which +makes renaming layouts easy! We will focus on the `Replace` constructor +here, as a lot of people will find that that's all they need. To use it +we again follow the documentation (try it yourself!)—import the module +and then change `myLayout` like this: + +``` haskell + myLayout = tiled ||| Mirror tiled ||| Full ||| threeCol + where + threeCol + = renamed [Replace "ThreeCol"] + $ magnifiercz' 1.3 + $ ThreeColMid nmaster delta ratio + tiled = Tall nmaster delta ratio + nmaster = 1 -- Default number of windows in the master pane + ratio = 1/2 -- Default proportion of screen occupied by master pane + delta = 3/100 -- Percent of screen to increment by when resizing panes +``` + +The new line `renamed [Replace "ThreeCol"]` tells the layout to throw +its current name away and use `ThreeCol` instead. After reloading +xmonad, you should now see this shorter name in your bar. The line +breaks here are just cosmetic, by the way; if you want you can write +everything in one line: + +``` haskell + threeCol = renamed [Replace "ThreeCol"] $ magnifiercz' 1.3 $ ThreeColMid nmaster delta ratio +``` + +## Get in Touch + +The `freenode/#xmonad` channel is very friendly and helpful. It is +possible that people will not immediately answer—we do have lives as +well, after all :). Eventually though, people will usually chime in if +they have something helpful to say; sometimes this takes 10 minutes, +other times it may well take 10 hours. If you don't have an IRC client +ready to go, the easiest way to join is via [webchat]—just jot down a +username and `#xmonad` as the channel and you should be good to go! +There is a [log] of the channel available, in case you do have to +disconnect at some point, so don't worry about missing any messages. We +also have a [matrix room] that's linked to IRC, in case you prefer +that. + +If you're not a fan of real-time interactions, you can also post to the +[xmonad mailing list] or the [xmonad subreddit]. + +## Trouble? + +Check `~/.xsession-errors` or your distribution's equivalent first. If +you're using a distribution that does not log into a file automatically, +you will have to set this up manually. For example, you could put +something like + +``` shell + if [[ ! $DISPLAY ]]; then + exec startx >& ~/.xsession-errors + fi +``` + +into your `~/.profile` file to explicitly log everything into +`~/.xsession-errors`. + +If you can't figure out what's wrong, don't hesitate to +[get in touch](#get-in-touch)! + +## Closing Thoughts + +That was quite a ride! Don't worry if you didn't understand everything +perfectly, these things take time. You can re-read parts of this guide +as often as you need to and—with the risk of sounding like a broken +record—if you can't figure something out really do not be afraid to +[get in touch](#get-in-touch). + +If you want to see a few more complicated examples of other peoples +xmonad configurations, look no further! Below are (in alphabetical +order) the configurations of a few of xmonad's maintainers. Just keep +in mind that these setups are very customized and perhaps a little bit +hard to replicate (some may rely on features only available in personal +forks or git), may or may not be documented, and most aren't very pretty +either :) + + - [byorgey](https://github.com/byorgey/dotfiles) + - [geekosaur](https://github.com/geekosaur/xmonad.hs/tree/pyanfar) + - [liskin](https://github.com/liskin/dotfiles/tree/home/.xmonad) + - [psibi](https://github.com/psibi/dotfiles/tree/master/xmonad) + - [slotThe](https://gitlab.com/slotThe/dotfiles/-/tree/master/xmonad/.config/xmonad) + - [TheMC47](https://github.com/TheMC47/dotfiles/tree/master/xmonad/.xmonad) + + +[log]: https://ircbrowse.tomsmeding.com/browse/xmonad +[EWMH]: https://specifications.freedesktop.org/wm-spec/wm-spec-1.3.html +[ICCCM]: https://tronche.com/gui/x/icccm/ +[webchat]: https://webchat.freenode.net/ +[matrix room]: https://matrix.to/#/#freenode_#xmonad:matrix.org +[about xmonad]: https://xmonad.org/about.html +[shell variable]: https://www.shellscript.sh/variables1.html +[xmonad-testing]: https://github.com/xmonad/xmonad-testing +[xmonad subreddit]: https://old.reddit.com/r/xmonad/ +[xmonad guided tour]: https://xmonad.org/tour.html +[xmonad mailing list]: https://mail.haskell.org/mailman/listinfo/xmonad +[xmonad's GitHub page]: https://github.com/xmonad/xmonad +[trayer-padding-icon.sh]: https://github.com/jaor/xmobar/issues/239#issuecomment-233206552 +[xmonad-contrib documentation]: https://hackage.haskell.org/package/xmonad-contrib +[GNU Image Manipulation Program]: https://www.gimp.org/ +[Basic Desktop Environment Integration]: https://wiki.haskell.org/Xmonad/Basic_Desktop_Environment_Integration + +[Hacks]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Util-Hacks.html +[PP record]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Hooks-DynamicLog.html#t:PP +[INSTALL.md]: https://github.com/xmonad/xmonad/blob/master/INSTALL.md#stack +[XMonad.Config]: https://github.com/xmonad/xmonad/blob/master/src/XMonad/Config.hs +[XMonad.ManageHook]: https://hackage.haskell.org/package/xmonad/docs/XMonad-ManageHook.html +[XMonad.Util.EZConfig]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Util-EZConfig.html +[XMonad.Layout.Renamed]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Layout-Renamed.html +[XMonad.Layout.Magnifier]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Layout-Magnifier.html +[XMonad.Doc.Contributing]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Doc-Configuring.html +[XMonad.Hooks.EwmhDesktops]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Hooks-EwmhDesktops.html +[XMonad.Layout.ThreeColumns]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Layout-ThreeColumns.html +[XMonad.Hooks.ManageHelpers]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Hooks-ManageHelpers.html +[XMonad.Util.ClickableWorkspaces]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Util-ClickableWorkspaces.html + +[xmobar]: https://xmobar.org/ +[battery]: https://github.com/jaor/xmobar/blob/master/doc/plugins.org#batteryp-dirs-args-refreshrate +[xmobar.hs]: https://github.com/jaor/xmobar/blob/master/examples/xmobar.hs +[Wikipedia page]: https://en.wikipedia.org/wiki/ICAO_airport_code#Prefixes +[quick-start.org]: https://github.com/jaor/xmobar/blob/master/doc/quick-start.org#configuration-options +[jao's xmobar.hs]: https://codeberg.org/jao/xmobar-config +[weather monitor]: https://github.com/jaor/xmobar/blob/master/doc/plugins.org#weather-monitors +[xmobar home page]: https://xmobar.org/ +[xmobar's `Installation` section]: https://github.com/jaor/xmobar#installation + +[Haskell]: https://www.haskell.org/ +[trayer-srg]: https://github.com/sargon/trayer-srg +[record update]: http://learnyouahaskell.com/making-our-own-types-and-typeclasses +[GNU Emacs conventions]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Key-Sequences.html#Key-Sequences From f97d2527ff1dc6e17c147ea15ca0bab243f5e580 Mon Sep 17 00:00:00 2001 From: slotThe Date: Sat, 8 May 2021 10:25:01 +0200 Subject: [PATCH 3/4] tutorial.md: Use logTitles Ever since 322e06eed9c4b23a465d2857df462b8a2d716246 `ppTitleUnfocused` does not exist anymore, as it was moved to `X.U.Loggers.logTitles`. This makes the tutorial aware of that. --- tutorial.md | 106 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 91 insertions(+), 15 deletions(-) diff --git a/tutorial.md b/tutorial.md index 8e2ec04..10e2f92 100644 --- a/tutorial.md +++ b/tutorial.md @@ -740,23 +740,33 @@ and then plug that into our main function: ``` As before, we now change things by modifying that `def` record until we -find something that we like. There are _a lot_ of options for the [PP -record]; I'd advise you to read through all of them now, so you don't -get lost! +find something that we like. First, for some functionality that we need +further down we need to import one more module: + +``` haskell + import XMonad.Util.Loggers +``` + +Now we are finally ready to make things pretty. There are _a lot_ of +options for the [PP record]; I'd advise you to read through all of them +now, so you don't get lost! ``` haskell myXmobarPP :: PP myXmobarPP = def { ppSep = magenta " • " - , ppTitle = wrap (white "[") (white "]") . magenta . ppWindow - , ppTitleUnfocused = wrap (lowWhite "[") (lowWhite "]") . blue . ppWindow , ppTitleSanitize = xmobarStrip , ppCurrent = wrap " " "" . xmobarBorder "Top" "#8be9fd" 2 , ppHidden = white . wrap " " "" , ppHiddenNoWindows = lowWhite . wrap " " "" , ppUrgent = red . wrap (yellow "!") (yellow "!") + , ppOrder = \[ws, l, _, wins] -> [ws, l, wins] + , ppExtras = [logTitles formatFocused formatUnfocused] } where + formatFocused = wrap (white "[") (white "]") . magenta . ppWindow + formatUnfocused = wrap (lowWhite "[") (lowWhite "]") . blue . ppWindow + -- | Windows should have *some* title, which should not not exceed a -- sane length. ppWindow :: String -> String @@ -771,12 +781,12 @@ get lost! lowWhite = xmobarColor "#bbbbbb" "" ``` -_IF YOU ARE ON A VERSION `< 0.17`_: Both `ppTitleUnfocused` and - `xmobarBorder` are not available yet, so you will have to remove them. - As an alternative to `xmobarBorder`, a common way to "mark" the - currently focused workspace is by using brackets; you can try - something like `ppCurrent = wrap (blue "[") (blue "]")` and see if you - like it. +_IF YOU ARE ON A VERSION `< 0.17`_: Both `logTitles` and `xmobarBorder` + are not available yet, so you will have to remove them. As an + alternative to `xmobarBorder`, a common way to "mark" the currently + focused workspace is by using brackets; you can try something like + `ppCurrent = wrap (blue "[") (blue "]")` and see if you like it. Also + read the bit about `ppOrder` further down! That's a lot! But don't worry, take a deep breath and remind yourself of what you read above in the documentation of the [PP record]. Even if @@ -787,8 +797,68 @@ focused window, `ppCurrent` formats the currently focused workspace, The rest is just deciding on some pretty colours and formatting things just how we like it. -If this is too much for you, you can also really just start with the -blank +An important thing to talk about may be `ppOrder`. Quoting from its +documentation: + + > By default, this function receives a list with three formatted + > strings, representing the workspaces, the layout, and the current + > window title, respectively. If you have specified any extra loggers + > in ppExtras, their output will also be appended to the list. + +So the first three argument of the list (`ws`, `l`, and `_` in our case, +where the `_` just means we want to ignore that argument and not give it +a name) are the workspaces to show, the currently active layout, and the +title of the focused window. The last element—`wins`—is what we gave to +`ppExtras`; if you added more loggers to that then you would have to add +more items to the list, like this: + +``` haskell + ppOrder = \[ws, l, _, wins, more, more2] -> [ws, l, wins, more, more2] +``` + +However, many people want to show _all_ window titles on the currently +focused workspace instead. For that, one can use `logTitles` from +[XMonad.Util.Loggers] (remember that module we just imported?). +However, `logTitles` logs _all_ titles. Naturally, we don't want to +show the focused window twice and so we suppress it here by ignoring the +third argument of `ppOrder` and not returning it. The functions +`formatFocused` and `formatUnfocused` should be relatively self +explanitory—they decide how to format the focused resp. unfocused +windows. + +By the way, the `\ ... ->` syntax in there is Haskell's way to express a +[lambda abstraction] (or anonymous function, as some languages call it). +All of the arguments of the function come directly after the `\` and +before the `->`; in our case, this is a list with exactly four elements +in it. Basically, it's a nice way to write a function inline and not +having to define it inside e.g. a `where` clause. The above could have +also be written as + +``` haskell + myXmobarPP :: PP + myXmobarPP = def + { -- stuff here + , ppOrder = myOrder + -- more stuff here + } + where + myOrder [ws, l, _, wins] = [ws, l, wins] + -- more stuff here +``` + +If you're unsure of the number of elements that your `ppOrder` will +take, you can also specify the list like this: + +``` haskell + ppOrder = \(ws : l : _ : wins : _) -> [ws, l, wins] +``` + +This says that it is a list of _at least_ four elements (`ws`, `l`, the +unnamed argument, and `wins`), but that afterwards everything is +possible. + +This config is really quite complicated. If this is too much for you, +you can also really just start with the blank ``` haskell myXmobarPP :: PP @@ -948,6 +1018,7 @@ this: import XMonad.Hooks.StatusBar.PP import XMonad.Util.EZConfig + import XMonad.Util.Loggers import XMonad.Util.Ungrab import XMonad.Layout.Magnifier @@ -991,15 +1062,18 @@ this: myXmobarPP :: PP myXmobarPP = def { ppSep = magenta " • " - , ppTitle = wrap (white "[") (white "]") . magenta . ppWindow - , ppTitleUnfocused = wrap (lowWhite "[") (lowWhite "]") . blue . ppWindow , ppTitleSanitize = xmobarStrip , ppCurrent = wrap " " "" . xmobarBorder "Top" "#8be9fd" 2 , ppHidden = white . wrap " " "" , ppHiddenNoWindows = lowWhite . wrap " " "" , ppUrgent = red . wrap (yellow "!") (yellow "!") + , ppOrder = \[ws, l, _, wins] -> [ws, l, wins] + , ppExtras = [logTitles formatFocused formatUnfocused] } where + formatFocused = wrap (white "[") (white "]") . magenta . ppWindow + formatUnfocused = wrap (lowWhite "[") (lowWhite "]") . blue . ppWindow + -- | Windows should have *some* title, which should not not exceed a -- sane length. ppWindow :: String -> String @@ -1178,6 +1252,7 @@ either :) [INSTALL.md]: https://github.com/xmonad/xmonad/blob/master/INSTALL.md#stack [XMonad.Config]: https://github.com/xmonad/xmonad/blob/master/src/XMonad/Config.hs [XMonad.ManageHook]: https://hackage.haskell.org/package/xmonad/docs/XMonad-ManageHook.html +[XMonad.Util.Loggers]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Util-Loggers.html [XMonad.Util.EZConfig]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Util-EZConfig.html [XMonad.Layout.Renamed]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Layout-Renamed.html [XMonad.Layout.Magnifier]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Layout-Magnifier.html @@ -1200,4 +1275,5 @@ either :) [Haskell]: https://www.haskell.org/ [trayer-srg]: https://github.com/sargon/trayer-srg [record update]: http://learnyouahaskell.com/making-our-own-types-and-typeclasses +[lambda abstraction]: https://wiki.haskell.org/Lambda_abstraction [GNU Emacs conventions]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Key-Sequences.html#Key-Sequences From fdc3bf0484f18729e6b0d64f6aa98a1e2e8c3cee Mon Sep 17 00:00:00 2001 From: slotThe Date: Mon, 24 May 2021 16:05:30 +0200 Subject: [PATCH 4/4] tutorial.md: Point IRC channel to libera.chat The reference to the freenode matrix channel is deleted for now; though it may be added back in later-on, as the matrix team is working towards bridging Libera. We may also link the other matrix channel at some point. Related: https://github.com/xmonad/xmonad-web/pull/21 --- tutorial.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/tutorial.md b/tutorial.md index 10e2f92..f2eef58 100644 --- a/tutorial.md +++ b/tutorial.md @@ -1172,17 +1172,15 @@ everything in one line: ## Get in Touch -The `freenode/#xmonad` channel is very friendly and helpful. It is -possible that people will not immediately answer—we do have lives as +The `irc.libera.chat/#xmonad` channel is very friendly and helpful. It +is possible that people will not immediately answer—we do have lives as well, after all :). Eventually though, people will usually chime in if they have something helpful to say; sometimes this takes 10 minutes, other times it may well take 10 hours. If you don't have an IRC client ready to go, the easiest way to join is via [webchat]—just jot down a -username and `#xmonad` as the channel and you should be good to go! -There is a [log] of the channel available, in case you do have to -disconnect at some point, so don't worry about missing any messages. We -also have a [matrix room] that's linked to IRC, in case you prefer -that. +username and you should be good to go! There is a [log] of the channel +available, in case you do have to disconnect at some point, so don't +worry about missing any messages. If you're not a fan of real-time interactions, you can also post to the [xmonad mailing list] or the [xmonad subreddit]. @@ -1230,11 +1228,10 @@ either :) - [TheMC47](https://github.com/TheMC47/dotfiles/tree/master/xmonad/.xmonad) -[log]: https://ircbrowse.tomsmeding.com/browse/xmonad +[log]: https://ircbrowse.tomsmeding.com/browse/lcxmonad [EWMH]: https://specifications.freedesktop.org/wm-spec/wm-spec-1.3.html [ICCCM]: https://tronche.com/gui/x/icccm/ -[webchat]: https://webchat.freenode.net/ -[matrix room]: https://matrix.to/#/#freenode_#xmonad:matrix.org +[webchat]: https://kiwiirc.com/nextclient/irc.libera.chat/?#xmonad [about xmonad]: https://xmonad.org/about.html [shell variable]: https://www.shellscript.sh/variables1.html [xmonad-testing]: https://github.com/xmonad/xmonad-testing