kwm

macOS’s default window manager leaves much to be desired. kwm is a highly configurable tiling window manager. This is a beginner’s guide to installing and configuring kwm and khd, which provides a keybinding interface.

Architecture

kwm runs a daemon (i.e. background process) that detects windows and performs actions on them. Actions include things such as focusing, moving, resizing, and swapping. Since it is continuously running, kwm maintains an internal representation of your window positioning based off a set of rules.

This differs from other styles of window management, in that if you manually modify a window (e.g. by dragging a corner or side), kwm will resist your change and resize it to what it thinks is its correct position. In most cases, kwm will tile the windows on each workspace to fill the screen.

Another thing that kwm does differently is keybindings. Unlike other macOS window managers, it doesn’t come with a set of keybindings. kwm reads from a set of rules and tiles windows automatically. You do not have to tell it to split a screen between two windows, or maximize a certain window. It just goes ahead and does it on its own.

Once the workspace is managed, however, you would probably like to perform actions such as moving focus, swapping windows, and switching spaces. That is the purpose of khd. Like kwm, khd is also a daemon, but instead of working with windows, it manages keybindings and communicates with kwm.

Installing kwm

Though kwm used to have a more involved installation process, the authors have now provided Homebrew port which greatly simplifies the process.

$ brew install koekeishiya/kwm/kwm

Note that kwm requires ‘Displays have separate spaces’ enabled.

To activate the daemon, and have it run automatically at login,

$ brew services start kwm

Note that sudo should not be necessary. After kwm starts up, your workspace should be tiled automatically. A restart may be necessary in some cases.

kwm configuration

To get started, copy the example configuration into your home directory. You can see where the example file is located by running brew info kwm.

$ brew info kwm

...

Copy the example config from /usr/local/Cellar/kwm/4.0.2/kwmrc into your home directory.
  mkdir -p ~/.kwm
  cp /usr/local/Cellar/kwm/4.0.2/kwmrc ~/.kwm/kwmrc

Open the kwmrc file and take a look. Each configuration begins with the kwmc prefix followed by a number of arguments. A detailed description of every possible option can be found on the wiki.

I’ve found that the defaults are generally pretty sane. Out of personal preference, I’ve chosen to disable focus-follows-mouse and blacklisted a few applications that I did not want to be tiled (e.g. Photoshop, Archive Utility, Finder, and System Preferences). Additionally, I’ve set the default focus color to transparent (0x00000000), which I’ll describe soon. Note that the colors are in 0xAARRBBGG format.

Adding keybindings

To add keybindings to kwm operations, you’ll need to install and configure khd.

$ brew install koekeishiya/khd/khd
$ brew services start khd

Similar to kwm, we use brew services to manage the khd daemon. I needed to restart until khd was working, but your results may vary. One way to check if the desired processes are running is to run

$ ps -ax | grep khd
45031 ??         0:01.23 /usr/local/Cellar/khd/1.1.0/bin/khd
...

If khd is up, you should see an executable in /usr/local/Cellar/khd show up.

Introduction to modal keybindings

khd is a modal-hotkey daemon. Modal means that at any time, you may be in any one of several modes, and the actions of each keybinding is dependent on the current mode. If you use vim, you may already be familiar with modal keybindings. Otherwise, the concept may be fairly new.

First, we’ll lay out the general idea of what we want our keybindings to accomplish, then we’ll walk through how to implement our system in khd.

Our ‘modes’

We will begin with three modes: default, switcher, and swap.

default

The default mode is meant to be the resting state of our system. In default mode, pressing keys shouldn’t interfere with the applications we’re using. If you are a vim user, we are aiming for something akin to normal mode (no editing capability).

default mode should have a single keybinding to enter a different mode with more functionality. We’ll call it switcher.

switcher

We’ll use switcher mode to do a lot of things. First, we want to be able to switch focus between windows. We’ll also switch focus between spaces (i.e. virtual desktops) and throw windows from one space to another. At the end, we’ll add a few extra functionalities such as opening new terminal windows.

Finally, from switcher mode, we will have the option to enter swap mode.

swap

In swap mode, we can make ‘edits’ to our window layout by swapping the positions of windows and rotating the entire workspace. This is like vim’s insert mode.

Define our modes

So, inside of a file named .khdrc, which should be located in your home directory, we first define our three modes, and assign them colors. The colors will be displayed by the border around focused windows.

# Enable kwm compatibility mode
khd kwm on

# Set mode colors
khd mode default  color 0x00000000
khd mode switcher color 0xddbdd322
khd mode swap     color 0xdd458588

Choose a prefix key

To switch from default into our other modes, we’ll need a prefix. Ideally, a prefix should be quick to type. I’ve remapped the caps key to trigger f19, which I’ll be using as my prefix. This can be done easily using Karabiner Elements which is available for macOS Sierra (f19 is keycode 80, or 0x50).

