Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

djmitche
djmitche

Posted on

     

Clipboards, Terminals, and Linux

I've recently switched toNeovim, and with it begun using the terminal mouse support. But, this has the side-effect that I can't just click-and-drag to select text in the terminal anymore -- Neovim controls that as well.

Which leads me to clipboards. Linux has two of them! Adding to the interest, I typically use Neovim remotely, via an SSH connection to aTmux session. And on my Linux system, I useurxvt as my terminal program. All of these are very UNIX-y tools, and somehow they all need to play nicely together.

With this sort of challenge, typically the best approach is to learn as much as possible about the different parts, do some experimentation to see how they work in practice, and then try to stitch together a working solution. Sometimes Reddit posts or other information you come across can be useful, but as often as not it's wrong or outdated, so using it without understanding it might just make things more confusing.

In the interest of sharing, here's some of what I've learned.

Security

A brief word about security: clipboards often have passwords in them, especially if you use an external password manager like LastPass. So it's worth thinking carefully about what un-trusted software might be able to read from your clipboard.

Depending on what you do with the information in this post, you might enableany binary you run to access your clipboard fromany host you SSH to. Maybe that's OK in your situation, but it is worth thinking about.

X Clipboards

X Windows has lots of "selections", but the two we'll be concerned with are PRIMARY and CLIPBOARD. Most times you select some text, that's automatically moved to the PRIMARY selection. When you specifically request copying a value (i.e., control-C), the value is put into the CLIPBOARD selection. Note that this means selecting some text and pressing Control-C will usually put that text inboth selections!

The commonxclip utility can be used to get or set clipboards. And, there's a tiny little utility script calledclipnotify that I found really useful for debugging this. I cloned and built it, then ran it in a loop:

./clipnotify-l |whilereadx;doclearforsinprimary clipboard;doecho==$s    xclip-o-selection$sechodonedone
Enter fullscreen modeExit fullscreen mode

I ran this in a dedicated terminal so I could easily see what was in the X selections at all times.

OSC-52

OSC-52 is how information about clipboards is transmitted over a terminal. This is anANSI terminal escape, so a bunch of bytes beginning with ESC that mean something to a terminal emulator. There are a bunch of OSC codes, but 52 is the one that handles clipboards. Documentation is a little hard to find, so I'll summarize how it works here.

Set Clipboard

Sending the sequence<ESC>]52;<board>;<content><BEL> to the terminal sets clipboard<board> to<content>. Here<ESC> is 0x1b and<BEL> is0x07, sometimes written\e and\a, respectively. The<content> is base64-encoded. The<board> can bec (clipboard) orp (primary) on Linux, but things like MacOS only supportc.

You can experiment with this withprintf in the shell:

printf$'\e]52;c;%s\a'"$(echofoobar |base64)"
Enter fullscreen modeExit fullscreen mode

This will put "foobar" in your clipboard.

Get Clipboard

Sending the sequence<ESC>52;<board>;?<BEL> to the terminal will "query" the value of<board>. The terminal responds with a sequence like the "set clipboard' above, containing the content of the requested board.

printf$'\e]52;p;?\a';sleep1;echo^[]52;;Zm9vYmFyCg==^G
Enter fullscreen modeExit fullscreen mode

This makes a bit of a mess of the terminal, but you can see the response, and that base64 decodes to the value in the PRIMARY selection.

Advertising Support for OSC-52

Terminals advertise their support for functionality via some really arcane symbols. The particular piece that indicates clipboards are supported isMs=\E]52%p1%s;%p2%s\007. This is typically found in the "terminfo" database, keyed by the terminal name, which an application finds in$TERM:

echo$TERMrxvt-unicode-256color
Enter fullscreen modeExit fullscreen mode

Bracketed Paste Mode

While in principle "paste" into a terminal application can just pretend the pasted data was typed on a keyboard, in practice this can go very badly. For example, if pasting into Vim when it's not in insert mode, the result is usually a mess. Even in insert mode, if autoindent is enabled when you are pasting code, the indentation is not what's expected. Vim has apaste option for this, but in fact there's a better way:bracketed paste mode.

An application using the terminal "sets" this mode, after which the terminal will prefix pasted content with<ESC>[200~ and suffix it with<ESC>]200~.

You can see this with a quick little Python program:

importsyssys.stdout.write('\x1b[?2004h')# set bracketed-paste modesys.stdout.flush()try:whileTrue:c=sys.stdin.read(1)ifc:print(repr(c))else:breakfinally:sys.stdout.write('\x1b[?2004l')# reset the mode
Enter fullscreen modeExit fullscreen mode

urxvt

Theurxvt terminal emulator doesn't support OSC-52 out of the box. Honestly, it does very little out of the box! But there's a tiny script,52-osc that adds this support. This is actually nice, because you can see what it does:

