< Index

What is suckless software?

Suckless is a group of developers creating permissively licensed (MIT/X Consortium) software for *nix operating systems with a very specific philosophy of high levels of hackability, low lines of code (LOC) count, and low resource usage/quick compile times. In their suite of software is a window manager (dwm), a terminal emulator (st), and a ubiquitous program launcher and hackable menu (dmenu). The idea of the software is that the programs are all written in C, and instead of having cumbersome config files and adding unnecessary extra lines of code, configuration is done in a faux-config file which is really just a C header file called config.h. Every time configuration is changed, the software must be recompiled - again to increase run speed and decrease resource usage by not having to parse a config file every time the program starts. I personally use and love suckless software, and I install the entire suckless suite on my Linux computers every time. If you would like to give it a shot, read on and I will explain how to install the suckless "desktop environment" on your computer.

Below is a picture of what your desktop will look like after following these steps (click to expand):

My desktop on dwm

What are the drawbacks to suckless software?

Like anything else in life, especially such a unique idea like the suckless philosophy, there will be drawbacks. The primary drawback to suckless software is that they usually come prepackaged with very minimal features - while usable, they aren't quite at a standard most would call acceptable out-of-the-box (even for me, and I don't care at all about desktop rices or anything like that). As an example, the terminal emulator, st, does not have an ability to scroll up by default, and basically is a glorified tty in a window on your desktop. This, combined with the fact that suckless software needs to be recompiled to add changes to the configuration, means that installing suckless software through your package manager is a complete waste of time (if you have already, I suggest that you uninstall your suckless software and instead start installing them the intended way as described below). The primary way of adding features to suckless software is by patching the software using .diff files that are shared either on the internet or directly on the suckless website. However, this means that two patches could either conflict, or one patch could change the file enough that the second patch can't apply correctly, and must be manually applied to the source files. This also means that every new release of a piece of suckless software, all of the patches will be broken until the patch writers (or you) fix them to be compatible with the changes in the newest release of the software. Suckless doesn't update their software very often though, so this isn't as big a deal as it sounds. The final problem is that suckless has an RSS feed for updates, but they do not post a changelog or anything of the sort anywhere (the RSS feed is literally just "st 0.1.2.3 released" and that's it), so if you really care about what changed you have to read the code diffs yourself.

Alright, I want to give suckless software a shot. What do I do?

First, I would create a directory somewhere in your home for where you want to keep suckless software (or any software you compile from source in general). I put my suckless software in the ~/source/ directory.

You will need git installed on your system as well as a C compiler (gcc). This should be as easy as sudo pacman -S git base-devel on Arch, or sudo apt install git build-essential on Ubuntu and the like.

Once installed, cd to the directory you created before and run the following commands:

git clone https://git.suckless.org/dwm
git clone https://git.suckless.org/slstatus
git clone https://git.suckless.org/dmenu
git clone https://git.suckless.org/st

The first command downloads the window manager (desktop environment replacement) dwm, the second command downloads slstatus which is used to set the contents of the bar at the top of dwm (not technically required but helpful), the third command downloads dmenu which is a program launcher which works great alongside dwm or any other window manager, and the last command downloads the terminal emulator st. When you want to update the software later, just cd into the directory for each one and run git pull to pull the latest version of the software.

Now, installing the software is as easy as cding into the directory for the piece of software you want to install, and running sudo make clean install. This will automatically clean any previous installation, and then compile and reinstall that program again so that it's usable on your system like any other software. These programs do require certain X11 libraries as build dependencies, however, which may or may not be installed on your system, particularly libXft and libXinerama. I believe installing the full X11 package with all software should install these (versus a minimal X11 server configuration package on some distributions); if not, you will have to install it manually. fontconfig should also be necessary on your system. For example, on Gentoo, the dependencies can be installed with sudo emerge --ask x11-libs/libXft x11-libs/libXinerama media-libs/fontconfig, or on Arch, they can be installed with sudo pacman -S libxft libxinerama fontconfig. Other distributions will probably just install the full X11 suite and fontconfig by default and this may not be necessary. If in doubt, just keep trying to compile and installing the libraries where it gets stuck until it works.

