• Type:

CH Why I Use Suckless Tools

Software is complicated. Foundational building blocks of desktop environments
tend to grow year over year until it’s difficult to understand or maintain them.
Suckless offers an alternative to this continuous cycle of bloat and
meaningless redesign. Suckless tools aim to keep things simple, minimal, usable
and hackable by default. Their window manager dwm is just a window
manager. It doesn’t handle things like transparency, compositing or volume
control. Their terminal st is just a terminal. It doesn’t handle fancy
things like ancient terminal kinds that died out long ago. It just displays
text. It doesn’t handle things that tmux or similar could take care of, because
tmux can do a better job at that than st ever could on its own.

Suckless tools are typically configured in C, the language they are written in.
However as a side effect of suckless tools having their configuration baked into
the executable at compile time, they start up instantly. If something goes
wrong while using them, you can easily jump right into the code that implements
them and nail down issues using basic debugger skills.

However, even though the window manager is meager, it still offers places for
you to make it look beautiful. For examples of beautiful dwm setups, see this
search of /r/unixporn on reddit
.

I would like to walk through my dwm setup, how I have it configured all of the
parts at play as well as an example of how I debug problems in my dwm config.

My dwm Config

As dwm is configured in C, there’s also a community of people creating
patches for dwm that add extra features like additional tiling
methods, the ability to automatically start things with dwm, transparency for
the statusbar and so much more. I use the following patches:

This combination of patches allows me to make things feel comfortable and
predictable enough that I can rely entirely on muscle memory for most of my
window management. Nearly all of it is done with the keyboard too.

Here is my config file. It’s logically broken into two big sections:

  • Variables
  • Keybinds

I’ll go into more detail about these below.

Variables

The main variables in my config control the following:

  • border width
  • size of the gaps when tiling windows
  • the snap width
  • system tray errata
  • the location of the bar
  • the fonts
  • colors
  • transparency values for the bar
  • workspace names (mine are based off of the unicode emoticon (ノ◕ヮ◕)ノ*:・゚✧)
  • app-specific hacks
  • default settings for the tiling layouts
  • if windows should be forced into place or not
  • window layouts

All of these things control various errata. As a side effect of making them all
compile time constants, these settings don’t have to be loaded into the program
because they’re already a part of it. I use the Hack font on my
desktop and with emacs.

Keybinds

The real magic of tiling window managers is that all of the window management
commands are done with my keyboard. Alt is the key I have devoted to controlling
the window manager. All of my window manager control chords use the alt key.

Here are the main commands and what they do:

Command Effect
Alt-p Spawn a program by name
Alt-Shift-Enter Open a new terminal window
Alt-b Hide the bar if it is shown, show the bar if it is hidden
Alt-j Move focus down the stack of windows
Alt-k Move focus up the stack of windows
Alt-i Increase the number of windows in the primary area
Alt-d Decrease the number of windows in the primary area
Alt-h Make the primary area smaller by 5%
Alt-l Make the primary area larger by 5%
Alt-Enter Move the currently active window into the primary area
Alt-Tab Switch to the most recently active workspace
Alt-Shift-C Nicely ask a window to close
Alt-t Select normal tiling mode for the current workspace
Alt-f Select floating (non-tiling) mode for the current workspace
Alt-m Select monocle (fullscreen active window) mode for the current workspace
Alt-u Select bottom-stacked tiling mode for the current workspace
Alt-o Select bottom-stacked horizontal tiling mode for the current workspace (useful on vertical monitors)
Alt-e Open a new emacs window
Alt-Space Switch to the most recently used tiling method
Alt-Shift-Space Detach the currently active window from tiling
Alt-1 thru Alt-9 Switch to a given workspace
Alt-Shift-1 thru Alt-Shift-9 Move the active window to a given workspace
Alt-0 Show all windows on all workspaces
Alt-Shift-0 Show the active window on all workspaces
Alt-Comma and Alt-Period Move focus to the other monitor
Alt-Shift-Comma and Alt-Shift-Period Move the active window to the other monitor
Alt-Shift-q Uncleanly exit dwm and kill the session

This is just enough commands that I can get things done, but not so many that I
get overwhelmed and forget what keybind does what. I have most of this committed
to muscle memory (and had to look at the config file to write out this table),
and as a result nearly all of my window management is done with my keyboard.

The rest of my config handles things like Alt-Right-Click to resize windows
arbitrarily, signals with dwmc and other overhead like that.

The Other Parts

The rest of my desktop environment is built up using a few other tools that
build on top of dwm. You can see the NixOS modules I’ve made for it
here
and here:

  • xrandr to set up my multiple
    monitors and rotation for them
  • feh to set my wallpaper
  • picom to handle compositing effects like
    transparency, blur and drop shadows for windows
  • pasystray for controlling my
    system volume
  • dunst for notifications
  • xmodmap for rebinding the caps
    lock key to the escape key
  • cabytcini to show the current time and
    weather in my dwm bar

