MacOS – Allow the Terminal.app to pass command key based keystrokes to terminal programs

bashmacosterminalx11

I want to allow programs running in the terminal the ability to interpret the keyboard short cut Cmd + S

I have already Remapped the Terminals' default binding of Cmd + S to Ctrl + S via the Keyboard System Preferences in OS X Lion, so this has stopped the Terminal App itself from interpreting that key press, e.g. the save dialog no longer comes up when pressing Cmd + S . However something is still grabbing the key input before any terminal program can.

For example when running bash typing Cmd + S in the X11.app terminal results in:

$s

being printed to the screen.

When I perform the same Cmd + S in the Terminal.app nothing is printed and the OS plays an Error Sound. I am not really sure why the keystroke is not making it to the terminal as expected, how do I allow Cmd + S to pass thru.

For more information on what / why I am trying to do this see question Bind <Cmd-S> in Vim

Best Answer

I have similar desires to yours. I solved it using a system-level modifier-key re-bind.

(Of course, this is a mess; but I find it worth it to maintain my MacVim-trained hybrid of OS-wide controls (save, new, copy, paste) within command-line vims.)

Here's how I did it:

  1. Install Karabiner, which is a very, very powerful program (and an essential part of any poweruser's / programmer's OS X installation, IMHO)
  2. Enable “Command_L to Control_L (only in Terminal)”:

  3. Map -prefixed controls matching whichever MacVim commands you're most used-to in command-line vim. For instance, to support ⌘S,

    noremap  <silent> <C-S>    :update<CR>
    vnoremap <silent> <C-S>    <C-C>:update<CR>
    inoremap <silent> <C-S>    <C-O>:update<CR>
    
  4. Restore expected -sequences for Terminal.app itself, by special-casing them in System Preferences. For instance, if you want ⌘N to still control Terminal tabs, instead of mapping it to vim files, try:

    Now, anything you don't add in this list, will be passed through verbatim to command-line vim (and, of course, any other command-line program you use.)


Caveats

This approach has several further subtle downsides, that I want to hilight for anyone considering taking it (as I do):

  • ⌘␣ will no longer map to the system-wide Spotlight. (I've an open issue on Karabiner, and will come back here once I have a stopgap solution.)
  • For reasons I can't ascertain, even with the ⌃W exclusions set up in System Preferences, the tab-closing behaviour of Terminal.app changes with this setup: ⌘W will close individual tabs in a window; but if the current tab is the last tab, it will not close the window. Instead, it has no effect. I've had to re-train myself to use ⇧⌘W to close the window if there's only one tab left.
  • If you've trained yourself to use ⌃V in MacVim to do block-wise selection, then you'll have to re-train yourself to use the alternative, ⌃Q (or, now, ⌘Q). Additionally, you'll have to add stty -ixon in your shell's startup-script, as ⌃Q is consumed by default to preform an archaic shell-control function (one with no modern usage.)

Alternative ⌘W behaviour

In addition to the above, I decided to further override ⌘W as a universal ‘close this’ key, applicable to multiple applications.

Instead of mapping ⌘W to ‘Close Tab,’ as above; I personally map that to ⇧⌘W (to retain “immediately close this tab” semantics.) Then, in any given program, I map ⌃W to some sort of close-save-quit functionality.

For instance, in my .vimrc:

noremap  <C-W>    :confirm quit<CR>
vnoremap <C-W>    <C-C>:confirm quit<CR>
inoremap <C-W>    <C-O>:confirm quit<CR>

… or my .zshrc:

function close_tab { exit }
zle -N close_tab_widget close_tab
bindkey           '' close_tab_widget
bindkey -M vicmd  '' close_tab_widget

With those two snippets in place, and your Terminal configured to ‘Close if shell exits cleanly,’ a series of ⌘Ws will first quit vim, and then close the shell entirely.

In addition, now additional on-exit functionality of programs can be preserved: for instance, vim will ask before quitting if files are unsaved (just like MacVim); and zsh will ask before quitting if there are background jobs.


Postscript: Not directly related to your question; but you should also check out ‘Ubiquitous Vim Mode’ in Karabiner. It's “a bit much” for me to use everyday, but it's a very cool idea, and surprisingly-well implemented.