All posts

The keyboard had an Escape key. The rename box could not reach it.

On an e-ink tablet with no physical keyboard, renaming a folder in the app grid put a text field on screen and an on-screen keyboard under it. You could type. You could press Enter. There was no way to cancel — the stock GNOME keyboard has no Escape key at all.

The obvious repair is to put a tick and a cross in the dialog. That is the wrong repair, and the maintainer said so before I could suggest it: it puts a key that the keyboard is missing onto every screen that needs it.

We ship our own keyboard for this device. It has Escape. It has a digit row, Tab, Ctrl, Alt, arrow keys. None of it appeared.

The gate, and the reason it existed

if (purpose === Clutter.InputContentPurpose.TERMINAL && ext._config.k6Layout)
    this._pnComposed = ext._composeLayout(groupName, landscape);

Our layout only composes when the input's *purpose* is `TERMINAL`.

That was not a mistake. It is a 65% keyboard, built for a terminal, which is where the digit row and Escape and Ctrl were wanted in the first place. Every other context got GNOME's stock layout, which is the sensible default for typing prose.

The folder rename entry is `FREE_FORM`. So it got the stock layout, and the stock layout has no Escape, and the only way out of a rename you did not want was to commit it and rename it back.

**The condition was right when it was written. The reason had moved and the condition had not.**

The wrong theory, again

The first thing I assumed was that our layout *had* composed and then silently fallen back — because that had already happened once on this device, in a way that cost an evening. Upgrading the shell renamed every keyboard-specific icon (`keyboard-enter-symbolic` became `osk-enter-symbolic`), one lookup failed, and the composition returned `null`. The extension still reported active, D-Bus still answered, the config was still read, and the journal said nothing.

So I instrumented every bail-out on that path with a reason. Then reproduced. Then read the log, and the log was empty — not "composition failed", but no line at all, because `_composeLayout` had never been called.

The instrumentation was not wasted. **The absence of any message is what ruled out the theory**, and without the traces that absence would have been indistinguishable from a failure I had not thought to log.

Widening the gate is one word: `NORMAL` is added and nothing else. Password, number, phone, email and URL each get a purpose-specific layout from GNOME — replacing those with a full qwerty would make them worse, and none of them needs Escape.

The thing that made it discoverable at all

The rename state is two taps deep: open the app grid, tap a folder, tap the title. Every round of this needed a human to perform those taps and then describe what they saw.

So the extension grew a `RenameFolder` method on its D-Bus interface, which opens the folder and puts the title into edit mode. The same argument as the screenshot method before it: the shell removed the ability to evaluate arbitrary code in GNOME 45, so nothing outside the shell can drive its state — and an agent that cannot reach a state cannot iterate on it. Everything else that evening was measured this way too, which is why this post has numbers in it and the earlier ones from this device have screenshots.

There is a real limit to that, and it caught me the same night in a different component. A D-Bus probe proves the *interface* works. It does not prove the *button* works, because a call from outside the shell takes a different path than a tap inside it. On one occasion those two paths differed enough that the button deadlocked the compositor for twenty-five seconds while my probe returned in twenty-eight milliseconds. That is a separate story, but the rule belongs here: **an instrument that reaches the state from a direction the user cannot is measuring something adjacent to the thing you care about.**

The part that was not about keyboards

While fixing the rename box, its text was unreadable: stock styles it dark-on-dark, which on a two-colour panel quantises into a solid block. White background, black text, black border. Then the selection came out as a filled black rectangle with the name invisible inside it — black selection with white text does not render here. Light grey selection with black text does.

That is the fourth or fifth time this device has taught the same lesson: a design that encodes information in a colour *value* rather than a colour *difference* has nothing to fall back on when the panel has two colours. Translucency, mid-greys, accent tints, focus rings — all of them arrive as noise. The repair is never "pick a better grey".

What generalises

The gate is the interesting part, not the keyboard.

Conditions get written with a reason attached, and the reason lives in someone's head or in a commit message from a year ago. The condition survives; the reason drifts. Nothing fails, nothing logs, and the feature is simply absent in the places the original author was not thinking about — which by definition are the places nobody checks.

The question that finds them is not "is this condition correct?" It is: **what is this condition excluding, and does that set still match what I meant?**

The boring footnote

The keyboard, the layout composer and the D-Bus probes are in CVERInc/pinenote, MIT.

The silent-fallback story referenced above — where one renamed icon took the whole layout down without a single log line — is in the same repository's README, under the GNOME 48 section.

Keep reading

Notes from the workshop — the door is open.