Some notes on the C language with a dwm tutorial
As a computer hobbyist in the sense that a someone in the automotive industry might also have racecar as a hobby, I have to say that next to HTML, the C language has been among the most rewarding.
Much like how the racecar hobbyist gravitates towards certain production models for their accessibility, the C language offers a variety of accessible programs to begin wrenching on including entire operating system and application stacks.
The dwm
Window Manager
Let us consider the excellent tiling window manager dwm. Most operating systems have dwm
available through their package managers, but this has the substantial downside of all dwm configuration requiring modifications to dwn’s source code to build your own dwm.
Note: What started as an essay is taking a pivot towards the tutorial genre. If you experience errors related to missing tools during this tutorial, feel free to install them and continue from where you left off.
Important: If you are reading this tutorial and unaware, the commands in this tutorial are prefixed by a prompt that ends with the $
character and preceeded by the path. So, by convention a prompt like ~$
denotes the current directory as your user’s home directory.
Thankfully compiling your own dwm in your user’s home directory is as simple as:
~$ mkdir suckless
~$ cd suckless
~/suckless$ git clone https://git.suckless.org/dwm
~/suckless/dwm$ cd dwm
At this point, you will observe with ls
a file named config.def.h
, this is where customizations go. You may note that some configuration options are surprisingly accessible if you are new to C. Before making any changes to config.def.h
, it is prudent to make a backup:
~/suckless/dwm$ cp config.def.h config.def.h.bak
Now, any time we might err when editing config.def.h
, we can restore a known working version with:
~/suckless/dwm$ cp config.def.h.bak config.def.h
You can compile and install dwm with:
~/suckless/dwm$ doas make clean install
or if your system instead uses sudo:
~/suckless/dwm$ sudo make clean install
dwm
natively supports dmenu
as an application launcher so it is natural to follow up by installing it:
~/suckless/dwm$ cd ..
~/suckless$ git clone https://git.suckless.org/dmenu
~/suckless$ cd dmenu
As customizing dmenu is outside the scope of this thread, we can skip to installation:
~/suckless/dmenu$ doas make clean install
And why don’t we complete this nascent DIY’er desktop suite with the st
terminal:
~/suckless/dmenu$ cd ..
~/suckless$ git clone https://git.suckless.org/st
~/suckless$ st
~/suckless/st$ doas make clean install
And return to our home directory.
If we have an X11 server configured and no display manager, we can now set up our dwm
to start when we login from the console.
~/suckless/st$ cd ~
Let us take a detour now to install xsetroot
. which enables a nifty trick that is coming. I put this here because on Alpine it is missing from the default X11 distribution. This may vary if you are not using Alpine Linux:
~$ doas apk add xsetroot
Now let us make a couple of config files. If you are operating from an existing desktop environment, you can backup your existing copies of these files using the copy tool as demonstrated previously.
Let us begin with ~/.xinitrc
which is a script composed of shell commands executed when beginning an xsession:
~$ nano .xinitrc
For the contents of this file, I am using something like:
while true; do
xsetroot -name " Time: $(date +"%R") | $(acpi -b | awk '{print $3, $4, $5}') "
sleep 3
done &
xscreensaver -no-splash &
exec dbus-launch --exit-with-session dwm
To break this apart, between while true; do
and done &
is a loop which will print:
- The 24 hour time with minute resolution
- A pipe character
- The acpi battery status, which is useful for portable computers
On a strictly plugged in computer, the default behavior is worth observing instead of this output.
Next we start xscreensaver
in the background with xscreensaver -no-splash &
, and follow with a line that launches dwm
inside of a dbus session.
Note: Everything in this .xinitrc
file was optional. A minimal file could have consisted entirely of exec dwm
followed by a newline character. The newline character is necessary because files that don’t end in newline are incomplete and incomplete files can’t be run.
The last file to configure is ~/.profile
, its contents can look like:
startx
Now, dwm
will start after logging in from the login console. You may now uninstall your current login display manager, if any, and expect to be greeted with a dwm
session after logging in.
If you wish to use your current login display manager, you may do so by creating a .desktop
file. The proper path and options for a .desktop
file are outside the scope of this threat because having a graphical login prompt is optional.
At this point you could reboot your system and expect to be greeted with a console login prompt, then enter a working desktop environment afterwards. Plenty of cheat sheets are available online to assist with the default keyboard shortcuts like Modifier
+Shift
+Enter
to spawn a terminal or Modifier
+P
to trigger the dmenu
launcher.
Back to config.def.h
With a working environment, let us take a look at the contents of our dwm
’s config.def.h
:
~$ less ~/suckless/dwm/config.def.h
Immediately at the top of the file we are greeted with a stanza controlling the appearance:
/* appearance */
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
static const char *fonts[] = { "monospace:size=11" };
static const char dmenufont[] = "monospace:size=11";
static const char col_gray1[] = "#222222";
static const char col_gray2[] = "#444444";
static const char col_gray3[] = "#bbbbbb";
static const char col_gray4[] = "#eeeeee";
static const char col_cyan[] = "#005577";
static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
[SchemeSel] = { col_gray4, col_cyan, col_cyan },
};
Some elements like the hex encoded RGB colors are straightfoward, and present perhaps the most trivial customization. A bit further down, we see another interesting stanza:
/* key definitions */
#define MODKEY Mod1Mask
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
This stanza sets the left Alt
key as the Modifier
key, a state of potential inconvenience known as ‘Mod1Mask’.
If we alter, just one single character in this stanza:
/* key definitions */
#define MODKEY Mod4Mask
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
Now, before we remake our dwm
, we will need to delete a file, config.h
, which the make
process generates from our config.def.h
:
~/suckless/dwm$ rm config.h
Now, when we compile a new dwm
the Super
key, which often wears a “Windows” four square logo, is our Modifier
key for dwm
. On at least one keyboard this key appears as the small left Alt
.
~/suckless/dwm$ doas make clean install
All is now right with the world.
This state, known as Mod4Mask
is more convenient because it prevents any dwm
shortcuts from conflicting with application shortcuts that use the left Alt
as their modifier like Emacs
.
The Duality of C
Beyond the two customizations we have discussed so far, anything is possible!
Anything is possible works two ways, because not everything we might call anything is going to be desirable. Much like when wrenching on racecar, there is an element of danger.
While the C shown here from config.def.h
is very well structured and readable, this is not requirement for C. There’s even obfuscated C competitions where competitors try to sneak hidden traps into valid C code. Understanding other people’s aftermarket modifications to their car’s wiring is a pursuit filled with peril.
Thankfully there is a wealth of existing C code to play with. C may not always be the best language, but it is always a language.