X is for the X Selection: Copy and Paste on Linux (Shallow Thoughts)

Akkana's Musings on Open Source Computing and Technology, Science, and Nature.

Mon, 07 Sep 2020

X is for the X Selection: Copy and Paste on Linux

There's so much confusion about copy and paste in Linux. Many people, coming from the Windows or Mac worlds, complain about copy/paste not working right. And while it's true that some apps don't handle copy/paste very well (Firefox in particular is notably flaky in this area), usually the problem is that nobody has ever told them about one of Linux's best features: the two types of selection, Primary and Clipboard.

The Primary Selection

When you sweep your mouse across some words to highlight them, or double-click to highlight a word, or triple-click to highlight a line, whatever you've highlighted is now in the primary selection.

Whenever you have a primary selection, you can paste it right away into another app using the middle mouse button. No need to do a Copy or Paste operation; just highlight, move your mouse to wherever you want to paste, and middle-click.

Middle-click paste is faster than the Win/Mac style Copy/Paste, because you don't have any extra steps, plus you don't need to move your hands from the mouse to the keyboard to Ctrl-C, then back to the mouse to move it into another app, then back to the keyboard to Ctrl-V. (Or keeping your hands on the mouse and going to the Edit menu twice, which is even slower.)

There's only one primary selection on your machine at any given time; though that's not always obvious, because if you then select something in a different app, the first app may or may not notice that it no longer owns the primary selection and un-highlight whatever it was.

The Clipboard Selection

The clipboard selection works pretty much the same as it does on Mac and Windows. Highlight something (now it's the primary selection), then you type Ctrl-C or use the Edit->Copy menu, and now it's the clipboard selection as well as the primary selection. You can select something else, changing the primary selection, but whatever you copied is still the clipboard. Paste also works the same way: type Ctrl-V or use the Edit->Paste menu, and the clipboard selection will be inserted wherever your active text cursor is (not where the mouse is).

The advantage of having both: you have that very fast and convenient primary/middlemouse paste, but if you want to copy something and have it stay there even while you click around and select other stuff, you can use the clipboard.

There's a third X selection, the secondary, but hardly anyone ever uses it, and there's also a more esoteric concept, also seldom used, called a kill ring. You can read more about the gory technical details of the X selections here: X Selections, Cut Buffers, and Kill Rings.

Check and Set Selections with xsel

Sometimes X selections get a little confusing. And not all applications use the same key bindings. For instance, Shift-Insert is supposed to work to paste the Primary selection at the current text cursor position (like Ctrl-V does for the Clipboard selection); but some apps, like Firefox, got this wrong and made Shift-Insert paste the Clipboard instead (there are several Firefox bugs on this, like bug 1070518 (which has a patch that no one bothered to look at), and the much older bug 228011. And some apps, like terminal windows and text editors, don't accept Ctrl-C and Ctrl-V because they use those keys for other purposes. Most people use Primary paste with those apps and don't bother with Clipboard paste; but sometime you might need to use the clipboard, like with annoying web apps that disable the Primary selection (Google Docs is the worst offender. but I've seen a few others).

The easiest way to debug something is xsel, a commandline program. xsel -p shows you the current primary selection; xsel -b shows you the clipboard.

You can also use xsel to set the selections, or copy one to another. For instance, I have a couple of shell aliases:

alias primary2clip='xsel -p | xsel -i -b'
alias clip2primary='xsel -b | xsel -i -p'

That way, if I want to middlemouse paste my primary selection into a Google doc and I find out Google has rudely disabled middlemouse paste, I can go to a shell and type primary2clip, and then I can Ctrl-V it into Google. Or if I have something selected in Google Docs and find out they've disallowed primary paste (I don't even know how they manage that, but they do), but I want to paste it somewhere else with middlemouse, I can Copy it in Google and then run clip2primary, and now it's in the Primary selection.

Other Types Beyond Plain Text

Mostly I only need to copy and paste plain text. But every now and then I want something else. xsel doesn't handle that, and you need a different program, xclip.

For instance, sometimes while I'm editing an HTML page, I want to paste a snippet from another HTML page I've already written. I edit my websites in Emacs, so I'm editing the HTML markup directly. But if I copy from the browser window where I'm viewing the web page, then paste into emacs, Firefox helpfully converts the HTML to plain text, losing all the markup. (Fun fact: I wrote the code that does that conversion. At least some of it.)

Most of the time that's the right thing. But if you're pasting from HTML to HTML, sometimes it would be nice to retain the HTML markup. That would save several steps via going to the browser window and doing a View Page Source (or navigating to the original source file on disk, since I'm probably pasting from another page on my own site) then searching through the source for the part you want to paste.

In theory, emacs could ask X what types are available, and if one of them is text/html, it could paste that if I'm editing in web-mode, html-mode, or one of the myriad other HTML editing modes. That's basically what Firefox and Thunderbird do if you paste into them. Alas, emacs isn't that smart, and there doesn't seem to be any way to query the selection type from elisp. But I can use xclip to query the types available:

$ xclip -o -t TARGETS
and I can use it to fetch the HTML target and feed it back in as a plaintext selection:
xclip -o -t text/html | xclip -i
and now the selection is something I can middlemouse paste into emacs, vim or any other text editor.

Except for one thing: Firefox, at least, precedes each selection with: <meta http-equiv="content-type" content="text/html; charset=utf-8"> which I don't want to have to delete every time. So I added a sed command to strip out that part, and made the rest into a shell function:

htmlsel2text() {
    if xclip -o -t TARGETS | grep -q text/html ; then
        xclip -o -t text/html | sed 's/<meta [^>]*>//' | xclip -i
        echo "HTML is now on clipboard"
        echo "No text/html on clipboard"

I suppose I could even program emacs to call this xclip pipeline whenever it sees a middlemouse or other paste command while in web mode. But calling an external program twice from an elisp function seems ugly. I'll wait and see how much I actually use this.

Pasting Images

You can even paste images using xclip. Our Raspberry Pi Club here is using Discord while we can't meet in person (at the suggestion of one of our younger members who uses Discord when gaming). One of the things the kids like to do in Discord is paste images into the chat.

Of course, you can put an image online and then paste its URL; but then the image doesn't show up directly in the chat. To have everyone see it, you need to paste the actual image bits.

That's something I'd never needed to do before (though I know it comes up a lot for people who use word processing programs), but it turned out I could do it with xclip. The only catch was that Discord only accepted images in PNG format, not JPG. So I needed to combine convert, from ImageMagick, with xlip:

convert myphoto.jpg png:- | xclip -selection clipboard -target image/png

Now the image bits are in the X Clipboard selection, and if I Paste into Discord, the image shows up there.

Update: I've posted a followup article on Key Bindings for Copy and Paste Under X11.

Tags: , ,
[ 12:28 Sep 07, 2020    More linux | permalink to this entry | ]

Comments via Disqus:

blog comments powered by Disqus