UPDATE 17 March 2013: I am placing this post into the public domain so people are free to incorporate it into their existing files which may be in the public domain already.

UPDATE 16 July 2012: I have updated this post to reflect my current keymaps, which I settled on after writing the original post.

As I’ve said before, I’m a huge fan of iTerm2. I am also a huge fan of tmux, the terminal multiplexor. If you’re not familiar with tmux, it’s conceptually the same thing as GNU screen, with some differences, and (in my opinion) some advantages. For example:

  • In screen, each instance is its own completely separate process. With tmux, the first time you run it, you start a server, but subsequent runs will instantiate clients that connect to that existing server. Why does that matter? Because it means you can lop a window off of one tmux session and glue it onto another. It also means you can view the same session from multiple clients simultaneously (say you left tmux attached at the office, you can attach to that same session at home without disrupting the session you left attached at work). You can even share a session with someone else. All of that’s possible because it’s all going on in the same server process.

  • tmux supports multiple panes within a window, where screen supports only windows.

  • If you don’t set a window title, tmux will auto-set it for you based on what you run in that window. So it might start as “bash” and change automatically to “vim” or “top” or “tail” depending on what you run inside. That is pretty handy to tell what’s going on in what window even if you don’t take the time to set a name for it.

It so happens that the authors of iTerm2 are tmux users as well, and they have integrated a tmux mode into iTerm2. It’s an early stage thing, so you have to forgive it, but at this point it still has some drawbacks. I am not going to talk about that integration much in this post, but I wanted to point out that it exists, that I know about it, and explain why I don’t use it.

  1. You can only connect iTerm2 to one iTerm2-aware tmux session at a time.

  2. They communicate via a special protocol. In practical terms, this means you “spend” a window for that channel. It sort of sits around lingering. To be fair, they added an option to auto-hide it on connect so it goes away by itself.

  3. Because this is still under development, you currently have to run the right version of iTerm2 with the right version of patched tmux. That can be a headache if your server side is used by multiple people. It also means an upgrade to iTerm2 sends you off to recompile tmux before you can use it in integrated mode again.

For these reasons, I have set aside the iTerm2 tmux integration and I’m just using regular tmux again. I’ll check it out again someday after it has matured (and preferably been built into tmux mainline so I don’t have to patch a copy myself).

But after using the integration, I found I was longing for easy hotkeys to navigate around my windows and panes in tmux. (Using the integrated mode, you use the iTerm-native hotkeys because it treats the tmux windows and panes as native iTerm tabs and panes).

I was frustrated by this because in tmux you use a prefix key for everything, so going to the next window means using Ctrl-B n and going to another pane might be Ctrl-B H. You can remap a lot of things, but it’s never as easy as native keys like Shift-Cmd-Right or similar that we are used to using on Macs and PCs.

At first, I played with the tmux mouse mode. It’s actually pretty nice. Assuming you send mouse events through to the server, tmux will recognize them (if you turn on the appropriate options), allowing you to select windows and panes using mouse clicks. You can even resize panes. Unfortunately, that comes at a cost: it breaks using normal mouse selection to copy from iTerm2 window into other programs on your Mac. Not good.

Then, I found “Send Hex Code.”

Angels rejoiced. Benjamins fell from the sky. My doctor told me to eat more ice cream.

Send Hex Code is just an unassuming option in a pulldown in the iTerm key mapping preferences. But when I saw it, I knew it was the solution to my problem. The thing is, you can represent any ASCII character as a hex value, and sending a sequence of ASCII characters is exactly what I needed to do.

I looked up some ASCII and ANSI hex values.

My first goal was to navigate between windows. The default maps for that are:

^B n        next window
^B p        previous window

Let’s rewrite those as hex bytes.

0x02 0x6E   next window
0x02 0x70   previous window

Next goal: navigate between panes in all four directions. This was stickier because it involves arrow keys, but! arrow keys are just escape sequences. So a few more hex bytes are all we need.

^B <arrow>      move to pane in <arrow> direction

