macOS’s default window manager leaves much to be
kwm is a highly configurable
tiling window manager. This is a beginner’s guide to installing and configuring
khd, which provides a keybinding interface.
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 is also a daemon, but instead of working
with windows, it manages keybindings and communicates with
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
kwm requires ‘Displays have separate spaces’ enabled.
To activate the daemon, and have it run automatically at login,
$ brew services start kwm
sudo should not be necessary. After
kwm starts up, your workspace
should be tiled automatically. A restart may be necessary in some cases.
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
kwmrc file and take a look. Each configuration begins with the
prefix followed by a number of arguments. A detailed description of every
possible option can be found on
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
To add keybindings to
kwm operations, you’ll need to install and configure
$ brew install koekeishiya/khd/khd $ brew services start khd
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 ...
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
We will begin with three modes: default, switcher, and swap.
default mode is meant to be the resting state of our system. In
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
default mode should have a single keybinding to enter a different mode with
more functionality. We’ll call it
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.
switcher mode, we will have the option to enter
swap mode, we can make ‘edits’ to our window layout by swapping the
positions of windows and rotating the entire workspace. This is like
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.
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
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.
default mode, we will switch into
switcher mode by hitting
the prefix key (keycode
0x50 in my case).
switcher mode, we will define a single press of
s to get us to
Finally, if we are either in
switcher, a single press of the prefix
should take us back to
default. To make this work, the configuration looks
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 (
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
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).
switcher mode, pressing s gets us to
swap mode (line 4). In
swap mode, pressing caps gets us back to
default mode (line 3).
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.
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 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
kwm represents window tilings internally–as a binary-space-paritioning
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:
We can bind these scripts to quick keybindings in
switcher mode using
Hyper key (untested)
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
khd -p to simulate our desired keypress. Note that I have not yet
tested this method, but it seems plausible.
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
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.