Setting up DWM

My previous article on this matter was rather lazy, so I'm going to write it again.

It's a pretty long article, so here's a table of contents:

  1. Installation
    1. Preamble
    2. Acquiring the source code
    3. Building
  2. Setup
  3. First run
  4. Getting function keys to work
    1. Brightness
    2. Volume
    3. Media keys
      1. MPD
      2. Non-MPD
    4. Chaining multiple commands to a single key
  5. Autostart
    1. Display manager instructions
    2. Non-display manager instructions
  6. Starting DWM
    1. Display manager instructions
    2. Non-display manager instructions
  7. DBUS
  8. Status Bar
  9. Miscellaneous
    1. Locking your screen
    2. Wallpapers
    3. Compositing/screen tearing
    4. Swapping caps-lock and control
  10. Do you need to patch DWM?
  11. Addendum

Installation

Preamble

There are many better guides on how to do this out on the internet, but the procedure is generally the same. The only thing you're going to have to do some research on is the dependencies. Each Linux (or BSD) distribution is going to have different package names for each dependency.

On Void Linux: sudo xbps-install base-devel libX11-devel libXft-devel libXinerama-devel freetype-devel fontconfig-devel

Acquiring the source code

Next, create a directory where you'll store DWMs source code. Clone the repositories there: git clone https://git.suckless.org/dwm

dmenu or st aren't requirements, so install those only if you want to.

Building

It's pretty simple. Change your current directory to wherever DWM's source code is. In a terminal, type make. Then, sudo make clean install. On any subsequent compilations, you can just use sudo make install.

You'll need to build DWM whenever you want to apply any tweaks you've made.

Setup

You've got DWM installed, but before we start it, let's make a quick change to the config file.

Open up config.h in a text editor. Search for termcmd. This will change the terminal that opens up by default. Change it to whatever your preferred terminal emulator is. I have it set as xfce4-terminal.

static const char *termcmd[] = { "xfce4-terminal", NULL };

You could, if you wanted to, throw any extra commands in there that you'd like to run whenever you open your terminal. We'll get to writing these kinds of commands in a bit.

While we're here, we might as well change the default modifier key. Out of the box, it's the alt key, which may interfere with other apps. We'll change that to the Windows/command key.

Still in config.h, search for MODKEY.

#define MODKEY Mod4Mask

Change whatever's in there to Mod4Mask. No need to patch or anything.

First run

You'll see an almost blank screen, save for a bar at the top. The main thing you're going to want to do now is open a terminal. You can do this through MODKEY+Shift+Enter.

We'll start tweaking our configuration file now.

Getting function keys to work

Nothing difficult at all!

At the top of config.h, add this line: #include <X11/XF86keysym.h>

The main function keys I need are for brightness, volume and media control. We'll set that up now.

Keybindings in DWM work as such: you have to define a command first (this happens separately), then you bind that command to a key.

Brightness

Here's how we'll do brightness control. First, you'll have to install a package. It generally goes by the name of brightnessctl. If you happen to get permission errors when running the program, you'll need to add your current user to the video group. We'll do that as such: usermod -aG video $USER. Use root privilages if needed. You may need to reboot.

Pop these two lines into your config.h.

static const char *brighter[] = { "brightnessctl", "set", "10%+", NULL, };
static const char *dimmer[] = { "brightnessctl", "set", "10%-", NULL, };

You can change the name of the commands if you'd like.

Now we'll bind the relevant keys to these commands. You'll find a section in your configuration file like this:

screenshot of the relevant section of the configuration file

We'll define our own keybindings at the bottom of this list, underneath this area:

screenshot of the area where we'll add keybindings

Pop these two lines down:

{ 0, XF86XK_MonBrightnessDown, spawn, { .v = dimmer } },
{ 0, XF86XK_MonBrightnessUp, spawn, {.v = brighter} },

The leading 0 is present so that we don't need a modifier key to run these commands. Make sure you don't forget the comma at the end!

Volume

I can't say for sure if this works with Pipewire. I happen to use Pulseaudio on my machine, and I do know that these commands work here. Under the commands section of your configuration file:

