mirror of
https://github.com/xmonad/xmonad.git
synced 2025-05-19 00:20:22 -07:00
1331 lines
51 KiB
Markdown
1331 lines
51 KiB
Markdown
# 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]:
|
|
|
|
<p>
|
|
<img alt="blank desktop" src="https://user-images.githubusercontent.com/50166980/111586872-c0eb0e00-87c1-11eb-9129-c2781bbbf227.png" width="550">
|
|
<img alt="blank desktop" src="https://user-images.githubusercontent.com/50166980/111586885-c47e9500-87c1-11eb-8e8e-83d4d33c4c8d.png" width="550">
|
|
</p>
|
|
|
|
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.17.0` and xmonad-contrib
|
|
version `>= 0.17.0` 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 big
|
|
warning labels for the respective version bounds, 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
|
|
|
|
``` console
|
|
$ 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.
|
|
|
|
In particular, it might be a good idea to set a wallpaper beforehand.
|
|
Otherwise, when switching workspaces or closing windows, you might start
|
|
seeing "shadows" of windows that were there before, unable to interact
|
|
with them.
|
|
|
|
## 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
|
|
-- NOTE: Only needed for versions < 0.18.0! For 0.18.0 and up, this is
|
|
-- already included in the XMonad import and will give you a warning!
|
|
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-C-s` to take a
|
|
snapshot of one window, and `M-f` 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-C-s", unGrab *> spawn "scrot -s" )
|
|
, ("M-f" , 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 were 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-C-s", unGrab *> spawn "scrot -s" )
|
|
, ("M-f" , 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.0`_: 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-C-s", unGrab *> spawn "scrot -s" )
|
|
, ("M-f" , 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-C-s", unGrab *> spawn "scrot -s" )
|
|
, ("M-f" , 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
|
|
```
|
|
|
|
and replace your `main` function above with:
|
|
|
|
``` haskell
|
|
main :: IO ()
|
|
main = xmonad $ ewmhFullscreen $ ewmh $ xmobarProp $ myConfig
|
|
```
|
|
|
|
---
|
|
|
|
_IF YOU ARE ON A VERSION `< 0.17.0`_: 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.
|
|
|
|
Further, the `xmobarProp` function does not exist in older versions.
|
|
Instead of it, use `xmobar` via `main = xmonad . ewmh =<< xmobar
|
|
myConfig` and carefully read the part about pipes later on (`xmobar`
|
|
uses pipes to make xmobar talk to xmonad). Do note the lack of
|
|
`ewmhFullscreen`, as explained above!
|
|
|
|
---
|
|
|
|
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.0`).
|
|
|
|
---
|
|
|
|
_IF YOU ARE ON A VERSION `< 0.17.0`_: 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", "<weather> <tempC>°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", "<volumestatus>"
|
|
, "--suffix" , "True"
|
|
, "--"
|
|
, "--on", ""
|
|
]
|
|
, Run Memory ["--template", "Mem: <usedratio>%"] 10
|
|
, Run Swap [] 10
|
|
, Run Date "%a %Y-%m-%d <fc=#8be9fd>%H:%M</fc>" "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 in xmobar's [quick-start.org]. 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.0`_: `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
|
|
. 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. 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 " • "
|
|
, 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
|
|
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 `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
|
|
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.
|
|
|
|
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
|
|
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`. If you want to use XMonad with a desktop environment, see
|
|
[Basic Desktop Environment Integration] for how to do this. For a
|
|
version using XMonad's built in functionality instead, see the [next
|
|
section][using-the-startupHook].
|
|
|
|
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.
|
|
|
|
<img alt="blank desktop" src="https://user-images.githubusercontent.com/50166980/111529498-84d49080-8762-11eb-9e81-c15dd844b0a9.png" width="660">
|
|
|
|
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).
|
|
|
|
### Using the `startupHook`
|
|
|
|
Instead of the `.xinitrc` file, one can also use XMonad's built in
|
|
`startupHook` in order to auto start programs. The
|
|
[XMonad.Util.SpawnOnce] library is perfect for this use case, allowing
|
|
programs to start only once, and not with every invocation of `M-q`!
|
|
|
|
This requires but a small change in `myConfig`:
|
|
|
|
``` haskell
|
|
-- import XMonad.Util.SpawnOnce (spawnOnce)
|
|
|
|
myConfig = def
|
|
{ modMask = mod4Mask -- Rebind Mod to the Super key
|
|
, layoutHook = myLayout -- Use custom layouts
|
|
, startupHook = myStartupHook
|
|
}
|
|
`additionalKeysP`
|
|
[ ("M-S-z", spawn "xscreensaver-command -lock")
|
|
, ("M-C-s", unGrab *> spawn "scrot -s" )
|
|
, ("M-f" , spawn "firefox" )
|
|
]
|
|
|
|
myStartupHook :: X ()
|
|
myStartupHook = do
|
|
spawnOnce "trayer --edge top --align right --SetDockType true \
|
|
\--SetPartialStrut true --expand true --width 10 \
|
|
\--transparent true --tint 0x5f5f5f --height 18"
|
|
spawnOnce "feh --bg-fill --no-fehbg ~/.wallpapers/haskell-red-noise.png"
|
|
-- … and so on …
|
|
```
|
|
|
|
## 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 dialog windows. 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-C-s", unGrab *> spawn "scrot -s" )
|
|
, ("M-f" , 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.Loggers
|
|
-- NOTE: Importing XMonad.Util.Ungrab is only necessary for versions
|
|
-- < 0.18.0! For 0.18.0 and up, this is already included in the
|
|
-- XMonad import and will generate a warning instead!
|
|
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-C-s", unGrab *> spawn "scrot -s" )
|
|
, ("M-f" , 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 " • "
|
|
, 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
|
|
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 `<fn>` 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" , "<fn=1>\62020</fn> "
|
|
, "--mediums", "<fn=1>\62018</fn> "
|
|
, "--highs" , "<fn=1>\62016</fn> "
|
|
...
|
|
]
|
|
...
|
|
}
|
|
```
|
|
|
|
For an explanation of the battery commands used above, see xmobar's
|
|
[battery] documentation.
|
|
|
|
You can also specify workspaces in the same way and feed them to xmobar
|
|
via the property (e.g. have `"<fn=1>\xf120</fn>"` as one of your
|
|
workspace names).
|
|
|
|
As an example how this would look like in a real configuration, you can
|
|
look at [Liskin's old][liskin-xmobarrc-old], [Liskin's current][liskin-xmobarrc],
|
|
[slotThe's][slotThe-xmobarrc], or [TheMC47's][TheMC47-xmobarrc] xmobar
|
|
configuration. Do note that the last three are Haskell-based and thus may
|
|
be a little hard to understand for newcomers.
|
|
|
|
[liskin-xmobarrc-old]: https://github.com/liskin/dotfiles/blob/75dfc057c33480ee9d3300d4d02fb79a986ef3a5/.xmobarrc
|
|
[liskin-xmobarrc]: https://github.com/liskin/dotfiles/blob/home/.xmonad/xmobar.hs
|
|
[TheMC47-xmobarrc]: https://github.com/TheMC47/dotfiles/tree/master/xmobar/xmobarrc
|
|
[slotThe-xmobarrc]: 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 `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 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].
|
|
|
|
## 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/lcxmonad
|
|
[EWMH]: https://specifications.freedesktop.org/wm-spec/wm-spec-1.3.html
|
|
[ICCCM]: https://tronche.com/gui/x/icccm/
|
|
[webchat]: https://web.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
|
|
[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://codeberg.org/xmobar/xmobar/issues/239#issuecomment-537931
|
|
[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://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-Hacks.html
|
|
[INSTALL.md]: INSTALL.md
|
|
[PP record]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Hooks-DynamicLog.html#t:PP
|
|
[XMonad.Config]: https://github.com/xmonad/xmonad/blob/master/src/XMonad/Config.hs
|
|
[XMonad.Doc.Contributing]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Doc-Configuring.html
|
|
[XMonad.Hooks.EwmhDesktops]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Hooks-EwmhDesktops.html
|
|
[XMonad.Hooks.ManageHelpers]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Hooks-ManageHelpers.html
|
|
[XMonad.Layout.Magnifier]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-Magnifier.html
|
|
[XMonad.Layout.Renamed]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-Renamed.html
|
|
[XMonad.Layout.ThreeColumns]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Layout-ThreeColumns.html
|
|
[XMonad.ManageHook]: https://xmonad.github.io/xmonad-docs/xmonad/XMonad-ManageHook.html
|
|
[XMonad.Util.ClickableWorkspaces]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-ClickableWorkspaces.html
|
|
[XMonad.Util.EZConfig]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-EZConfig.html
|
|
[XMonad.Util.Loggers]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Util-Loggers.html
|
|
[XMonad.Util.SpawnOnce]: https://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Util-SpawnOnce.html
|
|
|
|
[xmobar]: https://codeberg.org/xmobar/xmobar
|
|
[battery]: https://codeberg.org/xmobar/xmobar/src/branch/master/doc/plugins.org#batteryp-dirs-args-refreshrate
|
|
[xmobar.hs]: https://codeberg.org/xmobar/xmobar/src/branch/master/etc/xmobar.hs
|
|
[Wikipedia page]: https://en.wikipedia.org/wiki/ICAO_airport_code#Prefixes
|
|
[quick-start.org]: https://codeberg.org/xmobar/xmobar/src/branch/master/doc/quick-start.org#configuration-options
|
|
[jao's xmobar.hs]: https://codeberg.org/jao/xmobar-config
|
|
[weather monitor]: https://codeberg.org/xmobar/xmobar/src/branch/master/doc/plugins.org#weather-monitors
|
|
[xmobar's `Installation` section]: https://codeberg.org/xmobar/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
|
|
[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
|