Rethinking the GNOME clipboard issues | Eduard's Blog<br>Post<br>Cancel<br>Rethinking the GNOME clipboard issues<br>Contents Rethinking the GNOME clipboard issues
Introduction<br>A clipboard manager is one of those tools you don’t think about until you don’t have it. It’s a basic feature and QoL enhancement on everyone’s routine, and yet, some desktop environment(s) doesn’t offer one by default. You copy something, copy something else, and then realize you needed the first thing. Having the history there is great. What is not great is when the history starts hurting your graphical session.<br>I have used several clipboard managers on GNOME over the years, and they all eventually did the same thing: the desktop would hitch. A small stutter when I copied a screenshot. A longer one when I opened the history and it had grown to a few hundred entries. Search that lagged behind my typing. None of it was a dealbreaker on day one, but it got worse the more I used the tool, which is exactly backwards from what you want. Some worakrounds included disabling image history, limiting a lot the amount of text that they can keep in history, limiting the number of entries and more.<br>I have been thinking about this problem since a long time. It was not an easy work as Gnome doesn’t expose any of the wlr-data-control protocols, so you are forced to implement this on a extension, which carry all of the previously mentioned problems. This weekend, I decided to write Strata, a clipboard manager that solves these problems using several mechanisms that are not present on any of the other implementations. The code is at github.com/Edu4rdSHL/Strata, and the whole point of this post is the one thing I had to get right: the stutter is not a tuning problem you fix with a faster loop or a smaller cache. It is architectural. If you want it gone, you have to move the work somewhere the compositor can’t feel it.<br>Where the stutter comes from<br>GNOME Shell extensions run in GJS, which is single-threaded, and that single thread is shared with the entire compositor. The same loop that draws your cursor, animates your workspaces, and composites every window is the loop your extension runs on. There is no background thread to hide in. Whatever your extension does synchronously, the desktop does not do anything else while it happens.<br>That is fine for forwarding a click. It is not fine for the work a clipboard manager actually does. Hashing a payload to deduplicate it, running a SQLite query, decoding a pasted PNG so you can show a preview, building a thousand list rows when the history panel opens: every one of those is real CPU or I/O, and every millisecond of it is a millisecond the compositor is frozen. You see it as a hitch.<br>It gets worse as the history grows, which is the part that always bothered me. Loading the whole history into JavaScript memory, holding decoded images around, re-rendering a long list on every open, searching by walking the list: that is O(n) work, or worse, on the one thread that also has to draw the screen. A clipboard manager that is smooth with twenty items and janky with two thousand is not really smooth. It is just empty.<br>This is the pattern most GNOME clipboard tools share. The work lives in the extension, on the compositor thread, and the only knobs you get are how much work and how often. Strata starts from a different premise: get the work off that thread entirely, and the question of how much of it stops mattering.<br>Two components, one boundary<br>Strata is two processes that talk over the session D-Bus, and you need both.<br>The first is strata-daemon, written in Rust on top of tokio and zbus. It owns everything heavy: a SQLite database (with WAL and an FTS5 full-text index), content deduplication, thumbnail generation, and search. It exposes all of that as a D-Bus service named dev.edu4rdshl.Strata.<br>The second is the GNOME Shell extension, strata@edu4rdshl.dev, written in GJS. It draws the top-bar panel, runs the search box, handles paste-back, and watches the clipboard for new content. That is all it does. It renders UI and forwards events. It never hashes, never decodes an image, never touches SQLite.<br>The topology is small:
GNOME Shell (GJS) --D-Bus--> strata-daemon --> SQLite (~/.local/share/strata)<br>+--> thumbnails (~/.cache/strata)
The cost of this split is one IPC hop per operation. In practice that is cheap, because the session bus is shared-memory fast on the same machine, and as you’ll see the extension almost never waits on a reply in any path that matters. What you buy for that hop is the entire premise of the project: the expensive work runs in a separate process, on a thread pool, and the compositor cannot feel it no matter how long it takes.<br>The extension also supervises the daemon. On enable it spawns the binary and watches it, respawning with exponential backoff if it dies, and giving up only after a rapid crash loop. If a daemon is already running (for example, one you started as a systemd user service), the...