Each of these tools has their own place in the stack and they all work together
to give me a coherent and cohesive environment that I can use for Netflix,
programming, playing Steam games and more.

cabytcini is a program I created for myself as part of my goal to get more
familiar with Rust. As of the time of this post being written, it uses only 11
megabytes of ram and is configured using a config file located at
~/.config/cabytcini/gaftercu'a.toml. It scrapes data from the API server I use
for my wall-mounted clock to show me the weather in Montreal. I’ve been meaning
to write more about it, but it’s currently only documented in Lojban.

Debugging dwm

Software is imperfect, even smaller programs like dwm can still have bugs in
them. Here’s the story of how I debugged and bisected a problem with my dwm
config
recently.

I had just gotten the second monitor set up and noticed that whenever I sent a
window to it, the entire window manager seemed to get locked up. I tried sending
the quit command to see if it would respond to that, and it failed. I opened up
a virtual terminal with control-alt-F1 and logged in there, then I launched
htop to see if the process was blocked.

It reported dwm was using 100% CPU. This was odd. I then decided to break out
the debugger and see what was going on. I attached to the dwm process with gdb
-p (pgrep dwm)
and then ran bt full to see where it was stuck.

The backtrace revealed it was stuck in the drawbar() function. It was stuck in
a loop that looked something like this:

for (c = m->clients; c; c = c->next) {
    occ |= c->tags;
    if (c->isurgent)
            urg |= c->tags;
}

dwm stores the list of clients per tag in a singly linked list, so the root
cause could be related to a circular linked list somehow, right?

I decided to check this by printing c and c->next in GDB to see what was
going on:

gdb> print c
0xfad34f
gdb> print c->next
0xfad34f

The linked list was circular. dwm was stuck iterating an infinite loop. I looked
at the type of c and saw it was something like this:

struct Client {
	char name[256];
	float mina, maxa;
	float cfact;
	int x, y, w, h;
	int oldx, oldy, oldw, oldh;
	int basew, baseh, incw, inch, maxw, maxh, minw, minh;
	int bw, oldbw;
	unsigned int tags;
	int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
	Client *next;
	Client *snext;
	Monitor *mon;
	Window win;
};

So, next is a pointer to the next client (if it exists). Setting the pointer
to NULL would probably break dwm out of the infinite loop. So I decided to
test that by running:

gdb> set var c->next = 0x0

To set the next pointer to null. dwm immediately got unstuck and exited
(apparently my quit command from earlier got buffered), causing the login screen
to show up. I was able to conclude that something was wrong with my dwm setup.

I know this behavior worked on release versions of dwm, so I decided to load up
KDE and then take a look at what was going on with Xephyr and git
bisect
.

I created two fake monitors with Xephyr:

$ Xephyr -br -ac -noreset -screen 800x600 -screen 800x600 +xinerama :1 &

And then started to git bisect my dwm fork:

$ cd ~/code/cadey/dwm
$ git bisect init
$ git bisect bad HEAD
$ git bisect good cb3f58ad06993f7ef3a7d8f61468012e2b786cab

I registered the bad commit (the current one) and the last known good commit
(from when dwm 6.2 was
released
)
and started to recreate the conditions of the hang.

I set the DISPLAY environment variable so that dwm would use the fake
monitors:

$ export DISPLAY=:1

and then rebuilt/ran dwm:

$ make clean && rm config.h && make && ./dwm

Once I had dwm up and running, I created a terminal window and tried to send it
to the other screen. If it worked, I marked the commit as good with git bisect
good
, and if it hung I marked the commit as bad with git bisect bad. 7
iterations later and I found out that the attachbelow patch was
the culprit.

I reverted the patch on the master branch, rebuilt and re-ran dwm and tried to
send the terminal window between the fake monitors. It worked every time. Then I
committed the revert of attachbelow, pushed it to my NUR
repo
,
and then rebuilt my tower’s config once it passed CI.

Being a good internet citizen, I reported this to the suckless mailing
list
and then was able to get a
reply back not only confirming the bug, but also with a patch for the
patch
to fix the
behavior forever. I have yet to integrate this meta-patch into my dwm fork, but
I’ll probably get around to it someday.

This really demonstrates one of the core tenants of the suckless philosophy
perfectly. I am not very familiar with how the dwm codebase works, but I am able
to dig into its guts and diagnose/fix things because it is intentionally kept as
simple as possible.

If you use Linux on a desktop/laptop, I highly suggest taking a look at
suckless software and experimenting with it. It is super optimized for
understandability and hacking, which is a huge breath of fresh air these days.


This article was posted on 2020 M6 5. Facts and circumstances may have changed since publication. Please contact me before jumping to conclusions if something seems wrong or unclear.

Read More

Previous Post

CH Light turned into exotic Laughlin matter

Next Post

CH Starting Many Things

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top