Consistent copy and paste in Linux with i3wm

Published Jul 29, 2019

Last year, fed up with the absolute state of macOS, and after some exposure to minimalist tiling window managers such as i3, I decided to take computing into my own hands and make the switch to Linux.

While I’m happy with the switch, it hasn’t been without difficulty. One thing developers switching to Linux from macOS might immediately encounter is the annoying inconsistency of copy/paste shortcuts between most graphical apps and most any terminal emulator. This is because most Linux graphical apps use Ctrl-C and Ctrl-P for copy and paste, rather than something like Super-C and Super-P, which would be equivalent to macOS’s Command-C and Command-P. In a terminal emulator, Ctrl-C and Ctrl-P are properly reserved as control codes, and so terminal emulators use different shortcuts, such as Ctrl-Shift-C and Ctrl-Shift-P.

It’s a small user interface detail, but little workflow inconsistencies like just add to cognitive load that, as the human users of machines, we’d prefer to avoid. Fortunately, the machine can be made to conform to something more ergonomic for its human user.

NOTE: For my working solution without the background, skip down to the “My solution” heading below. Be advised that my solution assumes the use of a programmable keyboard, which I use 99% of my working time, but may be adaptable to other setups with a little creativity.

My first attempt

My first stab at this problem was to simply program my keyboard with a special key combination which sent the special copy/paste keycodes normally sent by keyboards that have built-in, dedicated copy/paste keys. In Linux, these are the keysyms XF86Copy and XF86Paste.

I edited my Keyboardio firmware and added these to a special macro layer activated by a momentary layer modifier key. Users of other programmable keyboards such as the ErgoDox and others can do this as well. In my setup, this gives me a key combination quite familiar to my decades-old muscle memory of the hundreds of thousands of copy/paste operations I’ve executed in my lifetime.

This worked surprisingly well! These keys worked as expected in most places, including my terminal emulator of choice, Kitty. Unfortunately, it wasn’t quite there, as it became apparent that support for these keys must be up to individual app developers, UI libraries, or some unknown grey area in-between. For example, GNOME Calendar didn’t allow me to copy from its event edit dialog, and qutebrowser didn’t recognize pastes into its command mode. Still, it made things consistent for 90% of my use, so I stuck with this for a while.

My second attempt

Ever the sleuth for new computing productivity tips, I came across Eli Rodríguez Pérez’s post about his keyboard setup. In particular, it’s here that I learned that Ctrl-Insert and Shift-Insert work as legacy copy/paste shortcuts, passed on to us from the ancients. After testing them out a bit, I was satisfied, and reprogrammed my copy/paste macro keys from before to send these shortcuts instead.

This attempt was promising, but I ran into issues. The main one was confusion between Linux’s two clipboards. In Linux, you can paste directly from the last text you selected (without copying), or from the last text you actually copied. I also had some issues with my terminal recognizing them correctly. Finally, my clipboard history utility, Greenclip, didn’t recognize text copied with this approach.

But this had brought me tantalizingly close to fully solving this long-standing little annoyance, and so I stuck to it.

My solution

After a little thinking through my constraints, I came up with what I think is a pretty good solution. The gist was to program my macro keys to again send XF86Copy and XF86Paste, but configure i3 to bind those keys to two tiny scripts. Those scripts would use xdotool to detect the focused application, and if we’re in a terminal window send Ctrl-Shift-C/Ctrl-Shift-P, otherwise send Ctrl-C/Ctrl-P.

So, configure i3:

bindsym --release XF86Copy exec --no-startup-id smart-copy
bindsym --release XF86Paste exec --no-startup-id smart-paste

And somewhere in your PATH, create the following bash scripts for smart-copy and smart-paste:

#!/bin/sh
# smart-copy

win=$(xprop -id $(xdotool getactivewindow) | grep 'WM_CLASS(STRING)' | cut -d'"' -f2)

case $win in
    "kitty") xdotool key ctrl+shift+c ;;
    *) xdotool key ctrl+c ;;
esac
#!/bin/sh
# smart-paste

win=$(xprop -id $(xdotool getactivewindow) | grep 'WM_CLASS(STRING)' | cut -d'"' -f2)

case $win in
    "kitty") xdotool key ctrl+shift+v ;;
    *) xdotool key ctrl+v ;;
esac

There we have it. Modify "kitty" to match the name of your terminal emulator, and if necessary, modify the shortcuts to what your terminal emulator uses as well.

The beauty of this approach is that it gets directly to the core of the problem, which is that almost all apps except the terminal use the same shortcut. Essentially, we have created shortcuts which act as the typical shortcut except in the exceptional case. Neat and tidy. And I could see this general approach being applied to similar problems getting inconsistent UIs in line with how we prefer to work.

More to come

I plan more posts like this to talk about various bits of my dotfiles and how I make my minimal i3 environment easier to use for my workflows. Machines are for people, even if they don’t always act like it! Be sure to subscribe to my Atom feed if this is the kind of thing that interests you.

If you've enjoyed reading this post, you may enjoy reading more, or even subscribing to my Atom feed, if you're old school like myself and still use an RSS reader. I am also available for consulting for full-stack web development and other software projects.

Get in touch