Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/tasks/37-navigation-back-verb.md

6.2 KiB

TASK 37 — "Back" as a first-class verb

Status: open, opened 2026-07-27. Prompted by @wiredwireless's xdg_navigation sketch (Matrix, 2026-07-24) and Casey's ask to work out how it fits here. Size: our half is small and worth doing now; the protocol half is upstream's problem and should not block us.

The outside idea, in one line

An app declares "my current view has somewhere to go back to"; the compositor decides how the user expresses "back" (edge swipe, key, mouse button 8, accessibility) and invokes it. Later: gesture progress with commit/cancel, so the app can drag the outgoing page as Android's predictive back does.

Verdict: the concept is sound. The framing needs work before it ships anywhere.

The idea is right and the pieces exist. Three things would decide whether it survives contact with freedesktop, and they are worth getting right regardless of whether we ever send it upstream.

1. The killer objection is "just send XF86Back." Apps already handle that keysym and mouse button 8. A compositor can synthesise it today, with no protocol at all. Any proposal that leads with the discrete back() call walks straight into this and loses.

2. The answer is can_go_back — the state channel, not the verb. A synthesised key is fire-and-forget: the compositor learns nothing about whether anything happened. It therefore cannot decide what to do when the app has nowhere to go — close the window? switch workspace? nothing? — so in practice it does nothing, and "back" is unreliable, which is exactly why it never caught on. Declared availability is the new capability, and it is what lets the compositor own the fallback policy while the app owns only its own stack.

Android is the precedent, and it cuts in this proposal's favour: onBackPressed() was the key-shaped model, and Android 13 replaced it with OnBackInvokedCallback — a registered callback with declared availability — precisely because the key model could not express delegation or predictive back. The platform being cited already tried the cheap version and migrated off it.

3. Lead with predictive back, not with back. Progress + commit + cancel cannot ride a key event under any encoding. That makes it a mechanism argument ("here is a thing input events structurally cannot do") rather than a policy argument ("here is a nicer way to do a thing you can already do"). Discrete back then falls out as the degenerate case: progress 0 → 1 in one step. Mechanism arguments are the register Wayland review accepts; policy arguments are the ones that summon the metaphysics thread.

Two design questions to answer before posting, not after

  • What happens at the root, when can_go_back is false. This is where the "what does back mean" fight actually lives, and the design already answers it — the compositor knows, so the compositor decides (close / switch / do nothing), and no app ever needs an opinion. Saying so up front removes a whole class of objection.
  • Scope. One verb, one state, one progress channel. No forward, no up, no home. Each extra verb multiplies the semantic argument and none of them have back's near-universal agreement. Also: this belongs on xdg_toplevel (precedent: xdg-dialog, xdg-toplevel-icon), not on bare wl_surface, and dialogs/popups need a stated answer.

How it fits here — we have already built the compositor half, twice

This is not an analogy; the shapes line up exactly.

  • services/Gestures.qml is the routing table in the sketch. It already maps gesture → named semantic action (squeezeAction, edgeAction, a bind(gesture, action) IPC) rather than gesture → hardcoded behaviour, which INTERFACE-ARCHITECTURE §4 requires of every input. Adding back as an action is one table entry.
  • can_go_back is the dial's enabled, and its refusal reason is the dial's reason. TASK-30's verb tables and TASK-31's radial dial already carry "this verb exists but is not available right now, and here is why, in the words the refusal used". A back verb is that shape with a one-bit state. Whatever we build for back should feed the same table, or we will have three places that enumerate capabilities instead of one — the exact drift TASK-31 was written to prevent.

What we do not have is the app half, and that is the whole point of doing this ourselves: our first-party apps are ours. culver already pushes a stack on narrow layouts (contacts, conversation) and pops it; souveraine-settings is a page tree; the player has views. Every one of them already knows its own answer to "where does back go" and has no way to say so.

The move

Implement the semantics over our own IPC now, shaped like the protocol so a later swap is a transport change and not a redesign:

  1. A navigation service in the shell holding, per focused app: canGoBack, and back() / backProgress(f) / backCommit() / backCancel().
  2. First-party apps report their stack depth into it. culver first — it is the app where the missing gesture is felt daily.
  3. back becomes a gesture-table action like any other (TASK-13's squeeze, the pill, an edge swipe), never a hardcoded binding.
  4. Predictive back last, and only after the discrete version feels right.

Third-party apps (Firefox, GTK apps) cannot participate and will not until something lands upstream. They get the legacy XF86Back key. This is a declared two-tier policy, not a silent fallback: the compositor knows exactly which tier a surface is in, and in tier 2 it does not claim to know whether anything happened. It must never present tier-2 behaviour as if it were tier 1.

The by-product matters as much as the feature: doing this first means that if the idea does go to freedesktop, it arrives as a working implementation with a device behind it, not as a proposal. That is the difference between being argued with and being eaten.

Connects to

TASK-30 (verb tables — back is a verb and belongs in them), TASK-31 (the dial — same enabled/reason shape, same routing table), TASK-13 (squeeze — another gesture wanting a semantic destination), INTERFACE-ARCHITECTURE.md §4, TASK-32 (pill and OSK as apps — the pill is one place a back gesture lands).