Define mode switching

Now, we’ll define a way to switch between modes. There is a lot of freedom in how to do this, so definitely use what you feel is the most natural.

Starting from default mode, we will switch into switcher mode by hitting the prefix key (keycode 0x50 in my case).

In switcher mode, we will define a single press of s to get us to swap mode.

Finally, if we are either in swap or switcher, a single press of the prefix should take us back to default. To make this work, the configuration looks like

- 0x50             :   khd -e "mode activate switcher"
switcher - 0x50    :   khd -e "mode activate default"
swap     - 0x50    :   khd -e "mode activate default"
switcher - s       :   khd -e "mode activate swap"

We can see that the syntax of a keybind is <keysym> : <command> where <keysym> is composed of the mode name, followed by a -, and then a literal (s), or a keycode (0x50).

To test out our mode configuration, we need to reload .khdrc. The easiest way to do this is

$ brew services restart khd

So we can walk through how this configuration captures the behavior we desire. Assuming we start in default mode, a single press of caps (which triggers 0x50) brings us to switcher mode. If we want to leave, another press of caps takes us back to default mode (this logic is captured on the second line).

In switcher mode, pressing s gets us to swap mode (line 4). In swap mode, pressing caps gets us back to default mode (line 3).

switcher functionality

Now, we are ready to actually add some functional keybindings. The commands triggered by these keybindings are all prefixed with kwmc, which is a program that can interact with kwm, the actual window manager. You can type these commands individually into a terminal, and observe the expected behavior.

# Switch focus
switcher - h             :   kwmc window -f west
switcher - l             :   kwmc window -f east
switcher - j             :   kwmc window -f south
switcher - k             :   kwmc window -f north

# Switch to space n
switcher - 1             :   kwmc space -fExperimental 1
switcher - 2             :   kwmc space -fExperimental 2
switcher - 3             :   kwmc space -fExperimental 3
switcher - 4             :   kwmc space -fExperimental 4
switcher - 5             :   kwmc space -fExperimental 5
switcher - 6             :   kwmc space -fExperimental 6
switcher - 7             :   kwmc space -fExperimental 7
switcher - 8             :   kwmc space -fExperimental 8
switcher - 9             :   kwmc space -fExperimental 9

# Throw to left/right space
switcher + shift - left  :   kwmc window -m space left;\
                             kwmc space -fExperimental left
switcher + shift - right :   kwmc window -m space right;\
                             kwmc space -fExperimental right

So in switcher mode, pressing h will switch focus to the left neighbor of the current window. Pressing 3 switches to the third space, and shift + left throws the currently focused window left one space.

swap functionality

# Swap windows
swap - h                :   kwmc window -s west
swap - j                :   kwmc window -s south
swap - k                :   kwmc window -s north
swap - l                :   kwmc window -s east

# Rotate
swap - r                :   kwmc tree rotate 90

In swap mode, pressing h swaps the currently focused window with its neighbor to the left. Pressing r performs a rotation of all windows on the workspace. The name of the command, tree rotate comes from the way kwm represents window tilings internally–as a binary-space-paritioning tree.

New Finder/iTerm windows

A common action that you may want to perform is opening a new Finder or iTerm window. First, consider the following two scripts:

# ~/bin/iterm.scpt
tell application "iTerm2"
  create window with default profile
end tell
# ~/bin/finder.scpt
tell application "Finder" to make new Finder window

We can bind these scripts to quick keybindings in switcher mode using

switcher - return        :   osascript ~/bin/iterm.scpt
switcher - n             :   osascript ~/bin/finder.scpt

Hyper key (untested)

As of the writing of this article, the typical way of using the caps key to trigger cmd + alt + ctrl + shift is not yet possible because Seil and Karabiner are not released for macOS Sierra.

As mentioned before, Karabiner Elements can still be used on macOS Sierra to rebind caps to f19. Then, the following configuration in .khdrc may be a possible way to trigger our hyper key

- 0x50   :   khd -p "cmd + alt + ctrl + shift"

which uses khd -p to simulate our desired keypress. Note that I have not yet tested this method, but it seems plausible.

Wrap up

Hopefully this article was a helpful introduction to the world of macOS window managers. Using a window manager may not be for everyone, but the ones who do rarely look back. My full configuration of kwm can be found in my dotfiles.

kwm is just one of many solutions, and I encourage you to find whatever system works for you. It still has a few kinks (such as occaisional window oscillations) but mostly feels stable. It’s greatest strength, configuration, may be its greatest weakness, especially for users who don’t need or don’t want the level of customization it provides.

But for the right person, kwm is a refreshing blend of functionality, configurability, power, and elegance.