Ramblings of an aging IT geek
← Ramblings of an aging IT geek
linux

clipferry: An X11 to Wayland Clipboard Bridge

A small Rust daemon that bridges X11 and Wayland clipboards under Xwayland (xwayland-satellite on niri, builtin on Hyprland). Lazy and without polling.

Listen to this post
A small ferry carrying an envelope across water between two islands

Clipferry solves the problem of X11 ↔ Wayland clipboard sync. Let me explain why I built this.

I often use niri or Hyprland with Steam. Steam, Proton and Wine are all X11 clients: they reach Wayland via xwayland-satellite on niri, and via Hyprland's builtin Xwayland on Hyprland.

Copying inside a Wayland app and pasting into a Wine window works. Copying out of an X11 app doesn't: the X11 selection never crosses back. A WoW SimC dump you want in raidbots, a talent or WeakAuras import string bound for Discord, a Path of Building code, a Factorio blueprint, an FFXIV gear set string: all stuck on the X11 side.

Recent xwayland-satellite releases have closed some of these gaps, but apps that don't play the usual focus games, and less-common MIME types, still fall through on both stacks.

dnut/clipboard-sync hasn't been touched since 2023 and has a much broader scope than I wanted. Everything else was bash around wl-copy and xclip, polling in a loop with content hashes for loop suppression and a pipe cost paid on every copy whether anyone pastes or not. Fine for text; grim for a 30 MB screenshot.

clipferry is a small Rust daemon that speaks both clipboard protocols directly and shuttles selections across on demand.

The design is deliberately narrow.

  • Lazy by default. When you copy, clipferry exchanges the list of MIME types with the other side. The bytes themselves only move when someone actually pastes. A 50 MB image copy is a handful of strings until a paste happens, not two full transfers up-front.
  • All the MIME types, not just text. Images, HTML, text/uri-list, and a small translation table for the X11-isms (x-special/gnome-copied-files, a couple of WeChat and Wine quirks), plus INCR in both directions for the awkwardly large ones.
  • Loop prevention by ownership identity, not hashing. clipferry knows which selection claims are its own, so it never chases its own tail. No content hashes, no sleeps, no races.
  • Sandboxed. It applies Landlock to itself: no filesystem, no TCP. Plus the usual systemd hardening. Clipboard content is never logged, and payload buffers are zeroed when they die.
  • Small. One process, a ~1 MiB binary, under 5 MiB resident.

By default it runs in backstop mode: it watches both clipboards passively, and only claims a selection when nothing else has bridged the other side within ~200 ms of a copy. If satellite's builtin sync handles a case, clipferry stays out of the way. Clipboard history managers (cliphist, clipse) coexist as designed.

Install is short:

# Arch
paru -S clipferry

# any distro
cargo install --git https://github.com/jmylchreest/clipferry
sudo cp contrib/clipferry.service /usr/lib/systemd/user/
systemctl --user enable --now clipferry.service
clipferry --oneshot-check    # sanity: both displays reachable?

There isn't much to configure, but there are a handful of flags to deal with edge cases. For most people the defaults will work. The README documents them.

If copy-out-of-X11 has been annoying you, give it a go. Issues and patches welcome on the repo.