subon_osc_seq{my($term,$op,$args)=@_;return()unless$opeq52;my($clip,$data)=split';',$args,2;if($dataeq'?'){my$data_free=$term->selection();Encode::_utf8_off($data_free);# XXX$term->tt_write("\e]52;$clip;".encode_base64($data_free,'')."\a");}else{my$data_decoded=decode_base64($data);Encode::_utf8_on($data_decoded);# XXX$term->selection($data_decoded,$clip=~/c/);$term->selection_grab(urxvt::CurrentTime,$clip=~/c/);}()}
Enter fullscreen modeExit fullscreen mode

Breaking that down, it's hooking into the OSC sequences, and specifically number 52. If it gets a query ('?'), it gets the current selection from$term->selection() and sends that back to the app running in the terminal in an OSC-52 escape, using$term->tt_write(). Otherwise, it decodes the data and sets the X selection with$term->selection(data, clipboard). The/c/ is a regular expression matchingc.

Putting theprintf andclipnotify bits from above together shows that when<board> isc then this plugin updates the CLIPBOARD selection, otherwise PRIMARY. This still works over an SSH connection to a remote host.

Unfortunately, because this is accomplished with a plugin, the terminal info for urxvt doesn't advertise this support.

Copy and Paste

Urxvt has a built-in plugin,selection-to-clipboard, to copy every selection to the clipboard. In fact, it populates both the PRIMARY and CLIPBOARD selections.

Right-click will paste from the PRIMARY selection. With the following in.Xresources, shift-ctrl-V will paste from the CLIPBOARD selection.

Rxvt.keysym.Shift-Control-V: eval:paste_clipboard
Enter fullscreen modeExit fullscreen mode

Either paste option supports bracketed-paste mode.

tmux

I typically runtmux on remote systems, so that I can leave everything running while my laptop is asleep, or if I lose my network connection. Tmux is basically a terminal emulator that runs in a terminal: you can run other things in a tmux window, and tmux lets you switch between those windows, show them on different systems, etc. But, that means that tmux is intercepting terminal escape codes, whether they're for cursor positioning or clipboard management. It is then using (possibly different) escape codes to draw the tmux UI on your terminal.

In its default settings, theprintf above won't do anything in tmux when running inside urxvt, because urxvt doesn't advertise support for it. That can be fixed in.tmux.conf:

set-option -ga terminal-override ',rxvt-uni*:XT:Ms=\E]52;%p1%s;%p2%s\007'
Enter fullscreen modeExit fullscreen mode

Note that this "fix" only works for things running in tmux. I do everything in tmux (even locally), so it's fine for me.

Buffers

Tmux has a concept ofbuffers which are named bits of text that can be injected into an application as if they were keyboard input. Tmux also has a "copy mode" where keyboard navigation can be used to select text that will be put into a buffer. The concept is pretty general, although I don't know what would be built from it.

Tmux is designed around the idea that you would only use buffers for copy/paste. That isn't especially practical, since for example web browsers tend not to be terminal applications!

Clipboard Integration

Tmux hasa wiki page about clipboards. The main integration isset-clipboard. If this is "external", then tmux will issue an OSC-52 sequence to update the system clipboard whenever a buffer is set. If this ison, then tmux will additionally accept OSC-52 sequences from applications running inside it.

set-option -g set-clipboard on
Enter fullscreen modeExit fullscreen mode

With both of these options set, repeating the same experiment within a tmux session reveals that any OSC-52 sequence now updates only the PRIMARY selection. That is, the implementation ofset-clipboard only updates the PRIMARY selection.

Furthermore, running the get-clipboardprintf from above confirms that tmux only returns its own paste buffer -- it never queries the external terminal emulator for the value of the system clipboard.

Even tmux's support for external commandsonly supports copy operations. There's no way to feed data on the system clipboard into tmux.

Bracketed Paste

When an application in a tmux pane has bracketed paste enabled, tmux will enable it in the parent terminal. It will also "pass through" the brackets from that parent terminal when a paste is performed. Basically, it just works.

Tmux also has a-p option to itspaste-buffer command to bracket the buffer contents.

Neovim

Neovim's:help clipboard lays out the situation pretty clearly, and it works quite nicely out of the box. It usesxsel to interact directly with X selections when$DISPLAY is set, and it usestmux commands when running in tmux. Itscheckhealth command makes it easy to see what it's chosen:

  • vim run locally, outside of tmux: xsel
  • vim run locally, in tmux: xsel
  • vim run via SSH, outside of tmux: no support
  • vim run via SSH, in tmux: tmux

Here, I'm not forwarding X11 via SSH. If I do so, and installxsel remotely, then Neovim will use xsel in all four of the above situations.

Tmux does enable bracketed paste mode.

Summary

All in all, this is not pretty!

Two clipboards (selections), handled slightly differently by each application -- so maybe copy in one app doesn't use the same selection that paste does in another. That's certainly more than ctrl-c/ctrl-v muscle memory can deal with, especially for those who also use more .. ahem .. user-friendly systems on a daily basis.

Most people think of having one clipboard.

Tmux is a pretty substantial impediment here, too, basically rendering tmux's buffers useless, since they can't mirror the "one clipboard".

At any rate, this continues my habit of writing posts as a way to learn about a new topic. I hope this information is useful to others as well!

Top comments(1)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
djmitche profile image
djmitche
@djmitche@mastodon.social
  • Location
    Upstate NY
  • Pronouns
    he/him
  • Joined

Slight amendment here: tmuxcan query the enclosing terminal via OSC-52 query, using the-l argument torefresh-client.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

@djmitche@mastodon.social
  • Location
    Upstate NY
  • Pronouns
    he/him
  • Joined

Trending onDEV CommunityHot

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp