I built three instruments to measure a keyboard. They were right about everything that did not matter.
The on-screen keyboard on my e-ink tablet was wasting space in landscape. That was the whole complaint. Twenty-three revisions later it is a 65% keyboard with punctuation, an inverted-T, and a navigation column, and the interesting part is not the keyboard.
It is how the evening divided. I spent it with an agent driving the machine over SSH: it built three instruments and measured everything they could reach, and it was right about every number it reported. Four of the decisions that shaped the final layout could not have come from any of those numbers. They came from me looking at the glass and saying that does not look right — three times while the measurements were confidently doing the opposite.
First you have to be able to see it
This machine is driven over SSH. It sits on a desk, e-ink, no keyboard attached most of the time, worked on from a terminal on another computer. To fix an on-screen keyboard you need to look at an on-screen keyboard, and that turned out to be the hard part.
GNOME will not let you. Since 45, `org.gnome.Shell.Screenshot` refuses any caller that is not the screenshot UI itself:
$ gdbus call --session --dest org.gnome.Shell.Screenshot ... .Screenshot
Error: GDBus.Error:org.freedesktop.DBus.Error.AccessDenied: Screenshot is not allowed
Fine — the device has a framebuffer. `/dev/fb0`, 1872x1404, 32bpp, readable with `dd`. Pull it, convert it, and out comes a clean picture of the boot splash. Plymouth had drawn that at startup and nothing had drawn over it since, because a Wayland compositor composites through DRM and never writes to the fbdev mapping. The framebuffer is a mirror of a moment three hours earlier, and it looks exactly like a screenshot.
It nearly got used. That would have been the second time in a week a stale copy passed for a live one, on the same device.
What works is neither: a GNOME extension runs *inside* the shell, which is the one process allowed to look. So the extension we were writing anyway grew a D-Bus interface — `Capture(path)` to take the picture, and `Geometry()` to report the actual allocation boxes of every actor in the keyboard. Plus a small script that counts down before firing, because the keyboard has to be on screen at the moment of the shot, which is exactly when your hands are busy holding the tablet sideways.
That is three instruments, and they immediately paid for themselves.
What the instruments found
**The keys were using 1207 of 1872 pixels.** GNOME reserves a band the full width of the monitor and a third of its height in landscape, then draws the keys inside a container that preserves the layout's column-to-row ratio and centres what it cannot fill. On a 4:3 panel turned sideways, that leaves a third of the band as blank margin. You pay for the whole strip and get two thirds of it.
**Ninety-eight pixels of that band are a word-suggestions strip.** It is added above the keys and reserved whether or not anything can appear in it. On a terminal nothing ever can — suggestions come from predictive input, and a terminal input field does not get any. A fifth of the keyboard, permanently empty, on the layout where space is scarcest.
**Three keys could not spell their own names.** `Tab` rendered as `T…`, `Ctrl` as `C…`, `?123` as `?…`. I had assumed for a week that those were keys I did not recognise. They were keys I could not read: at 93 pixels a column, three characters need about 105.

*One screenshot, all three findings. The band runs the full width of the image; the keys stop well short of both edges, the empty strip sits above them, and the three keys on the left of the bottom row are the ones that cannot spell themselves.*
And then the one worth the post. The agent's override for the width was doing nothing at all, and no error appeared anywhere. It reasoned about it for two rounds and got nowhere, then made the code print what it had actually received:
[pn-osk] setRatio(1872,468) -> forcing -Infinity/-Infinity
An actor that has never been allocated reports its box as the empty one, whose corners are positive and negative infinity. The guard written for that was `|| fallback`, which catches zero and does not catch `-Infinity`, because `-Infinity` is truthy. The resulting `NaN` ratio was silently ignored in favour of the layout's own — which looks precisely like an override that was never installed.
Two rounds of reasoning produced nothing. One line of "print what you got" produced the answer. The missing instrument was not a better ruler. It was the code saying out loud what it had been handed.
The deepest one came later, and it had been eating the work the whole time. `KeyContainer` attaches keys to a `Clutter.GridLayout`:
const KEY_SIZE = 2;
this._gridLayout.attach(key, left * KEY_SIZE, top * KEY_SIZE,
width * KEY_SIZE, height * KEY_SIZE);
`attach()` takes integers. So a width has to survive being multiplied by two, which makes half a column the finest thing this keyboard can express. Anything narrower is truncated downward, without a word: 1.25 renders as 1.
Several rounds had gone into widening keys to 1.25. None of those changes had happened. Worse, the results had been watched and a rule drawn from them — that icons do not scale with key width — which is a conclusion derived from experiments that never ran.
That one was not found by an instrument either. It was found because I said the navigation column looks about half a column short, and someone went to check whether my eyes were lying.
What the instruments could not find
Here is the layout that survived. None of the four decisions in it came from a measurement, and three of them reversed what the measurements were doing at the time.

*Landscape above, portrait below — the same layout, the same key positions, with the modifiers wearing symbols in portrait because at 82px a column the words do not fit. Rotating the tablet no longer moves a key.*
**Do not align the keys.** Hours went into getting the left edge into a straight line: Esc, Tab, Caps, Shift and Ctrl the same width, `q`, `a` and `z` in one column. It was tidy, it was verifiable, and it was wrong. A physical keyboard's left edge is a staircase — 1, 1.5, 1.75, 2.25 — and that stagger is what your fingers navigate by. Aligned keyboards are harder to type on. The arithmetic had no opinion about this, because "is this pleasant to type on" is not a number an allocation box contains.
**Widen up and down, but only where they are not neighbours.** Making the up arrow wider than left and right reads as a proper inverted-T. Doing the same to the down arrow, which sits *between* left and right, made left look orphaned — the glyphs are centred, so a wider neighbour pushes them apart. Same change, two rows, opposite results. Nothing in the geometry predicts that; you have to see it.
**The backtick cannot move.** There was a proposal to relocate `` ` ~ `` off the number row to fix a stagger problem, and the argument was good: it restores the exact ANSI relationship between the digits and the letters, for free. It was also a proposal to move a key I press constantly, in exchange for a correctness property nobody would ever feel. Which keys get used, and how much, is not in the source.
**The delete key should not exist.** It went through three full rounds: a wider cell, a text label instead of a symbol, then a mirrored icon borrowed from the theme's right-to-left variants. Three rounds of making a key fit better, and not once did the question come up of whether it would ever be pressed. On a Mac there is no forward delete key. The whole exercise was making room for a keystroke that was never going to happen.
That last one is the one I keep thinking about. "How big should this be" is a design question. "Should this exist" is the question one level above it, and an entire evening went by in the room below without anyone looking up.
The upstream half
The terminal layout that ships with GNOME has no Escape key. Not in any of its four levels — a layout that exists specifically for terminals, carrying Ctrl and Alt and arrow keys, with no way to leave vim.
There is already a merge request for it, open since November 2022. Reading the thread, what seems to be holding it is not the Escape key but everything else it moves: it removes Caps Lock and turns Tab into an icon, which needs a decision from design. So the comment we left carries a smaller diff — Escape in the top left corner, Backspace giving up one of its two columns, nothing removed and nothing renamed — and a screenshot of it running, because the resource bundle got patched and booted rather than described.
**Update, a few hours later.** That diff was wrong, and it has been withdrawn. Row 0 of the layout is the letter row — Tab sits immediately before `q` — so inserting Escape there moves every letter one column right, inverting the exact stagger this post spends a paragraph defending. The check that passed before it was sent was that the row still totalled the same width, which is a conservation law and therefore indifferent to arrangement. The thread now carries the correction, and a proposal that moves no letters at all: long-pressing Shift already latches it into Caps Lock, so the dedicated Caps Lock key is the one that can go, and its slot — the left of the home row — is where terminal users remap Escape to anyway.
The other two findings went in as issues rather than patches (#9331, #9332). The empty suggestions strip may well be deliberate — reserving the space stops the keys jumping when a suggestion arrives — and that is not visible from outside. Filing it as a patch would have been guessing at someone's intent and then editing it.
The boring footnote
The extension, the geometry notes, and the screenshot tool are in CVERInc/pinenote, MIT. `GEOMETRY.md` has the numbers you need before changing a width: what a column is in millimetres, what a label needs in order to fit, and what each row has left to spend.
The keyboard in that repo is not a design that was computed. Every number in it was measured by something that cannot see, and every decision about those numbers came from someone looking at the glass who could not have measured any of it.
Keep reading
-
I could recite the rule six hours before I broke it in public
A stranger on a four-year-old merge request made my colleague's argument back at me, about a rule I had learned the same night and could still state correctly. Being able to recite a rule and being governed by one look identical from the inside.
-
Three days without power held the picture. One boot erased it.
The picture had been on the panel for four days. The file I installed it as had been read exactly never — the driver takes its default screen from the initramfs, and the thing actually holding my cat up was the API with Temporary in the name.
-
I thought I was changing the wallpaper. My cat outlasts the battery.
There was no wallpaper to change. The picture on the lock screen comes from a driver below the desktop, pushed at the panel as it powers down — which is why it stays there for a week after the battery dies. Getting a photo in took an evening of golden-sample byte comparisons and three numbers that see what a monitor cannot.