Copying to clipboard from tmux and Vim using OSC 52

Suraj N. Kurapati

  1. Problem
    1. Solution
      1. OSC 52 (the new way)
        1. X11 forwarding (the old way)


        I switched to an Acer C720 chromebook as my primary computer recently. However, its X11-based Aura desktop environment lacks X11 clipboard integration, which prevents me from using X11 forwarding to run the xsel(1) program as a means of copying and pasting text between my system clipboard and programs running remotely in SSH. Now I had to find a different solution for my copy/paste needs.


        In my search, I came upon the OSC 52 escape sequence, which causes the receiving terminal to copy text into the system clipboard. However, the current stable tmux(1) version 1.9a only allows 180 bytes of text to be copied in this way, so I reported this issue to the tmux-users mailing list where developer Nicholas Marriott quickly provided a patch to fix it.

        Armed with a patched tmux(1), I distilled my working example of the OSC 52 escape sequence into the yank(1) shell script and integrated it into my tmux and vim configuration. Now I can readily copy text from tmux and vim running in any terminal session, whether local or remote or even nested therein, all without X11 forwarding and having to keep environment variables like $DISPLAY and $XAUTHORITY up to date!

        Finally, to paste text from the system clipboard into a terminal session, I simply use the Control-Shift-V shortcut of the Secure Shell terminal emulator. Thus, what began as a problem led to the discovery of a superior solution! :-)

        OSC 52 (the new way)

        In ~/.tmux.conf file:

        # transfer copied text to attached terminal with yank:
        bind-key -t vi-copy y copy-pipe 'yank > #{pane_tty}'
        # transfer copied text to attached terminal with yank:
        bind-key -n M-y run-shell 'tmux save-buffer - | yank > #{pane_tty}'
        # transfer previously copied text (chosen from a menu) to attached terminal:
        bind-key -n M-Y choose-buffer 'run-shell "tmux save-buffer -b \"%%\" - | yank > #{pane_tty}"'

        In ~/.vimrc file:

        " copy the current text selection to the system clipboard
        if has('gui_running') || has('nvim') && exists('$DISPLAY')
          noremap <Leader>y "+y
          " copy to attached terminal using the yank(1) script:
          noremap <silent> <Leader>y y:call system('yank > /dev/tty', @0)<Return>

        In ~/bin/yank file, which is also available on GitHub:

        # Usage: yank [FILE...]
        # Copies the contents of the given files (or stdin if no files are given) to
        # the terminal that runs this program.  If this program is run inside tmux(1),
        # then it also copies the given contents into tmux's current clipboard buffer.
        # If this program is run inside X11, then it also copies to the X11 clipboard.
        # This is achieved by writing an OSC 52 escape sequence to the said terminal.
        # The maximum length of an OSC 52 escape sequence is 100_000 bytes, of which
        # 7 bytes are occupied by a "\033]52;c;" header, 1 byte by a "\a" footer, and
        # 99_992 bytes by the base64-encoded result of 74_994 bytes of copyable text.
        # In other words, this program can only copy up to 74_994 bytes of its input.
        # However, in such cases, this program tries to bypass the input length limit
        # by copying directly to the X11 clipboard if a $DISPLAY server is available;
        # otherwise, it emits a warning (on stderr) about the number of bytes dropped.
        # See for the 4*ceil(n/3) length formula.
        # See for copy limits.
        # See for DCS in tmux.
        # Written in 2014 by Suraj N. Kurapati and documented at
        buf=$( cat "$@" )
        # copy to tmux if possible
        test -n "$TMUX" && tmux set-buffer "$buf"
        # copy to X11 if possible...
        test -n "$DISPLAY" && printf %s "$buf" | { xsel -ib || xclip -sel c ;} && exit
        # ...otherwise copy to terminal
        len=$( printf %s "$buf" | wc -c ) max=74994
        test $len -gt $max && echo "$0: input is $(( len - max )) bytes too long" >&2
        esc="\033]52;c;$( printf %s "$buf" | head -c $max | base64 | tr -d '\r\n' )\a"
        test -n "$TMUX" && esc="\033Ptmux;\033$esc\033\\"
        printf "$esc"

        X11 forwarding (the old way)

        In ~/.tmux.conf file:

        # transfer copied text to X primary selection
        bind-key -n M-y run-shell 'tmux save-buffer - | xsel -p -i'
        # transfer copied text to X clipboard selection
        bind-key -n M-Y run-shell 'tmux save-buffer - | xsel -b -i'
        # paste X primary selection
        bind-key -n M-p run-shell 'xsel -p -o | tmux load-buffer - \; paste-buffer \; delete-buffer'
        # paste X clipboard selection
        bind-key -n M-P run-shell 'xsel -b -o | tmux load-buffer - \; paste-buffer \; delete-buffer'

        In ~/.vimrc file:

        " copy to primary selection
        noremap <Leader>y "*y
        " copy to clipboard selection
        noremap <Leader>Y "+y
        " paste from primary selection
        noremap <Leader>p "*p
        " paste from clipboard selection
        noremap <Leader>P "+p