tmux with XMonad Bindings
The Reason
I’ve been playing around with tmux
a lot lately and have come to like it
quite a lot for the panes and windows - something I used quite a lot with my
XMonad setup on my old Fujitsu laptop. The panes act as my individual
terminals with which I can write code, read documentation, chat on IRC (using
weechat
or irssi
), play music, etc. In other words, I do quite a lot from
within terminals and being able to split up a terminal into panes (like XMonad
launching tiled terminals) is quite nice. Furthermore, treating the windows
as my workspaces lets me quickly organize myself just like with XMonad.
However, the main issue I have found with tmux is with its keyboard bindings.
Everything in tmux is bound to a prefix (Ctrl-b) followed by a single keystroke
or multiple keystrokes. For instance, splitting a window into two horizontal
panes is the sequence Ctrl-b "
and splitting it horizontally is Ctrl-b %
.
I pride myself in being able to pick up things like this quickly, but my mind
continued to nag me about the need of a prefix as well as the use of keys like
double quotes and percent. I was used to XMonad, where creating a new pane
involved a single Mod-Shift-Enter
and navigating between tiles was a simple
Mod-Tab
. Because of this, I began to look into ways to rebind tmux keys to
be more like XMonad.
The Process
tmux provides multiple ways to rebind keys and perform startup actions. The first is to execute the actions from the terminal:
tmux bind-key d kill-pane
Another option is to perform the task within a running tmux instance by
entering Ctrl-b :
, which enters a command mode for you to enter tmux actions.
Of course, these methods were not what I needed. What I discovered was that
tmux could source a file to get its bindings. You could have .tmux.conf
within your home directory or use
tmux source my_tmux.conf
For me, I began to work with the default .tmux.conf
file. Keybindings were
easy to rebind using bind-key
and unbind-key
. For instance, if I wanted
to bind the space key to change the layout - XMonad uses Mod-Space
by
default - I would use the following:
bind-key Space next-layout
However, a simple bind-key
does not remove the prefix! This means that the
above would actually be Ctrl-b Space
as the combination. Luckily, tmux
provides a way to avoid the prefix when performing actions. The -n
switch
indicates that no prefix should be used.
bind-key -n C-Space next-layout
The above indicates that the series of keystrokes Ctrl-Space
should change
the layout used in tmux, no prefix needed.
The Issue
The issue I discovered was that modifier keys - Control, Shift, Function, Alt -
were not fully supported in tmux. In fact, modifier keys are not fully
supported in a lot of applications. Instead, you see certain keycodes appear
when a modifier key is used in combination with a normal key. If you execute
xmodmap
in your terminal, you should get a list of modifier keys in your
computer. Entering xmodmap -pk
into your terminal yields the actual table
containing the representations of each key without modifiers, with the shift
key, with the mode switch key, with the shift and mode switch keys, with the
alt key, and with the alt and shift keys. If you print this table, you’ll
notice that quite a few keys do not have bindings for shift/mode switch keys.
Furthermore, after looking at tmux’s source, it appears that only certain keys
are checked for modifiers before passing the keystroke to the application
running within tmux. Because of this, I cannot use a setup like
Ctrl-Shift-Return
for creating a new terminal tile using standard tmux.
The Potential Solution
At least, I did not believe that I could. I discovered that tmux provided even more functionality through the ability to not only launch shell programs but also check the return status of said programs!
# Run command if system is Mac OS X
if-shell 'test `uname` == "DARWIN"' <COMMAND> [OPTIONAL COMMAND]
Because of this ability, I thought about having a small program that could be executed to indicate whether modifier keys like control and shift were currently being pressed down. Returning success indicates they were and returning failure indicates they were not.
The challenge appeared when I realized that modifier keys were mostly unable to be tracked in this manner. tmux itself was not at fault for this limitation; so, I had to dig deeper to find out how to retrieve this bindings. I had seen some utilities that could detect shift and control key presses, but they were bound in the X11 system, which I did not want to impose as a restriction for my setup. In fact, my hope was that this could be run very easily without an X11 system. Furthermore, as a new owner of a Macbook Air - Linux will be put on it soon enough - I wanted this to be able to work on OS X as well.
The Mac OS X Solution
Cocoa provides the functionality to directly check if modifier keys are pressed, which is incredibly useful. So, I simply wrote a small Cocoa application that returns success based on the state of modifier keys. The documentation indicates that Mac OS X v10.6+ is needed to use this functionality; so, this means my solution will only work for Snow Leopard or higher (sorry Leopard and Tiger).
You can find the small program bundled with the main project here.
The Linux Solution
This took a little digging before I realized that I needed to access the
keyboard interface directly, rather than accessing information from a
program. This meant accessing /dev/my_keyboard_interface
, which would vary
from computer to computer.
I wrote a small C program to demonstrate this functionality here.
Unfortunately, after joining IBM in January of 2014, I was not able to continue pursuing this project.
The Final Result
Overall, the configuration combined with the modifier keys captured by an external program successfully produced a working replica of XMonad’s key bindings using tmux, giving me a more comfortable layout for moving panes and navigating.
You can find the project here.