14 KiB
STEP20C event-loop pending input probe
Status: EXPERIMENTAL / INCONCLUSIVE after bounded execution. STEP20C remains NEXT and multi-output Mosaic NOT YET SUPPORTED. This probe changes no production compositor, package, UX or backend scheduling policy.
Question and historical limit
The ordering probe observed 20 applied input-region changes, with none between a matching native output commit and headless present. Its runner waited for each camera IPC response, client confirmation and pointer confirmation. Those cross-process waits can outlast a commit-to-present interval. The old observations remain valid for their executed order. This probe asks whether a normal Wayland request, submitted without all those intermediate runner waits, is naturally applied in that interval.
Dispatch and repaint model
The pinned wlroots 0.20.2 and installed Wayland server 1.26.0 source (tag
commit 87cc8a8728a923fc57938faa81ba0e74f34ecdc7) show that
wl_event_loop_dispatch runs queued idles before polling, dispatches ready
epoll sources with timers first, then dispatches idles created by those
sources. Thus a client fd already in the same returned epoll batch can run
after a repaint timer and before its newly queued present idle; a request
arriving later may encounter the idle first. The event loop does not promise
that an already written client request wins over an idle callback. A Wayland client flush sends
queued bytes toward its connection; it does not run a server handler. Distinct
states are written by client, possibly readable at server,
dispatched by server, and applied to effective input. The latter is
observed in the server surface-commit callback, after wlroots has committed
the new input_region. No readiness is inferred solely from client logs.
SwayFX's output_repaint_timer_handler builds scene output state and calls
wlr_output_commit_state. The accepted commit signal is synchronous. The
headless backend queues output_defer_present; wlroots adds a Wayland idle
callback for the matching native commit_seq. A later callback emits
present, but destruction or allocation failure can prevent it. A normal
surface set_input_region changes pending state and wl_surface.commit
makes it effective. SceneFX's scene hit predicate reads that effective region
through wlr_surface_point_accepts_input; SwayFX node_at_coords and seat
notifications follow. Surface input-only commits need not render a new
output buffer. Output build/commit/present and later input hit are different
identities; a later hit may legally use newer input than submitted pixels.
Identities and clocks
Keep native (output, commit_seq) for accepted commits and presents; never
predict a sequence. Keep surface resource ID, observed current.seq,
input_changed, effective bounds and client command identity separately.
Private compositor callbacks use CLOCK_MONOTONIC nanoseconds to establish
server order. Runner and client logs have separate process order unless
their clocks are checked; a runner timestamp is not treated as a server
handler timestamp. OUTPUT_BUILD, OUTPUT_COMMIT, OUTPUT_PRESENT,
SURFACE_COMMIT, SCENE_HIT and POINTER_DELIVERY are passive existing
private trace sites. The unmodified SwayFX control supplies client events
and sampled pixels, not native output event traces.
Fixed experiment
Two headless outputs remain HEADLESS-1 (-800,0) and HEADLESS-2 (0,0),
both 800×600, scale 1, normal transform. The same mapped T4 client performs
normal wl_surface.set_input_region, wl_surface.commit and client flush.
Compare standard SwayFX splith, private splith and private mosaic, with
no popup or grab. Runs own private mode-0700 runtime directories, discovered
IPC sockets, logs and process groups. Captures sample fixed pixels only.
- E0: verify outputs, mapping, full-region right button receipt, empty region no target hit, and fixed pixels.
- E1: two sequential full/empty transitions using the historical waits. Retain runner submissions/acknowledgements and server callback order.
- E2: submit a camera IPC request when applicable, then a client input mutation without awaiting the IPC response or client confirmation. Await completion only after both submissions. Preserve whether application was before commit, between commit/present, after present, or without a new output commit. Up to five transitions per private mode.
- E3: submit a normal client region request before an ordinary rendering transition. Record client flush and server handler times; do not call the request server-ready without proof.
- E4: attempt a same-interval hit only if E2 naturally creates the commit/mutation/present order. Record actual scene hit and client delivery; no destination may be inferred for a hit that did not run.
- E5: restore full input on the same surface and verify client receipt, target hit and stable sampled pixels.
The runner offers a sequential control and an asynchronous submission mode.
It records each request and checks completion after submission; an IPC
request sent is not treated as an applied camera change. No sleep or special
idle callback is added to the compositor, headless backend, or Wayland server.
No recursive dispatch, source-priority change, direct current.input write,
forged present or output geometry change is allowed. At most five attempts
per experimental configuration; if none interleave, stop with
INCONCLUSIVE / NOT EXERCISED. The grabbed-popup camera variant is allowed
only after a natural E2 interleaving creates a Mosaic clipping question.
Analysis and stop rule
Emit raw and structured event-order.jsonl, client-requests.json,
surface-commits.json, output-builds.json, output-commits.json,
presentations.json, input-regions.json, scene-hits.json,
client-delivery.json, timelines.json, matrix.json and summary.json
outside Git. Each timeline must state submitted request, observed server
application, last accepted commit, last observed and next present, actual
hit/input/client receipt, and which relations remain inferred or unknown.
Compare one minimal-trace run with a run disabling only the additional
surface log site: commit/present counts, duration, gap distribution, applied
surface mutations and button receipts. Functional agreement alone does not
prove timing neutrality. SHA-256 manifests cover all raw evidence used.
If five attempts produce no natural interleaving, close this temporal line as INCONCLUSIVE for this backend and matrix. Existing ordering may be sufficient for sampled states, but a global architectural sufficiency claim would require more evidence. An added Mosaic transaction contract requires a concrete required clipping/hit invariant and a real counterexample, not merely a later input state than an already committed output buffer.
Executed campaign and result
The source fixture rebuilt pinned private SceneFX, Mosaic and wlroots in
/tmp/swayfx-mosaic-nested-grab-6C8kpcQB/. The first sequential run failed
because the generated runner lacked its sequential constant. A later
asynchronous control run failed because an untargeted border pixel 3 IPC
command ran after a click on an empty input region had changed focus. Both
failed runs are retained under failed-runs/; neither supplies temporal
evidence. The corrected fixture then ran two sequential region cycles per
mode, followed by five asynchronous cycles per mode. The standard control
remained uninstrumented. The separate trace-off comparison used two
sequential cycles per private mode.
After the five-attempt timing bound, a separate provenance check cloned
published c1f0e3621ddfba29fde9660d044d595c00ee8610 into
/tmp/step20c-ordering-clone-final2/, overlaid only this uncommitted
fixture, and rebuilt both the historical and pending-input private sources
without rerunning timing attempts. Recursive source comparisons found no
differences between either clone-built private Mosaic/SceneFX tree and its
corresponding measured lab. The copied T4 client source SHA-256 matched
2f06888fbb77d541545aa54280567b8571ef5001e6880686bc9667b4091eddeb.
The clone-built private Meson model test passed. This establishes source
equivalence of the measured builds to reconstruction from a Git clone; it
does not make the runs byte-identical or add temporal observations.
For the evidence freeze, another fresh clone at
c1f0e3621ddfba29fde9660d044d595c00ee8610 independently rebuilt the
same pinned chain in /tmp/swayfx-mosaic-nested-grab-9wm8BpSJ/. One private
Mosaic sequential cycle yielded 45 accepted output commits, 43 observed
presents and two applied target input mutations; one asynchronous cycle
yielded 48/46 and two mutations. Both runs had two positive client flushes,
two actual top-level right-button receipts, eight captures, failure=null
and zero commit-to-mutation-to-present intervals. The asynchronous runner
recorded two distinct client submissions before their confirmations. This
small replay verifies fixture behavior, not a new scheduling conclusion.
Its outside-Git manifest covers all 56 regular raw run files, passed every
entry check and has SHA-256
f8b54f2ef7f9caca3837e956341ddb5a62f261d0115840d6fd8b0b3540c1e8b9.
Every successful mode had two correctly placed 800×600 outputs, the mapped
420×320 toplevel, eight captures, six received right-button presses (initial
full region plus five restorations), five empty and five full commands in
the asynchronous campaign, and zero popup grabs. The sampled left-output
pixel (342,175) was (35,68,204) at E0, E2 empty and E5 restored in all
three modes. The control verifies pixels and client receipt; it has no
native server commit/present trace.
| Asynchronous mode | Accepted native commits | Observed presents | Applied target input mutations | Client flushes | Natural commit→mutation→present |
|---|---|---|---|---|---|
Standard SwayFX splith |
Uninstrumented | Uninstrumented | Uninstrumented | 10 | Unobservable |
Private splith |
82 | 80 | 10 | 10 | 0 |
Private mosaic |
83 | 81 | 10 | 10 | 0 |
All client flush calls reported positive byte counts (40 or 64), and the
server applied the 10 corresponding input-only mutations in each private
mode. Client and server both used CLOCK_MONOTONIC; paired flush-to-handler
gaps were 30,176–113,331 ns in private splith and 15,058–111,367 ns in
private Mosaic. In E2, the runner submitted the IPC request then the client
request before waiting for either completion. In E3 it submitted the client
request before the IPC rendering request. The server traces nevertheless
place every applied target mutation outside a matching accepted
commit-to-present interval. A submitted request is still not proof that its
fd was ready in a particular epoll batch. The native counts differ from the
historical 62/60 and 84/82 because this runner also changes border state in
standard layout and overlaps normal camera requests in Mosaic. Both private
modes again have two initial sequence-2 commits with NO PRESENT OBSERVED;
their cause remains UNEXPLAINED.
E0 passed the sampled geometry, client and pixel controls. E1 passed two sequential transitions per mode. E2 submitted five bounded overlapping cycles per mode and found zero natural interleavings. E3 proved the client submission and subsequent normal server application, without proving fd readiness before repaint. E4 was NOT TESTED, because no E2 interval existed in which to attempt a hit. E5 restored full input on the same surface; actual button receipt resumed. Private scene hits after effective empty input did not target the toplevel. Some transient full-state private standard hits were nil during ordinary border changes, but later full-state hits and the client deliveries succeeded; no distinct Mosaic clipping failure was demonstrated. There was no popup/grab variant.
Trace-on two-cycle sequential controls measured private splith 47/45
commit/present, 4 applied target mutations, 3 buttons, 4.808 s and a
76,762 ns median commit→present gap; trace-off measured 48/46,
4 client flushes, 3 buttons, 4.799 s and 96,800.5 ns. Private Mosaic gave
59/57, 4 mutations, 3 buttons, 4.826 s and 86,591 ns trace-on; trace-off
gave 60/58, 4 client flushes, 3 buttons, 4.837 s and 86,020 ns. Applied
mutation counts cannot be observed directly with the new log site disabled;
the client and hit behavior still pass. Native counts and gap distributions
vary, so perfect timing neutrality is not claimed.
The lab contains event-order.jsonl, client-requests.json,
surface-commits.json, output-builds.json, output-commits.json,
presentations.json, input-regions.json, scene-hits.json,
client-delivery.json, timelines.json, matrix.json and summary.json.
timelines.json records each submitted client flush, applied server handler,
surrounding native events and executed later hit; per-hit client delivery is
left unknown where the client log has order but no same-clock event stamp.
The outside-Git raw-evidence-sha256.txt covers all 280 regular files in
runs/, sequential-runs/, trace-off-runs/, failed-runs/ and structured
results, excluding the two source/derived manifest files and itself. All
entries passed SHA-256 verification. The manifest hash is
6eaeee3fcd77c4f2b7682924d2ff8ebdd5b306acab715bead68ce9e655f5490a.
Builds, binaries, generated protocol source and sockets are outside this
raw-evidence scope and outside Git.
Verdict: INCONCLUSIVE / NOT EXERCISED for the sought temporal order. The request/confirmation decoupling worked, and applied mutations and later hit/client behavior are real, but the discriminating interval was still not exercised. No concrete Mosaic clipping invariant was violated and no added transaction contract is established. Architecture: UNDECIDED. Stop this temporal line for the headless backend and fixed matrix; STEP20C can next examine effects/damage, scaling, or resize/migration as separate gates.