static const char *up_vol[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK", "+5%", NULL };
static const char *down_vol[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%", NULL };
static const char *mute_vol[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "toggle", NULL };

Then, wherever your keybindings are:

{ 0, XF86XK_AudioMute, spawn, { .v = mute_vol } },
{ 0, XF86XK_AudioLowerVolume, spawn, { .v = down_vol } },
{ 0, XF86XK_AudioRaiseVolume, spawn, { .v = up_vol } },

Media keys

There are a few ways you could go about this.

Are you a user of MPD?

If you don't know what MPD is, don't worry! Move on to the next section.

If you are, you have two options. If you're like me and just want your media keys to do nothing but play/pause audio from MPD, then we'll do the following.

Firstly, install mpc. The commands we'll use are:

static const char *playpause[] = { "mpc", "toggle", NULL };
static const char *nextsong[] = { "mpc", "next", NULL };
static const char *prevsong[] = { "mpc", "prev", NULL };

Then:

{ 0, XF86XK_AudioPlay, spawn, { .v = playpause } },
{ 0, XF86XK_AudioPrev, spawn, { .v = prevsong } },
{ 0, XF86XK_AudioNext, spawn, { .v = nextsong } },

Alternatively, if you'd like to be able to control all of your media through the media keys, including MPD, then you'll need a package generally named something along the lines of mpDris. Install that, then we'll set it up in the next section.

Are you not a user of MPD?

Install a package, generally named playerctl. We'll use the following commands:

static const char *playpause[] = { "playerctl", "play-pause", NULL };
static const char *nextsong[] = { "playerctl", "next", NULL };
static const char *prevsong[] = { "playerctl", "previous", NULL };

Then bind these commands to the appropriate keys, as shown here.

That's function keys set up.

If you want a list of the names of all of the function keys, XF86 keyboard symbols.

If you'd like to figure out the name of a specific key, install xev. Run that from a terminal, and check the output. You're going to have to do some digging to find the name of the currently pressed key in the output.

screenshot of output of the xev program

Be careful though! Moving your mouse is also going to register in this program.

Chaining commands to a single key

If you want to chain multiple commands to a single key, you can simply define individual commands for the things you'd like to do, then bind both of those commands to the same key. Here's an example:

{ 0, XF86XK_Eject, spawn, { .v = zero_brightness } },
{ 0, XF86XK_Eject, spawn, { .v = lock } },

The code above will lock the screen and set the backlight to zero

Autostart

Do you use a display manager?

Plop your startup instructions in ~/.xprofile. Keep reading!

Do you not use a display manager?

Plop your startup instructions in ~/.xinitrc.

Plopping your startup instructions

It's the same procedure for both. You can literally just list a bunch of shell commands. Here's mine:

# Autostart Section
sct 5500
/usr/bin/setxkbpmap -option "ctrl:swapcaps"
nitrogen --restore

Ensure your .xprofile or .xinitrc is executable!

Starting DWM

Do you use a display manager?

You'll need to create a file. Let's call it dwm.desktop. Put the following content in it:

[Desktop Entry]
Encoding=UTF-8
Name=DWM
Comment=Dynamic Window Manager
Exec=/usr/locla/bin/dwm
Type=XSession

Place this file in /usr/share/xsessions. Then select DWM from your display manager.

Do you not use a display manager?

We'll put this in ~/.xinitrc:

exec /usr/local/bin/dwm

Then, whenever you login to your TTY, simply type startx. Beware that commands placed after this line will not run, think of this as the EOF of your .xinitrc.

DBUS

If you're having issues with DBUS on DWM specifically, such as apps not starting or MRPIS not working, then there's probably something to do with DBUS.

I had this issue, and this is how I solved it. Since I don't use a display manager, I just put the following into ~/.xinitrc:

exec dbus-launch --exit-with-session /usr/local/bin/dwm

I suspect you could do the same in your dwm.desktop file, but I am not sure.

Status bar

I'm going to ask you something: do you really need to use Polybar? I'm also going to answer this question for you: no you don't! Use the built-in status bar! It can go a long way with some creative formatting.

Firstly, install xsetroot.

We'll set up a loop in your .xinitrc or .xprofile. This is my loop as of now:

while true; do
	xsetroot -name "$( playerctl metadata --format '{{ artist }} - {{ album }} - {{ title }} | $( date + '%F %R:%S' )"
done &

As you can see, this is just a bunch of shell commands, with some manual formatting. This code produces a status bar as such:

screenshot of my status bar

Essentially, you'll be using xsetroot -name and then insert your commands inside a single string. Wrap each command with $(). For example, $(date). String together multiple commands like these, be creative with your formatting, and you'll get a status bar as good as Polybar. You can even have icons by using unicode iconography!

Miscellaneous

Locking your screen

There's a bajillion ways you could do this, but I do it through i3lock. The command I use to lock my screen is as such: i3lock --color=000000.

Wallpapers

You can install nitrogen. Point it to wherever your wallpapers are, and you can set them from there.

Compositing/screen tearing

I'm gonna hand you over to the internet here, I do not have much experience here.

Swapping caps-lock and control

This isn't remotely related to DWM but it's such a nice quality of life improvement, especially with window managers.

You can do this through this command: /usr/bin/setxkbmap -option "ctrl:swapcaps". Now, your caps lock key will act as control, and control will act as caps lock.

Do you need to patch DWM?

Resounding no unless there's actually something you need. Patching DWM is a very messy process, so save yourself the pain by not doing that. If you find yourself needing many patches (perfectly reasonable), you're probably better off using another window manager. That'll give you a much better experience! Window managers such as Qtile or i3 are also great!

Addendum

I've tried to cover most of the things I dealt with when I started with DWM, but inevitably, there are going to be missing things. The internet is your best friend! The ArchWiki too!

Another nice guide to using DWM: Dave's Visual Guide to DWM.

screenshot of my DWM desktop
Enjoy!

This article was written on 26/05/2024. If you have any thoughts, feel free to send me an email with them. Have a nice day!