The ANSI arrow sequence is <Esc>[<direction> where direction is one of [A, B, C, D] (mapped to [up, down, right, left]). Rewritten as hex:

0x02 0x1B 0x5B 0x41     move to pane above
0x02 0x1B 0x5B 0x42     move to pane below
0x02 0x1B 0x5B 0x43     move to pane at right
0x02 0x1B 0x5B 0x44     move to pane at left

Lastly, I wanted to be able to resize any given pane in any direction using key bindings. I added some bindings to ~/.tmux.conf (borrowing directional keys from my favorite editor, Vim).

bind-key J resize-pane -D
bind-key K resize-pane -U
bind-key H resize-pane -L
bind-key L resize-pane -R

Those bindings rewritten as hex values:

0x02 0x4B   resize upward
0x02 0x4A   resize downward
0x02 0x48   resize leftward
0x02 0x4C   resize rightward

Armed with all of my hex sequences, I headed to the iTerm2 preferences. Under the “Keys” section, you can add new items to the global shortcut list. (You can also do this in a profile’s key settings if you have a reason to not want bindings to be available to all profiles.) I added the below sequences, binding them to hotkeys.

Mac hotkey      Hex sequence            Purpose

Cmd-Left        0x02 0x1B 0x5B 0x44     Move to pane at left
Cmd-Down        0x02 0x1B 0x5B 0x42     Move to pane below
Cmd-Up          0x02 0x1B 0x5B 0x41     Move to pane above
Cmd-Right       0x02 0x1B 0x5B 0x43     Move to pane at right

Cmd-Opt-Left    0x02 0x48               Resize leftward
Cmd-Opt-Down    0x02 0x4A               Resize downward
Cmd-Opt-Up      0x02 0x4B               Resize upward
Cmd-Opt-Right   0x02 0x4C               Resize rightward

Ctrl-Cmd-Left   0x02 0x70               Move to window at left
Ctrl-Cmd-Right  0x02 0x6E               Move to window at right

But I am a Vim user. h,j,k,l have been mapped into my fingers for more than a decade, and they are on the main keyboard, so I don’t need to move my hand to the side. Why not map those as well?

Mac hotkey      Hex sequence            Purpose

Cmd-H           0x02 0x1B 0x5B 0x44     Move to pane at left
Cmd-J           0x02 0x1B 0x5B 0x42     Move to pane below
Cmd-K           0x02 0x1B 0x5B 0x41     Move to pane above
Cmd-L           0x02 0x1B 0x5B 0x43     Move to pane at right

Cmd-Opt-H       0x02 0x48               Resize leftward
Cmd-Opt-J       0x02 0x4A               Resize downward
Cmd-Opt-K       0x02 0x4B               Resize upward
Cmd-Opt-L       0x02 0x4C               Resize rightward

Ctrl-Cmd-H      0x02 0x70               Move to window at left
Ctrl-Cmd-L      0x02 0x6E               Move to window at right

The panel to enter each key binding into looks like this.


And this is what my final key map looks like.



Now I can move between tmux windows with Ctrl-Cmd-{direction}, move among panes with Cmd-{direction}, and resize with Cmd-Opt-{direction}. It’s really nice, and I can use Vim keys (H,J,K,L) or arrows.

(Yes, I am aware that this overrides certain keymaps. You will lose the “Hide” hotkey, Cmd-H, but I never use that so I didn’t care. iTerm also maps Cmd-{left,right} to navigate between tabs, but Cmd-{digit} and Shift-Cmd-{left,right} will still work.)

Lastly, a few finishing touches for my tmux.conf. The colors I use here assume Solarized Dark for the iTerm color scheme. They may look awful with another color scheme (or they may look fine).

# Watch background windows for activity
setw -g monitor-activity on

# Make the active pane more visible
set -g pane-border-bg default
set -g pane-border-fg colour245
set -g pane-active-border-bg default
set -g pane-active-border-fg colour46

# Make the active window's name stand out
setw -g window-status-current-fg brightwhite
setw -g window-status-current-bg black

# Use color to indicate activity in a background window
# (Note this is inverted, fg means bg and vice versa.)
setw -g window-status-activity-fg white
setw -g window-status-activity-bg brightred

You can always find my current .tmux.conf file on GitHub.