Now that you know how to compile them, let's actually configure the software (the way I like it configured; your preferences may vary). We're going to do a manual configuration for slstatus and dwm, and install a patch for st to add the ability to scroll up to see terminal history. Note that when you're configuring suckless software, there will be a config.def.h file that's installed by default from the git repository. Don't edit this file directly! Instead, copy it to a file named config.h and make changes there instead. The config.def.h file exists as the defaults that came with the software, and when compiling the software without a config.h file created yet, the Makefile will copy it over to config.h; otherwise it is ignored. It's just there so you can diff it with your personalized config.h file to see what you changed, and patches or git pull of new updates will always change the config.def.h file, not your custom config.h.

Configuring dwm and slstatus

dwm

Now, let's open config.h for dwm. (If you'd like, you can go to a tty and open dwm and mess around with it a little bit to see what it's like before configuring it. To quit default dwm, press Alt+Shift+Q on your keyboard.) By default, dwm highlights your active window with this light blue color, but that's the same color used by the bar at the top and, in general, it's kind of hard to notice. So, I like to change the highlight color of my active window to bright pink so I can always tell which one is selected (I told you I'm not a ricer).

To do this, notice at the top that there's a lot of static const strings describing different colors. We're going to add our custom color. Make a new line somewhere next to the col_grays and col_cyan and name it col_pink (or whatever color you'd like), like so:

static const char col_pink[] = "#f13edb";

(#f13edb is the hex color code for a shade of pink.) Then, we need to change the actual highlight color to the new color. A little further down is a little table called *colors; in here, we're going to change it from this:

static const char *colors[][3]      = {
	/*               fg         bg         border   */
	[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
	[SchemeSel]  = { col_gray4, col_cyan,  col_cyan  },
};

To this:

static const char *colors[][3]      = {
	/*               fg         bg         border   */
	[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
	[SchemeSel]  = { col_gray4, col_cyan,  col_pink  },
};

So, we added a new color, defined it using its color code, and then changed the border for a selected window to be that new color. Great.

There's just a few other things I like to change in here. On line 36 is a const float called mfact; this is the relative size of your master window compared to your child windows on each of your desktops (they're not really desktops, but who cares). If you change mfact to equal 0.5, this will make the master and child windows take an even 50/50 split of your screen (which is preferrable, because then if you have exactly two windows open on a desktop, they both split the screen real estate down the middle). There's also a #DEFINE MODKEY on line 49; changing this from Mod1Mask to Mod4Mask makes dwm use the Super key (aka, the Windows key) for keyboard shortcuts instead of Alt, which is preferrable to most people (especially for people who use Emacs, which often uses the Alt key). Lastly, on line 4 is a borderpx variable; this sets the width of window borders. I think the default is too small, so I set this to equal 3 instead of 1 to make the borders a little thicker, to help visibility of the selected window even more.

dwm comes with keyboard shortcuts to run different programs built in by default. For example, Mod+P opens the program launcher (defaults to dmenu), and Mod+Shift+Enter opens the default terminal (defaults to st). "Mod" in these examples means the modkey that we set earlier, so if you're following this guide, it should be the Super (Windows) key. Since we're going to be using dmenu and st, we don't have to change anything here; if you'd like to change these to alternative programs (such as rofi and alacritty, respectively), then you can change lines 61 and 62 and replace dmenu_run and st with your programs of choice (the stuff after the command name are the command-line options to be passed to the program, ending in NULL). For example:

static const char *dmenucmd[] = { "rofi", NULL };
static const char *termcmd[]  = { "alacritty", NULL };

You could change the name of dmenucmd to something else if you're changing it to no longer use dmenu, if you wish to be consistent - this requires changing all other uses of the variable name as well, however. Now you can just run sudo make clean install again in the dwm/ directory to apply the changes and reinstall the program.

slstatus

slstatus is a simple program to set the text in the bar of dwm. You can actually set this to the output of any command you want directly using the xsetroot -name command (as explained tersely in dwm's README file), so this step is completely optional, but I like to use slstatus personally.

By default, slstatus just changes your dwm status to the date and time. This is probably fine for most people, so you can just compile it and leave it if you'd like (we'll talk about how to properly start it with dwm later). However, I run Linux on ThinkPad laptops, so let's see how to add battery information.

Copy the config.def.h file to config.h again like last time, and open it up. There's only one entry right now, which is that it's set to display the date and time, as discussed earlier. We can add new lines here to add information about the battery percentage, the battery's charging stage (charging/uncharging/neither), and the estimated amount of battery life remaining if it continues draining at the same rate. To do that, change the following line:

        { datetime, "%s",           "%F %T" },

To this:

	{ datetime,          "%s  ",   "%F %T" },
	{ battery_perc,      "%s ",    "BAT0" },
	{ battery_state,     "%s ",    "BAT0" },
	{ battery_remaining, "%s",     "BAT0" },

Now, the additional battery information will be available in dwm when slstatus is running. The battery is set to BAT0, so if nothing shows up, you may have to change the battery number. The spaces after the %s in the second column are the spaces between the different pieces of information; add or remove them (or type anything else in there) as you would prefer.

slstatus has many preset different functions for the bar, such as the ones used here, datetime and the battery ones, but you can also run an arbitrary console command and print the output to the bar. Format it like so:

        { run_command,          "%s",     "uname -r" },

Where uname -r is replaced with whatever command you would like to run.

Now just compile slstatus and we'll move on to st.

st

Note: I do not use st anymore because I have not been able to get it to render fonts from foreign languages such as Chinese/Japanese (as mentioned below). I now use Alacritty. To change dwm to spawn Alacritty (or a different terminal) instead of st, change the line in config.h starting with static const char *termcmd[] to be static const char *termcmd[] = { "alacritty", NULL}; and recompile dwm.

I like most things about st (minus complaints levied below) except that it has no ability to scroll up (which is crucial for commands with long output that you have to make choices in, such as emerge on Gentoo, which cannot be piped into less). So, let's apply the scrollback patch to st.

Patches for st can be found at https://st.suckless.org/patches/ and in particular we want the one called scrollback. Download the latest .diff file (at the time of writing, 0.8.5), and save it to your st folder we created earlier. (Or, you can run wget https://st.suckless.org/patches/scrollback/st-scrollback-0.8.5.diff while in your st directory to save the file from the command line.)

To apply a patch, go into your st directory and run patch -i st-scrollback-0.8.5.diff. This will apply all of the patches to all of the files. Then, make sure to copy over the changes made to the file config.def.h to your config.h file (or, if you don't have any other config changes, just delete the config.h file if it exists), and recompile the program. Success! Now in st, to scroll up and down through your terminal's history, you can use Shift+PgUp and Shift+PgDn.

Note that there's a chance that your patch could fail. Generally, the first patch you apply to a piece of suckless software should work, but every subsequent patch becomes less and less likely to succeed. If you run into a problem, usually the two patches aren't actually mutually exclusive (although they can be), so if you read which part of the patch failed to apply, you can actually manually add that code into the file it's supposed to go to and it should work. I don't personally use a lot of patches, though, so I won't go too into detail about this.

st's emoji crashing bug

st (and other suckless software, but only st is really affected in normal use) has a problem where if you try and display an emoji and have any color emojis installed on your system, then the application will crash. This is particularly a problem if you use the RSS reader newsboat like I recommended in the RSS guide posted earlier and you're subscribed to feeds which have emojis in the feed, such as certain YouTube channels. This is not a bug with suckless software but rather with libXft that we had to install as a build dependency. There's an upstream patch designed to fix this, but it's been stuck in merge request hell and who knows when it'll actually be merged. On Arch Linux, the patch to libXft can be applied by uninstalling the normal libxft package and installing the AUR package libxft-bgra; on Gentoo, the patch file from that merge request can be placed in your /etc/portage/patches/x11-libs/libXft/ directory (create it and its parents if it doesn't exit), and then libXft can be recompiled with sudo emerge --ask x11-libs/libXft. (On other distributions you may be screwed, I recommend using a different terminal unfortunately.) Then, not only will st not crash, but it will afterwards have full color emoji support if you have emoji fonts installed and configured in fontconfig (in case you wanted that for some reason).

st won't display CJK/foreign fonts, even when manually configured in fontconfig

This is my most recent (6/7/22) problem I've been having with st. While I was able to get it to support emojis, for some reason no matter what I did I could not get st to display other languages consistently. It seems that characters that are not normal monospace width are completely rejected by st, including Asian Chinese-derived languages. Because of this reason, I've actually switched to using Alacritty because fonts from every language just work perfectly by default without any configuration.

dmenu

dmenu is perfectly fine the way it comes, just compile it and you're good to go.

Setting up dwm to be your window manager

Now that we have dwm and the rest of our suckless software compiled and configured, we can start using it. There's two ways we can go here: either a display manager, or just using startx with an edited ~/.xinitrc file. I think a display manager is best if you're not totally sold on dwm so you can try it out, but personally I have never used display managers and don't know how, so that will be for this article on the Arch wiki or similar resources to explain. Instead, I'll go into how to use startx from your login tty to start dwm.

The first thing we need to do is make sure we have xinit installed, which allows us to run startx. Again, like the dependencies for dwm, this should come installed if you did a full install of X on your computer; if not, you will need to install the xinit package from your package manager with sudo emerge --ask x11-apps/xinit or sudo pacman -S xorg-xinit or similar for your distribution. After that is installed, then we will need to edit the ~/.xinitrc file, which is run whenever you run the startx command from your terminal. If it doesn't exist, create it; if it does exist, then take a look at it, and if it doesn't have any lines that start with exec, then you're good. If it does have an exec in it, you might already have a display manager or another desktop environment installed, so I would do further research. Otherwise continue on. (You could also just rename the .xinitrc file to something like backup.xinitrc and create a new one so worst case you can change it back later.)

.xinitrc is really just a shell script, so you can run anything you would normally run in a shell in this script; for example, if you want to change your touchpad to be more sensitive, you can put it in this file, and when you start your desktop environment from a terminal, that fix will be applied every run. We're just worried with putting dwm in there for the moment though.

If you needed to create the file, put the line #!/bin/sh at the top. Then, at the bottom of the file (a few lines down from that line, called a hashbang), place the following code:

#start slstatus and then start dwm
slstatus &
exec dwm

(The first line is just a comment.) This will run slstatus as a background task (that's what the & does) and then closes the script and replaces it with dwm, starting the window manager. Now, save the file, and in your tty, whenever you want to start your desktop, just type the command startx. Make sure you're not root, and don't use sudo! X is not meant to be run as root and it's a major security risk. Assuming you only have a single desktop environment you want to run (such as dwm in this case), this basically is a lightweight way of doing the same thing that a display manager does. When you start your computer, you just log into the tty instead of the display manager, and then the only additional input required is typing startx to start your desktop. I personally prefer this over a display manager.

Using all this crap software you made me install

dwm (and related software) is like vim, and is almost entirely keyboard driven. You can technically click the numbers in the top left of dwm to move between workspaces, but that's about it for mouse functionality. Everything else is done through keyboard shortcuts, and if you're clicking, you're kind of doing it wrong.

The most basic command is to open the program launcher (dmenu) and run a program; this is done with Mod+P - remembering that earlier we set Mod to Super (Windows key), otherwise it defaults to Alt. Then, you can type the name of the program you want to run (its name if you were to run it in a terminal) and once it's the first option, you can press enter and it will launch. If you want to open a terminal, the special keyboard shortcut for that is Mod+Shift+Enter (or you can just use dmenu). To move between workspaces, press Mod+# where # is the number key for the workspace you want to switch to, 1-9. To move focus between different windows in a workspace, either mouse over the one you'd like to select, or press Mod+J or Mod+K (vim keys) to scroll through them. To make a window a master window, select it and press Mod+Enter. To move a window to a different workspace, select it and press Mod+Shift+#. To close a window, use Mod+Shift+C. To close dwm and return to the tty, use Mod+Shift+Q. Note that if your .xinitrc is written wrong, this last shortcut may not work properly and will lock up the tty; if it's written as above, it should work just fine. I would also close all the open programs in dwm first, to be safe.

Something that can happen is occasionally you might press the wrong keys and make the ratio of the master window to the child windows bigger or smaller. This is done by using Mod+H and Mod+L. So, you can just use those shortcuts to fix it back to the way you like it, or change the ratio on-the-fly.

st is literally just as simple as a tty - there's really not much to it. The only keybind that particularly matters is that we added support for scrollback, which can be done using Shift+PgUp and Shift+PgDn. There is also the ability to zoom in and out (i.e. increase font size) by using Ctrl+Shift+PgUp and Ctrl+Shift+PgDn.

And there you go! Now you have the exact desktop that I'm using right now on my ThinkPad T420 running Gentoo!

May 18, 2022