test(mosaic): record minimal core build and partial runtime GPU gates

This commit is contained in:
fy59 2026-10-14 19:28:20 +02:00
parent c4bdfb46aa
commit 70954e26f6
84 changed files with 11093 additions and 0 deletions

View file

@ -4,6 +4,25 @@
### STEP20C experimental evidence
- Record the new minimal-core campaign separately from historical source-only
evidence: exact replay, six actual C UB reproductions, private camera guard,
extracted C unit normal/UBSan/ASan, OOM abort/P1, static build/local ABI and
five-file private Stage1. No compositor sanitizer or full package build.
- Preserve12 unique runs/88 output captures (90 PNG including two markers),
no retries, scale 1.25/R90 and resize/migration sampled results, incomplete
standard/vertical/fullscreen/hidden-return gates and E6 frozen-validator FAIL
despite actual native five-second timeout before ACK/teardown. All E terminal
IPC1/compositor0 and client-normal-closure limits remain separate.
- Record core whole-H1 optimized return FAIL 33,789/max RGB 6/5/1, H2 exact,
shadow NOT EXERCISED and distinct GPU diagnostic's sole rejected epoch.
Modifier-aware audit establishes an all-plane stride fixture predicate defect
without qualifying GPU read_success or whole-field causality. Native lifetime,
atomic generation, complete damage and package/notices/legal/maintenance gates
remain open. Recommendation PRIVATE_CORE_NEEDS_TARGETED_EVIDENCE; scientific
result PARTIALLY_ADVANCED. Evidence delta review ACCEPTED; final dossier review
pending and publication PREPARED at this edition. Production STEP20C NEXT,
multi-output NOT YET SUPPORTED; no production/UX/package or historical rewrite.
- Record the distinct saved/input/GPU batch: six runs,59 captures,240 reserved
actions,42 common/six GPU offline tests. Native five-second saved timeout and
whole-H2 sampled restoration pass on C3/A/G; C3 natural instance retirement

View file

@ -616,3 +616,38 @@ documentary publication PREPARED at this edition, with actual refs in the privat
post-push receipt. STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT;
STEP20D–G PLANNED; production multi-output NOT YET SUPPORTED. No production,
UX or package contract changes, and no authorization for further graphics runs.
## Minimal private core build/runtime evidence — 2026-10-14
The [new minimal-core dossier](design/STEP20C_MINIMAL_CORE_ABI_PACKAGE_AND_INTEGRATION_DECISION.md)
supplements the historical source-only core without rewriting that state.
Its exact private binary SHA 29baf31 is now built with static SceneFX and its
own reviewed sources. Real extracted C bodies pass normal/UBSan/ASan checks
after a private finite/range camera guard; sidecar/listener/popup unit proof
does not establish full native scene or GPU lifetime. Targeted sidecar OOM
still produces abort: OOM_ABORT_EXPERIMENTAL, P1 availability risk, with no
production availability response adopted.
Eleven E runs and one distinct GPU diagnostic consume 12/14 reservations and
88 output captures. Core scale 1.25/R90 and resize/migration have sampled positive
pixels/input evidence. Horizontal standard return, vertical/control and
fullscreen/hidden-owner policy remain incomplete. E6 independently observes
a five-second native timeout before ACK/teardown, while its frozen validator
receipt remains INCONCLUSIVE and native saved identity is unobservable.
SPIKE_DAMAGE records extents added to rings, not presented-buffer identity or
complete frame coverage. Native buffer/GPU lifetime, atomic scene/input/present
generation and complete damage remain NOT PROVEN. No particular atomic-counter
mechanism is selected; applied rendering/input coherence remains required.
Core whole-H1 optimized return fails 33,789 pixels/max RGB 6/5/1 with sampled H2
exact; shadow contribution is NOT EXERCISED. The distinct GPU 1905a20 first epoch
is rejected by a modifier-inappropriate all-plane stride predicate, with
read_success NOT PROVEN; whole-field causality remains INCONCLUSIVE. These
findings adopt no shader correction, popup UX, public ABI or production dependency.
Native compositor/tree authority and Row0 borrowing remain unchanged.
Scientific status PARTIALLY_ADVANCED; recommendation PRIVATE_CORE_NEEDS_TARGETED_EVIDENCE.
Scoped evidence delta review ACCEPTED; final dossier review pending and
documentary publication PREPARED at this edition. STEP20A DONE; STEP20B
EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED; production multi-output
NOT YET SUPPORTED. Production integration is not authorized.

View file

@ -561,3 +561,37 @@ documentary publication PREPARED at this edition, with actual refs in the privat
post-push receipt. STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT;
STEP20D–G PLANNED; production multi-output NOT YET SUPPORTED. No production,
UX or package contract changes, and no authorization for further graphics runs.
## Minimal private core limits — 2026-10-14
The [new exact-core campaign](design/STEP20C_MINIMAL_CORE_ABI_PACKAGE_AND_INTEGRATION_DECISION.md)
is separate from the historical source-only core: build and extracted C unit
normal/UBSan/ASan gates now pass on their own sources. Compositor sanitizer
remains NOT RUN; OOM sidecar allocation abort remains experimental/P1 and does
not qualify production availability. Local ELF/relocations/layout/provider
checks and five-file Stage1 pass, but future pairs, maintained interposition,
full package reproduction and notices/legal review remain open.
Twelve runs consume 88 captures without retries. E0-pristine/E0-core/vertical
fail before images, leaving no comparable new standard control. E1's first
grabbed chain is sampled but second-owner ancestry stops standard return.
Fractional/R90 and resize/migration pass their bounded measurements; E4 has an
actual non-leaf fullscreen ancestor without comparable standard policy control.
E5 dismissal prevents same-chain hidden-owner return. E6 has a real native
five-second timeout before ACK/teardown, but retains its frozen-validator FAIL,
normal-close limit and unobserved native saved identity. Runner process cleanup
PASS is not universal Wayland error 0 closure; E2 helpers error 32/marker timeout,
E1/E6 closure gaps and G1 client exit 1 remain visible.
Core classic blur is exercised. Whole-H1 A/A-return remains FAIL 33,789 pixels,
max RGB 6/5/1, with H2 sampled exact; shadow contribution is NOT EXERCISED.
G1's sole first epoch is rejected: the fixture's all-plane image-stride predicate
is inappropriate for the observed AMD DCC modifier. Raw GPU read_success stays
NOT PROVEN, GL error queue UNOBSERVED and whole-field causality INCONCLUSIVE.
Native buffer/GPU lifetime, atomic generation and complete damage remain
NOT PROVEN; damage-ring extents do not prove frame presentation.
Recommendation PRIVATE_CORE_NEEDS_TARGETED_EVIDENCE, scientific status
PARTIALLY_ADVANCED. Evidence delta review ACCEPTED; final dossier review pending,
publication PREPARED at this edition. Production multi-output NOT YET SUPPORTED;
STEP20C NEXT. No production, UX, package or historical evidence change.

View file

@ -449,3 +449,33 @@ STEP20D–G PLANNED; production multi-output NOT YET SUPPORTED. No production,
UX or package contract changes, and no authorization for further graphics runs.
New experimental batch reports: [saved natural timeout and epoch identity](design/STEP20C_SAVED_NATURAL_TIMEOUT_AND_EPOCH_IDENTITY.md), [input event time and applied camera](design/STEP20C_INPUT_EVENT_TIME_AND_APPLIED_CAMERA.md), [GPU FBO/cache causality](design/STEP20C_GPU_FBO_CACHE_CAUSALITY.md), [native present/damage/lifetime](design/STEP20C_NATIVE_PRESENT_DAMAGE_LIFETIME.md), [minimal private source-only core](design/STEP20C_MINIMAL_PRIVATE_INTEGRATION_CORE.md), [gate closure V2](design/STEP20C_GATE_CLOSURE_DECISION_V2.md) and [current integration draft](design/STEP20C_PRODUCTION_INTEGRATION_DECISION_DRAFT.md). The [fixture documentation](../tooling/probes/step20c-saved-input-gpu-batch/README.md) distinguishes sealed inputs from post-run results.
## Minimal private core build/runtime/GPU gate — 2026-10-14
This new campaign is EXPERIMENTAL / PARTIALLY_ADVANCED, recommendation
PRIVATE_CORE_NEEDS_TARGETED_EVIDENCE. It preserves the historical source-only
core while recording the new core 29baf31 build/C unit/local ABI/Stage1 evidence
and its own graphics. Twelve unique runs consume 88 output captures, with 90
retained PNG files including two source markers; no retry and two conditional
slots NOT EXERCISED. Fractional/R90 and resize/migration are sampled positive;
standard/vertical/fullscreen/hidden return remain incomplete. Native timeout
is observed on E6 before ACK/teardown, while its global validator fails. Core
whole-H1 return FAIL and distinct G1 rejected first epoch remain visible.
| Report | Scope and limits |
| --- | --- |
| [Build and C failure paths](design/STEP20C_MINIMAL_CORE_BUILD_AND_C_FAILURE_PATHS.md) | Exact parents/replay, six original UB reproductions, private camera guard, real C unit sanitizers, OOM abort/P1, ELF and Stage1 |
| [Core graphics qualification](design/STEP20C_MINIMAL_CORE_GRAPHICS_QUALIFICATION.md) | Eleven E runs, per-case measurements/exits/normal-client closure and missing controls |
| [Lifetime, damage and transactions](design/STEP20C_MINIMAL_CORE_LIFETIME_DAMAGE_AND_TXN.md) | Real timeout despite rejected receipt, sampled input/damage, native identity/GPU/atomic/complete-damage NOT PROVEN |
| [GPU full-field freshness follow-up](design/STEP20C_GPU_FULL_FIELD_FRESHNESS_FOLLOWUP.md) | Separate GPU 1905a20, one rejected epoch, modifier-aware fixture diagnosis without accepting raw readback |
| [ABI/package and integration decision](design/STEP20C_MINIMAL_CORE_ABI_PACKAGE_AND_INTEGRATION_DECISION.md) | Budget/identity/marker ledger, exact-pair/local stage limits, P0–P3 and human decisions |
The [compact source/provenance fixture](../tooling/probes/step20c-minimal-core-gate/README.md)
preserves reviewed fixture inputs and small evidence metadata; core bodies,
functional core patches, GPU hook, binaries, raw data and build trees stay in
the private LAB. Existing commands are provenance, not permission to replay
spent reservations. Scoped evidence delta review ACCEPTED; final independent
dossier review pending and documentary publication PREPARED at this edition.
Actual publication refs belong to the private post-push receipt/final handoff.
STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED;
production multi-output NOT YET SUPPORTED. No production/UX/package change.

View file

@ -553,3 +553,31 @@ documentary publication PREPARED at this edition, with actual refs in the privat
post-push receipt. STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT;
STEP20D–G PLANNED; production multi-output NOT YET SUPPORTED. No production,
UX or package contract changes, and no authorization for further graphics runs.
## Minimal private core handoff — 2026-10-14
The [new build/runtime/GPU dossier](design/STEP20C_MINIMAL_CORE_ABI_PACKAGE_AND_INTEGRATION_DECISION.md)
advances the private core beyond its immutable historical source-only state:
exact build, real extracted C normal/UBSan/ASan, local ABI and five-file Stage1
are recorded, with actual sampled scale 1.25/R90 and resize/migration evidence.
The campaign spends 12/14 runs and88 captures; two conditional slots remain
NOT EXERCISED, not a new runtime authorization. No consumed case is retried.
Recommendation PRIVATE_CORE_NEEDS_TARGETED_EVIDENCE; scientific result
PARTIALLY_ADVANCED. The smallest further evidence gates are a comparable
standard/vertical control and full horizontal return; fullscreen policy and
viable hidden-owner return; native saved/buffer/GPU lifetime and coherent
applied input/render/present proof; complete damage and accepted whole-field
GPU discrimination. The real E6 timeout does not close its rejected global
receipt or native saved identity. G1's modifier-inappropriate stride predicate
must be addressed prospectively with valid metadata/read-success observability
before any separately scoped diagnostic; no shader change is justified here.
Human decisions still include OOM production availability, offscreen popup UX,
release/security ownership, legal distribution and any further experiment's
scope. Exact-pair maintenance and full future package reproduction remain gates.
Final dossier review is pending; publication PREPARED at this edition, with
actual refs in the private post-push receipt/final handoff. Private success
does not authorize integration, distribution, daily use or STEP20D.
STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED;
production multi-output NOT YET SUPPORTED.

View file

@ -0,0 +1,98 @@
# STEP20C — GPU full-field freshness follow-up
> EXPERIMENTAL / INCONCLUSIVE — 2026-10-14. One distinct G1 diagnostic run, no retry. The source design/preflight was accepted; its only raw epoch was rejected before any accepted GPU receipt or analysis. Final dossier review pending; documentary publication PREPARED. STEP20C NEXT; production multi-output NOT YET SUPPORTED.
Ticket: `SWAYFX_MOSAIC_STEP20C_MINIMAL_CORE_BUILD_RUNTIME_AND_GPU_FIELD_GATE_V1`.
LAB: `/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_/`, private and outside Git.
The [historical ROI report](STEP20C_GPU_FBO_CACHE_CAUSALITY.md) remains immutable:
its108 native ROI reads support local outside-fresh-domain history, first
sampled fresh divergence down-2, with whole-field causality INCONCLUSIVE.
Those observations do not qualify the new core or G1.
## Source question and distinct identity
`LAB/gpu-design/HANDOFF.md` records a source-only prospective design. Full
physical 800×600 ping/pong textures remain allocated across down/up passes;
fresh rasterized regions change by pass, rather than resizing those textures.
The audit distinguishes geometry regions, viewport/scissor state, shader
qualifiers, actual buffer formats, copy/cache and arithmetic precision. It does
not treat a `mediump` source declaration or a precision query as a measurement
of effective GPU rounding. The intended experiment extends local support
observations to A/B/A fields with prospective readback/data bounds and explicit
GL/FBO/pack/restoration checks. It does not modify functional shaders, clears,
clamps or samplers.
The separately built/executed diagnostic has SHA 256
`1905a20fa72ebcc7ec47d1584a4653654280288096a20005199eb75673ac0383`, Build ID
`1505e31c3dc08d2a7799c2ee9e1b2705eb61f5fe`. It is not core SHA 29baf31.
Source gate SHA 256
`58535958c0c55b3bee758bfda413df594302122f9a9da2e1a4aedde3889b9f5f`
and independent source/runtime preflights are ACCEPTED for their scoped inputs.
Actual maps/logs identify the diagnostic and wlroots SHA 256
`7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`,
without a dynamic SceneFX mapping. Logs identify EGL Mesa/radeonsi, OpenGL ES3.2
and AMD Radeon780M, with renderD128 descriptors observed. Two EGL_BAD_ALLOC
messages occur during eglQueryDevicesEXT before successful renderer startup;
no causal connection to the later metadata rejection is demonstrated.
## Sole run and immediate rejection
The recorded command `python -B gpu-field/runner.py` returns1 after 4.743s,
`Rejected: dmabuf fields`. It spends one run and three output captures: two H2
references and one H1 witness. Only epoch 1/A is armed. The native log emits
`GPU_FIELD end epoch=1 points=9 complete=1`; these are publication markers for
raw data, not an accepted readback result. No B/epoch 2 or A-return/epoch 3,
accepted epoch receipt or full-field analysis is produced.
| Raw file | Size | SHA 256 |
| --- | ---: | --- |
| `LAB/gpu-field/runtime/epoch-1.meta` | 19,686 bytes | `c8ca51bca694db9e21471356a4260064ed81ab7013a88d4edb4b8572044a825c` |
| `LAB/gpu-field/runtime/epoch-1.rgba` | 8,312,048 bytes | `a6dc7fd873e1ab7e52e04625bacb3251ba8e1aad2e766811efebde7ae7932a99` |
The metadata has 24 records:1 begin,9 samples,11 writes,2 copies,1 end.
`read_success=NOT_PROVEN`, `GL_ERROR_QUEUE=UNOBSERVED`, effective precision
UNCERTAIN. File sizes/hashes establish retained integrity, not glReadPixels
success, synchronization neutrality or independently complete GL restoration.
The read-only diagnosis in `LAB/audit/gpu-rejection/G1-rejection.md` compares
the writer and frozen validator. The validator requires **every** DMA-BUF plane
stride to be at least800×4=3200. All nine samples report DMA-BUF available,
size 800×600 and planes `[[3584,0],[1024,2293760]]`. The first stride passes;
the second1024 fails that first demonstrated rejecting predicate. Role formats
match the validator: main XR24/875713112, effects AB24/875708993, cache
XB24/875709016. Neither absent DMA-BUF nor a wrong role format explains this
immediate rejection. This initial rejection-only audit establishes no GPU
failure, driver defect or specific DCC explanation; raw pixels remain rejected.
A subsequent read-only modifier audit, separately recorded in
`LAB/audit/gpu-rejection/modifier-layout-audit.json`, observes the same modifier
`020000001046bb04` in all nine objects: AMD vendor 2, GFX11 tile version 4,
DCC 1, RETILE 0, PIPE_ALIGN 1. The local libdrm header and the
[Linux DRM format modifier header](https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/drm/drm_fourcc.h)
describe main image surface in plane0 and DCC surface in plane1 for this
non-retiled layout. Requiring the image stride `width*4` on every plane is
therefore not justified for this modifier: a fixture predicate defect is
established. This interpretation neither proves global DMA-BUF validity nor
GL read success and changes no raw gate. No validator repair or replay occurs.
## Cleanup, status and next evidence
Compositor exits 0; background, filler0 and glass exit1 while the other tracked
processes exit0. Scientific cleanup remains INCONCLUSIVE; mechanical cleanup
passes with the private runtime removed and identified processes reaped.
`LAB/gpu-execution/G1-summary.json` indexes49 artefacts with tree manifest SHA 256
`ccd45c0a56c1ee72ff7bcd84711090745012911a7ecc3e0d890aa52ccb715329`.
The scout's read-only audit verifies these49 hashes; it does not rerun the
validator or reconstruct unavailable data. No diagnostic or core retry,
functional shader correction, system-library change or historical replay follows.
`GPU_FULL_FIELD_DESIGN_REVIEW=PASS` denotes the accepted design review receipt.
`GPU_FULL_FIELD_DIAGNOSTIC=INCONCLUSIVE` and
`GPU_WHOLE_FIELD_CAUSALITY=INCONCLUSIVE` denote actual evidence. A new proposed
gate must first specify and review valid per-plane metadata semantics and
read-success/error/restoration observability, using primary format evidence;
only a separately accepted scope/budget may acquire another diagnostic matrix.
No remaining slot is consumed by this documentary suggestion. The
[integration dossier](STEP20C_MINIMAL_CORE_ABI_PACKAGE_AND_INTEGRATION_DECISION.md)
retains PRIVATE_CORE_NEEDS_TARGETED_EVIDENCE; this rejected first epoch closes
neither core graphics nor whole-field causality.

View file

@ -0,0 +1,182 @@
# STEP20C — minimal core ABI, package and integration decision
> EXPERIMENTAL / PARTIALLY_ADVANCED — 2026-10-14. Ticket `SWAYFX_MOSAIC_STEP20C_MINIMAL_CORE_BUILD_RUNTIME_AND_GPU_FIELD_GATE_V1`. Recommendation: **PRIVATE_CORE_NEEDS_TARGETED_EVIDENCE**. Evidence delta review ACCEPTED for experimental fidelity; final independent dossier review pending at this edition. Documentary publication PREPARED, not an executed publication receipt. STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED; production multi-output NOT YET SUPPORTED.
LAB is `/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_/`, private and outside Git.
The report combines the [build/C evidence](STEP20C_MINIMAL_CORE_BUILD_AND_C_FAILURE_PATHS.md),
[graphics matrix](STEP20C_MINIMAL_CORE_GRAPHICS_QUALIFICATION.md),
[lifetime/damage/transaction limits](STEP20C_MINIMAL_CORE_LIFETIME_DAMAGE_AND_TXN.md)
and [GPU follow-up](STEP20C_GPU_FULL_FIELD_FRESHNESS_FOLLOWUP.md). It supplements
the [historical source-only report](STEP20C_MINIMAL_PRIVATE_INTEGRATION_CORE.md);
historical FROZEN evidence and consumed budgets remain unchanged.
## Identity, budget and lifecycle state
The private core SHA 256 is
`29baf31aa3eeed259825c87d0799fa5d3dc83710afb967154604eb1c6657f965`, Build ID
`1b42e557e38db3440ae8dcf7ac967e4132223365`. The distinct GPU diagnostic is
`1905a20fa72ebcc7ec47d1584a4653654280288096a20005199eb75673ac0383`, Build ID
`1505e31c3dc08d2a7799c2ee9e1b2705eb61f5fe`. Both are tied to the observed
wlroots SHA 256 `7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`.
No cross-binary qualification transfer is accepted.
| Resource | Actual consumption | Boundary |
| --- | --- | --- |
| Headless runs | 12/14:11 E and1 G1 | No retries; two conditional slots NOT EXERCISED |
| Output captures | 88:85 E and3 G1 | 180 maximum; failures remain consumed |
| Retained PNG files | 90 | 88 output captures plus two source markers |
| Core builds | One initial SceneFX and one initial Mosaic | Two per-component maximum; no correction core build |
| GPU builds | One distinct diagnostic SceneFX/Mosaic pair | Separate identity and evidence |
| Compositor sanitizer | Zero builds/runs | NOT RUN; C unit sanitizer results remain separate |
| Private stages | Stage1 only | No public package/install/rollback test |
The read-only final protection/budget/process audit records 12 unique run tokens,
166 reserved capture limits and 88 actual debits. Configuration/chain/camera/
move/click/grab/mutation action counts are 137/9/13/34/25/12/28. Its 316 protected
file bindings and 307 historical-reference bindings remain unchanged. All 12
outcomes report runtime removal; the audit finds zero private process candidates
and zero LAB sockets. These are the audit's recorded observations, not a new
execution performed for documentation.
The three initial E failures occur before PNG; they are spent attempts, not
controls. R2 has four runner exits 1/four 0; all E terminal IPC exits 1 with
compositor0 are preserved. Mechanical cleanup passes; process recovery is not
universal scientific client closure. G1 scientific cleanup is INCONCLUSIVE.
No unconsumed slot authorizes another run after documentary closure by itself.
| Lifecycle distinction | Actual state |
| --- | --- |
| PRIVATE_CORE_BUILDABLE | PASS for the exact reviewed private sources/build |
| PRIVATE_CORE_TESTED_MATRIX | PARTIAL, as the per-cell ledger below specifies |
| READY_FOR_INTEGRATION_REVIEW | NOT READY; targeted evidence and contract decisions remain |
| PRODUCTION_AUTHORIZED | FALSE |
| Documentary preparation | PREPARED at this edition |
| Final independent dossier review | PENDING at this edition |
Source and prebuild/delta reviewers accepted their scoped inputs. The evidence
delta reviewer checks 319 recorded hashes with 319 matches and no divergence,
accepting fidelity with E/F qualification PARTIAL. These scoped reviews do not
substitute for final independent review of the final dossier bytes. Actual
publication commits, remote refs and closeout status belong to the private
post-push receipt and final handoff, not a prospective assertion in this edition.
## Exact-pair ABI and private stage
The core is ELF64,6,744,472 bytes,0755, statically incorporates the private
SceneFX archive and has no SceneFX DT_NEEDED or RPATH/RUNPATH. `ldd -r` exits 0
without unresolved symbols observed. Private clip exports are present. Existing
non-graphical `LD_BIND_NOW --version` evidence has 43 bindings for40 unique scene
symbols. Layout measurements are node 112/tree 128 bytes, offsets parent 8,
enabled 32, visible 88, children 112. These checks qualify local observations for
the exact pair; they do not establish universal runtime ABI compatibility,
future update safety or absence of interposition hazards. The historical85
overlaps are not silently reclassified as defects or a new core inventory.
The actual archive SHA 256 is
`191ec14fc08c4aeb83e6b313c0f23cda67e10bebe15e29d47bfd192256286f3a`.
Compile-command/Ninja dependency inventory records 885 unique hashed files,
277 translation units and32 generated protocols/shaders. The build report
preserves the failed provenance helper and initially unrecorded Ninja exit,
with a separate later read-only exit0 record; no original receipt is rewritten.
Stage1 verifies exactly the following regular files, without symlinks:
| DESTDIR path | Mode |
| --- | --- |
| `/usr/bin/swayfx-mosaic` | 0755 |
| `/usr/share/licenses/swayfx-mosaic/SceneFX-LICENSE` | 0644 |
| `/usr/share/licenses/swayfx-mosaic/SwayFX-LICENSE` | 0644 |
| `/usr/share/licenses/swayfx-mosaic/wlroots-LICENSE` | 0644 |
| `/usr/share/licenses/swayfx-mosaic/INCORPORATED-NOTICES` | 0644 |
This passes file-shape validation under the
[approved private static contract](STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md).
Notices remain PARTIAL / LEGAL_NOT_APPROVED; exact incorporation coverage,
full future package reproduction and named release/security ownership are
not closed. No full makepkg/package build or real install/rollback test is
claimed. The current [package contract](../PACKAGING.md), PKGBUILD/system
SceneFX dependency and one-binary production package remain unchanged.
Ordinary SwayFX and `/usr/bin/sway` remain independent fallback requirements.
## Evidence marker ledger
| Marker | Recorded result |
| --- | --- |
| MINIMAL_CORE_SOURCE_REPLAY | PASS |
| CORE_C_GEOMETRY_TESTS | PASS, extracted-body unit scope |
| CORE_OOM_POLICY | OOM_ABORT_EXPERIMENTAL; production availability open |
| CORE_SANITIZER_TESTS | PASS unit normal/UBSan/ASan; compositor NOT RUN |
| CORE_PRIVATE_BUILD | PASS |
| CORE_PRIVATE_ELF_ABI | PASS local ELF/relocation/layout checks only |
| CORE_PRIVATE_STAGE | PASS five-file shape; notices PARTIAL / LEGAL_NOT_APPROVED |
| CORE_GRAPHICS_PREFLIGHT | PASS for the frozen inputs actually executed |
| CORE_PRISTINE_STANDARD | INCONCLUSIVE, initial pre-capture fixture failure |
| CORE_HORIZONTAL | INCONCLUSIVE overall; first-chain PARTIALLY_FEASIBLE, standard return NOT EXERCISED |
| CORE_VERTICAL | INCONCLUSIVE, pre-capture fixture failure |
| CORE_FRACTIONAL | PARTIALLY_FEASIBLE for sampled scale 1.25 matrix, closure limits retained |
| CORE_R90 | PARTIALLY_FEASIBLE for sampled scale 1.25/R90 matrix, closure limits retained |
| CORE_DYNAMIC | PARTIALLY_FEASIBLE for sampled resize/clamp/migration/input |
| CORE_FULLSCREEN | INCONCLUSIVE, actual non-leaf ancestor but no comparable standard control |
| CORE_HIDDEN_OWNER | PARTIALLY_FEASIBLE hidden samples; full same-chain return NOT EXERCISED |
| CORE_SAVED_TIMEOUT | INCONCLUSIVE full scenario; native five-second timeout observed before ACK/teardown |
| CORE_FX | PARTIALLY_FEASIBLE; classic exercised, whole-H1 FAIL, shadow NOT EXERCISED |
| CORE_SAMPLED_APPLIED_INPUT | PARTIALLY_FEASIBLE |
| CORE_SAMPLED_DAMAGE | PARTIALLY_FEASIBLE |
| CORE_NATIVE_BUFFER_IDENTITY | NOT_PROVEN |
| CORE_GPU_LIFETIME | NOT_PROVEN |
| CORE_ATOMIC_GENERATION | NOT_PROVEN |
| CORE_COMPLETE_DAMAGE | NOT_PROVEN |
| GPU_FULL_FIELD_DESIGN_REVIEW | PASS for accepted scoped source/preflight review |
| GPU_FULL_FIELD_DIAGNOSTIC | INCONCLUSIVE, sole raw epoch rejected |
| GPU_WHOLE_FIELD_CAUSALITY | INCONCLUSIVE |
| CORE_PRIVATE_PACKAGE_ABI | PARTIALLY_FEASIBLE; exact-pair components pass, maintenance/package/legal gates open |
| CORE_FINAL_INDEPENDENT_REVIEW | NOT_PROVEN at this edition; final review pending |
| CORE_DOCUMENTARY_PUBLICATION | PARTIAL at this edition; PREPARED lifecycle state, no publication PASS asserted here |
`SWAYFX_MOSAIC_STEP20C_MINIMAL_CORE_BUILD_RUNTIME_AND_GPU_FIELD_GATE_V1=PARTIALLY_ADVANCED`
records the scientific advancement of this campaign. It does not mean
PRODUCTION_READY or supersede raw failures.
## Risks, reproduction scope and closing criteria
P0–P3 below prioritize remaining evidence/decisions; an unclosed gate is not
automatically a demonstrated product defect. The scoped reviewers report no
blocking product finding in the accepted evidence delta.
| Priority / fact | Identity, source and reproducer | Scope and closing criterion | Dependencies / human decision |
| --- | --- | --- | --- |
| P0 integration exclusion: production is not authorized | Exact core 29baf31, ticket and canonical architecture/package contracts | Preserve production boundaries until relevant gates and contracts are explicitly accepted | Separate production/merge/distribution decision; no automatic consequence of private success |
| P1 OOM availability: sidecar allocation failure aborts | Exact extracted SceneFX bodies, `c-tests-lifecycle` targeted second-allocation failure; three raw SIGABRT -6 | Unit path proven, compositor survival not proven; close with an accepted availability response and exact C/runtime evidence | OptionA remains experimental; alternativesB/C not demonstrated, no new public ABI/policy invented |
| P1 native lifetime and coherent applied state remain unproved | Core29baf31 E6 wire/native timeout; native scene transaction/source callbacks; no C3/PI identity transfer | Close native buffer identity, reference/retirement/release/GPU completion, coherent scene/input/present and complete damage independently | Required rendering/input coherence contract; choose mechanism only after documented decision |
| P1 whole-field effects uncertainty | Core29baf31 E7 whole-H1 FAIL 33,789/max RGB 6/5/1; G1 distinct1905a20 rejected first epoch | Preserve raw differences; acquire accepted discriminating full-field evidence with valid modifier-aware metadata and GL success/restoration observability | New scoped/reviewed experiment; no shader/clear/clamp change from an unaccepted hypothesis |
| P1 maintenance and distribution | Core exact-pair symbols/layout/providers, Stage1 and incorporation inventory | Complete notices/legal review, reproducible future package, dependency/security rebuild and exact-pair requalification plan | Name release/security owners; separate public distribution decision; all other pairs NOT QUALIFIED |
| P2 missing controls and transitions | Initial E0/vertical fixture failures, E1 second owner wrong ancestry, E4 no standard fullscreen control, E5 dismissed chain | New accepted fixture/scope for comparable standard/vertical, full standard return, actual fullscreen policy and viable hidden-owner return evidence | Offscreen-popup hide/dismiss/reposition/click-through UX remains UNDECIDED; no universal promise |
| P2 limited client closure and sanitizer scope | E1/E6 normal close unacquired, E2 helpers error 32/marker timeout, G1 clients1; unit sanitizers only | Attest normal client closure independently from terminal/process cleanup; native compositor sanitizer if justified by a new gate | Separate bounded scope and binary identity; no consumed retry |
| P3 evidence/publication completion | Final source/report/fixture bytes, exact manifests and scoped delta review | Final independent dossier review, compact public provenance and actual publication/closeout receipts | Publication lifecycle recorded truthfully; do not upgrade gates merely to finish documentation |
## Recommendation and next decisions
**PRIVATE_CORE_NEEDS_TARGETED_EVIDENCE** is the single recommendation.
The new core is actually buildable and passes its real extracted C tests and
sampled scale/R90/dynamic matrix. It still lacks a comparable new standard
control, full horizontal return, qualified fullscreen/hidden transition,
native saved/GPU lifetime and atomic-generation/complete-damage evidence.
G1's rejected first epoch cannot settle whole-field GPU attribution. These
facts justify targeted evidence, not a redesign conclusion or integration readiness.
The next human decisions are the production OOM availability response;
offscreen popup/fullscreen expectations where controls are incomplete;
release/security maintenance owners and legal distribution closure; and the
scope of any further targeted native/graphics/GPU gates. This report adopts
none of those decisions. Native compositor authority and Row0 borrowing remain
required, and [UX](../UX.md), AGENTS, production sources/tests, PKGBUILD and
the cumulative production patch are unchanged. No STEP20D or daily-driver
qualification follows. Private raw logs/readbacks/builds/core patches stay
outside Git; compact public evidence can record their hashes and limitations.
The [public source/provenance fixture](../../tooling/probes/step20c-minimal-core-gate/README.md)
preserves compact exact inputs and metadata. Its C extraction runners require
the external private source bodies; archived graphics runners cannot relaunch
consumed reservations. It contains no functional core patch, extracted core
body, GPU hook, binary or raw readback. It does not constitute an independently
redistributable source package or an authorization for a new experiment.

View file

@ -0,0 +1,232 @@
# STEP20C — build du noyau privé minimal et chemins d'échec C
> EXPERIMENTAL — édition du 2026-10-14. Ticket `SWAYFX_MOSAIC_STEP20C_MINIMAL_CORE_BUILD_RUNTIME_AND_GPU_FIELD_GATE_V1`. Ce rapport consigne uniquement les preuves déjà acquises des phases provenance, C et build privé. ACCEPTED qualifie les revues bornées ; PASS qualifie les contrôles indiqués. Aucun verdict global de la matrice graphique n'est établi ici. STEP20A DONE ; STEP20B EXPERIMENTAL ; STEP20C NEXT ; STEP20D–G PLANNED ; production multi-output NOT YET SUPPORTED.
LAB désigne `/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_/`, laboratoire
privé hors Git. Ses chemins désignent des preuves locales accessibles, pas une
distribution autonome ni des artefacts publiés. Le ticket précédent et ses
laboratoires restent historiques et en lecture seule. Le
[rapport source-only historique](STEP20C_MINIMAL_PRIVATE_INTEGRATION_CORE.md)
conserve son état BUILD/C/UBSan/OOM NOT RUN ; les preuves nouvelles ci-dessous
portent sur un nouveau candidat et sa propre identité.
Les [contrats d'architecture](../ARCHITECTURE.md), [UX](../UX.md),
[packaging](../PACKAGING.md) et le
[registre des décisions statiques privées](STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md)
gardent leur autorité. Row 0 emprunte `workspace->tiling` ; le compositeur natif
reste l'autorité de scène, transactions, géométrie et input. Aucun sous-système,
contrat UX popup ou mécanisme de génération atomique n'est adopté par ce rapport.
## Provenance et rejeu du candidat historique
Le dépôt de départ est `main`, commit
`c4bdfb46aadaa4e30a9a05a3238702f56651eec6`, propre lors du contrôle initial.
`LAB/audit/source-replay.json` enregistre le rejeu réel des deux patches minimaux,
les contrôles forward/reverse et l'équivalence SHA256 de 417 fichiers Mosaic et
85 fichiers SceneFX. `LAB/audit/parent-provenance.json` lie les parents aux
archives et conserve l'inventaire historique sélectionné/exclu et ses hashes.
Ces deltas s'appliquent à leurs parents exacts ; le patch Mosaic minimal n'est
pas présenté comme un delta directement applicable au SwayFX pristine.
| Composant | Parent ou identité épinglée |
| --- | --- |
| SwayFX pristine 0.6 | `fd71a6bdc061bd633b488ae7b83e8a6981d22f86` |
| Parent Mosaic privé | `cd550382b311efdbfe3ed892f4e1d281d852e754` |
| Parent SceneFX 0.5 | `37ccd723bef49e6891156ffafce8f549f01446cc` |
| Source wlroots | `d783533489e1f75d6886c2ab5c5960090ef268f8` |
| Bibliothèque wlroots observée | `/usr/lib/libwlroots-0.20.so`, SHA256 `7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837` |
Le dossier historique distingue huit patches fonctionnels et deux patches de
commentaires. Le rejeu conserve clip/input natif, ownership root/nested popup,
hidden-owner guard, retirement/retour standard et lifetime fonctionnelle ; il
n'ajoute pas les instrumentations PI/C3/OPT ni un compteur de génération.
Les quatre statements de log intrinsèques conservés ne prouvent pas leur
neutralité. Le manifeste de provenance reste la référence pour les composants
sélectionnés ; aucun résultat des anciens binaires A/G/GPU ne se transfère.
| Artefact historique source-only | SHA256 |
| --- | --- |
| `LAB/mosaic-minimal.patch` | `af680be7322a9257769cadc137c487b7f9f880a0b0d66df7701ae26b8baf1fe6` |
| `LAB/scene-minimal.patch` | `750337f75f9dc5ca655d3cd10f49bdd04169cca4dc53459d7f0d9eb8ad4db25a` |
| Transaction avant correction caméra | `e5066df7808a1633c635582c052ba89931c2856a54f6b471ff1009a4b455ef6c` |
| Scene privée | `99992429f9a1d2754042989d89904fb6d705f963d74f4456d737fc9da3e0927d` |
## UB caméra réellement reproduite et correction privée
`LAB/c-tests/` compile les corps C extraits et conserve les commandes, sorties,
bindings source et manifeste initial. La suite définie initiale passe avec
177 assertions, mais six reproducteurs UBSan empêchent la qualification C :
NaN, +Inf, -Inf, borne supérieure int64 et valeur sous la borne inférieure
déclenchent `float-cast-overflow` ; INT64_MIN déclenche un overflow signé dans
la soustraction de `workspace_tiling_scene_x`. La portée d'atteignabilité de
ces valeurs dans le compositeur runtime n'est pas démontrée. Ces échecs bruts
restent conservés, sans réécriture rétrospective du verdict initial BLOCKED.
Une correction privée bornée garde le corps fonctionnel existant et vérifie
`isfinite(scroll)` et `INT_MIN <= scroll <= INT_MAX` avant conversion et
soustraction. Les caméras valides gardent la troncature vers zéro et le clamp
final de position. Pour une caméra invalide, l'origine reste non décalée et
le clip vide de la politique existante d'`arrange_output` masque la scène.
Ce garde correspond à la plage déjà employée ; il n'invente pas une politique
UX des valeurs invalides.
| Nouvelle identité privée | SHA256 |
| --- | --- |
| `LAB/camera-boundary-fix.patch` | `adec2cb5dd11c4347c519e13717b9a0744bac8389f277ce85b39fc13df9c459e` |
| `LAB/mosaic-fixed/sway/desktop/transaction.c` | `0b1d31bb8458dceaff34cc2a2987044b3096e6ebc96e68b522c0ac3540bc8ecd` |
| `LAB/audit/candidate-source-freeze.json` | `f2f5c4f4823c0dd33bc2a9085d5eaa114256a2501add0ffd417f8d796728d787` |
La seule différence fonctionnelle de cette réparation est la transaction
privée. `LAB/c-tests-fixed/` enregistre 184 assertions définies, 14 cas
d'intersection sous trois modes d'alias, 12 translations sous deux modes
d'alias et 13 gardes scroll. Normal, UBSan avec `float-cast-overflow` et ASan
passent, y compris les six anciennes valeurs déclenchantes. La revue
`LAB/reviews/camera-delta-review.json` est ACCEPTED pour ce delta : 26 commandes
enregistrées ont un exit zéro ; le reviewer ne lance aucun build/runtime.
Le manifeste initial `c-tests` demeure intact.
## Lifecycle, popup et OOM : preuve unitaire uniquement
`LAB/c-tests-lifecycle/bindings.json` lie cinq slices exactes aux offsets byte,
lignes, SHA256 du source intégral et SHA256 extrait : sidecar/find/remove/destroy,
setter/getter, translate/intersect/point-inside, iterator de hit et
`probe_clip_popup` avec son intersection. Les corps sont copiés sans réécriture.
Les types autour sont réduits ; coordinates, conversions, logging et scene
update sont des supports explicites, dont la mise à jour de scène est stub.
Les listes/listeners/signaux destroy utilisent Wayland serveur installé et les
intersections utilisent Pixman installé ; ces bibliothèques ne sont pas elles-mêmes
recompilées avec sanitizers.
Normal, UBSan et ASan avec détection des fuites passent pour les slices :
- NULL répété sans allocation/update, clip copié et idempotent, zéro installé
puis suppression NULL ; une sidecar/listener par arbre, retrait explicite et
retrait via émission native des signaux destroy ;
- intersection héritée parent/child, refus hors clip et sous masque zéro avant
callback `point_accepts_input`, translation invalide refusée ;
- véritable corps `probe_clip_popup` : propriétaire tiled Mosaic, viewport local,
ascendance fullscreen **current**, contradiction pending ignorée, standard,
workspace inactif, owner caché puis visible, fullscreen workspace, owner
non-tiled, clip absent et owner absent ; héritage nested de la sidecar root.
L'échec initial de fixture est conservé dans `attempt-1/` : Pixman peut retourner
`not_empty=1` pour un rectangle dégénéré d'aire zéro, avec extents
`(15,25,15,25)` et aucun point contenu. Le diagnostic indépendant le reproduit.
Le prédicat final vérifie l'absence de rectangles dont largeur **et** hauteur
sont positives. Aucun corps extrait n'a changé ; cette assertion initiale ne
démontre pas un défaut du source.
L'injection vise exclusivement le `calloc` de sidecar après une première
allocation réussie. Les trois variantes terminent avec le code brut
`-6 / SIGABRT`, `RLIMIT_CORE=(0,0)`, sans diagnostic sanitizer précédant l'arrêt
volontaire. Cela exerce l'abort existant et ne prouve ni survie du compositeur
ni état graphique sûr après panne. La revue OOM conserve l'option A,
`CORE_OOM_POLICY=OOM_ABORT_EXPERIMENTAL`, risque de disponibilité **P1**.
L'abort ne constitue pas une disponibilité production acceptable ni un
fail-closed survivant. Les alternatives B/C ne sont pas démontrées et aucune
signature publique ou politique de remplacement n'est définie ici.
`LAB/reviews/c-prebuild-review.json` accepte le premier build privé non-OOM
pour 502 sources et les bindings finaux, avec `C_gates=PASS_UNIT_ONLY`.
Construction/destruction intégrale de scène, stockage réellement détruit,
protocole XDG/grabs/serials, cycle natif root/nested, render/damage, transaction,
concurrence, GPU et shutdown restent hors preuve unitaire. L'émission native
d'un signal sur un node de fixture en stack ne qualifie pas ces lifetimes.
**Sanitizers unitaires : PASS ; compositeur sanitizer : NOT RUN dans ces phases.**
## Build privé, lien statique et identité ELF propre
`LAB/build-audit/handoff.json` lie le build à la revue acceptée et au manifeste
des 502 sources. Un build initial SceneFX et un build initial Mosaic sont
enregistrés : respectivement 38/38 et 275/275 tâches, exits zéro. SceneFX utilise
`default_library=static`, prefix `/usr`, sans exemples ; Mosaic utilise le
mode package privé, prefix `/usr`, bindir `bin`, sans swaybar/swaynag/man-pages
ni wallpaper. Le `.pc` effectif est celui de `scene-build/meson-uninstalled/` ;
la commande de lien contient `LAB/scene-build/libscenefx-0.5.a`, SHA256
`191ec14fc08c4aeb83e6b313c0f23cda67e10bebe15e29d47bfd192256286f3a`.
Le contrôle syntaxique C17 du header SceneFX privé avec `WLR_USE_UNSTABLE`
passe. `meson test --no-rebuild --print-errorlogs` donne un test Mosaic passé
sur un ; SceneFX termine zéro avec **zéro test défini**. Les avertissements
`LIBINPUT_CONFIG_SCROLL_CIRCULAR` sont conservés dans les logs ; ils n'ont pas
été supprimés pour présenter une compilation sans avertissement.
| Identité du nouveau noyau | Valeur |
| --- | --- |
| Binaire | `LAB/mosaic-build/sway/swayfx-mosaic` |
| SHA256 | `29baf31aa3eeed259825c87d0799fa5d3dc83710afb967154604eb1c6657f965` |
| Build ID | `1b42e557e38db3440ae8dcf7ac967e4132223365` |
| Format / taille / mode | ELF64 / 6 744 472 octets / 0755 |
| SceneFX dynamique | Aucun `DT_NEEDED` SceneFX |
| Chemin dynamique incorporé | Aucun RPATH/RUNPATH |
| Relocations | `ldd -r` exit zéro, aucun symbole non résolu observé |
Ce SHA est distinct des binaires historiques et du diagnostic GPU séparé.
Il qualifie une construction privée BUILDABLE, sans transfert de preuves
graphiques vers ou depuis ces autres binaires.
L'inventaire `LAB/build-audit/new-build-source-inventory.json` dérive des
`compile_commands` et dépendances Ninja : 885 fichiers uniques hashés,
277 unités de traduction, 32 fichiers générés protocoles/shaders. Son SHA256
est `fa9a4e673b77472c34619fd76f692c18cddd9ff1df95eb4c5e8126961ec1fe84`.
Le helper de collecte de provenance a initialement échoué sur
`str has no attribute decode`, après écriture des stdout. La récupération
s'appuie sur ces sorties et un nouveau contrôle `pkg-config --path` enregistré.
L'exit original de l'introspection Ninja reste **NOT RECORDED** dans le handoff.
Une vérification ultérieure distincte et en lecture seule,
`LAB/build-audit/link-command-raw.json`, enregistre exit zéro et présence de
l'archive, avec stdout SHA256
`268ae3f27b8fcfeda63b8d9ab8c70f3c5f50e4fa38660452377056e6e6fa32ca`.
Cette nouvelle observation ne réécrit pas l'exit perdu de la collecte initiale.
## ABI locale et forme du stage privé
`LAB/audit/core-abi-provider-summary.json` résume une capture existante
non graphique `LD_BIND_NOW --version`, exit zéro : 43 bindings pour
40 symboles scène uniques. Les providers et layouts appartiennent à cette
paire exacte ; la résolution forcée ne démontre pas une compatibilité ABI
runtime. Le helper local mesure `sizeof(wlr_scene_node)=112`,
`sizeof(wlr_scene_tree)=128`, offsets parent=8, enabled=32, visible=88 et
children=112. Ces mesures locales ne constituent pas une promesse ABI future.
Un seul stage `LAB/stage1` est enregistré avec `meson install --no-rebuild
--destdir`, exit zéro. `LAB/build-audit/stage1-manifest.json` vérifie exactement
cinq fichiers réguliers, sans symlink ni chemin inattendu :
| Chemin dans DESTDIR | Mode |
| --- | --- |
| `/usr/bin/swayfx-mosaic` | 0755 |
| `/usr/share/licenses/swayfx-mosaic/SceneFX-LICENSE` | 0644 |
| `/usr/share/licenses/swayfx-mosaic/SwayFX-LICENSE` | 0644 |
| `/usr/share/licenses/swayfx-mosaic/wlroots-LICENSE` | 0644 |
| `/usr/share/licenses/swayfx-mosaic/INCORPORATED-NOTICES` | 0644 |
Le binaire stagé possède le même SHA256 que le build. La forme des chemins et
modes passe ; notices **PARTIAL / LEGAL_NOT_APPROVED**. Aucun package complet
`makepkg`, installation réelle ou validation juridique n'est acquis. Les rôles
nommés de release/security, maintenance et requalification des nouvelles paires
restent des gates du contrat privé accepté.
## Frontière de preuve et suite du dossier
Les rapports/reçus `c-tests`, `c-tests-fixed`, `c-tests-lifecycle`, les revues
OOM/caméra/prébuild et `build-audit` conservent commandes, sorties brutes,
manifests et bindings. Leur contenu est consulté ; aucun ancien build, test,
stage ou runtime n'a été répété pour cette rédaction. Les états historiques
FROZEN et SOURCE_ONLY demeurent inchangés.
Les preuves ci-dessus ferment uniquement les contrôles C unitaires indiqués,
la compilation privée, la résolution locale observée et la forme du stage.
GPU lifetime/release/completion, génération atomique scène/input/present,
damage complet et qualifications graphiques pertinentes sont **NOT PROVEN**
par ces phases. Les résultats de la matrice runtime en cours appartiennent à
leur propre dossier ; ce rapport ne leur attribue aucun verdict terminal ni
recommandation globale.
Sources de production, tests du dépôt, API publique, `AGENTS.md`, UX/Packaging,
`PKGBUILD` et patch cumulatif restent inchangés. Aucun commit, push, installation
ou opération sur la session quotidienne n'est effectué par cette rédaction.
La prochaine étape documentaire justifiée est de relier ce rapport aux preuves
runtime réellement terminées lors de la clôture distincte du nouveau ticket,
sans déduire leur résultat de la réussite du build ou des slices C.

View file

@ -0,0 +1,128 @@
# STEP20C — minimal private core graphics qualification
> EXPERIMENTAL — 2026-10-14. Ticket `SWAYFX_MOSAIC_STEP20C_MINIMAL_CORE_BUILD_RUNTIME_AND_GPU_FIELD_GATE_V1`. Eleven E runs executed once; measurement, raw runner exit, terminal IPC and client closure remain separate. Evidence delta review ACCEPTED for experimental fidelity; final dossier review pending at this edition. Documentary publication PREPARED. STEP20C NEXT; production multi-output NOT YET SUPPORTED.
LAB is `/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_/`, outside Git. Paths
below identify private local evidence, not a self-contained public distribution.
The [build/C report](STEP20C_MINIMAL_CORE_BUILD_AND_C_FAILURE_PATHS.md) records
the exact parents, private repair, unit tests and build. The
[integration decision](STEP20C_MINIMAL_CORE_ABI_PACKAGE_AND_INTEGRATION_DECISION.md)
records aggregate limits and remaining gates. Historical reports remain immutable.
## Exact identities and preflight
The core is `LAB/mosaic-build/sway/swayfx-mosaic`, SHA 256
`29baf31aa3eeed259825c87d0799fa5d3dc83710afb967154604eb1c6657f965`, Build ID
`1b42e557e38db3440ae8dcf7ac967e4132223365`. The observed wlroots library is
`/usr/lib/libwlroots-0.20.so`, SHA 256
`7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`.
Core observations do not qualify A/G, historical diagnostics or the distinct
new GPU diagnostic. E0-pristine uses the pristine control, not the core.
Initial preflight accepted 1,095 files, manifest
`9c75d2249a45d2a9cbdbdb3e0cbff91db82bb89b14d6f580ca12a9ed1ed0b705`.
All three initial attempts failed before captures on
`AttributeError: 'NoneType' object has no attribute 'endswith'` while inspecting
an ancestor's `floating` field. They spent their reservations and were not
retried. Offline fixture repairs and reviews before the remaining cases produced
the R2 manifest `5e49909975ea6776a677280a4e444d5014699bf36b68c68fba3345626b196fd3`
with 1,097 files and review
`0e7525b69fdd3e3f75b5c62fb0c3c833557024c6153c3ee033c2d9eebe9d66d0`
ACCEPTED. This is fixture repair before new reservations, not a core algorithm
change or a rerun of E0/vertical.
Private headless runtime directories, Wayland displays and explicit IPC sockets
were used. Actual logs identify the EGL/Mesa/radeonsi renderer; a requested
renderer environment variable alone is not treated as proof. Each scenario has
its own config, startup/maps/output observations, event log, receipt and raw
stdout/stderr/exit under `LAB/graphics/runs/<case>/run/` and the execution folders.
## Executed matrix
Capture counts are output-capture debits. The fractional and R90 cases each
also retain one marker-source PNG, excluded from capture counts.
| Case | Raw runner exit | Measurement outcome | Output captures | Retained boundary |
| --- | ---: | --- | ---: | --- |
| E0-pristine | 1 | INCONCLUSIVE | 0 | Ancestor fixture failure before comparable standard control |
| E0-core | 1 | INCONCLUSIVE | 0 | Same failure before standard-layout measurement |
| E2-vertical | 1 | INCONCLUSIVE | 0 | Same failure before vertical pixels/input |
| E1 | 1 | INCONCLUSIVE | 6 | First chain sampled; second ancestry fails before standard return |
| E2-fractional | 0 | PASS, sampled | 9 | Scale1.25 grabs/pixels/input/dismissal; marker teardown timeout retained |
| E2-r90 | 0 | PASS, sampled | 9 | Scale1.25/R90 grabs/pixels/input/dismissal; same teardown limit |
| E3 | 0 | PASS, sampled | 17 | Resize/clamp and same-owner workspace migration/input |
| E4 | 1 | INCONCLUSIVE | 12 | Actual current fullscreen ancestor; comparable standard control absent |
| E5 | 1 | NOT_EXERCISED for full scenario | 10 | Hidden-owner pixels/input partial; same-chain return absent after dismissal |
| E6 | 1 | INCONCLUSIVE | 8 | Natural timeout observed; frozen held-serial validator rejects receipt |
| E7 | 0 | PARTIALLY_FEASIBLE | 14 | Classic blur exercised; whole-H1 return FAIL; shadow unexercised |
The E matrix spends 11 runs and 85 output captures, retains 87 PNG files,
and does not contain a successful new pristine standard control. All E terminal
IPC commands return 1 while the compositor exits 0. Raw R2 runner exits are
**four 1 / four 0**, in addition to the three initial exits 1. The separate
`LAB/gpu-execution/r2-count-discrepancy.json` corrects an earlier aggregate
progress statement of three/five; per-run raw files and historical summaries
remain unchanged. A runner exit 0 is not a universal terminal-IPC PASS.
## Popup geometry, input and remaining transitions
E1 uses two horizontal headless outputs with the Mosaic owner on H1 and a
whole-H2 stable reference. Its first TOPLEVEL→A→B chain has real
`wl_pointer.button` grab serials 31 and 40, native A350×170 and B300×130.
A is at `(-218,105,350,170)`, B at `(-118,165,300,130)`, both actually cross
x=0. Sampled H1/H2 color counts are owner 142,794/0, A24,080/0, B15,340/0.
Outside input yields `popup_done` and destruction B then A, with no invisible
popup button delivered. The second owner opens on workspace 2/H2/splith rather
than the intended Mosaic ancestry; validation stops before the standard-return
path. That transition, B-close/A-survival and final standard input are
NOT EXERCISED in this core campaign. First-chain evidence is PARTIALLY_FEASIBLE;
the global E1 result remains INCONCLUSIVE.
E2-fractional has H1 physical 1000×750, scale 1.25, logical 800×600 at x=-800;
H2 is800×600 scale1. E2-r90 has H1 physical 750×1000, scale 1.25, transform90,
the same logical output and negative origin, with asymmetric marker evidence.
Each has real grab serials 33/42 and the same logical A/B crossing geometry.
H1/H2 color counts are owner 223,038/0, A37,650/0, B23,961/0. Visible input,
forbidden popup input and sampled outside dismissal pass; reference button
pairs are retained. These samples do not promise universal click-through.
E3 resizes H1 logically800→960→800 and moves the same workspace 6 and owner 10
H1→H2→H1. The clamp is960 after relative request1920 with three children and
gaps0; the existing 640 minimum is an experimental source fact, not final UX
sizing. Four input checks pass at recorded global points `(-273,168)`,
`(-275,168)`, `(366,168)` and `(-434,168)`. Native grab serials 40/49 persist
in the sampled chain. No universal transaction atomicity follows.
E4 observes owner 9 with fullscreen0 beneath non-leaf container 10 with current
fullscreen1; workspace 6 also reports a fullscreen marker. The non-leaf actual
ancestor, rather than that workspace marker alone, establishes the exercised
ascendance. Real grab serials 49/58 and nested captures/input are retained.
Without a comparable standard fullscreen control, exemption/cross-output
policy remains INCONCLUSIVE; an absent clip is not classified as a leak by fiat.
E5 samples hidden-owner masking and input, then the outside click legitimately
causes popup_done and destroys A/B before camera restoration. The same-chain
return is NOT EXERCISED. This does not settle hide/dismiss/reposition UX.
E6's timeout and E7's rendering limits are detailed in the
[lifetime/damage report](STEP20C_MINIMAL_CORE_LIFETIME_DAMAGE_AND_TXN.md).
## Closure, review and reproduction boundary
Every E runner receipt labels `scientific_cleanup=PASS` and records private
runtime removal. That label is the runner's process-recovery gate, not proof
that every client closed normally with Wayland error 0. E1/E6 normal client
closure is not acquired. E2 owners report error 0, but reference/filler helpers
report error 32 after compositor stop; each marker swaybg reaches a three-second
cleanup timeout. E3/E4/E5/E7 normal closure is attested for the relevant clients;
E7's three FX clients terminate error 0 normally before compositor shutdown.
Mechanical cleanup and protocol/normal closure therefore remain separate.
`LAB/graphics-execution/summary.json`, `LAB/graphics-execution-R2/summary.json`
and their per-run manifests preserve hashes and raw observations. Existing
commands are recorded as `python -B graphics/run.py <case>`; they are provenance,
not instructions to rerun a consumed reservation. Any future experiment needs
its own accepted scope, fixture gate and budget. The evidence delta review
checks 319 hashes without divergences and accepts fidelity with E/F qualification
PARTIAL. Final independent dossier review is pending; publication is PREPARED
at this edition, with actual publication identities reserved for the private
post-push receipt and final handoff. No production source, UX or package change.

View file

@ -0,0 +1,101 @@
# STEP20C — minimal core lifetime, damage and transaction limits
> EXPERIMENTAL — 2026-10-14. Same new minimal-core ticket and LAB as the [graphics report](STEP20C_MINIMAL_CORE_GRAPHICS_QUALIFICATION.md). Evidence delta review ACCEPTED for experimental fidelity; final independent dossier review pending. Publication PREPARED. STEP20C NEXT; production multi-output NOT YET SUPPORTED.
LAB is `/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_/`, private and outside Git.
The exact core SHA 256 is
`29baf31aa3eeed259825c87d0799fa5d3dc83710afb967154604eb1c6657f965`.
Historical C3 saved instance15 and PI/C3/OPT instrumentation are not transferred
to this core. Missing PI hooks do not demonstrate that an event did not occur.
The [build/C report](STEP20C_MINIMAL_CORE_BUILD_AND_C_FAILURE_PATHS.md) records
real extracted-body unit tests and their reduced support types; these do not
replace compositor lifetime or rendering proof.
## Natural timeout: native observation and rejected global receipt
E6 opens a real XDG client, arms `hold_next_configure`, then changes runtime
gaps with `gaps inner current set 30`. Owner configure 39 has assigned636×513
and is genuinely held at monotonic ns266629075726834. The native compositor
log records `Transaction ... timed out (1 waiting)` at 00:00:07.998, approximately
five seconds after transaction setup. The held captures precede the timeout;
the natural-timeout captures follow it, before release/ACK and teardown.
This is not an ACK-driven completion or a shutdown timeout.
The release command is recorded at ns266634202244001, ACK 39 at
ns266634202350097 and commit 39 at ns266634203165208. The frozen validator later
requires the held serial to remain unACKed across the accumulated log and
rejects it after the legitimate release/ACK. The raw E6 runner exit1 and
`Ambiguous: held serial must remain unACKed` are retained. A real natural timeout
is demonstrated independently, but the complete saved scenario remains
INCONCLUSIVE with its global receipt not accepted.
Evidence is in `LAB/graphics/runs/E6/run/owner.log`, `compositor.log`,
`events.jsonl`, saved captures and outcome, indexed by
`LAB/graphics-execution-R2/summary.json`. No retrospective validator rerun,
corrected receipt or consumed run retry is used. Normal owner closure is not
acquired; owner error 32 at compositor teardown prevents a universal scientific
normal-close claim. There is no native saved-node identity instrumentation in
this SHA, so saved node/buffer identity is NOT OBSERVABLE here and the native
buffer identity gate remains NOT PROVEN. An SHM commit or a screenshot cannot
identify the retained native saved buffer or its GPU release.
## Input and damage: sampled evidence only
E1's first grabbed chain, E2 fractional/R90 and E3 resize/migration establish
sampled applied geometry, clipped pixels and input observations. E5 supplies
partial hidden-owner evidence before outside dismissal ends the same chain.
Current fullscreen ancestor exemption is actually reached in E4, but its
policy lacks a comparable standard control. The matrix does not establish
an atomic snapshot shared by transactions, scene, hit testing and present.
The private SceneFX `scene_output_damage` intersects damage with the physical
output bounds, logs `SPIKE_DAMAGE` extents and adds the region to damage rings.
An extent is a bounding box of a submitted region. It is not the complete
region, a native frame-content identity, a successful output commit, a present
receipt or proof that every changed pixel was presented. No absent present
event is invented from accepted damage or output state. Scale/transform
observations and sampled whole-output PNG comparisons remain separate from
all-frame logical-to-physical damage coverage.
## Effects and residual restoration
E7 actually exercises classic blur at a fixed64×64 ROI:4,096 pixels change at
constant source, with adjacent variation12.783068783068783 off versus
0.09027777777777778 on. The original source and prospective ROI remain fixed.
Native filter extents are not observable because core OPT instrumentation was
removed. Sampled blur evidence is not evidence for every decoration, saved
buffer, shadow or cache path.
The optimized A/B/A source return retains byte-identical source SHM, but whole
H1 A/A-return equality fails at 33,789 pixels, bbox `[480,0,800,600]`, maximum
absolute RGB delta `[6,5,1]`. Whole H2 remains exact in all three sampled states.
These raw full-image differences remain FAIL; no crop, mask or posthoc
tolerance converts them to PASS. Historical pristine/A/G differences remain
historical comparisons, not a new matched control or evidence transferred
to this SHA. Whole-field causal attribution remains INCONCLUSIVE.
Shadows are enabled, but the observed off/on images are identical. Visible
shadow contribution is therefore **SHADOW_NOT_EXERCISED**, not a validated
shadow exclusion result. The three FX clients close normally with error 0 before
the compositor. E7 remains PARTIALLY_FEASIBLE despite its runner exit0.
The [GPU follow-up](STEP20C_GPU_FULL_FIELD_FRESHNESS_FOLLOWUP.md) documents the
separate diagnostic's rejected epoch and does not repair this core result.
## Gate ledger and closure criteria
| Gate | Recorded status | Evidence needed to close the wider gate |
| --- | --- | --- |
| CORE_SAMPLED_APPLIED_INPUT | PARTIALLY_FEASIBLE | Finish comparable standard/fullscreen/hidden return paths with actual identities |
| CORE_SAMPLED_DAMAGE | PARTIALLY_FEASIBLE | Correlate exact regions and homologous sampled changed pixels without implying all-frame coverage |
| CORE_NATIVE_BUFFER_IDENTITY | NOT PROVEN | Actual native source/scene/saved/output buffer identity join on this binary |
| CORE_GPU_LIFETIME | NOT PROVEN | Native references, retirement/release and GPU completion evidence, independent of client release/teardown |
| CORE_ATOMIC_GENERATION | NOT PROVEN | Evidence for one coherent applied render/input/transaction/present state; no counter mechanism selected |
| CORE_COMPLETE_DAMAGE | NOT PROVEN | Full relevant frame coverage including effects, saved buffers and stale pixels |
CPU unit normal/UBSan/ASan results cover the extracted sidecar/listener/popup
paths only. Full compositor sanitizer is NOT RUN. OOM unit abort remains
`OOM_ABORT_EXPERIMENTAL`, P1 availability risk, not production-safe survival.
Node destruction, Wayland client release, mechanical process cleanup and GPU
completion are different observations. The native compositor/tree authority,
Row0 borrowing and required applied rendering/input consistency are unchanged.
No atomic architecture, popup UX, shader fix or production integration follows.

View file

@ -0,0 +1,99 @@
# Minimal private core gate — preserved experimental fixtures
This is the source archive of the consumed
`SWAYFX_MOSAIC_STEP20C_MINIMAL_CORE_BUILD_RUNTIME_AND_GPU_FIELD_GATE_V1`
campaign, not a production dependency or a new execution authorization.
Scientific result: **PARTIALLY_ADVANCED**. Recommendation:
**PRIVATE_CORE_NEEDS_TARGETED_EVIDENCE**. STEP20C remains NEXT;
production multi-output remains NOT YET SUPPORTED.
The private laboratory is `/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_`.
The ledger has 12 distinct consumed runs out of 14, 88 output-capture debits
out of 180, one initial core build per component, one diagnostic GPU build
pair and one DESTDIR stage. Failed attempts remain consumed. The two optional
slots are not retry slots. No existing run, build, stage or test is to be
repeated under this archive. No production installation was performed.
## Contents and provenance
`provenance.json` binds every copied file byte-for-byte to the private LAB and
references the larger immutable manifests and receipts by SHA-256. Generated
extracted `.inc` bodies, functional private core patches, fork sources, clients'
binaries, build trees, PNG captures, raw GPU readbacks and large logs remain
outside Git. This is deliberately not a self-contained compositor source kit.
- `c-tests-fixed/`: C17 geometry tests and exact-byte extraction/command
scripts. The private finite-scroll correction passed 184 assertions in
normal, UBSan and ASan+UBSan variants; the original six UB reproductions
remain in the LAB. Extraction requires `mosaic-fixed/` and `scene-core/`
under the private LAB and generates the excluded core bodies. The reduced
adapters do not reproduce full compositor ownership or ABI.
- `c-tests-lifecycle/`: real core slice extraction and reduced adapters using
native Wayland listeners and Pixman regions. Ordinary variants passed;
targeted sidecar allocation failure terminated each variant with raw
`-6 / SIGABRT`. **OOM_ABORT_EXPERIMENTAL**, availability risk P1, is not an
accepted production contract. Its README describes the exact coverage and
the preserved first fixture failure.
- `graphics/`: the final R2 Python fixture sources, sealed configurations,
C17 client/protocol sources, input trace fixture and bounded repair tests.
`run.py`, `gate.py`, `prepare.py` and `seal.py` retain historical laboratory
assumptions; moving them does not establish a valid source freeze, binary
identity, accepted independent review or durable ticket ledger.
- `gpu-field/`: frozen readback validator, analysis and offline test source.
`test_native_hash.py` requires the excluded diagnostic header under
`scene/render/fx_renderer/gpu_field_private.h`; its earlier ten CPU hash
cases passed, but no new execution is claimed by preservation.
- `evidence/`: final consumed budget, independent E/F review transcription,
GPU rejection/layout audit, local ABI provider summary and the separate
correction of an aggregate R2 exit-count error. Transcriptions are labelled
as such; original runtime receipts are unchanged.
The exact core binary is
`29baf31aa3eeed259825c87d0799fa5d3dc83710afb967154604eb1c6657f965`
(Build ID `1b42e557e38db3440ae8dcf7ac967e4132223365`). The GPU diagnostic is
`1905a20fa72ebcc7ec47d1584a4653654280288096a20005199eb75673ac0383`
(Build ID `1505e31c3dc08d2a7799c2ee9e1b2705eb61f5fe`). Their runtime data
are distinct. Both used the measured wlroots library
`7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`.
## Recorded execution and limits
The historical C commands were `python run.py` from the corresponding private
C test folder; each command, stdout/stderr and raw return code is retained
there. Graphical runs were reserved through `python -B graphics/run.py <ID>`
and the sole G1 through `python -B gpu-field/runner.py`, with private runtime
directories, sockets, reviewed byte freezes and local/global debits. These
commands describe consumed evidence; they are not instructions to launch again.
Any future reproduction requires a separately authorized scope and budget,
exact parent replay, new private source/binary identities and independent gates.
Never point these scripts at a daily desktop socket or reuse a consumed token.
E0 controls and the vertical cell stopped before captures on a fixture error.
E1's first chain, fractional/R90 and resize/migration provide sampled evidence;
standard return and fullscreen policy are not qualified by comparable controls.
E5's chain was dismissed by outside input before camera return. E6 observed a
natural timeout before late ACK, but its frozen validator still failed.
E7 classic blur was exercised; shadows were not. Whole-H1 A/A-return retained
33,789 different pixels without mask or tolerance. Exact sampled H2 does not
prove complete damage, native saved identity, GPU lifetime or atomic generation.
G1 stopped after nine first-A sample records and 8,312,048 raw bytes because
the frozen validator rejected the auxiliary DMA-BUF plane stride. No accepted
epoch receipt, B/A-return epochs or causal analysis exist. Read success remains
NOT PROVEN, the GL error queue UNOBSERVED and whole-field causality INCONCLUSIVE.
The modifier audit explains the invalid per-plane stride assumption without
changing the validator, accepting rejected bytes or refunding the run.
Runner exits, terminal IPC exit 1, compositor/client exits and normal client
closure are separate evidence. A runner's `scientific_cleanup=PASS` here is its
process-recovery gate; E1/E6 normal close was not acquired and E2 helpers show
error 32 despite nested owners closing normally. G1 scientific cleanup remains
INCONCLUSIVE. Mechanical process recovery alone is not a GPU lifetime proof.
Read the [build/C report](../../../docs/design/STEP20C_MINIMAL_CORE_BUILD_AND_C_FAILURE_PATHS.md),
[graphics qualification](../../../docs/design/STEP20C_MINIMAL_CORE_GRAPHICS_QUALIFICATION.md),
[lifetime/damage report](../../../docs/design/STEP20C_MINIMAL_CORE_LIFETIME_DAMAGE_AND_TXN.md),
[GPU follow-up](../../../docs/design/STEP20C_GPU_FULL_FIELD_FRESHNESS_FOLLOWUP.md)
and [ABI/integration decision](../../../docs/design/STEP20C_MINIMAL_CORE_ABI_PACKAGE_AND_INTEGRATION_DECISION.md)
for the actual scientific matrix and remaining gates. Publication of this source
archive does not authorize merging the private core, distribution or daily use.

View file

@ -0,0 +1,45 @@
{
"status": "EXPERIMENTAL",
"bindings": [
{
"symbol": "probe_clip_box_intersection",
"source": "mosaic-fixed/sway/desktop/transaction.c",
"source_sha256": "0b1d31bb8458dceaff34cc2a2987044b3096e6ebc96e68b522c0ac3540bc8ecd",
"start_byte": 38141,
"end_byte_exclusive": 39015,
"start_line": 1051,
"sha256": "a0d4f17e4bdb7a7b0600274e8a5bee5213c3bdd945f4aa1923f532dd58a07d0b",
"kind": "exact_function"
},
{
"symbol": "scene_clip_translate",
"source": "scene-core/types/scene/wlr_scene.c",
"source_sha256": "99992429f9a1d2754042989d89904fb6d705f963d74f4456d737fc9da3e0927d",
"start_byte": 23041,
"end_byte_exclusive": 23587,
"start_line": 721,
"sha256": "403181f798637dbb18f361743e93c423a3348c12c7ef2b903dfffb54c056f437",
"kind": "exact_function"
},
{
"symbol": "workspace_tiling_scene_x",
"source": "mosaic-fixed/sway/desktop/transaction.c",
"source_sha256": "0b1d31bb8458dceaff34cc2a2987044b3096e6ebc96e68b522c0ac3540bc8ecd",
"start_byte": 30441,
"end_byte_exclusive": 31414,
"start_line": 873,
"sha256": "4630ba624924d87b5febcd9cf8dfc6dfb204cabfd147f872bd745ad7f1cbf1a3",
"kind": "exact_function"
},
{
"symbol": "arrange_output:camera_clip_conversion_statement",
"source": "mosaic-fixed/sway/desktop/transaction.c",
"source_sha256": "0b1d31bb8458dceaff34cc2a2987044b3096e6ebc96e68b522c0ac3540bc8ecd",
"start_byte": 36332,
"end_byte_exclusive": 36539,
"start_line": 1003,
"sha256": "2bdcea206578c1bba4f455e65c550e4c922f6073e41accd25723c3105ecd1aa2",
"kind": "exact_statement_in_adapter"
}
]
}

View file

@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""WHY: exercise exact core C rather than an algorithmic rewrite.
CONTRACT: inputs are read-only; every extracted byte range is independently bound.
INVARIANT: generated function bodies are identical to their source slices.
"""
from pathlib import Path
import hashlib, json, re
LAB=Path(__file__).resolve().parent.parent
OUT=LAB/'c-tests-fixed'
bindings=[]
def capture(path,name):
raw=(LAB/path).read_bytes()
pattern=rb'^(?:static )?(?:bool|int) '+name.encode()+rb'\('
found=list(re.finditer(pattern,raw,re.M))
assert len(found)==1,(path,name,len(found))
start=found[0].start(); opening=raw.index(b'{',start)
depth=1; end=opening+1
while depth:
if raw[end:end+1]==b'{': depth+=1
elif raw[end:end+1]==b'}': depth-=1
end+=1
snippet=raw[start:end]
bindings.append(dict(symbol=name,source=str(path),source_sha256=hashlib.sha256(raw).hexdigest(),start_byte=start,end_byte_exclusive=end,start_line=raw[:start].count(b'\n')+1,sha256=hashlib.sha256(snippet).hexdigest(),kind='exact_function'))
assert snippet==raw[start:end]
return snippet
parts=[]
for path,name in [('mosaic-fixed/sway/desktop/transaction.c','probe_clip_box_intersection'),('scene-core/types/scene/wlr_scene.c','scene_clip_translate'),('mosaic-fixed/sway/desktop/transaction.c','workspace_tiling_scene_x')]:
parts.append(capture(Path(path),name))
raw=(LAB/'mosaic-fixed/sway/desktop/transaction.c').read_bytes()
start=raw.index(b'if (isfinite(row->state.scroll_x) &&')
end=raw.index(b'\n\t\t\t\t\twlr_scene_tree_set_clip',start)
statement=raw[start:end]
bindings.append(dict(symbol='arrange_output:camera_clip_conversion_statement',source='mosaic-fixed/sway/desktop/transaction.c',source_sha256=hashlib.sha256(raw).hexdigest(),start_byte=start,end_byte_exclusive=end,start_line=raw[:start].count(b'\n')+1,sha256=hashlib.sha256(statement).hexdigest(),kind='exact_statement_in_adapter'))
(OUT/'core-functions.inc').write_bytes(b'\n\n'.join(parts)+b'\n')
(OUT/'core-guard.inc').write_bytes(statement+b'\n')
(OUT/'bindings.json').write_text(json.dumps(dict(status='EXPERIMENTAL',bindings=bindings),indent=2)+'\n')

View file

@ -0,0 +1,160 @@
// WHY: directly execute the accepted private core's C numerical branches.
// CONTRACT: only primitive scene boxes and borrowed row/workspace fields are
// adapted; no geometry algorithm is reimplemented. This is not integration.
// INVARIANT: core-functions.inc bodies and core-guard.inc statement match the
// byte ranges and SHA256 recorded by extract.py in bindings.json.
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct wlr_box { int x,y,width,height; };
struct list { void **items; };
struct mosaic_row { struct { double scroll_x; } state; };
struct mosaic { struct list *rows; };
struct sway_workspace { struct { int layout; } current; struct mosaic *mosaic; };
struct side_gaps { int left,right,top,bottom; };
enum { L_SPLITH=1, L_MOSAIC=7 };
#include "core-functions.inc"
static unsigned checks;
#define CHECK(x) do { checks++; if (!(x)) { fprintf(stderr,"FAIL %s:%d %s\n",__FILE__,__LINE__,#x); exit(1); } } while(0)
static bool box_equal(struct wlr_box a, struct wlr_box b) {
return a.x==b.x && a.y==b.y && a.width==b.width && a.height==b.height;
}
// WHY: guard is a statement in arrange_output, not a callable helper.
// CONTRACT: exact statement is included with its real row/clip field names.
// INVARIANT: only setup/return are adapters; conversion branches are unchanged.
static struct wlr_box applied_guard(struct mosaic_row *row) {
struct wlr_box clip={0,0,800,600};
#include "core-guard.inc"
return clip;
}
struct fixture { struct mosaic_row row; void *items[1]; struct list rows; struct mosaic mosaic; struct sway_workspace ws; };
static void setup(struct fixture *f, double scroll) {
*f=(struct fixture){0}; f->row.state.scroll_x=scroll;
f->items[0]=&f->row; f->rows.items=f->items;
f->mosaic.rows=&f->rows; f->ws.mosaic=&f->mosaic;
f->ws.current.layout=L_MOSAIC;
}
static void intersection_tests(void) {
struct test { struct wlr_box a,b,want; bool success; } cases[]={
{{0,0,20,20},{10,5,20,20},{10,5,10,15},true},
{{-20,-30,20,40},{-10,-20,30,20},{-10,-20,10,20},true},
{{0,0,10,10},{10,0,10,10},{0},false},
{{0,0,10,10},{0,10,10,10},{0},false},
{{INT_MIN,0,1,1},{INT_MAX-1,0,1,1},{0},false},
{{INT_MIN,INT_MIN,INT_MAX,INT_MAX},{INT_MIN,INT_MIN,INT_MAX,INT_MAX},{INT_MIN,INT_MIN,INT_MAX,INT_MAX},true},
{{INT_MAX-1,INT_MAX-1,1,1},{INT_MAX-1,INT_MAX-1,1,1},{INT_MAX-1,INT_MAX-1,1,1},true},
{{INT_MAX,0,1,1},{0,0,1,1},{0},false},
{{0,INT_MAX,1,1},{0,0,1,1},{0},false},
{{0,0,0,10},{0,0,1,1},{0},false},
{{0,0,-1,10},{0,0,1,1},{0},false},
{{0,0,1,-1},{0,0,1,1},{0},false},
{{0,0,1,1},{0,0,0,1},{0},false},
{{0,0,1,1},{0,0,1,-1},{0},false},
};
for (size_t i=0;i<sizeof(cases)/sizeof(cases[0]);i++) {
for(int alias=0;alias<3;alias++) {
struct wlr_box a=cases[i].a,b=cases[i].b,d={91,92,93,94};
struct wlr_box *dst=alias==1?&a:alias==2?&b:&d;
struct wlr_box before=*dst;
bool result=probe_clip_box_intersection(dst,&a,&b);
CHECK(result==cases[i].success);
CHECK(box_equal(*dst,result?cases[i].want:before));
}
}
printf("intersection cases=14 aliases=3 PASS\n");
}
static void translation_tests(void) {
struct test { struct wlr_box local,want; int x,y; bool success; } cases[]={
{{2,3,10,20},{-8,8,10,20},-10,5,true},
{{INT_MIN,INT_MIN,INT_MAX,INT_MAX},{INT_MIN,INT_MIN,INT_MAX,INT_MAX},0,0,true},
{{INT_MAX-1,0,1,1},{INT_MAX-1,0,1,1},0,0,true},
{{INT_MAX,0,1,1},{0},0,0,false},
{{0,INT_MAX,1,1},{0},0,0,false},
{{INT_MIN,0,1,1},{0},-1,0,false},
{{0,INT_MIN,1,1},{0},0,-1,false},
{{INT_MAX,0,0,0},{INT_MAX,0,0,0},0,0,true},
{{0,0,-1,1},{0},0,0,false},
{{0,0,1,-1},{0},0,0,false},
{{0,0,0,1},{0,0,0,1},0,0,true},
{{1,0,0,0},{0},INT_MAX,0,false},
};
for (size_t i=0;i<sizeof(cases)/sizeof(cases[0]);i++) {
for(int alias=0;alias<2;alias++) {
struct wlr_box a=cases[i].local,d={91,92,93,94};
struct wlr_box *dst=alias?&a:&d,before=*dst;
bool result=scene_clip_translate(&a,cases[i].x,cases[i].y,dst);
CHECK(result==cases[i].success);
CHECK(box_equal(*dst,result?cases[i].want:before));
}
}
printf("translation cases=12 aliases=2 PASS\n");
}
static void scroll_tests(void) {
double values[]={0,1.75,-1.75,(double)INT_MAX,(double)INT_MIN,
nextafter((double)INT_MAX,0),nextafter((double)INT_MAX,INFINITY),
nextafter((double)INT_MIN,0),nextafter((double)INT_MIN,-INFINITY),
NAN,INFINITY,-INFINITY,nextafter(0x1p63,0)};
int expected[]={0,-1,1,-INT_MAX,INT_MAX,-INT_MAX+1,0,INT_MAX,0,0,0,0,0};
struct wlr_box area={0}; struct side_gaps gaps={0};
for(size_t i=0;i<sizeof(values)/sizeof(values[0]);i++) {
struct fixture f; setup(&f,values[i]);
struct wlr_box clip=applied_guard(&f.row);
bool valid=isfinite(values[i]) && values[i]>=INT_MIN && values[i]<=INT_MAX;
CHECK(clip.width==(valid?800:0)); CHECK(clip.height==(valid?600:0));
if(valid) CHECK(clip.x==(int)values[i]);
CHECK(workspace_tiling_scene_x(&f.ws,&area,&gaps)==expected[i]);
}
struct fixture bounds;
setup(&bounds,(double)INT_MIN);
area.x=INT_MAX; gaps.left=INT_MAX;
CHECK(workspace_tiling_scene_x(&bounds.ws,&area,&gaps)==INT_MAX);
setup(&bounds,(double)INT_MAX);
area.x=INT_MIN; gaps.left=INT_MIN;
CHECK(workspace_tiling_scene_x(&bounds.ws,&area,&gaps)==INT_MIN);
setup(&bounds,1.75); area.x=-10; gaps.left=-3;
CHECK(workspace_tiling_scene_x(&bounds.ws,&area,&gaps)==-14);
setup(&bounds,-1.75);
CHECK(workspace_tiling_scene_x(&bounds.ws,&area,&gaps)==-12);
struct fixture f; setup(&f,NAN); f.ws.current.layout=L_SPLITH;
area.x=INT_MAX; gaps.left=1;
CHECK(workspace_tiling_scene_x(&f.ws,&area,&gaps)==INT_MAX);
f.ws.current.layout=L_MOSAIC; f.ws.mosaic=NULL;
area.x=INT_MIN; gaps.left=-1;
CHECK(workspace_tiling_scene_x(&f.ws,&area,&gaps)==INT_MIN);
printf("scroll guard cases=13 safe camera casts=10 standard/no-mosaic=2 PASS\n");
}
// WHY: undefined conversions must not contaminate successful geometry tests.
// CONTRACT: one command-line reproducer executes one real core cast per process.
// INVARIANT: sanitizer stderr/raw exit are preserved, with no diagnostic suppression.
static int reproduce(const char *name) {
double value;
if(!strcmp(name,"nan")) value=NAN;
else if(!strcmp(name,"pos-inf")) value=INFINITY;
else if(!strcmp(name,"neg-inf")) value=-INFINITY;
else if(!strcmp(name,"int64-upper")) value=0x1p63;
else if(!strcmp(name,"int64-lower")) value=-0x1p63;
else if(!strcmp(name,"below-int64-lower")) value=nextafter(-0x1p63,-INFINITY);
else return 2;
struct fixture f; setup(&f,value);
struct wlr_box area={0}; struct side_gaps gaps={0};
fprintf(stderr,"REPRO workspace_tiling_scene_x scroll=%s (%a)\n",name,value);
fflush(stderr);
int result=workspace_tiling_scene_x(&f.ws,&area,&gaps);
CHECK(result==0);
area.x=INT_MAX; gaps.left=INT_MAX;
CHECK(workspace_tiling_scene_x(&f.ws,&area,&gaps)==INT_MAX);
area.x=INT_MIN; gaps.left=INT_MIN;
CHECK(workspace_tiling_scene_x(&f.ws,&area,&gaps)==INT_MIN);
printf("RESULT %d bounded extremes PASS\n",result); return 0;
}
int main(int argc,char **argv) {
if(argc==3 && !strcmp(argv[1],"--reproduce")) return reproduce(argv[2]);
if(argc!=1) return 2;
intersection_tests(); translation_tests(); scroll_tests();
printf("PASS checks=%u\n",checks); return 0;
}

View file

@ -0,0 +1,9 @@
{
"status": "EXPERIMENTAL",
"CORE_C_GEOMETRY_TESTS": "PASS",
"CORE_SANITIZER_TESTS": "PASS",
"scope": "exact extracted C only; no compositor integration",
"raw_commands": "commands.json",
"bindings": "bindings.json",
"original_evidence": "../c-tests immutable"
}

View file

@ -0,0 +1,41 @@
#!/usr/bin/env python3
"""WHY: retain exact commands, raw stdout/stderr and unmodified exit codes.
CONTRACT: only local C compilation/execution; no compositor, network or injection.
INVARIANT: sanitizer diagnostics are enabled and failing reproductions are not PASS.
"""
from pathlib import Path
import hashlib,json,subprocess,os
HERE=Path(__file__).resolve().parent
LAB=HERE.parent
records=[]
def run(name,argv):
result=subprocess.run(argv,cwd=HERE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
(HERE/(name+'.stdout')).write_bytes(result.stdout)
(HERE/(name+'.stderr')).write_bytes(result.stderr)
record=dict(name=name,argv=argv,cwd=str(HERE),raw_exit_code=result.returncode,stdout=name+'.stdout',stderr=name+'.stderr')
records.append(record)
(HERE/'commands.json').write_text(json.dumps(records,indent=2)+'\n')
print(name,'exit',result.returncode,flush=True)
return result.returncode
run('compiler-version',['cc','--version'])
run('extract',['python','extract.py'])
bindings=json.loads((HERE/'bindings.json').read_text())['bindings']
for binding in bindings:
raw=(LAB/binding['source']).read_bytes()
assert hashlib.sha256(raw).hexdigest()==binding['source_sha256']
span=raw[binding['start_byte']:binding['end_byte_exclusive']]
assert hashlib.sha256(span).hexdigest()==binding['sha256']
base=['cc','-std=c17','-O0','-g3','-Wall','-Wextra','-Werror','geometry.c','-lm']
assert run('compile-normal',base+['-o','geometry-normal'])==0
assert run('compile-ubsan',base+['-fsanitize=undefined,float-cast-overflow','-fno-sanitize-recover=all','-o','geometry-ubsan'])==0
assert run('normal-defined',['./geometry-normal'])==0
assert run('ubsan-defined',['./geometry-ubsan'])==0
assert run('compile-asan',base+['-fsanitize=address,undefined,float-cast-overflow','-fno-sanitize-recover=all','-o','geometry-asan'])==0
assert run('asan-defined',['./geometry-asan'])==0
for scenario in ['nan','pos-inf','neg-inf','int64-upper','int64-lower','below-int64-lower']:
for mode in ['normal','ubsan','asan']:
assert run(mode+'-'+scenario,['./geometry-'+mode,'--reproduce',scenario])==0
summary=dict(status='EXPERIMENTAL',CORE_C_GEOMETRY_TESTS='PASS',CORE_SANITIZER_TESTS='PASS',scope='exact extracted C only; no compositor integration',raw_commands='commands.json',bindings='bindings.json',original_evidence='../c-tests immutable')
(HERE/'handoff.json').write_text(json.dumps(summary,indent=2)+'\n')
entries=[dict(path=p.name,sha256=hashlib.sha256(p.read_bytes()).hexdigest(),bytes=p.stat().st_size) for p in sorted(HERE.iterdir()) if p.is_file() and p.name!='manifest.json']
(HERE/'manifest.json').write_text(json.dumps(dict(status='EXPERIMENTAL',entries=entries),indent=2)+'\n')

View file

@ -0,0 +1,96 @@
# Tests unitaires C17 privés — lifecycle et popup
Statut : **EXPERIMENTAL**, preuve unitaire bornée, aucune qualification runtime
compositeur. Les anciens `c-tests`/`c-tests-fixed` restent inchangés.
Reproduction : `python3 run.py` depuis ce dossier. Le runner extrait les slices,
compile normal/UBSan/ASan puis exécute chaque variante sans injection et avec
`--allocation-unavailable`, avec `RLIMIT_CORE=(0,0)`. `commands.json` conserve
argv, codes bruts et overrides sanitaires ; chaque commande possède stdout et
stderr bruts. Le runner échoue si un code diffère de 0, sauf l'injection où il
attend précisément -6 (SIGABRT). Les premières sorties erronées de fixture sont
conservées dans `attempt-1/`.
## Provenance et frontière de fixture
`extract.py` copie des intervalles exacts de bytes, sans réécriture du corps :
- `sidecar.inc` : struct/global/find/remove/destroy et conversion tree-from-node ;
- `setter.inc` : set/get clip ;
- `clip.inc` : translate/intersect/point_inside ;
- `hit.inc` : struct node_at_data et scene_node_at_iterator ;
- `popup.inc` : probe_clip_box_intersection et probe_clip_popup.
`bindings.json` lie chaque intervalle aux SHA-256 du source intégral et de la
slice, offsets byte et lignes source. Les corps compilés sont ces fichiers.
`scene.c` définit uniquement autour de `setter.inc` une macro `calloc` ciblant
`sidecar_calloc`. La première allocation réussit ; l'injection ensuite active
retourne NULL au prochain sidecar. Aucun autre calloc n'est interposé. Aucun
source fonctionnel n'a été modifié, aucune ABI publique n'a été reproduite.
`support.h` expose des structs minimaux ; coordinates/box predicates/list_find,
conversions buffer/rect, scene update et logging sont des supports explicites,
avec commentaires WHY/CONTRACT/INVARIANT. Les coordonnées fixture sont petites,
sans overflow dans le support ; les arithmetic checks étudiés sont les corps
réels extraits. Les listes/listeners et destroy signals sont Wayland serveur
installé, les intersections sont Pixman installé. Ces bibliothèques sont liées
normalement et ne sont pas reconstruites avec sanitizers. Les logs produit sont
no-op ; on mesure les mises à jour, pas le damage/rendu. Les types réduits ne
prouvent ni taille/offset des vrais structs ni compatibilité ABI.
## Couverture réellement exercée
**PASS unitaire** normal, UBSan, ASan (ASan leak detection activée) :
- aucun clip : get false, NULL répété sans mise à jour ni allocation ;
- premier clip puis update : une allocation, une sidecar et un listener,
box copiée, copie identique idempotente ;
- plusieurs arbres, suppression explicite head, destroy tail puis head,
toutes sidecars/listeners retirés par les signaux Wayland natifs ;
- clip zéro installé, get true, zéro pixel d'aire positive ; NULL l'enlève ;
- coordonnées parent/child, région intersectée sur X par parent et Y par child ;
- hit permis dans l'intersection, refus hors parent et sous masque zéro avant
point_accepts_input ; compteur callback vérifié ;
- translation simple, négative dimension et origine/endpoint non représentable
refusés ; render region sans pixels et input refusé après rejet ;
- vrai probe_clip_popup : owner tiled Mosaic éligible, popup-local viewport,
ascendance current fullscreen exemptée, pending fullscreen contradictoire
ignoré, standard sans clip, workspace inactif masqué, owner entièrement
caché masqué puis retour visible, fullscreen workspace, non-tiled owner,
clip tiling absent et owner absent ;
- nested node hérite réellement de la sidecar root via point_inside_clip.
Les intersections popup réelles sont exercées par ces branches (dst alias
viewport à l'intersection output, dst séparé à l'intersection owner). La suite
numérique complète/scroll reste dans `c-tests-fixed`, non refaite ici.
## Injection OOM et limite de disponibilité
**CORE_OOM_POLICY=OOM_ABORT_EXPERIMENTAL** : les trois variantes montrent
allocation sidecar réussie puis panne ciblée de la seconde, avec terminaison
brute **-6 / SIGABRT**, core dump interdit. Aucun diagnostic sanitaire précède
l'arrêt volontaire. Ceci vérifie le chemin abort existant, pas la survie du
compositeur, pas un contrat de disponibilité production, pas un état graphique
observable après OOM. La politique production reste ouverte.
## Échec initial et correction de fixture
L'assertion initiale imposait `pixman_region32_not_empty(region)==false` pour un
rectangle nul. Le contrôle indépendant `pixman-diagnostic.c` montre qu'avec le
Pixman installé l'intersection (15,25,0,0) conserve un rectangle dégénéré :
return=1, not_empty=1, extents=(15,25,15,25), contains_point false. Le test final
vérifie qu'aucun rectangle n'a largeur ET hauteur positives. Aucun corps extrait
n'a changé. Cet échec initial de fixture n'est pas une panne produit.
## NOT TESTED / NOT EXERCISED
Aucune création réelle de scènes via l'API intégrale, aucun parcours complet
scene_node_at/scene_nodes_in_box, aucune transaction generation, surface XDG,
grab/serial/protocole popup, cycle natif root/nested popup, rendu/GPU, damage,
décoration/FX/saved buffer, taille/offset ABI, mutation concurrente ou shutdown
compositeur. Le listener est émis réellement mais node storage est stack fixture,
non destruction de l'arbre intégral. Rien ici ne ferme une cellule runtime.
STEP20A=DONE ; STEP20B=EXPERIMENTAL ; STEP20C=NEXT ;
STEP20D–G=PLANNED ; PRODUCTION_MULTI_OUTPUT=NOT_YET_SUPPORTED.
Aucun commit/push, aucune modification dépôt/système/session.

View file

@ -0,0 +1,52 @@
[
{
"source": "/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_/scene-core/types/scene/wlr_scene.c",
"source_sha256": "99992429f9a1d2754042989d89904fb6d705f963d74f4456d737fc9da3e0927d",
"start_byte": 1904,
"end_byte_exclusive": 2939,
"start_line": 54,
"end_line": 88,
"slice_sha256": "d4c8f7cf78bfcc0e3177942be1d39019c1f2fbd43c35cdbdff586a1f10c4b34b",
"output": "sidecar.inc"
},
{
"source": "/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_/scene-core/types/scene/wlr_scene.c",
"source_sha256": "99992429f9a1d2754042989d89904fb6d705f963d74f4456d737fc9da3e0927d",
"start_byte": 23041,
"end_byte_exclusive": 25162,
"start_line": 721,
"end_line": 787,
"slice_sha256": "78c83c3f11cba654b0a099aca06d10b36299f5a6a199ba2cd08df8943da02923",
"output": "clip.inc"
},
{
"source": "/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_/scene-core/types/scene/wlr_scene.c",
"source_sha256": "99992429f9a1d2754042989d89904fb6d705f963d74f4456d737fc9da3e0927d",
"start_byte": 31395,
"end_byte_exclusive": 33052,
"start_line": 983,
"end_line": 1026,
"slice_sha256": "1e70ed409c8a55d67fea7f1d166edeaa8fd5fe1237aecd9be64cef20c75ed997",
"output": "setter.inc"
},
{
"source": "/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_/scene-core/types/scene/wlr_scene.c",
"source_sha256": "99992429f9a1d2754042989d89904fb6d705f963d74f4456d737fc9da3e0927d",
"start_byte": 64327,
"end_byte_exclusive": 65551,
"start_line": 2037,
"end_line": 2082,
"slice_sha256": "31423e212f75590085a4ad75c3ce60555c849e579e0bc0497c04d03b71d0104b",
"output": "hit.inc"
},
{
"source": "/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_/mosaic-fixed/sway/desktop/transaction.c",
"source_sha256": "0b1d31bb8458dceaff34cc2a2987044b3096e6ebc96e68b522c0ac3540bc8ecd",
"start_byte": 38141,
"end_byte_exclusive": 43558,
"start_line": 1051,
"end_line": 1188,
"slice_sha256": "cc962a736fa68496753485de7fd3dd971857cce894c977a7a3fbfd2837238511",
"output": "popup.inc"
}
]

View file

@ -0,0 +1,16 @@
import pathlib, hashlib, json
ROOT=pathlib.Path(__file__).resolve().parent
LAB=ROOT.parent
bindings=[]
def extract(path,start,end,name):
data=path.read_bytes(); a=data.index(start.encode()); b=data.index(end.encode(),a)
piece=data[a:b]; (ROOT/name).write_bytes(piece)
bindings.append(dict(source=str(path),source_sha256=hashlib.sha256(data).hexdigest(),start_byte=a,end_byte_exclusive=b,start_line=data[:a].count(b'\n')+1,end_line=data[:b].count(b'\n')+1,slice_sha256=hashlib.sha256(piece).hexdigest(),output=name))
s=LAB/'scene-core/types/scene/wlr_scene.c'
extract(s,'struct scene_tree_clip {','struct wlr_scene_rect *wlr_scene_rect_from_node', 'sidecar.inc')
extract(s,'static bool scene_clip_translate(', 'static bool scene_node_update_iterator(', 'clip.inc')
extract(s,'void wlr_scene_tree_set_clip(', 'struct wlr_scene_rect *wlr_scene_rect_create(', 'setter.inc')
extract(s,'struct node_at_data {','struct wlr_scene_node *wlr_scene_node_at(', 'hit.inc')
t=LAB/'mosaic-fixed/sway/desktop/transaction.c'
extract(t,'static bool probe_clip_box_intersection(', 'void arrange_popups(', 'popup.inc')
(ROOT/'bindings.json').write_text(json.dumps(bindings,indent=2)+'\n')

View file

@ -0,0 +1,21 @@
{
"status": "PASS_UNIT_ONLY",
"core_oom_policy": "OOM_ABORT_EXPERIMENTAL",
"normal_defined": 0,
"ubsan_defined": 0,
"asan_defined": 0,
"allocation_unavailable_raw_returncode": {
"normal": -6,
"ubsan": -6,
"asan": -6
},
"core_dump_limit": 0,
"compositor_runs": 0,
"clients_launched": 0,
"functional_source_modifications": false,
"production_availability_qualified": false,
"initial_fixture_assertion_failure_preserved": "attempt-1",
"limitations": "README.md",
"source_bindings": "bindings.json",
"commands": "commands.json"
}

View file

@ -0,0 +1,56 @@
#include "support.h"
// WHY: popup ownership is tested without transaction/scene construction.
// CONTRACT: each reduced state exposes precisely the fields used by popup.inc.
// INVARIANT: current ancestry is distinct from deliberately contradictory pending state.
enum {L_STANDARD,L_MOSAIC,FULLSCREEN_NONE=0,FULLSCREEN_WORKSPACE=1};
struct sway_workspace;
struct sway_container {struct {struct sway_workspace *workspace;struct sway_container *parent;int fullscreen_mode;} current,pending; struct {int current_width,current_height;} animation_state;struct wlr_scene_tree *scene_tree;};
struct list {void **items;int length;};
static int list_find(struct list *l,void *p){for(int i=0;i<l->length;i++)if(l->items[i]==p)return i;return -1;}
struct sway_output {int lx,ly,width,height;struct {struct sway_workspace *active_workspace;} current;};
struct sway_workspace {struct {int layout;struct list *tiling;struct sway_container *fullscreen;struct sway_output *output;} current;void *mosaic;struct {struct wlr_scene_tree *tiling;} layers;};
struct sway_view {struct sway_container *container;};
struct sway_popup_desc {struct sway_view *view;};
#include "popup.inc"
static unsigned input_calls;
static bool input(struct wlr_scene_buffer *b,double *x,double *y){(void)b;(void)x;(void)y;input_calls++;return true;}
static void expect_box(struct wlr_scene_tree *t,int x,int y,int w,int h){struct wlr_box b;assert(wlr_scene_tree_get_clip(t,&b));assert(b.x==x&&b.y==y&&b.width==w&&b.height==h);}
static void destroy(struct wlr_scene_tree *t){wl_signal_emit_mutable(&t->node.events.destroy,NULL);assert(wl_list_empty(&t->node.events.destroy.listener_list));}
// WHY: native Pixman may retain a zero-area rectangle as a nonempty region.
// CONTRACT: masking requires zero pixels, not a particular region encoding.
// INVARIANT: any rectangle with positive width AND height fails this check.
static bool has_pixels(pixman_region32_t *r){int count;pixman_box32_t *boxes=pixman_region32_rectangles(r,&count);for(int i=0;i<count;i++)if(boxes[i].x2>boxes[i].x1&&boxes[i].y2>boxes[i].y1)return true;return false;}
static void lifecycle(void){
struct wlr_scene_tree p,c;init_node(&p.node,WLR_SCENE_NODE_TREE,NULL,10,20);init_node(&c.node,WLR_SCENE_NODE_TREE,&p,5,5);
struct wlr_scene_buffer leaf={.point_accepts_input=input};init_node(&leaf.node,WLR_SCENE_NODE_BUFFER,&c,0,0);
struct wlr_box out,b={0,0,100,100};assert(!wlr_scene_tree_get_clip(&p,&out));wlr_scene_tree_set_clip(&p,NULL);assert(updates==0&&allocations==0);
wlr_scene_tree_set_clip(&p,&b);assert(allocations==1&&clip_count()==1&&wl_list_length(&p.node.events.destroy.listener_list)==1);
b.width=80;wlr_scene_tree_set_clip(&p,&b);assert(allocations==1&&clip_count()==1&&updates==2&&wl_list_length(&p.node.events.destroy.listener_list)==1);
wlr_scene_tree_set_clip(&p,&b);assert(updates==2);b.width=1;expect_box(&p,0,0,80,100);
b=(struct wlr_box){0,0,100,20};wlr_scene_tree_set_clip(&c,&b);assert(clip_count()==2);
assert(inside(&leaf.node,16,26));assert(!inside(&leaf.node,95,30));assert(hit(&leaf.node,16,26)&&input_calls==1);assert(!hit(&leaf.node,95,30)&&input_calls==1);
pixman_region32_t r;pixman_region32_init_rect(&r,-100,-100,400,400);intersect(&leaf.node,&r);pixman_box32_t *ext=pixman_region32_extents(&r);assert(ext->x1==15&&ext->y1==25&&ext->x2==90&&ext->y2==45);pixman_region32_fini(&r);
b=(struct wlr_box){0};wlr_scene_tree_set_clip(&c,&b);expect_box(&c,0,0,0,0);assert(!hit(&leaf.node,16,26)&&input_calls==1);
pixman_region32_init_rect(&r,0,0,100,100);intersect(&leaf.node,&r);assert(!has_pixels(&r));pixman_region32_fini(&r);
wlr_scene_tree_set_clip(&c,NULL);assert(!wlr_scene_tree_get_clip(&c,&out)&&clip_count()==1);unsigned before=updates;wlr_scene_tree_set_clip(&c,NULL);assert(updates==before);assert(hit(&leaf.node,16,26)&&input_calls==2);
wlr_scene_tree_set_clip(&c,&b);destroy(&p);assert(clip_count()==1);destroy(&c);assert(clip_count()==0);
b=(struct wlr_box){1,2,3,4};assert(translate(&b,10,20,&out)&&out.x==11&&out.y==22);assert(!translate(&b,INT_MAX,0,&out));b.width=-1;assert(!translate(&b,0,0,&out));
// Exact translation rejection must empty render region and reject input too.
b=(struct wlr_box){INT_MAX,0,1,1};wlr_scene_tree_set_clip(&p,&b);assert(!inside(&p.node,0,0));pixman_region32_init_rect(&r,0,0,10,10);intersect(&p.node,&r);assert(!has_pixels(&r));pixman_region32_fini(&r);destroy(&p);
puts("PASS lifecycle: NULL/zero/copy/idempotence/single allocation/listener/removal/native destroy/native Pixman/inherited hit-before-input/translation rejection");
}
static void popups(void){
struct wlr_scene_tree tiling,owner_scene,popup,nested;init_node(&tiling.node,WLR_SCENE_NODE_TREE,NULL,10,20);init_node(&owner_scene.node,WLR_SCENE_NODE_TREE,NULL,20,30);init_node(&popup.node,WLR_SCENE_NODE_TREE,NULL,25,35);init_node(&nested.node,WLR_SCENE_NODE_TREE,&popup,0,0);
struct sway_output output={.width=200,.height=200};struct sway_container parent={.scene_tree=&owner_scene,.animation_state={30,30}},child={0};void *items[]={&parent};struct list list={items,1};struct sway_workspace ws={.current={.layout=L_MOSAIC,.tiling=&list,.output=&output},.mosaic=&ws,.layers={&tiling}};output.current.active_workspace=&ws;child.current.workspace=&ws;child.current.parent=&parent;struct sway_view view={&child};struct sway_popup_desc desc={&view};struct wlr_box b={0,0,100,100},out;wlr_scene_tree_set_clip(&tiling,&b);
child.pending.fullscreen_mode=FULLSCREEN_WORKSPACE;probe_clip_popup(&popup.node,&desc);expect_box(&popup,-15,-15,100,100);assert(inside(&nested.node,30,40)&&!inside(&nested.node,150,40));
parent.current.fullscreen_mode=FULLSCREEN_WORKSPACE;probe_clip_popup(&popup.node,&desc);assert(!wlr_scene_tree_get_clip(&popup,&out));parent.current.fullscreen_mode=FULLSCREEN_NONE;
ws.current.layout=L_STANDARD;probe_clip_popup(&popup.node,&desc);assert(!wlr_scene_tree_get_clip(&popup,&out));ws.current.layout=L_MOSAIC;
output.current.active_workspace=NULL;probe_clip_popup(&popup.node,&desc);expect_box(&popup,0,0,0,0);output.current.active_workspace=&ws;
owner_scene.node.x=150;probe_clip_popup(&popup.node,&desc);expect_box(&popup,0,0,0,0);assert(!inside(&nested.node,30,40));owner_scene.node.x=20;probe_clip_popup(&popup.node,&desc);expect_box(&popup,-15,-15,100,100);
ws.current.fullscreen=&parent;probe_clip_popup(&popup.node,&desc);assert(!wlr_scene_tree_get_clip(&popup,&out));ws.current.fullscreen=NULL;
list.length=0;probe_clip_popup(&popup.node,&desc);assert(!wlr_scene_tree_get_clip(&popup,&out));list.length=1;
wlr_scene_tree_set_clip(&tiling,NULL);probe_clip_popup(&popup.node,&desc);expect_box(&popup,0,0,0,0);
desc.view=NULL;probe_clip_popup(&popup.node,&desc);assert(!wlr_scene_tree_get_clip(&popup,&out));destroy(&tiling);destroy(&popup);destroy(&nested);destroy(&owner_scene);assert(clip_count()==0);
puts("PASS popup: real root clip/current fullscreen ancestor/pending contradiction/standard/inactive/hidden owner/restoration/workspace fullscreen/not tiled/missing clip/no owner; nested clip inheritance (unit only)");
}
int main(int argc,char **argv){if(argc==2&&strcmp(argv[1],"--allocation-unavailable")==0){struct wlr_scene_tree tree,prior;init_node(&tree.node,WLR_SCENE_NODE_TREE,NULL,0,0);init_node(&prior.node,WLR_SCENE_NODE_TREE,NULL,0,0);struct wlr_box b={0,0,10,10};wlr_scene_tree_set_clip(&prior,&b);assert(allocations==1&&clip_count()==1&&wl_list_length(&prior.node.events.destroy.listener_list)==1);allocation_unavailable=true;puts("INJECTION prior sidecar succeeded; next sidecar calloc returns NULL; expecting experimental abort");fflush(stdout);wlr_scene_tree_set_clip(&tree,&b);return 99;}lifecycle();popups();return 0;}

View file

@ -0,0 +1,27 @@
import pathlib, subprocess, json, os, resource, hashlib
R=pathlib.Path(__file__).resolve().parent
records=[]
def run(name,args,env=None):
def limit():resource.setrlimit(resource.RLIMIT_CORE,(0,0))
p=subprocess.run(args,cwd=R,capture_output=True,env=env,preexec_fn=limit)
(R/(name+'.stdout')).write_bytes(p.stdout);(R/(name+'.stderr')).write_bytes(p.stderr)
records.append(dict(name=name,argv=args,returncode=p.returncode,core_limit=0,environment_overrides={} if env is None else {k:v for k,v in env.items() if k in ('ASAN_OPTIONS','UBSAN_OPTIONS')}))
return p.returncode
assert run('extract',['python3','extract.py'])==0
run('compiler-version',['cc','--version'])
assert run('pkg-config',['pkg-config','--cflags','--libs','wayland-server','pixman-1'])==0
pkg=(R/'pkg-config.stdout').read_text().split()
assert run('library-versions',['pkg-config','--modversion','wayland-server','pixman-1'])==0
assert run('compile-pixman-diagnostic',['cc','-std=c17','pixman-diagnostic.c',*pkg,'-o','pixman-diagnostic'])==0
assert run('pixman-diagnostic',['./pixman-diagnostic'])==0
for variant,flags in [('normal',[]),('ubsan',['-fsanitize=undefined','-fno-sanitize-recover=all']),('asan',['-fsanitize=address'])]:
args=['cc','-std=c17','-g','-O1','-Wall','-Wextra','-Wno-unused-function','-Wno-unused-parameter','-fno-omit-frame-pointer',*flags,'scene.c','lifecycle.c',*pkg,'-o','lifecycle-'+variant]
rc=run('compile-'+variant,args)
if rc==0:
env=os.environ.copy();env.update(ASAN_OPTIONS='detect_leaks=1:abort_on_error=1',UBSAN_OPTIONS='halt_on_error=1:print_stacktrace=1')
run(variant+'-defined',['./lifecycle-'+variant],env)
run(variant+'-allocation-unavailable',['./lifecycle-'+variant,'--allocation-unavailable'],env)
(R/'commands.json').write_text(json.dumps(records,indent=2)+'\n')
print(json.dumps([{k:r[k] for k in ("name","returncode")} for r in records],indent=2))
failed=[r for r in records if r["returncode"] != (-6 if r["name"].endswith("-allocation-unavailable") else 0)]
raise SystemExit(bool(failed))

View file

@ -0,0 +1,18 @@
#include "support.h"
unsigned updates, allocations;
bool allocation_unavailable;
#include "sidecar.inc"
// WHY: make only the sidecar allocation deterministically unavailable.
// CONTRACT: macro scope is solely the exact setter/getter extracted slice.
// INVARIANT: Wayland, Pixman, libc and all other allocations are untouched.
static void *sidecar_calloc(size_t n,size_t size) {allocations++; if(allocation_unavailable)return NULL;return calloc(n,size);}
#define calloc sidecar_calloc
#include "setter.inc"
#undef calloc
#include "clip.inc"
#include "hit.inc"
unsigned clip_count(void) {unsigned n=0;for(struct scene_tree_clip *p=scene_tree_clips;p;p=p->next)n++;return n;}
bool inside(struct wlr_scene_node *n,double x,double y){return scene_node_point_inside_clip(n,x,y);}
void intersect(struct wlr_scene_node *n,pixman_region32_t *r){scene_node_intersect_clip(n,r);}
bool hit(struct wlr_scene_node *n,double x,double y){struct node_at_data d={.lx=x,.ly=y};int nx,ny;assert(wlr_scene_node_coords(n,&nx,&ny));return scene_node_at_iterator(n,nx,ny,&d);}
bool translate(const struct wlr_box *b,int x,int y,struct wlr_box *g){return scene_clip_translate(b,x,y,g);}

View file

@ -0,0 +1,53 @@
#ifndef SUPPORT_H
#define SUPPORT_H
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-server-core.h>
#include <pixman.h>
// WHY: isolate static core functions without a compositor or GPU.
// CONTRACT: these reduced structs contain only fields read by exact slices;
// they are fixtures, never an ABI/layout assertion about wlroots or Sway.
// INVARIANT: all nodes, containers and workspace pointers are borrowed and
// live until their real Wayland destroy listeners have been emitted.
struct wlr_box { int x,y,width,height; };
enum { WLR_SCENE_NODE_TREE, WLR_SCENE_NODE_BUFFER, WLR_SCENE_NODE_RECT, WLR_SCENE_NODE_SHADOW, WLR_SCENE_NODE_OPTIMIZED_BLUR, WLR_SCENE_NODE_BLUR };
struct wlr_scene_tree;
struct wlr_scene_node { int type,x,y; struct wlr_scene_tree *parent; struct { struct wl_signal destroy; } events; bool coords_valid; };
struct wlr_scene_tree { struct wlr_scene_node node; };
struct wlr_scene_buffer { struct wlr_scene_node node; bool (*point_accepts_input)(struct wlr_scene_buffer *, double *, double *); };
struct wlr_scene_rect { struct wlr_scene_node node; bool accepts_input; struct { struct wlr_box area; } clipped_region; };
// WHY: coordinate lookup requires hierarchy but not compositor ownership.
// CONTRACT: use small fixture translations; failed lookup is injectable.
// INVARIANT: no pending geometry is substituted for applied node coordinates.
static bool wlr_scene_node_coords(struct wlr_scene_node *n,int *x,int *y) { *x=*y=0; for (;n;n=n->parent?&n->parent->node:NULL) {if(!n->coords_valid)return false; *x+=n->x; *y+=n->y;} return true; }
static bool wlr_box_contains_point(const struct wlr_box *b,double x,double y) {return x>=b->x && y>=b->y && x<(double)b->x+b->width && y<(double)b->y+b->height;}
static bool wlr_box_empty(const struct wlr_box *b) {return b->width<=0||b->height<=0;}
static struct wlr_scene_buffer *wlr_scene_buffer_from_node(struct wlr_scene_node *n) {return (struct wlr_scene_buffer *)n;}
static struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *n) {return (struct wlr_scene_rect *)n;}
// WHY: mutation callbacks/loggers otherwise pull in the full scene engine.
// CONTRACT: only callback invocation is counted; no render/damage claim.
// INVARIANT: stubs neither install nor remove clips/listeners.
#define WLR_INFO 1
#define WLR_ERROR 2
#define SWAY_INFO 1
#define wlr_log(level, ...) ((void)(level))
#define sway_log(level, ...) ((void)(level))
extern unsigned updates, allocations;
extern bool allocation_unavailable;
static void scene_node_update(struct wlr_scene_node *n,void *unused) {(void)n;(void)unused; updates++;}
void wlr_scene_tree_set_clip(struct wlr_scene_tree *,const struct wlr_box *);
bool wlr_scene_tree_get_clip(struct wlr_scene_tree *,struct wlr_box *);
struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *);
unsigned clip_count(void);
bool inside(struct wlr_scene_node *,double,double);
void intersect(struct wlr_scene_node *,pixman_region32_t *);
bool hit(struct wlr_scene_node *,double,double);
bool translate(const struct wlr_box *,int,int,struct wlr_box *);
static void init_node(struct wlr_scene_node *n,int type,struct wlr_scene_tree *parent,int x,int y) {*n=(struct wlr_scene_node){.type=type,.parent=parent,.x=x,.y=y,.coords_valid=true};wl_signal_init(&n->events.destroy);}
#endif

View file

@ -0,0 +1,93 @@
# G1 — diagnostic factuel du rejet DMA-BUF
Statut : **INCONCLUSIVE — lecture seule**, limité au rejet du seul epoch 1.
Ce document n'accepte ni les pixels bruts ni le résultat GPU. Aucun run,
rebuild, revalidation du ticket, test, analyse numérique ou reconstruction de
contenu masqué n'a été effectué pour cet audit.
## Constat
`gpu-field/validate.py::validate_object` exige `p[0] >= 800*4` pour **tous**
les plans DMA-BUF (`validate.py`, ligne 46), puis rejette sous le message
`dmabuf fields`. Le writer `gpu-field/scene/render/fx_renderer/gpu_field_private.h::gf_object`
(lignes 198–213) sérialise chaque plan comme `[stride, offset]` à partir de
`wlr_dmabuf_attributes.stride[]` et `offset[]`; `dmabuf_available` provient de
`wlr_buffer_get_dmabuf`.
Dans chaque échantillon 0–8 de `gpu-field/runtime/epoch-1.meta`,
`dmabuf_available=true`, la taille est `[800,600]`, deux plans sont présents,
et le modifier comporte 16 caractères. Les plans sont toujours
`[[3584,0],[1024,2293760]]`. Le premier plan passe le contrôle stride
(3584 ≥ 3200); le second le fait échouer (1024 < 3200). C'est le premier
prédicat de `dmabuf fields` et la cause immédiate démontrable du rejet.
| Point | Échantillon | DRM format décimal / rôle | Plans [stride, offset] | Champ fautif |
|---:|---|---|---|---|
| 0 | main-before-blur | 875713112 / main | [[3584,0],[1024,2293760]] | stride plan 1 = 1024 < 3200 |
| 1 | down-0 | 875708993 / effets | [[3584,0],[1024,2293760]] | idem |
| 2 | down-1 | 875708993 / effets | [[3584,0],[1024,2293760]] | idem |
| 3 | down-2 | 875708993 / effets | [[3584,0],[1024,2293760]] | idem |
| 4 | up-2 | 875708993 / effets | [[3584,0],[1024,2293760]] | idem |
| 5 | up-1 | 875708993 / effets | [[3584,0],[1024,2293760]] | idem |
| 6 | up-0 | 875708993 / effets | [[3584,0],[1024,2293760]] | idem |
| 7 | cache-after-copy | 875709016 / cache | [[3584,0],[1024,2293760]] | idem |
| 8 | output-presubmit | 875713112 / main | [[3584,0],[1024,2293760]] | idem |
Les formats correspondent aux constantes du validateur : 875713112 = XR24
(XRGB, accepté pour main), 875708993 = AB24 (ABGR, accepté pour effets), et
875709016 = XB24 (XBGR, accepté pour cache). Le rejet n'est donc pas une
absence de DMA-BUF ni un format de rôle inadéquat. L'audit n'attribue pas de
cause plus générale à l'instrumentation ou au pilote.
## Portée des preuves
Résumé `gpu-execution/G1-summary.md` et JSON : 49 artefacts; les 49 chemins et
SHA-256 publiés dans `gpu-execution/G1-summary.json` correspondent aux fichiers
présents. Le manifeste d'arbre porte le SHA-256
`ccd45c0a56c1ee72ff7bcd84711090745012911a7ecc3e0d890aa52ccb715329`.
Pour les artefacts centraux :
- `epoch-1.meta`: 19 686 octets, SHA-256
`c8ca51bca694db9e21471356a4260064ed81ab7013a88d4edb4b8572044a825c`.
- `epoch-1.rgba`: 8 312 048 octets, SHA-256
`a6dc7fd873e1ab7e52e04625bacb3251ba8e1aad2e766811efebde7ae7932a99`.
- Métadonnées : 1 begin, 9 sample, 11 write, 2 copy, 1 end; fin native
`epoch=1 points=9 complete=1`. Ceci établit la publication des métadonnées
et des octets rapportés, pas leur validité comme lecture GPU.
- `read_success=NOT_PROVEN`; `GL_ERROR_QUEUE=UNOBSERVED`; précision effective
`UNCERTAIN`. Restauration GL/EGL n'est attestée qu'au moyen des champs
échantillonnés (`before/after`, contextes, readbuffer); une preuve complète
indépendante de la restauration n'est **NOT PROVEN**.
- Aucun reçu d'epoch accepté ni analyse n'a été produit. Aucun epoch 2/3 n'a
été lancé. La valeur byte-count et les SHA prouvent l'intégrité des fichiers
par rapport au résumé, pas le succès de `glReadPixels`.
- Deux `EGL_BAD_ALLOC` sont enregistrés pendant `eglQueryDevicesEXT`, puis
l'initialisation Mesa/radeonsi et l'observation DMA-BUF se poursuivent.
Aucune preuve ne relie ces messages au rejet de métadonnées; aucun lien causal
n'est affirmé.
## Texte de publication fidèle
« G1 a publié un seul epoch brut, ensuite rejeté par le validateur : son
contrôle exige une stride d'au moins 3200 octets sur chaque plan DMA-BUF, alors
que le second plan rapporté pour les neuf échantillons a une stride de 1024.
Les données restent non acceptées; `read_success` n'est pas prouvé et la queue
GL n'a pas été observée. G1 est INCONCLUSIVE. »
## Empreintes des sources d'interprétation
- `gpu-field/validate.py`: `158a820a2f170cabce37f7b8965ce23c3887cfbced32fecaea4914ea6f423ef7`.
- `gpu-field/scene/render/fx_renderer/gpu_field_private.h`:
`16f5287c5eb5fb463aef526525631c55e299f5ae860976b46cc0fb901cf1e611`.
- `gpu-execution/G1-summary.json`:
`fe48671c95a06210768c3addf08b61dbf67efa80953d49b389e3b851687c3d47`.
- `gpu-field/README.md`:
`df1d6782e5bead9f01d6dbf998c1c47d09ec01bc3e2db0da90975fea14d5c32c`.
## Actions et limites
Lecture/parsing ciblés, `stat`, SHA-256 et comparaison avec les prédicats
source uniquement. Aucun script historique du labo n'a été importé ou lancé.
Pas de modification des sources, des reçus ou des artefacts G1. La seule
écriture est cette note sous `audit/gpu-rejection/`.

View file

@ -0,0 +1,242 @@
{
"ticket": "SWAYFX_MOSAIC_STEP20C_MINIMAL_CORE_BUILD_RUNTIME_AND_GPU_FIELD_GATE_V1",
"limits": {
"runs": 14,
"captures": 180,
"captures_per_run": 20,
"duration_s": 150,
"scene_builds": 2,
"mosaic_builds": 2,
"sanitizer_pairs": 1,
"gpu_pairs": 1,
"stages": 3
},
"runs": [
{
"id": "E0-pristine",
"campaign": "E",
"token": "d5d99af8e1034170a27f455173c472f9",
"started_ns": 265195895647802,
"capture_limit": 12,
"actions": {
"configuration": 5,
"chain": 1
},
"status": "RESERVED"
},
{
"id": "E0-core",
"campaign": "E",
"token": "50e3a2160730479d83e73bfb0ce3f376",
"started_ns": 265229524683326,
"capture_limit": 12,
"actions": {
"configuration": 5,
"chain": 1
},
"status": "RESERVED"
},
{
"id": "E2-vertical",
"campaign": "E",
"token": "8997061f77f848e6a9ab412808da1d77",
"started_ns": 265237917885458,
"capture_limit": 14,
"actions": {
"configuration": 5,
"chain": 1
},
"status": "RESERVED"
},
{
"id": "E1",
"campaign": "E",
"token": "1c2e22e91e8642908828a7dd76225bf2",
"started_ns": 266456114241785,
"capture_limit": 20,
"actions": {
"configuration": 8,
"chain": 2,
"camera": 1,
"move": 5,
"capture": 6,
"click": 4,
"grab": 2
},
"status": "RESERVED"
},
{
"id": "E2-fractional",
"campaign": "E",
"token": "df9cce31d956416987af0415d34d335e",
"started_ns": 266479875163857,
"capture_limit": 14,
"actions": {
"capture": 9,
"configuration": 8,
"chain": 1,
"camera": 1,
"move": 5,
"click": 4,
"grab": 2
},
"status": "RESERVED"
},
{
"id": "E2-r90",
"campaign": "E",
"token": "d003f65819084b1a9ccecb734869e21a",
"started_ns": 266570714460305,
"capture_limit": 14,
"actions": {
"capture": 9,
"configuration": 8,
"chain": 1,
"camera": 1,
"move": 5,
"click": 4,
"grab": 2
},
"status": "RESERVED"
},
{
"id": "E3",
"campaign": "E",
"token": "2504fa708f814b049ebac25720ba8e1c",
"started_ns": 266585515990064,
"capture_limit": 18,
"actions": {
"capture": 17,
"configuration": 14,
"chain": 1,
"camera": 2,
"move": 7,
"click": 6,
"grab": 2,
"mutation": 4
},
"status": "RESERVED"
},
{
"id": "E4",
"campaign": "E",
"token": "fd186717df944b40b5892ccd2a3c7a32",
"started_ns": 266601283871546,
"capture_limit": 14,
"actions": {
"configuration": 12,
"move": 4,
"capture": 12,
"mutation": 3,
"click": 3,
"grab": 2
},
"status": "RESERVED"
},
{
"id": "E5",
"campaign": "E",
"token": "e345eac838a64b448c60e44a13208c85",
"started_ns": 266612083508283,
"capture_limit": 12,
"actions": {
"configuration": 14,
"chain": 1,
"camera": 3,
"move": 6,
"capture": 10,
"click": 4,
"grab": 2
},
"status": "RESERVED"
},
{
"id": "E6",
"campaign": "E",
"token": "7d0d2ba90c8944abb35ed085081874f1",
"started_ns": 266626062884303,
"capture_limit": 10,
"actions": {
"configuration": 7,
"camera": 1,
"move": 1,
"capture": 8,
"mutation": 3
},
"status": "RESERVED"
},
{
"id": "E7",
"campaign": "E",
"token": "acf05a078b3944f8865a456693171914",
"started_ns": 266644863139276,
"capture_limit": 14,
"actions": {
"configuration": 27,
"capture": 14,
"mutation": 10,
"camera": 2
},
"status": "RESERVED"
},
{
"id": "G1",
"campaign": "G",
"token": "217efe3627ea4475b04eb7b0e7419a0a",
"started_ns": 267265321818230,
"capture_limit": 12,
"actions": {
"configuration": 24,
"move": 1,
"capture": 3,
"mutation": 8,
"camera": 2
},
"status": "RESERVED",
"source_freeze_sha256": "58535958c0c55b3bee758bfda413df594302122f9a9da2e1a4aedde3889b9f5f",
"binary_sha256": "1905a20fa72ebcc7ec47d1584a4653654280288096a20005199eb75673ac0383"
}
],
"builds": [
{
"kind": "scene",
"ordinal": 1,
"label": "initial",
"status": "completed",
"lab": "/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_",
"before_setup": true
},
{
"kind": "mosaic",
"ordinal": 1,
"label": "initial",
"status": "completed",
"lab": "/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_",
"before_setup": true
},
{
"type": "gpu_pair",
"count": 1,
"label": "G1 diagnostic GPU field",
"status": "completed",
"lab": "/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_",
"source_freeze_sha256": "2dada96f19cba3d8f9566f5c24084f81db41da3b3cdb3eed01e4553ee01d98a9",
"before_setup": true,
"started_ns": 1791994916188850162,
"finished_ns": 1791995000519248557,
"build_identity": "/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_/gpu-field/build-identity.json",
"build_identity_sha256": "5065d3d4823b8a19d6f80595d4f2b3e1d62ddb671acf9312049de5841a0ed81d",
"scene_gpu_builds": 1,
"mosaic_gpu_builds": 1
}
],
"stages": [
{
"ordinal": 1,
"label": "stage1",
"status": "completed",
"lab": "/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_",
"before_staging": true
}
]
}

View file

@ -0,0 +1,54 @@
{
"binary_sha256": "29baf31aa3eeed259825c87d0799fa5d3dc83710afb967154604eb1c6657f965",
"basis": "existing non-graphical LD_BIND_NOW --version capture; no replay",
"raw_exit": 0,
"binding_records": 43,
"unique_scene_symbols": [
"wlr_scene_buffer_create",
"wlr_scene_buffer_from_node",
"wlr_scene_buffer_send_frame_done",
"wlr_scene_buffer_set_buffer",
"wlr_scene_buffer_set_buffer_with_options",
"wlr_scene_buffer_set_color_encoding",
"wlr_scene_buffer_set_color_range",
"wlr_scene_buffer_set_dest_size",
"wlr_scene_buffer_set_opacity",
"wlr_scene_buffer_set_opaque_region",
"wlr_scene_buffer_set_primaries",
"wlr_scene_buffer_set_source_box",
"wlr_scene_buffer_set_transfer_function",
"wlr_scene_buffer_set_transform",
"wlr_scene_drag_icon_create",
"wlr_scene_get_scene_output",
"wlr_scene_layer_surface_v1_configure",
"wlr_scene_layer_surface_v1_create",
"wlr_scene_node_coords",
"wlr_scene_node_destroy",
"wlr_scene_node_place_above",
"wlr_scene_node_place_below",
"wlr_scene_node_set_enabled",
"wlr_scene_node_set_position",
"wlr_scene_output_build_state",
"wlr_scene_output_create",
"wlr_scene_output_destroy",
"wlr_scene_output_needs_frame",
"wlr_scene_output_send_frame_done",
"wlr_scene_output_set_position",
"wlr_scene_rect_from_node",
"wlr_scene_subsurface_tree_create",
"wlr_scene_subsurface_tree_set_clip",
"wlr_scene_surface_create",
"wlr_scene_surface_send_frame_done",
"wlr_scene_surface_try_from_buffer",
"wlr_scene_timer_finish",
"wlr_scene_tree_create",
"wlr_scene_tree_from_node",
"wlr_scene_xdg_surface_create"
],
"providers_receipt_sha256": "ada6f7436dfc7f4ab0914d303ec701e00efbc61d4d595a00cd160e47ef09e9ef",
"limits": [
"forced relocations observed without runtime scene qualification",
"shared layouts require exact pair requalification",
"notices LEGAL_NOT_APPROVED/PARTIAL; release/security people not designated"
]
}

View file

@ -0,0 +1,35 @@
{
"kind": "parent transcription of accessible independent reviewer final response; not verbatim",
"reviewer": "/root/core_evidence_delta_review",
"role": "reviewer / GPT-6.1-Sol / medium / isolated",
"verdict": "ACCEPTED for experimental evidence fidelity; E/F qualification PARTIAL",
"artifact_hash_checks": {
"checked": 319,
"matches": 319,
"divergences": 0
},
"mandatory_limits": [
"E0-pristine/E0-core/E2-vertical INCONCLUSIVE before captures; no standard comparable control",
"E1 first chain PARTIALLY_FEASIBLE; second owner on workspace2/H2/splith after outside click, fails wrong ancestry before standard return; run INCONCLUSIVE",
"E2 scale1.25 and R90 raw measurements PASS; owners error0, reference/filler error32 after compositor stop; marker swaybg cleanup timeout3s retained",
"E3 resize/clamp960/workspace6/owner10 migration and four input checks sampled PASS; no universal atomicity",
"E4 actual nonleaf con10 fullscreen1 contains owner9 fullscreen0; workspace fullscreen marker alone not evidence; policy INCONCLUSIVE missing control",
"E5 hide sampled pixels/input partial; outside click causes popup_done before camera restore; same-chain return NOT_EXERCISED",
"E6 held39 ns266629075726834; native timeout at00:00:07.998 before captures and release ns266634202244001; ACK39 ns266634202350097; global frozen validator FAIL remains INCONCLUSIVE; owner error32; saved native identity NOT OBSERVABLE",
"E7 classic ROI4096 changed; wholeH1 A/Areturn FAIL33789pixels bbox[480,0,800,600], maxRGB[6,5,1]; H2 exact; shadows enable but off/on identical => SHADOW_NOT_EXERCISED; three FX clients error0 normalclose1 before compositor",
"All E terminal IPC exit1 preserved with compositor0",
"Runner scientific_cleanup PASS is process recovery gate, not universal normal client close",
"R2 runner exits four1/four0; initial three1; total11E plus1G1; no retries",
"Core SHA29baf31aa3eeed259825c87d0799fa5d3dc83710afb967154604eb1c6657f965; no historical/G1 identity transfer"
],
"F": {
"CORE_SAMPLED_APPLIED_INPUT": "PARTIALLY_FEASIBLE",
"CORE_SAMPLED_DAMAGE": "PARTIALLY_FEASIBLE",
"CORE_NATIVE_BUFFER_IDENTITY": "NOT_PROVEN",
"CORE_GPU_LIFETIME": "NOT_PROVEN",
"CORE_ATOMIC_GENERATION": "NOT_PROVEN",
"CORE_COMPLETE_DAMAGE": "NOT_PROVEN"
},
"blocking_product_findings": [],
"no_executions_or_edits_by_reviewer": true
}

View file

@ -0,0 +1,20 @@
{
"basis": "new read-only audit; no validator rerun, fixture edit, runtime or retroactive gate acceptance",
"observed_modifier": "020000001046bb04",
"decode": {
"vendor": 2,
"tile_version": 4,
"DCC": 1,
"DCC_RETILE": 0,
"DCC_PIPE_ALIGN": 1
},
"local_header": "/usr/include/libdrm/drm_fourcc.h",
"local_header_sha256": "992f33804acda60b0b46f1a272942c3592484ea3217d469239f1cb12249de2eb",
"primary_source": "https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/drm/drm_fourcc.h",
"inference": "AMD/GFX11 DCC without retile encodes main surface plane0 and DCC surface plane1. Applying 800*4 minimum image stride to each plane is not justified by this layout. This accounts for validator rejection, not GL read success or all DMA allocation validity. All nine sample object records contain the same observed modifier.",
"raw_G1": "INCONCLUSIVE",
"read_success": "NOT_PROVEN",
"GL_ERROR_QUEUE": "UNOBSERVED",
"samples_checked": 9,
"uniform_modifier": true
}

View file

@ -0,0 +1,20 @@
{
"kind": "R2 progress-message raw-exit count correction",
"prior_progress_message_claim": "3 raw exit 1, 5 raw exit 0",
"verified_count": {
"raw_exit_1": 4,
"raw_exit_0": 4,
"per_run": {
"E1": 1,
"E2-fractional": 0,
"E2-r90": 0,
"E3": 0,
"E4": 1,
"E5": 1,
"E6": 1,
"E7": 0
}
},
"source": "R2 raw .exit files, independently consistent with per-run rows in graphics-execution-R2/summary.json",
"note": "Prior R2 summary and raw receipts left unchanged; only the earlier aggregate progress statement was incorrect."
}

View file

@ -0,0 +1,96 @@
"""Pure read-only native-field float64 ideal analysis. No fitted tolerances.
WHY/CONTRACT/INVARIANT: the ideal bilinear coordinates are a source-derived
model, never a measurement of mediump interpolation or linked GPU arithmetic.
"""
import numpy as np
from validate import need, FRESH, reconstruct
def field(epoch,p):
s=epoch['samples'][p];x,y,w,h=s['roi']
return np.frombuffer(epoch['raw'][s['offset']:s['offset']+s['bytes']],dtype=np.uint8).reshape(h,w,4)
def deltas(a,b):
need(a.shape==b.shape,'homologous field dimensions')
d=b.astype(np.int16)-a.astype(np.int16);changed=np.any(d!=0,axis=2);yy,xx=np.where(changed)
return dict(pixels=int(changed.sum()),bbox=[int(xx.min()),int(yy.min()),int(xx.max()+1),int(yy.max()+1)] if len(xx) else None,
min=d.min(axis=(0,1)).tolist(),max=d.max(axis=(0,1)).tolist(),maxabs=int(np.abs(d).max()),
signed_histograms=[{str(int(k)):int(v) for k,v in zip(*np.unique(d[:,:,c],return_counts=True))} for c in range(4)])
def bilinear(src,x,y):
# Physical texture edge clamp, never clamp to its fresh domain.
x=np.clip(x,0,src.shape[1]-1);y=np.clip(y,0,src.shape[0]-1)
x0=np.floor(x).astype(int);y0=np.floor(y).astype(int);x1=np.minimum(x0+1,src.shape[1]-1);y1=np.minimum(y0+1,src.shape[0]-1)
tx=(x-x0)[...,None];ty=(y-y0)[...,None]
return src[y0,x0]*(1-tx)*(1-ty)+src[y0,x1]*tx*(1-ty)+src[y1,x0]*(1-tx)*ty+src[y1,x1]*tx*ty
def ideal(src,w,h,down,fresh):
src=src.astype(np.float64);yy,xx=np.mgrid[:h,:w]
if down:
x,y=2*xx+.5,2*yy+.5;taps=[(0,0,4/8),(-5,-5,1/8),(5,5,1/8),(5,-5,1/8),(-5,5,1/8)]
else:
x,y=xx/2-.25,yy/2-.25;taps=[(-2.5,0,1/12),(2.5,0,1/12),(0,-2.5,1/12),(0,2.5,1/12),(-1.25,-1.25,2/12),(1.25,1.25,2/12),(1.25,-1.25,2/12),(-1.25,1.25,2/12)]
mask=np.zeros(src.shape[:2],dtype=np.float64);mask[:fresh[1],:fresh[0]]=1
inside=src*mask[...,None];outside=src*(1-mask)[...,None]
a=np.zeros((h,w,4));b=np.zeros_like(a)
for dx,dy,weight in taps:a+=bilinear(inside,x+dx,y+dy)*weight;b+=bilinear(outside,x+dx,y+dy)*weight
return a+b,a,b
def residual_stats(d):
return dict(min=d.min(axis=(0,1)).tolist(),max=d.max(axis=(0,1)).tolist(),maxabs=float(np.abs(d).max()),nonzero_pixels=int(np.any(d!=0,axis=2).sum()))
def composition(rgb,coefficient=119):
need(coefficient==119,'prospective composition coefficient')
return np.floor(np.array((68,32,16))+rgb[:,:,:3].astype(np.float64)*(119/255)+.5)
def whole_hypothesis(epochs):
a,b=epochs[0],epochs[2];stats={}
for p in range(7):
aa,bb=field(a,p),field(b,p);fresh=FRESH[p];stats[str(p)]=dict(full=deltas(aa,bb),fresh=deltas(aa[:fresh[1],:fresh[0]],bb[:fresh[1],:fresh[0]]))
first=next((p for p in range(1,7) if stats[str(p)]['fresh']['pixels']),None)
return dict(SOURCE_MAIN_WHOLE_EQUALITY='EXACT' if not stats['0']['full']['pixels'] else 'REFUTED',
FIRST_FRESH_DIVERGENCE_WHOLE=first,H_FRONTIERE_CHAMP_ENTIER='COMPATIBLE_EXACT_PREDICTIONS' if not any(stats[str(p)]['fresh']['pixels'] for p in (0,1,2)) and first==3 else 'REFUTED',fields=stats)
def ideal_residuals(epochs,output=None):
results=[];native=[]
for e in (epochs[0],epochs[2]):
r=reconstruct(e);arr=lambda b:np.frombuffer(b,dtype=np.uint8).reshape(600,800,4)
srcs=[field(e,0),field(e,1),field(e,2),arr(r['P2']),arr(r['Q2']),arr(r['P1'])]
predicted=[];measured=[];freshparts=[];tails=[]
for i,src in enumerate(srcs):
w,h=FRESH[i+1];v,a,b=ideal(src,w,h,i<3,FRESH[i]);dst=field(e,i+1)[:h,:w]
predicted.append(v);measured.append(dst);freshparts.append(a);tails.append(b)
native.append((predicted,measured,freshparts,tails))
for i in range(6):
pa,ma,fa,ta=[native[0][k][i] for k in range(4)];pb,mb,fb,tb=[native[1][k][i] for k in range(4)]
pd=pb-pa;md=mb.astype(float)-ma.astype(float);res=md-pd
results.append(dict(stage=i,absolute_A=residual_stats(ma-pa),absolute_Areturn=residual_stats(mb-pb),differential=residual_stats(res),fresh_difference=residual_stats(fb-fa),outside_difference=residual_stats(tb-ta),ideal_source_exact_but_destination_different=int((np.all(pd==0,axis=2)&np.any(md!=0,axis=2)).sum())))
if output:
np.savez(output/f'stage-{i}-ideal-and-residuals.npz',ideal_A=pa,ideal_Areturn=pb,absolute_A=ma-pa,absolute_Areturn=mb-pb,differential=res,fresh=fb-fa,outside=tb-ta)
return dict(model='SOURCE_DERIVED_FLOAT64_IDEAL_NO_TOLERANCE',effective_precision='NOT_MEASURED',residuals=results)
def glass_relation(epochs,run,states):
from PIL import Image
run=__import__('pathlib').Path(run)
pngs=[np.array(Image.open(run/(tag+'-H1.png')).convert('RGB')) for tag in ('optimized-A','optimized-B','optimized-A-return')]
need(all(p.shape==(600,800,3) for p in pngs),'whole H1 PNG dimensions')
aa=pngs[0];ar=pngs[2];changed=np.any(aa!=ar,axis=2)
# Fixed prospective H1 domain, every pixel accounted for; unknown predicates
# stay unknown. CORE IPC pending geometry cannot prove atomic applied order.
relation=[]
for e,png in zip((epochs[0],epochs[2]),(aa,ar)):
u=field(e,6);pred=composition(u)
direct=png.astype(float)-pred
flipped=png.astype(float)-pred[::-1]
relation.append(dict(raw_direct_all_H1_residual=residual_stats(direct),raw_flip_y_all_H1_residual=residual_stats(flipped),admissibility='NOT_PROVEN',orientation='SOURCE_PROJECTION_PREDICTS_GL_LOW_TO_HIGH; constant-y witness does not prove orientation'))
sampled=[]
for e in epochs:
cache=field(e,7);pre=field(e,8)
if e['rows'][0]['epoch']==2:u=field(e,6)
else:u=field(e,6)[292:308,784:800]
sampled.append(dict(epoch=e['rows'][0]['epoch'],U0_cache_RGBA=deltas(u,cache),cache_presubmit_RGBA=deltas(cache,pre),ideal_glass_presubmit_RGB_residual=residual_stats(pre[:,:,:3].astype(float)-composition(u))))
return dict(WHOLE_VISIBLE_GLASS_RELATION='INCONCLUSIVE',WHOLE_H1_DEFECT_REPRODUCED='EXERCISED' if changed.any() else 'NOT_EXERCISED',
whole_H1_RGBA_equivalent_RGB=dict(pixels=int(changed.sum()),min=(ar.astype(int)-aa).min(axis=(0,1)).tolist(),max=(ar.astype(int)-aa).max(axis=(0,1)).tolist()),
exhaustive_partition=dict(total_pixels=480000,admissible=0,other=0,predicate_unknown=480000,changed_admissible=0,changed_other=0,changed_predicate_unknown=int(changed.sum())),
candidate_geometry=states,all_H1_relation=relation,CACHE_COPY_SAMPLED=sampled,
meaning='Raw formula residuals cover every H1 pixel and do not define admissibility. No precision tolerance or anomaly mask.',GPU_FULL_FIELD='OBSERVED_UNDER_VALIDATOR_GATES',COMPLETE_DAMAGE='NOT_PROVEN',ATOMIC_GENERATION='NOT_PROVEN')

View file

@ -0,0 +1,146 @@
"""Offline tests importing the SAME validator/model called by the runner."""
import copy,json,struct,tempfile,unittest
from pathlib import Path
import numpy as np
import validate as v
import analysis as a
def bits(x):return struct.unpack('I',struct.pack('f',x))[0]
def obj(n,cache=False):
return dict(fx=f'0x{n}',wlr=f'0x{n+100}',fbo=n,rbo=n+10,tex=n+20,image=f'img{n}',dimensions=[800,600],dmabuf_available=True,drm_format=v.XBGR if cache else v.ABGR,modifier='0000000000000000',dmabuf_size=[800,600],planes=[[3200,0]])
def fixture(e):
objects=[obj(1),obj(2),obj(3),obj(2),obj(3),obj(2),obj(3),obj(4,True),obj(1)]
rows=[dict(kind='begin',epoch=e,label='B' if e==2 else 'A',context='ctx',output='HEADLESS-1',pass_=e,scale=1,transform=0,radius=5,passes=3,strength=1,effects_neutral=True,GL_ERROR_QUEUE='UNOBSERVED',read_success='NOT_PROVEN',rows='GL_LOW_TO_HIGH',strings=['vendor','renderer','OpenGL ES 3.2','ES GLSL'],precision=[[0]*5]*6)]
rows[0]['pass']=rows[0].pop('pass_');raw=bytearray()
for p in range(9):
if 1<=p<=6:
uniforms=[dict(name=n,type=5126,value=[bits(x) for x in vals]) for n,vals in [('radius',[5]),('halfpixel',[1/800,1/600] if p<=3 else [1/3200,1/2400]),('proj',[1]*9),('tex_proj',[1]*9)]]
rows.append(dict(kind='write',sequence=p,stage=p-1,fbo=objects[p]['fbo'],program=7,read_fbo=1,texture=objects[p-1]['tex'],context='ctx',box=[0,0,800,600],region=[[0,0,*v.FRESH[p]]],viewport=[0,0,800,600],scissor=[0,0,0,0],scissor_enabled=0,blend=0,stencil=0,dither=1,color_mask=[1]*4,src=objects[p-1],dst=objects[p],sampler=[9729,9729,33071,33071],uniforms=uniforms))
if p==7:
rows.append(dict(kind='write',sequence=7,stage=-1,fbo=4,region=[[0,0,800,600]]))
rows.append(dict(kind='copy',src=objects[6],dst=objects[7],region=[[0,0,800,600]]))
x,y,w,h=v.plan(e,p)
yy,xx=np.mgrid[:h,:w];data=np.stack([(xx+x+p*13)%199,(yy+y+p*17)%199,(xx+yy+p*23)%199,np.full((h,w),255)],axis=2).astype('uint8').tobytes()
rows.append(dict(kind='sample',point=p,name=v.NAMES[p],roi=[x,y,w,h],fresh=list(v.FRESH[p]),offset=len(raw),bytes=len(data),sha256=v.sha(data),object=objects[p],before=[1,2,3,4,4,0,0,0,36064],after=[1,2,3,4,4,0,0,0,36064],context_before='ctx',context_after='ctx',target_read_buffer=36064,attachment=[36161,objects[p]['rbo'],35863,9729,8,8,8,0 if p==7 else 8],renderbuffer=[800,600,32856],before_ns=e*10000+p*10+1,after_ns=e*10000+p*10+2,fbo_status=36053,sentinel_alpha_written=True,valid=True));raw.extend(data)
rows.append(dict(kind='end',epoch=e,points=9,stages=6,writes=7,bytes=len(raw),total_bytes=sum(v.plan(k,p)[2]*v.plan(k,p)[3]*4 for k in range(1,e+1) for p in range(9)),sha256=v.sha(raw)))
return rows,bytes(raw)
class RuntimeValidator(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.temp=tempfile.TemporaryDirectory();cls.d=Path(cls.temp.name);cls.base={}
for e in (1,2,3):
rows,raw=fixture(e);cls.base[e]=rows;(cls.d/f'epoch-{e}.rgba').write_bytes(raw);(cls.d/f'epoch-{e}.rgba').chmod(0o600)
(cls.d/f'epoch-{e}.arm').write_text(('B' if e==2 else 'A')+'\n');(cls.d/f'epoch-{e}.arm').chmod(0o600)
cls.reset()
@classmethod
def reset(cls):
for e,rows in cls.base.items():
(cls.d/f'epoch-{e}.meta').write_text(''.join(json.dumps(r)+'\n' for r in rows));(cls.d/f'epoch-{e}.meta').chmod(0o600)
@classmethod
def tearDownClass(cls):cls.temp.cleanup()
def tearDown(self):self.reset()
def mutate(self,fn):
rows=copy.deepcopy(self.base[1]);fn(rows);(self.d/'epoch-1.meta').write_text(''.join(json.dumps(r)+'\n' for r in rows))
with self.assertRaises((v.Rejected,KeyError,IndexError)):v.validate_epoch(self.d,1)
def test_real_validator_accepts(self):self.assertEqual(len(v.validate(self.d)),3)
def test_arm_missing_invalid(self):
p=self.d/'epoch-1.arm';data=p.read_bytes()
try:
for bad in (b'',b'B\n',b'A\nextra'):
p.write_bytes(bad)
with self.assertRaises(v.Rejected):v.validate_epoch(self.d,1)
finally:p.write_bytes(data)
def test_metadata_mode(self):
p=self.d/'epoch-1.meta';p.chmod(0o644)
with self.assertRaises(v.Rejected):v.validate_epoch(self.d,1)
def test_budget(self):self.assertEqual(v.read_plan(),dict(reads=27,bytes=16633312))
def test_invalid_fourth(self):
with self.assertRaises(v.Rejected):v.plan(4,0)
def test_restore_each_field(self):
for k in range(9):
with self.subTest(k=k):self.mutate(lambda r,k=k:r[1]['after'].__setitem__(k,777))
def test_context(self):self.mutate(lambda r:r[1].update(context_after='other'))
def test_bad_format_channels(self):
for k,value in ((0,5890),(1,99),(2,5126),(3,35904),(4,10),(5,0),(6,5),(7,0)):
with self.subTest(k=k):self.mutate(lambda r,k=k,value=value:r[1]['attachment'].__setitem__(k,value))
def test_bad_dmabuf(self):self.mutate(lambda r:r[1]['object'].update(drm_format=0))
def test_rbo_size(self):self.mutate(lambda r:r[1]['renderbuffer'].__setitem__(0,799))
def test_readbuffer(self):self.mutate(lambda r:r[1].update(target_read_buffer=0))
def test_truncated_and_corrupt(self):
rp=self.d/'epoch-1.rgba';old=rp.read_bytes()
try:
for bad in (old[:-1],bytes([old[0]^1])+old[1:]):
rp.write_bytes(bad)
with self.assertRaises(v.Rejected):v.validate_epoch(self.d,1)
finally:rp.write_bytes(old)
def test_incomplete_reordered_extra(self):
for fn in (lambda r:r.pop(),lambda r:r.__setitem__(1,r[3]),lambda r:r.insert(3,copy.deepcopy(r[2]))):self.mutate(fn)
def test_admissibility(self):
for k,val in [('epoch',4),('label','B'),('output','HEADLESS-2'),('scale',1.25),('transform',1),('radius',4),('strength',.5),('effects_neutral',False)]:
with self.subTest(k=k):self.mutate(lambda r,k=k,val=val:r[0].__setitem__(k,val))
def test_sentinel(self):self.mutate(lambda r:r[1].update(sentinel_alpha_written=False))
def test_roi_outside(self):self.mutate(lambda r:r[1]['roi'].__setitem__(0,800))
def test_budget_offset(self):self.mutate(lambda r:r[-1].update(total_bytes=v.LIMIT+1))
def test_region_hole_same_extents(self):self.mutate(lambda r:r[2].update(region=[[0,0,400,149],[0,150,400,300]]))
def test_alias_source_fbo(self):self.mutate(lambda r:r[2]['src'].update(fbo=r[2]['dst']['fbo']))
def test_replaced_buffer(self):self.mutate(lambda r:r[3]['object'].update(wlr='replaced'))
def test_color_clear(self):self.mutate(lambda r:r.insert(-1,dict(kind='clear',mask=16384,fbo=2)))
def test_external_write(self):
def fn(r):r.insert(-1,dict(kind='write',stage=-1,sequence=8,fbo=2,region=[[0,0,1,1]]));r[-1]['writes']=8
self.mutate(fn)
def test_unknown_destroy(self):self.mutate(lambda r:r.insert(-1,dict(kind='destroy',fbo=2)))
def test_missing_draw(self):self.mutate(lambda r:r.pop(2))
def test_exact_reconstruction(self):
e=v.validate_epoch(self.d,1);r=v.reconstruct(e)
p2=np.frombuffer(r['P2'],dtype='uint8').reshape(600,800,4);q2=np.frombuffer(r['Q2'],dtype='uint8').reshape(600,800,4);p1=np.frombuffer(r['P1'],dtype='uint8').reshape(600,800,4)
np.testing.assert_array_equal(p2[:75,:100],a.field(e,3));np.testing.assert_array_equal(p2[75:],a.field(e,1)[75:]);np.testing.assert_array_equal(p2[:75,100:],a.field(e,1)[:75,100:])
np.testing.assert_array_equal(q2[:150,:200],a.field(e,4));np.testing.assert_array_equal(q2[150:],a.field(e,2)[150:]);np.testing.assert_array_equal(p1[:300,:400],a.field(e,5));np.testing.assert_array_equal(p1[300:],p2[300:]);np.testing.assert_array_equal(p1[:300,400:],p2[:300,400:])
def test_B_cannot_reconstruct(self):
with self.assertRaises(v.Rejected):v.reconstruct(v.validate_epoch(self.d,2))
def test_exact_predictions_mutated(self):
epochs=v.validate(self.d)
for p in (0,1,2):
e=copy.deepcopy(epochs);s=e[2]['samples'][p];raw=bytearray(e[2]['raw']);raw[s['offset']]^=1;e[2]['raw']=bytes(raw)
self.assertEqual(a.whole_hypothesis(e)['H_FRONTIERE_CHAMP_ENTIER'],'REFUTED')
class Controller(unittest.TestCase):
def test_durable_one_shot_arm(self):
import runner
with tempfile.TemporaryDirectory() as tmp:
d=Path(tmp)
runner.arm(d,1,'A');self.assertEqual((d/'epoch-1.arm').read_bytes(),b'A\n')
with self.assertRaises(v.Rejected):runner.arm(d,1,'A')
with self.assertRaises(v.Rejected):runner.arm(d,2,'B')
with self.assertRaises(v.Rejected):runner.arm(d,4,'A')
with self.assertRaises(v.Rejected):runner.arm(d,2,'A')
def test_real_SHM_validator_mismatch(self):
import runner
with tempfile.TemporaryDirectory() as tmp:
d=Path(tmp);log=[]
for i,colors in enumerate(('00101010,00202020','00303030,00404040','00101010,00202020'),1):
cs=[int(x,16) for x in colors.split(',')];raw=b''.join(struct.pack('<I',cs[x//8%2]) for x in range(800))*600
p=d/f'background-{i}.shm';p.write_bytes(raw)
log.append(f'SHM_SOURCE mode=background path={p} bytes={len(raw)} stride=3200 format=XRGB8888 colors={colors}')
self.assertTrue(runner.fx_validators.sources(d,'\n'.join(log))['ABA_byte_exact'])
p=d/'background-3.shm';bad=bytearray(p.read_bytes());bad[0]^=1;p.write_bytes(bad)
with self.assertRaises(ValueError):runner.fx_validators.sources(d,'\n'.join(log))
class Model(unittest.TestCase):
def test_constant_and_partition(self):
src=np.full((80,100,4),64,dtype='uint8')
for down in (False,True):
pred,fresh,tail=a.ideal(src,20,15,down,(10,10));np.testing.assert_allclose(pred,64,rtol=0,atol=1e-13);np.testing.assert_array_equal(pred,fresh+tail)
def test_linear_ramp_bilinear(self):
yy,xx=np.mgrid[:100,:100];src=np.repeat((xx+yy)[:,:,None],4,axis=2).astype(float)
np.testing.assert_array_equal(a.bilinear(src,np.array([[10.5]]),np.array([[20.25]])),np.full((1,1,4),30.75))
def test_tap_weights_ramp(self):
yy,xx=np.mgrid[:100,:100];src=np.repeat((xx+yy)[:,:,None],4,axis=2).astype(float)
down,_,_=a.ideal(src,30,30,True,(100,100));self.assertEqual(down[10,10,0],41)
up,_,_=a.ideal(src,50,50,False,(100,100));self.assertAlmostEqual(up[20,20,0],19.5)
def test_composition(self):
src=np.full((1,1,4),255);np.testing.assert_array_equal(a.composition(src),[[[187,151,135]]])
with self.assertRaises(v.Rejected):a.composition(src,136)
if __name__=='__main__':unittest.main(verbosity=2)

View file

@ -0,0 +1,20 @@
"""Compile exact private SHA helper bytes for a CPU-only hash unit check.
No compositor, GL context, client or diagnostic source-pair build is launched.
"""
import hashlib,json,subprocess,tempfile
from pathlib import Path
HERE=Path(__file__).resolve().parent
header=(HERE/'scene/render/fx_renderer/gpu_field_private.h').read_text()
start=header.index('static uint32_t gf_rot');end=header.index('static void gf_log')
code=header[start:end]
with tempfile.TemporaryDirectory(prefix='g1-cpu-hash-') as tmp:
p=Path(tmp);source=p/'sha.c';binary=p/'sha'
source.write_text('#include <stdint.h>\n#include <stddef.h>\n#include <stdio.h>\n'+code+'\nint main(void){unsigned char data[10000];size_t n=fread(data,1,sizeof(data),stdin);char h[65];gf_sha(data,n,h);puts(h);return 0;}\n')
c=subprocess.run(['cc','-std=c17','-Wall','-Wextra','-Werror',str(source),'-o',str(binary)],capture_output=True,text=True)
if c.returncode:raise RuntimeError(c.stderr)
lengths=[0,3,55,56,63,64,65,127,128,10000]
for n in lengths:
data=bytes((i*37)%256 for i in range(n))
r=subprocess.run([str(binary)],input=data,capture_output=True)
assert r.returncode==0 and r.stdout.decode().strip()==hashlib.sha256(data).hexdigest(),n
print(json.dumps(dict(status='PASS_CPU_ONLY',cases=len(lengths),exact_helper_sha256=hashlib.sha256(code.encode()).hexdigest(),graphical_launches=0,source_pair_builds=0)))

View file

@ -0,0 +1,151 @@
"""EXPERIMENTAL G1 runtime validator; importable offline, no launch or writer.
WHY/CONTRACT/INVARIANT: parse actual bounded publications, never trust a PASS
field or extents for regions; rejected metadata gives no native qualification.
"""
import hashlib
import json
import struct
import os
from pathlib import Path
WIDTH, HEIGHT = 800, 600
LIMIT = 16633312
META_LIMIT = 1024 * 1024
NAMES = ('main-before-blur','down-0','down-1','down-2','up-2','up-1','up-0','cache-after-copy','output-presubmit')
FRESH = ((800,600),(400,300),(200,150),(100,75),(200,150),(400,300),(800,600),(800,600),(800,600))
BXY = ((784,292),(392,142),(192,67),(92,29),(192,67),(392,142),(784,292),(784,292),(784,292))
GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, GL_UNSIGNED_NORMALIZED = 36064, 36161, 35863
# DRM little-endian AB24/XB24; actual import formats must be recorded.
ABGR, XBGR, ARGB, XRGB = [int.from_bytes(x,'little') for x in (b'AB24',b'XB24',b'AR24',b'XR24')]
class Rejected(ValueError): pass
def need(v, msg):
if not v: raise Rejected(msg)
def sha(data): return hashlib.sha256(data).hexdigest()
def plan(epoch, point):
need(epoch in (1,2,3) and 0<=point<9,'epoch/point budget')
if epoch==2 or point>=7: return (*BXY[point],16,16)
if 3<=point<=5: return (0,0,*FRESH[point])
return (0,0,800,600)
def read_plan():
total=sum(plan(e,p)[2]*plan(e,p)[3]*4 for e in (1,2,3) for p in range(9))
need(total==LIMIT and total<16*1024*1024,'planned raw budget')
return dict(reads=27,bytes=total)
def object_identity(o):
return tuple(o[k] if k!='dimensions' else tuple(o[k]) for k in ('fx','wlr','fbo','rbo','image','dimensions'))
def validate_object(o, role="effects"):
need(o['dimensions']==[800,600] and all(o[k] for k in ('fx','wlr','fbo','rbo')),'existing physical object')
need(o['dmabuf_available'] is True and o['dmabuf_size']==[800,600] and len(o['planes']) in range(1,5),'real dmabuf')
need(o['drm_format'] in ((XBGR,) if role=='cache' else (ABGR,) if role=='effects' else (ABGR,XBGR,ARGB,XRGB)),'source compatible actual DRM format')
need(len(o['modifier'])==16 and all(len(p)==2 and p[0]>=800*4 and p[1]>=0 for p in o['planes']),'dmabuf fields')
def validate_sample(s, raw, epoch, context, offset):
p=s['point']; need(s['name']==NAMES[p] and s['roi']==list(plan(epoch,p)) and s['fresh']==list(FRESH[p]),'fixed ROI/fresh domain')
x,y,w,h=s['roi'];need(x>=0 and y>=0 and w>0 and h>0 and x+w<=800 and y+h<=600,'ROI texture bounds')
n=w*h*4;need(s['offset']==offset and s['bytes']==n and offset+n<=len(raw),'read framing/truncation')
data=raw[offset:offset+n];need(sha(data)==s['sha256'],'sample SHA')
need(s['before']==s['after'] and len(s['before'])==9,'READ/DRAW/RBO/PBO/pack/readbuffer restoration')
need(s['context_before']==s['context_after']==context,'EGL context restoration')
need(s['target_read_buffer']==GL_COLOR_ATTACHMENT0,'per-FBO readbuffer unchanged')
a=s['attachment'];need(len(a)==8 and a[:3]==[GL_RENDERBUFFER,s['object']['rbo'],GL_UNSIGNED_NORMALIZED] and a[3]==9729 and a[4:7]==[8,8,8] and a[7] in (0,8),'actual attachment channels/type/encoding')
need(len(s['renderbuffer'])==3 and s['renderbuffer'][:2]==[800,600] and s['renderbuffer'][2]>0,'actual RBO size/format')
validate_object(s['object'],'cache' if p==7 else 'main' if p in (0,8) else 'effects')
need(a[7]==(0 if s['object']['drm_format'] in (XBGR,XRGB) else 8),'alpha storage source compatibility')
need(s['sentinel_alpha_written'] is True and 205 not in data[3::4],'sentinel incomplete read')
need(type(s['before_ns']) is int and type(s['after_ns']) is int and 0<s['before_ns']<=s['after_ns'],'sample monotonic interval')
need(s['valid'] is True and s['fbo_status']==36053,'C preconditions/FBO completeness failed')
return offset+n
def validate_writes(rows, samples, context):
writes=[r for r in rows if r['kind']=='write'];blur=[r for r in writes if r['stage']>=0]
need(len(blur)==6 and [r['stage'] for r in blur]==list(range(6)),'six draws strict order')
need([r['sequence'] for r in writes]==list(range(1,len(writes)+1)),'all writes ordered')
objs=[samples[p]['object'] for p in range(7)]
ping,pong=object_identity(objs[1]),object_identity(objs[2])
need(ping!=pong and objs[1]['wlr']!=objs[2]['wlr'] and objs[1]['fbo']!=objs[2]['fbo'],'distinct ping/pong alias')
need([object_identity(o) for o in objs[1:]]==[ping,pong,ping,pong,ping,pong],'same physical buffers within pass')
for i,r in enumerate(blur):
need(r['region']==[[0,0,*FRESH[i+1]]] and r['box']==[0,0,800,600],'exact domain union, no holes')
need(object_identity(r['src'])==object_identity(objs[i]) and object_identity(r['dst'])==object_identity(objs[i+1]),'source/destination continuity')
need(r['texture']==r['src']['tex'] and r['src']['tex']>0,'actual source texture mapping')
for seen,draw_obj in ((objs[i],r['src']),(objs[i+1],r['dst'])):
need(seen['tex']==0 or seen['tex']==draw_obj['tex'],'existing texture replaced')
need(r['src']['wlr']!=r['dst']['wlr'] and r['src']['fbo']!=r['dst']['fbo'],'feedback alias')
need(r['fbo']==r['dst']['fbo'] and r['context']==context and r['viewport']==[0,0,800,600],'draw FBO/context/viewport')
need(r['blend']==r['stencil']==r['scissor_enabled']==0 and r['color_mask']==[1,1,1,1],'unmasked raster write')
need(r['sampler']==[9729,9729,33071,33071] and r['program']>0 and r['texture']>0,'effective sampler/program')
u={x['name']:x for x in r['uniforms']}
need(all(k in u for k in ('radius','halfpixel','proj','tex_proj')),'effective blur uniforms/matrices')
floats=lambda n: [struct.unpack('f',struct.pack('I',b))[0] for b in u[n]['value']]
need(floats('radius')==[5.0] and len(floats('proj'))==len(floats('tex_proj'))==9,'uniform radius/matrix')
expected=(1/800,1/600) if i<3 else (1/3200,1/2400)
need(floats('halfpixel')==[struct.unpack('f',struct.pack('f',x))[0] for x in expected],'effective halfpixel')
fbo_ids={objs[1]['fbo'],objs[2]['fbo']}
need(not any(r['stage']<0 and r['fbo'] in fbo_ids for r in writes),'external ping/pong write')
need(not any(r['kind']=='clear' and r['mask']&16384 for r in rows),'color clear')
need(not any(r['kind'] in ('destroy','replace','resize','migration') for r in rows),'object lifetime/layout change')
copies=[r for r in rows if r['kind']=='copy']
cache=[r for r in copies if r['dst']['fbo']==samples[7]['object']['fbo']]
need(len(cache)==1 and object_identity(cache[0]['src'])==pong and cache[0]['region']==[[0,0,800,600]],'cache copy exact source/domain')
# Every copy includes its corresponding render write, not just a declaration.
for c in copies:
need(any(w['stage']==-1 and w['fbo']==c['dst']['fbo'] and w['region']==c['region'] for w in writes),'copy actual raster write missing')
# Stages and reads alternate; no unseen color draw between output and sample.
positions={id(r):i for i,r in enumerate(rows)}
for i,r in enumerate(blur):
begin=positions[id(samples[i])];end=positions[id(samples[i+1])]
between=[w for w in writes if begin<positions[id(w)]<end]
need(between==[r],'additional/missing write in native interval')
return True
def validate_epoch(directory, epoch):
d=Path(directory);need(epoch in (1,2,3),'fourth epoch')
mp=d/f'epoch-{epoch}.meta';rp=d/f'epoch-{epoch}.rgba';arm=d/f'epoch-{epoch}.arm'
need(arm.is_file() and not arm.is_symlink() and arm.read_bytes()==(('B' if epoch==2 else 'A')+'\n').encode(),'arm absent/invalid')
for file in (mp,rp,arm):
need(file.is_file() and file.stat().st_uid==os.geteuid() and file.stat().st_mode&0o777==0o600,'exclusive mode/ownership')
need(mp.is_file() and rp.is_file() and not mp.is_symlink() and not rp.is_symlink(),'complete exclusive publication')
need(mp.stat().st_size<=META_LIMIT and rp.stat().st_size<=8312048,'bounded publications')
text=mp.read_bytes();need(text.endswith(b'\n'),'metadata framing')
try: rows=[json.loads(line) for line in text.splitlines()]
except (ValueError,UnicodeError) as e: raise Rejected('malformed metadata') from e
need(rows and rows[0]['kind']=='begin' and rows[-1]['kind']=='end','begin/end framing')
b,e=rows[0],rows[-1];need(b['epoch']==e['epoch']==epoch and b['label']==('B' if epoch==2 else 'A'),'epoch order/label')
need(b['output']=='HEADLESS-1' and b['scale']==1 and b['transform']==0 and b['radius']==5 and b['passes']==3 and b['strength']==1 and b['effects_neutral'] is True,'acquisition admissibility')
need(b['GL_ERROR_QUEUE']=='UNOBSERVED' and b['read_success']=='NOT_PROVEN' and b['rows']=='GL_LOW_TO_HIGH','observability limits retained')
need(len(b['strings'])==4 and b['strings'][2].startswith('OpenGL ES 3.') and len(b['precision'])==6,'actual API capabilities')
need(all(r['kind'] in ('begin','write','clear','copy','sample','end') for r in rows),'unknown write/event')
samples=[r for r in rows if r['kind']=='sample'];need([s['point'] for s in samples]==list(range(9)),'nine reads ordered')
raw=rp.read_bytes();offset=0
for s in samples: offset=validate_sample(s,raw,epoch,b['context'],offset)
need(all(samples[p]['after_ns']<=samples[p+1]['before_ns'] for p in range(8)),'native sample temporal order')
need(offset==len(raw)==e['bytes']==(9216 if epoch==2 else 8312048) and sha(raw)==e['sha256'],'epoch size/whole SHA')
need(e['points']==9 and e['stages']==6 and e['writes']==sum(r['kind']=='write' for r in rows),'end counts')
need(e['total_bytes']==sum(plan(k,p)[2]*plan(k,p)[3]*4 for k in range(1,epoch+1) for p in range(9)),'total read budget')
validate_writes(rows,samples,b['context'])
return dict(rows=rows,samples=samples,raw=raw)
def validate(directory):
d=Path(directory);need(not any(d.glob('epoch-4.*')),'fourth epoch forbidden')
epochs=[validate_epoch(d,e) for e in (1,2,3)]
need(len({x['rows'][0]['pass'] for x in epochs})==3,'different local acquisition pass')
return epochs
def reconstruct(epoch):
need(epoch['rows'][0]['epoch'] in (1,3),'B partial fields cannot reconstruct')
validate_writes(epoch['rows'],epoch['samples'],epoch['rows'][0]['context'])
def data(p):
s=epoch['samples'][p];return epoch['raw'][s['offset']:s['offset']+s['bytes']]
def replace(base, patch, w,h):
need(len(base)==800*600*4 and len(patch)==w*h*4,'reconstruction dimensions')
out=bytearray(base)
for y in range(h):out[y*800*4:(y*800+w)*4]=patch[y*w*4:(y+1)*w*4]
return bytes(out)
p2=replace(data(1),data(3),100,75);q2=replace(data(2),data(4),200,150);p1=replace(p2,data(5),400,300)
return dict(P2=p2,Q2=q2,P1=p1,U0=data(6),status='SOURCE_DERIVED_RECONSTRUCTION')

View file

@ -0,0 +1,112 @@
"""Small campaign scenarios over the same reviewed Session; no historical runner copy."""
import re,time
from PIL import ImageChops
from engine import *
from geometry import *
def init_reference(s,prefix_fillers=1):
s.cmd('workspace 2');s.launch_client('reference','static-reference');rid=s.find('reference')['id'];s.cmd(f'[con_id={rid}] border none')
s.cmd('workspace 1')
for i in range(prefix_fillers):s.launch_client('filler'+str(i),'static-reference')
s.cmd('layout mosaic' if s.spec['mode']=='mosaic' else 'layout splith')
return rid
def stable_reference(s):
s.inject('move',[s.outputs[0]['logical'][0]+10,s.outputs[0]['logical'][1]+10]);time.sleep(.15)
a=s.capture('H2-reference1','HEADLESS-2');time.sleep(.6);b=s.capture('H2-reference2','HEADLESS-2');exact(a,b)
need(all(v==(48,48,56) for v in a.get_flattened_data()),'static uniform H2');s.save('reference-stability',dict(status='PASS',exact=exact(a,b)));return a
def root_state(s,state,origin,window_start,expect_hidden=False,previous=None,expected_tree=None):
# WHY/CONTRACT/INVARIANT: no PI/native scene identity exists in CORE.
# Pixel/input evidence qualifies sampled clipping only, never generation.
return dict(status='NOT OBSERVABLE',native_tree_identity='NOT OBSERVABLE',
applied_generation='NOT OBSERVABLE',owner_id=state['owner']['id'],
workspace_id=state['workspace']['id'],window=[window_start,time.monotonic_ns()])
def open_chain(s,tail_fillers=0):
s.debit('chain');s.launch_client('owner',s.spec['client'],s.spec['placement'])
for i in range(tail_fillers):s.launch_client('tail'+str(i),'static-reference')
s.cmd(f'[con_id={s.find("owner")["id"]}] focus');state=s.calibrate('owner');baseline=s.reference if hasattr(s,'reference') else stable_reference(s);s.reference=baseline
im=s.capture('owner-top-'+str(sum(e['kind']=='spawn' and e['name']=='owner' for e in s.events)),'HEADLESS-1');rect=[*state['origin'],state['owner']['window_rect']['width'],state['owner']['window_rect']['height']]
xy=solid_point(im,COLORS['TOPLEVEL'],s.outputs[0],s.frames['HEADLESS-1'],rect)
need(contains([*state['origin'],420,575 if s.spec['axis']==1 else 320],xy),'opening native bounds')
ta,ba=s.click('owner',xy,'left',True);s.wait(lambda:'POPUP_A MAP ' in s.log('owner'));time.sleep(.2)
aim=s.capture('popup-A-'+str(sum(e['kind']=='spawn' and e['name']=='owner' for e in s.events)),'HEADLESS-1');prov=Provenance(state['owner']['id'],state['workspace']['id'],'bounded-opening-window',state['evidence'])
xyA=solid_point(aim,COLORS['POPUP_A'],s.outputs[0],s.frames['HEADLESS-1'])
tb,bb=s.click('owner',xyA,'left',True);s.wait(lambda:'POPUP_B MAP ' in s.log('owner'));time.sleep(.2)
window=time.monotonic_ns();time.sleep(.15);p=validators.grab(s.log('owner'));g=geometry(s.log('owner'),state['origin'],prov)
s.delivery('owner',rect,xy,ta,ba,'TOPLEVEL',p['grabs'][0]['serial']);s.delivery('owner',g['A'],xyA,tb,bb,'POPUP_A',p['grabs'][1]['serial'])
[validators.crosses(g[k],s.spec['axis'],s.spec['boundary']) for k in ('A','B')]
left,right=s.pair_capture('mapped-'+str(sum(e['kind']=='spawn' and e['name']=='owner' for e in s.events)))
for name in ('POPUP_A','POPUP_B'):
need(count(left,COLORS[name])>0,'permitted popup pixels missing')
if s.spec['mode']=='pristine':need(count(right,COLORS[name])>0,'standard popup continuation missing')
if s.spec['mode']=='mosaic':validators.rgb(baseline,right);root=root_state(s,state,state['origin'],window)
else:root=dict(status='NOT OBSERVABLE',meaning='unmodified control has no private clip instrumentation')
s.save('chain',dict(protocol=p,geometry=g,state=state,root=root,H2_exact=s.spec['mode']=='mosaic',color_counts={n:[count(left,c),count(right,c)] for n,c in COLORS.items()}))
return state,baseline,p,g,left,right
def popup_campaign(s):
init_reference(s);state,baseline,p,g,left,right=open_chain(s)
xy=solid_point(left,COLORS['POPUP_B'],s.outputs[0],s.frames['HEADLESS-1'],g['B']);tails,buttons=s.click('owner',xy);s.delivery('owner',g['B'],xy,tails,buttons,'POPUP_B')
forbidden=intersection(g['B'],s.outputs[1]['logical']);need(forbidden[2]>6 and forbidden[3]>6,'H2 crossing interior missing')
xy=[forbidden[0]+forbidden[2]//2,forbidden[1]+forbidden[3]//2]
if s.spec['mode']=='pristine':
# The point must be real standard paint, not just a requested extent.
xy=solid_point(right,COLORS['POPUP_B'],s.outputs[1],s.frames['HEADLESS-2'],g['B']);tails,buttons=s.click('owner',xy);s.delivery('owner',g['B'],xy,tails,buttons,'POPUP_B')
else:
tails,buttons=s.click('owner',xy);ds=dismissal(tails['owner'],buttons['owner']);s.save('dismissal',dict(proof=ds,reference_buttons=buttons.get('reference'),interpretation='outside grab dismissal; no universal click-through claim'))
need(not any('target=POPUP_A ' in l or 'target=POPUP_B ' in l for v in buttons.values() for l in v),'forbidden popup delivery')
s.close_client('owner');time.sleep(.2);final=s.pair_capture('final');exact(baseline,final[1]);need(not any(count(im,COLORS[n]) for im in final for n in ('POPUP_A','POPUP_B')),'popup residual')
return 'PASS'
def hidden_campaign(s):
init_reference(s,2);state,baseline,p,g,left,right=open_chain(s,2)
xy=solid_point(left,COLORS['POPUP_B'],s.outputs[0],'logical',g['B']);tails,buttons=s.click('owner',xy);s.delivery('owner',g['B'],xy,tails,buttons,'POPUP_B')
delta=state['scene'][0]+state['scene'][2]-s.outputs[0]['logical'][0]+16;start=time.monotonic_ns();s.scroll('owner',delta);hidden=s.owner('owner')
need(intersection(hidden['scene'],s.outputs[0]['logical'])[2]==0,'owner not completely hidden')
images=s.pair_capture('fully-hidden');need(not any(count(im,COLORS[n]) for im in images for n in ('POPUP_A','POPUP_B')),'hidden popup residual');exact(baseline,images[1])
root=root_state(s,hidden,hidden['origin'],start,True,previous=state);cut=len(s.log('owner'));s.inject('move',[-790,590]);need('target=POPUP_A ' not in s.log('owner')[cut:] and 'target=POPUP_B ' not in s.log('owner')[cut:],'hidden popup pointer enter')
tails,buttons=s.click('owner',[s.outputs[0]['logical'][0]+10,590]);need(not any('target=POPUP_A ' in l or 'target=POPUP_B ' in l for rows in buttons.values() for l in rows),'hidden popup forbidden button')
if 'POPUP_DONE ' in s.log('owner')[cut:]:
s.save('hidden-dismissal',dict(status='NOT_EXERCISED',reason='legitimate hidden-owner dismissal prevents same-chain return input',raw=tails));s.scroll('owner',-delta);s.close_client('owner');final=s.pair_capture('final');exact(baseline,final[1]);return 'NOT_EXERCISED'
return_start=time.monotonic_ns();s.scroll('owner',-delta);restored=s.owner('owner');need(restored['origin']==state['origin'],'same owner origin restored')
images=s.pair_capture('returned');exact(baseline,images[1]);returned_root=root_state(s,restored,restored['origin'],return_start,previous=hidden,expected_tree=root.get('tree'));fresh=validators.grab(s.log('owner'));need(fresh['ids']==p['ids'] and fresh['grabs']==p['grabs'],'unchanged live chain')
xy=solid_point(images[0],COLORS['POPUP_B'],s.outputs[0],'logical',g['B']);tails,buttons=s.click('owner',xy);s.delivery('owner',g['B'],xy,tails,buttons,'POPUP_B')
s.save('hidden-return',dict(status='PASS',hidden=hidden,empty_root=root,returned=restored,returned_root=returned_root,ids=p['ids']))
s.close_client('owner');final=s.pair_capture('final');exact(baseline,final[1]);return 'PASS'
def sample_surviving(s,images,tag):
if 'POPUP_DONE ' in s.log('owner'):
s.save(tag+'-input',dict(status='NOT_EXERCISED',reason='legitimate popup dismissal, no new chain reserved'));return
state=s.owner('owner');prot=validators.grab(s.log('owner'));geo=geometry(s.log('owner'),state['origin'],Provenance(state['owner']['id'],state['workspace']['id'],'NOT OBSERVABLE','mutation paint+pointer'))
index=next(i for i,o in enumerate(s.outputs) if o['name']==state['workspace']['output'])
try:point=solid_point(images[index],COLORS['POPUP_B'],s.outputs[index],s.frames[s.outputs[index]['name']],geo['B'])
except ValueError:
s.save(tag+'-input',dict(status='NOT_EXERCISED',reason='B has no measured visible interior'));return
tails,buttons=s.click('owner',point);s.delivery('owner',geo['B'],point,tails,buttons,'POPUP_B');s.save(tag+'-input',dict(status='PASS',point=point,protocol=prot))
def dynamic_campaign(s):
blank=s.capture('startup-vacated-H1','HEADLESS-1');init_reference(s,2);s.cmd('gaps inner 0');state,baseline,p,g,left,right=open_chain(s)
owner_id=state['owner']['id'];ws_id=state['workspace']['id'];order=state['workspace'].get('mosaic',{}).get('rows',[{}])[0].get('children')
s.cmd('output HEADLESS-1 resolution 960x600 position -960 0',True);s.outputs[0]={**s.outputs[0],'logical':[-960,0,960,600],'mode':[960,600]};time.sleep(.35)
snap=s.snapshot('resized960');sample_surviving(s,s.pair_capture('resized960'),'resized960')
clamp=dict(status='NOT EXERCISED',reason='standard control has no Mosaic camera')
if s.spec['mode']=='mosaic':
ws=workspace(snap['tree'],'1');row=ws['mosaic']['rows'][0];n=len(row['children']);gap=max(0,ws.get('gaps',{}).get('inner',0));cell=max(640,(960-(n-1)*gap)//n);maximum=max(0,n*cell+(n-1)*gap-960)
request=maximum+960;before=row['scroll_x'];s.scroll('owner',request);settled=s.snapshot('clamped960');actual=workspace(settled['tree'],'1')['mosaic']['rows'][0]['scroll_x']
need(actual==maximum,'source-derived clamp mismatch');clamp=dict(status='PASS',count=n,gap=gap,minimum_cell_width=640,source='frozen mosaic.c125-152',maximum=maximum,before=before,requested_relative=request,observed=actual)
s.cmd('output HEADLESS-1 resolution 800x600 position -800 0',True);s.outputs[0]=dict(s.spec['outputs'][0]);time.sleep(.35);s.snapshot('resized-back');sample_surviving(s,s.pair_capture('resized-back'),'resized-back')
stages=[]
for out,tag in [('HEADLESS-2','migrated-H2'),('HEADLESS-1','migrated-back-H1')]:
s.cmd('workspace 1');s.cmd(f'move workspace to output {out}',True);time.sleep(.35);snap=s.snapshot(tag);ws=workspace(snap['tree'],'1');owner=s.find('owner');need(ws['id']==ws_id and ws['output']==out and owner['id']==owner_id,'migrated exact owner/workspace')
if order is not None:need(ws['mosaic']['rows'][0]['children']==order,'logical row order changed')
pictures=s.pair_capture(tag);sample_surviving(s,pictures,tag);stages.append(dict(tag=tag,workspace=ws,owner=owner,popup_done=re.findall(r'(POPUP_[AB]) POPUP_DONE',s.log('owner'))))
if out=='HEADLESS-2':need(not any(count(pictures[0],COLORS[n]) for n in ('POPUP_A','POPUP_B')),'old output retained popup pixels')
s.save('dynamic-ownership',dict(status='PASS',clamp=clamp,stages=stages,ids_before=p['ids'],ids_after=validators.grab(s.log('owner'))['ids'] if 'POPUP_DONE ' not in s.log('owner') else 'legitimate dismissal recorded',atomic_generation='NOT PROVEN'))
s.close_client('owner')
for name in list(s.proc):
if name.startswith('filler'):s.close_client(name,'close')
time.sleep(.2);final=s.pair_capture('final');exact(baseline,final[1]);exact(blank,final[0]);return 'PASS'

View file

@ -0,0 +1,333 @@
#define _POSIX_C_SOURCE 200809L
#include "layer-shell-client.h"
#include "xdg-shell-client.h"
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
/* WHY: one persistent background supplies real A/B commits below blur_tree.
* CONTRACT: fixed opaque background, premultiplied ARGB glass and static H2.
* INVARIANT: no animation, synthetic configure, popup or held transaction. */
static struct wl_display *display;
static struct wl_compositor *compositor;
static struct wl_shm *shm;
static struct xdg_wm_base *wm;
static struct zwlr_layer_shell_v1 *shell;
static struct wl_output *h1;
static struct wl_surface *surface;
static struct xdg_surface *xdg;
static struct xdg_toplevel *top;
static struct zwlr_layer_surface_v1 *layer;
static const char *mode;
static int width = 420, height = 320;
static unsigned commits, live_buffers, releases;
static bool close_received;
static char pattern = 'A';
static volatile sig_atomic_t running = 1;
struct pixels {
struct wl_buffer *buffer;
void *map;
size_t size;
struct pixels *next;
};
static struct pixels *buffers;
static long long stamp(void) {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (long long)t.tv_sec * 1000000000LL + t.tv_nsec;
}
static void free_pixels(struct pixels *p) {
struct pixels **it = &buffers;
while (*it && *it != p)
it = &(*it)->next;
if (*it)
*it = p->next;
wl_buffer_destroy(p->buffer);
munmap(p->map, p->size);
free(p);
--live_buffers;
}
static void release(void *data, struct wl_buffer *b) {
fprintf(stderr, "RELEASE ns=%lld buffer=%u\n", stamp(),
wl_proxy_get_id((struct wl_proxy *)b));
++releases;
free_pixels(data);
}
static const struct wl_buffer_listener buffer_listener = {.release = release};
static void draw(void) {
/* CONTRACT: exactly one runner command per reserved A/B image.
* INVARIANT: configures cannot draw, and the sequence is A/B/A only. */
if (!strcmp(mode, "background") &&
(width != 800 || height != 600 || commits >= 3 ||
pattern != (commits == 1 ? 'B' : 'A')))
exit(2);
if (width <= 0 || height <= 0 || width > 4096 || height > 4096 ||
live_buffers >= 8)
exit(2);
size_t size = (size_t)width * (size_t)height * 4;
/* CONTRACT: each mapping stays immutable until native wl_buffer.release.
* Bounds keep pool/stride integers valid, with at most eight outstanding. */
char name[512];
const char *runtime = getenv("XDG_RUNTIME_DIR");
if (!runtime || snprintf(name, sizeof(name), "%s/blur-shm-XXXXXX", runtime) >=
(int)sizeof(name))
exit(2);
int fd = mkstemp(name);
if (fd < 0)
exit(2);
unlink(name);
if (ftruncate(fd, (off_t)size) < 0)
exit(2);
struct pixels *p = calloc(1, sizeof(*p));
if (!p)
exit(2);
p->map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
p->size = size;
if (p->map == MAP_FAILED)
exit(2);
uint32_t *pixels = p->map;
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x) {
uint32_t color = 0x00303038;
if (!strcmp(mode, "background"))
color = pattern == 'A' ? (x / 8 % 2 ? 0x000f192d : 0x00f5f5f5)
: (x / 8 % 2 ? 0x00b02040 : 0x0020c0e0);
else if (!strcmp(mode, "glass"))
color = 0x88442010;
else if (!strcmp(mode, "filler"))
color = 0x00000000;
pixels[(size_t)y * (size_t)width + (size_t)x] = color;
}
/* WHY: retained bytes let the shared validator verify A/B/A source identity.
* CONTRACT: dumps are outside Git in the owned run, not generated fixtures. */
const char *dumpdir = getenv("MOSAIC_FX_SHM_DIR");
char dump[1024];
if (!dumpdir || snprintf(dump, sizeof(dump), "%s/%s-%u.shm", dumpdir,
mode, commits + 1) >= (int)sizeof(dump)) exit(2);
FILE *raw = fopen(dump, "wb");
if (!raw || fwrite(p->map, 1, size, raw) != size || fclose(raw)) exit(2);
fprintf(stderr, "SHM_SOURCE ns=%lld mode=%s index=%u path=%s bytes=%zu "
"stride=%d format=%s alpha=%u colors=%s premultiplied=1\n",
stamp(), mode, commits + 1, dump, size, width * 4,
(!strcmp(mode, "glass") || !strcmp(mode, "filler")) ? "ARGB8888" : "XRGB8888",
!strcmp(mode, "glass") ? 136 : !strcmp(mode, "filler") ? 0 : 255,
!strcmp(mode, "background") ? (pattern == 'A' ? "00f5f5f5,000f192d" : "0020c0e0,00b02040") :
!strcmp(mode, "glass") ? "88442010" : !strcmp(mode, "filler") ? "00000000" : "00303038");
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, (int)size);
p->buffer = wl_shm_pool_create_buffer(
pool, 0, width, height, width * 4,
(!strcmp(mode, "glass") || !strcmp(mode, "filler"))
? WL_SHM_FORMAT_ARGB8888
: WL_SHM_FORMAT_XRGB8888);
p->next = buffers;
buffers = p;
++live_buffers;
wl_buffer_add_listener(p->buffer, &buffer_listener, p);
wl_surface_attach(surface, p->buffer, 0, 0);
wl_surface_damage_buffer(surface, 0, 0, width, height);
wl_surface_commit(surface);
fprintf(stderr,
"CLIENT_COMMIT ns=%lld mode=%s surface=%u buffer=%u index=%u "
"pattern=%c size=%dx%d damage=0,0,%d,%d\n",
stamp(), mode, wl_proxy_get_id((struct wl_proxy *)surface),
wl_proxy_get_id((struct wl_proxy *)p->buffer), ++commits, pattern,
width, height, width, height);
wl_shm_pool_destroy(pool);
close(fd);
}
static void ping(void *d, struct xdg_wm_base *b, uint32_t s) {
xdg_wm_base_pong(b, s);
}
static const struct xdg_wm_base_listener wm_listener = {.ping = ping};
static void top_configure(void *d, struct xdg_toplevel *t, int32_t w, int32_t h,
struct wl_array *a) {
if (w > 0)
width = w;
if (h > 0)
height = h;
}
static void top_close(void *d, struct xdg_toplevel *t) { running = 0; }
static const struct xdg_toplevel_listener top_listener = {
.configure = top_configure, .close = top_close};
static void configure(void *d, struct xdg_surface *s, uint32_t serial) {
xdg_surface_ack_configure(s, serial);
draw();
}
static const struct xdg_surface_listener xdg_listener = {.configure =
configure};
/* CONTRACT: configures ACK geometry, never submit an unreserved image.
* WHY: duplicate layer configures must not spend autonomous mutations.
* INVARIANT: the runner reserves then sends every A/B draw command. */
static void layer_configure(void *d, struct zwlr_layer_surface_v1 *s,
uint32_t serial, uint32_t w, uint32_t h) {
if (w == 0 || h == 0 || w > 4096 || h > 4096)
exit(2);
if (commits && ((int)w != width || (int)h != height))
exit(2);
width = (int)w;
height = (int)h;
zwlr_layer_surface_v1_ack_configure(s, serial);
fprintf(stderr, "BACKGROUND_CONFIGURED ns=%lld serial=%u size=%dx%d\n",
stamp(), serial, width, height);
}
static void layer_close(void *d, struct zwlr_layer_surface_v1 *s) {
running = 0;
}
static const struct zwlr_layer_surface_v1_listener layer_listener = {
.configure = layer_configure, .closed = layer_close};
static void output_name(void *d, struct wl_output *o, const char *name) {
if (!strcmp(name, "HEADLESS-1"))
h1 = o;
}
static void output_geometry(void *d, struct wl_output *o, int32_t x, int32_t y,
int32_t w, int32_t h, int32_t sp, const char *m,
const char *n, int32_t tr) {}
static void output_mode(void *d, struct wl_output *o, uint32_t flags, int32_t w,
int32_t h, int32_t refresh) {}
static void output_done(void *d, struct wl_output *o) {}
static void output_scale(void *d, struct wl_output *o, int32_t scale) {}
static void output_description(void *d, struct wl_output *o, const char *desc) {
}
static const struct wl_output_listener output_listener = {
.geometry = output_geometry,
.mode = output_mode,
.done = output_done,
.scale = output_scale,
.name = output_name,
.description = output_description};
static void global(void *d, struct wl_registry *r, uint32_t id,
const char *name, uint32_t v) {
if (!strcmp(name, "wl_compositor"))
compositor =
wl_registry_bind(r, id, &wl_compositor_interface, v < 4 ? v : 4);
else if (!strcmp(name, "wl_shm"))
shm = wl_registry_bind(r, id, &wl_shm_interface, 1);
else if (!strcmp(name, "xdg_wm_base"))
wm = wl_registry_bind(r, id, &xdg_wm_base_interface, v < 3 ? v : 3);
else if (!strcmp(name, "zwlr_layer_shell_v1"))
shell =
wl_registry_bind(r, id, &zwlr_layer_shell_v1_interface, v < 2 ? v : 2);
else if (!strcmp(name, "wl_output") && v >= 4) {
struct wl_output *o = wl_registry_bind(r, id, &wl_output_interface, 4);
wl_output_add_listener(o, &output_listener, NULL);
}
}
static void removed(void *d, struct wl_registry *r, uint32_t id) {}
static const struct wl_registry_listener registry_listener = {
.global = global, .global_remove = removed};
static void stop(int s) { running = 0; }
/* WHY: only the runner's explicit close command proves orderly client shutdown.
* CONTRACT: protocol close callbacks and process signals stop the loop without
* setting close_received; arbitrary commands are ignored. */
static void handle_command(const char *cmd) {
if (!strcmp(mode, "background") && (cmd[0] == 'A' || cmd[0] == 'B') &&
cmd[1] == '\n') {
pattern = cmd[0];
draw();
fprintf(stderr, "COMMAND %c DONE ns=%lld\n", pattern, stamp());
} else if (!strcmp(cmd, "close\n")) {
close_received = true;
fprintf(stderr, "CLOSE_RECEIVED ns=%lld mode=%s\n", stamp(), mode);
running = 0;
}
}
int main(int argc, char **argv) {
if (argc != 2)
return 2;
mode = argv[1];
if (strcmp(mode, "background") && strcmp(mode, "glass") &&
strcmp(mode, "filler") && strcmp(mode, "reference"))
return 2;
signal(SIGTERM, stop);
display = wl_display_connect(NULL);
if (!display)
return 1;
struct wl_registry *r = wl_display_get_registry(display);
wl_registry_add_listener(r, &registry_listener, NULL);
if (wl_display_roundtrip(display) < 0 || wl_display_roundtrip(display) < 0)
return 1;
if (!compositor || !shm || !wm)
return 2;
xdg_wm_base_add_listener(wm, &wm_listener, NULL);
surface = wl_compositor_create_surface(compositor);
if (!strcmp(mode, "background")) {
if (!shell || !h1)
return 2;
layer = zwlr_layer_shell_v1_get_layer_surface(
shell, surface, h1, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND,
"mosaic-dynamic-background");
zwlr_layer_surface_v1_add_listener(layer, &layer_listener, NULL);
zwlr_layer_surface_v1_set_size(layer, 0, 0);
zwlr_layer_surface_v1_set_anchor(layer,
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
zwlr_layer_surface_v1_set_exclusive_zone(layer, -1);
zwlr_layer_surface_v1_set_keyboard_interactivity(layer, 0);
} else {
xdg = xdg_wm_base_get_xdg_surface(wm, surface);
xdg_surface_add_listener(xdg, &xdg_listener, NULL);
top = xdg_surface_get_toplevel(xdg);
xdg_toplevel_add_listener(top, &top_listener, NULL);
const char *app = !strcmp(mode, "reference") ? "org.mosaic.StaticReference"
: !strcmp(mode, "glass") ? "org.mosaic.BlurGlass"
: "org.mosaic.BlurFiller";
xdg_toplevel_set_app_id(top, app);
xdg_toplevel_set_title(top, app);
}
wl_surface_commit(surface);
while (running) {
if (wl_display_dispatch_pending(display) < 0)
break;
if (wl_display_flush(display) < 0 && errno != EAGAIN)
break;
struct pollfd fds[2] = {
{.fd = wl_display_get_fd(display), .events = POLLIN},
{.fd = STDIN_FILENO, .events = POLLIN}};
int n = poll(fds, 2, 100);
if (n < 0) {
if (errno == EINTR)
continue;
break;
}
if (fds[0].revents & (POLLIN | POLLERR | POLLHUP))
if (wl_display_dispatch(display) < 0)
break;
if (fds[1].revents & POLLIN) {
char cmd[64];
if (!fgets(cmd, sizeof(cmd), stdin))
break;
handle_command(cmd);
}
}
int error = wl_display_get_error(display);
if (layer)
zwlr_layer_surface_v1_destroy(layer);
if (top)
xdg_toplevel_destroy(top);
if (xdg)
xdg_surface_destroy(xdg);
wl_surface_destroy(surface);
if (!error && wl_display_roundtrip(display) < 0) error = wl_display_get_error(display);
fprintf(stderr, "OBJECTS_DESTROYED ns=%lld mode=%s live_buffers=%u releases=%u\n",
stamp(), mode, live_buffers, releases);
wl_display_flush(display);
while (buffers)
free_pixels(buffers);
wl_registry_destroy(r);
wl_display_disconnect(display);
fprintf(stderr, "EXIT ns=%lld mode=%s error=%d commits=%u normalclose=%d releases=%u\n",
stamp(), mode, error, commits, close_received, releases);
return error || !close_received ? 1 : 0;
}

View file

@ -0,0 +1,303 @@
#define _POSIX_C_SOURCE 200809L
#include "layer-shell-client.h"
#include "xdg-shell-client.h"
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
/* WHY: one persistent background supplies real A/B commits below blur_tree.
* CONTRACT: fixed opaque background, premultiplied ARGB glass and static H2.
* INVARIANT: no animation, synthetic configure, popup or held transaction. */
static struct wl_display *display;
static struct wl_compositor *compositor;
static struct wl_shm *shm;
static struct xdg_wm_base *wm;
static struct zwlr_layer_shell_v1 *shell;
static struct wl_output *h1;
static struct wl_surface *surface;
static struct xdg_surface *xdg;
static struct xdg_toplevel *top;
static struct zwlr_layer_surface_v1 *layer;
static const char *mode;
static int width = 420, height = 320;
static unsigned commits, live_buffers;
static char pattern = 'A';
static volatile sig_atomic_t running = 1;
struct pixels {
struct wl_buffer *buffer;
void *map;
size_t size;
struct pixels *next;
};
static struct pixels *buffers;
static long long stamp(void) {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (long long)t.tv_sec * 1000000000LL + t.tv_nsec;
}
static void free_pixels(struct pixels *p) {
struct pixels **it = &buffers;
while (*it && *it != p)
it = &(*it)->next;
if (*it)
*it = p->next;
wl_buffer_destroy(p->buffer);
munmap(p->map, p->size);
free(p);
--live_buffers;
}
static void release(void *data, struct wl_buffer *b) {
fprintf(stderr, "RELEASE ns=%lld buffer=%u\n", stamp(),
wl_proxy_get_id((struct wl_proxy *)b));
free_pixels(data);
}
static const struct wl_buffer_listener buffer_listener = {.release = release};
static void draw(void) {
/* CONTRACT: exactly one runner command per reserved A/B image.
* INVARIANT: configures cannot draw, and the sequence is A/B/A only. */
if (!strcmp(mode, "background") &&
(width != 800 || height != 600 || commits >= 3 ||
pattern != (commits == 1 ? 'B' : 'A')))
exit(2);
if (width <= 0 || height <= 0 || width > 4096 || height > 4096 ||
live_buffers >= 8)
exit(2);
size_t size = (size_t)width * (size_t)height * 4;
/* CONTRACT: each mapping stays immutable until native wl_buffer.release.
* Bounds keep pool/stride integers valid, with at most eight outstanding. */
char name[512];
const char *runtime = getenv("XDG_RUNTIME_DIR");
if (!runtime || snprintf(name, sizeof(name), "%s/blur-shm-XXXXXX", runtime) >=
(int)sizeof(name))
exit(2);
int fd = mkstemp(name);
if (fd < 0)
exit(2);
unlink(name);
if (ftruncate(fd, (off_t)size) < 0)
exit(2);
struct pixels *p = calloc(1, sizeof(*p));
if (!p)
exit(2);
p->map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
p->size = size;
if (p->map == MAP_FAILED)
exit(2);
uint32_t *pixels = p->map;
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x) {
uint32_t color = 0x00303038;
if (!strcmp(mode, "background"))
color = pattern == 'A' ? (x / 8 % 2 ? 0x000f192d : 0x00f5f5f5)
: (x / 8 % 2 ? 0x00b02040 : 0x0020c0e0);
else if (!strcmp(mode, "glass"))
color = 0x88442010;
else if (!strcmp(mode, "filler"))
color = 0x00000000;
pixels[(size_t)y * (size_t)width + (size_t)x] = color;
}
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, (int)size);
p->buffer = wl_shm_pool_create_buffer(
pool, 0, width, height, width * 4,
(!strcmp(mode, "glass") || !strcmp(mode, "filler"))
? WL_SHM_FORMAT_ARGB8888
: WL_SHM_FORMAT_XRGB8888);
p->next = buffers;
buffers = p;
++live_buffers;
wl_buffer_add_listener(p->buffer, &buffer_listener, p);
wl_surface_attach(surface, p->buffer, 0, 0);
wl_surface_damage_buffer(surface, 0, 0, width, height);
wl_surface_commit(surface);
fprintf(stderr,
"CLIENT_COMMIT ns=%lld mode=%s surface=%u buffer=%u index=%u "
"pattern=%c size=%dx%d damage=0,0,%d,%d\n",
stamp(), mode, wl_proxy_get_id((struct wl_proxy *)surface),
wl_proxy_get_id((struct wl_proxy *)p->buffer), ++commits, pattern,
width, height, width, height);
wl_shm_pool_destroy(pool);
close(fd);
}
static void ping(void *d, struct xdg_wm_base *b, uint32_t s) {
xdg_wm_base_pong(b, s);
}
static const struct xdg_wm_base_listener wm_listener = {.ping = ping};
static void top_configure(void *d, struct xdg_toplevel *t, int32_t w, int32_t h,
struct wl_array *a) {
if (w > 0)
width = w;
if (h > 0)
height = h;
}
static void top_close(void *d, struct xdg_toplevel *t) { running = 0; }
static const struct xdg_toplevel_listener top_listener = {
.configure = top_configure, .close = top_close};
static void configure(void *d, struct xdg_surface *s, uint32_t serial) {
xdg_surface_ack_configure(s, serial);
draw();
}
static const struct xdg_surface_listener xdg_listener = {.configure =
configure};
/* CONTRACT: configures ACK geometry, never submit an unreserved image.
* WHY: duplicate layer configures must not spend autonomous mutations.
* INVARIANT: the runner reserves then sends every A/B draw command. */
static void layer_configure(void *d, struct zwlr_layer_surface_v1 *s,
uint32_t serial, uint32_t w, uint32_t h) {
if (w == 0 || h == 0 || w > 4096 || h > 4096)
exit(2);
if (commits && ((int)w != width || (int)h != height))
exit(2);
width = (int)w;
height = (int)h;
zwlr_layer_surface_v1_ack_configure(s, serial);
fprintf(stderr, "BACKGROUND_CONFIGURED ns=%lld serial=%u size=%dx%d\n",
stamp(), serial, width, height);
}
static void layer_close(void *d, struct zwlr_layer_surface_v1 *s) {
running = 0;
}
static const struct zwlr_layer_surface_v1_listener layer_listener = {
.configure = layer_configure, .closed = layer_close};
static void output_name(void *d, struct wl_output *o, const char *name) {
if (!strcmp(name, "HEADLESS-1"))
h1 = o;
}
static void output_geometry(void *d, struct wl_output *o, int32_t x, int32_t y,
int32_t w, int32_t h, int32_t sp, const char *m,
const char *n, int32_t tr) {}
static void output_mode(void *d, struct wl_output *o, uint32_t flags, int32_t w,
int32_t h, int32_t refresh) {}
static void output_done(void *d, struct wl_output *o) {}
static void output_scale(void *d, struct wl_output *o, int32_t scale) {}
static void output_description(void *d, struct wl_output *o, const char *desc) {
}
static const struct wl_output_listener output_listener = {
.geometry = output_geometry,
.mode = output_mode,
.done = output_done,
.scale = output_scale,
.name = output_name,
.description = output_description};
static void global(void *d, struct wl_registry *r, uint32_t id,
const char *name, uint32_t v) {
if (!strcmp(name, "wl_compositor"))
compositor =
wl_registry_bind(r, id, &wl_compositor_interface, v < 4 ? v : 4);
else if (!strcmp(name, "wl_shm"))
shm = wl_registry_bind(r, id, &wl_shm_interface, 1);
else if (!strcmp(name, "xdg_wm_base"))
wm = wl_registry_bind(r, id, &xdg_wm_base_interface, v < 3 ? v : 3);
else if (!strcmp(name, "zwlr_layer_shell_v1"))
shell =
wl_registry_bind(r, id, &zwlr_layer_shell_v1_interface, v < 2 ? v : 2);
else if (!strcmp(name, "wl_output") && v >= 4) {
struct wl_output *o = wl_registry_bind(r, id, &wl_output_interface, 4);
wl_output_add_listener(o, &output_listener, NULL);
}
}
static void removed(void *d, struct wl_registry *r, uint32_t id) {}
static const struct wl_registry_listener registry_listener = {
.global = global, .global_remove = removed};
static void stop(int s) { running = 0; }
int main(int argc, char **argv) {
if (argc != 2)
return 2;
mode = argv[1];
if (strcmp(mode, "background") && strcmp(mode, "glass") &&
strcmp(mode, "filler") && strcmp(mode, "reference"))
return 2;
signal(SIGTERM, stop);
display = wl_display_connect(NULL);
if (!display)
return 1;
struct wl_registry *r = wl_display_get_registry(display);
wl_registry_add_listener(r, &registry_listener, NULL);
if (wl_display_roundtrip(display) < 0 || wl_display_roundtrip(display) < 0)
return 1;
if (!compositor || !shm || !wm)
return 2;
xdg_wm_base_add_listener(wm, &wm_listener, NULL);
surface = wl_compositor_create_surface(compositor);
if (!strcmp(mode, "background")) {
if (!shell || !h1)
return 2;
layer = zwlr_layer_shell_v1_get_layer_surface(
shell, surface, h1, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND,
"mosaic-dynamic-background");
zwlr_layer_surface_v1_add_listener(layer, &layer_listener, NULL);
zwlr_layer_surface_v1_set_size(layer, 0, 0);
zwlr_layer_surface_v1_set_anchor(layer,
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
zwlr_layer_surface_v1_set_exclusive_zone(layer, -1);
zwlr_layer_surface_v1_set_keyboard_interactivity(layer, 0);
} else {
xdg = xdg_wm_base_get_xdg_surface(wm, surface);
xdg_surface_add_listener(xdg, &xdg_listener, NULL);
top = xdg_surface_get_toplevel(xdg);
xdg_toplevel_add_listener(top, &top_listener, NULL);
const char *app = !strcmp(mode, "reference") ? "org.mosaic.StaticReference"
: !strcmp(mode, "glass") ? "org.mosaic.BlurGlass"
: "org.mosaic.BlurFiller";
xdg_toplevel_set_app_id(top, app);
xdg_toplevel_set_title(top, app);
}
wl_surface_commit(surface);
while (running) {
if (wl_display_dispatch_pending(display) < 0)
break;
if (wl_display_flush(display) < 0 && errno != EAGAIN)
break;
struct pollfd fds[2] = {
{.fd = wl_display_get_fd(display), .events = POLLIN},
{.fd = STDIN_FILENO, .events = POLLIN}};
int n = poll(fds, 2, 100);
if (n < 0) {
if (errno == EINTR)
continue;
break;
}
if (fds[0].revents & (POLLIN | POLLERR | POLLHUP))
if (wl_display_dispatch(display) < 0)
break;
if (fds[1].revents & POLLIN) {
char cmd[64];
if (!fgets(cmd, sizeof(cmd), stdin))
break;
if (!strcmp(mode, "background") && (cmd[0] == 'A' || cmd[0] == 'B') &&
cmd[1] == '\n') {
pattern = cmd[0];
draw();
fprintf(stderr, "COMMAND %c DONE ns=%lld\n", pattern, stamp());
} else if (!strncmp(cmd, "close", 5))
running = 0;
}
}
int error = wl_display_get_error(display);
fprintf(stderr, "EXIT ns=%lld mode=%s error=%d commits=%u\n", stamp(), mode,
error, commits);
if (layer)
zwlr_layer_surface_v1_destroy(layer);
if (top)
xdg_toplevel_destroy(top);
if (xdg)
xdg_surface_destroy(xdg);
wl_surface_destroy(surface);
wl_display_flush(display);
while (buffers)
free_pixels(buffers);
wl_registry_destroy(r);
wl_display_disconnect(display);
return error ? 1 : 0;
}

View file

@ -0,0 +1,706 @@
/* Generated by wayland-scanner 1.26.0 */
#ifndef WLR_LAYER_SHELL_UNSTABLE_V1_CLIENT_PROTOCOL_H
#define WLR_LAYER_SHELL_UNSTABLE_V1_CLIENT_PROTOCOL_H
#include <stdint.h>
#include <stddef.h>
#include "wayland-client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @page page_wlr_layer_shell_unstable_v1 The wlr_layer_shell_unstable_v1 protocol
* @section page_ifaces_wlr_layer_shell_unstable_v1 Interfaces
* - @subpage page_iface_zwlr_layer_shell_v1 - create surfaces that are layers of the desktop
* - @subpage page_iface_zwlr_layer_surface_v1 - layer metadata interface
* @section page_copyright_wlr_layer_shell_unstable_v1 Copyright
* <pre>
*
* Copyright © 2017 Drew DeVault
*
* Permission to use, copy, modify, distribute, and sell this
* software and its documentation for any purpose is hereby granted
* without fee, provided that the above copyright notice appear in
* all copies and that both that copyright notice and this permission
* notice appear in supporting documentation, and that the name of
* the copyright holders not be used in advertising or publicity
* pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
* </pre>
*/
struct wl_output;
struct wl_surface;
struct xdg_popup;
struct zwlr_layer_shell_v1;
struct zwlr_layer_surface_v1;
#ifndef ZWLR_LAYER_SHELL_V1_INTERFACE
#define ZWLR_LAYER_SHELL_V1_INTERFACE
/**
* @page page_iface_zwlr_layer_shell_v1 zwlr_layer_shell_v1
* @section page_iface_zwlr_layer_shell_v1_desc Description
*
* Clients can use this interface to assign the surface_layer role to
* wl_surfaces. Such surfaces are assigned to a "layer" of the output and
* rendered with a defined z-depth respective to each other. They may also be
* anchored to the edges and corners of a screen and specify input handling
* semantics. This interface should be suitable for the implementation of
* many desktop shell components, and a broad number of other applications
* that interact with the desktop.
* @section page_iface_zwlr_layer_shell_v1_api API
* See @ref iface_zwlr_layer_shell_v1.
*/
/**
* @defgroup iface_zwlr_layer_shell_v1 The zwlr_layer_shell_v1 interface
*
* Clients can use this interface to assign the surface_layer role to
* wl_surfaces. Such surfaces are assigned to a "layer" of the output and
* rendered with a defined z-depth respective to each other. They may also be
* anchored to the edges and corners of a screen and specify input handling
* semantics. This interface should be suitable for the implementation of
* many desktop shell components, and a broad number of other applications
* that interact with the desktop.
*/
extern const struct wl_interface zwlr_layer_shell_v1_interface;
#endif
#ifndef ZWLR_LAYER_SURFACE_V1_INTERFACE
#define ZWLR_LAYER_SURFACE_V1_INTERFACE
/**
* @page page_iface_zwlr_layer_surface_v1 zwlr_layer_surface_v1
* @section page_iface_zwlr_layer_surface_v1_desc Description
*
* An interface that may be implemented by a wl_surface, for surfaces that
* are designed to be rendered as a layer of a stacked desktop-like
* environment.
*
* Layer surface state (layer, size, anchor, exclusive zone,
* margin, interactivity) is double-buffered, and will be applied at the
* time wl_surface.commit of the corresponding wl_surface is called.
*
* Attaching a null buffer to a layer surface unmaps it.
*
* Unmapping a layer_surface means that the surface cannot be shown by the
* compositor until it is explicitly mapped again. The layer_surface
* returns to the state it had right after layer_shell.get_layer_surface.
* The client can re-map the surface by performing a commit without any
* buffer attached, waiting for a configure event and handling it as usual.
* @section page_iface_zwlr_layer_surface_v1_api API
* See @ref iface_zwlr_layer_surface_v1.
*/
/**
* @defgroup iface_zwlr_layer_surface_v1 The zwlr_layer_surface_v1 interface
*
* An interface that may be implemented by a wl_surface, for surfaces that
* are designed to be rendered as a layer of a stacked desktop-like
* environment.
*
* Layer surface state (layer, size, anchor, exclusive zone,
* margin, interactivity) is double-buffered, and will be applied at the
* time wl_surface.commit of the corresponding wl_surface is called.
*
* Attaching a null buffer to a layer surface unmaps it.
*
* Unmapping a layer_surface means that the surface cannot be shown by the
* compositor until it is explicitly mapped again. The layer_surface
* returns to the state it had right after layer_shell.get_layer_surface.
* The client can re-map the surface by performing a commit without any
* buffer attached, waiting for a configure event and handling it as usual.
*/
extern const struct wl_interface zwlr_layer_surface_v1_interface;
#endif
#ifndef ZWLR_LAYER_SHELL_V1_ERROR_ENUM
#define ZWLR_LAYER_SHELL_V1_ERROR_ENUM
enum zwlr_layer_shell_v1_error {
/**
* wl_surface has another role
*/
ZWLR_LAYER_SHELL_V1_ERROR_ROLE = 0,
/**
* layer value is invalid
*/
ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER = 1,
/**
* wl_surface has a buffer attached or committed
*/
ZWLR_LAYER_SHELL_V1_ERROR_ALREADY_CONSTRUCTED = 2,
};
#endif /* ZWLR_LAYER_SHELL_V1_ERROR_ENUM */
#ifndef ZWLR_LAYER_SHELL_V1_LAYER_ENUM
#define ZWLR_LAYER_SHELL_V1_LAYER_ENUM
/**
* @ingroup iface_zwlr_layer_shell_v1
* available layers for surfaces
*
* These values indicate which layers a surface can be rendered in. They
* are ordered by z depth, bottom-most first. Traditional shell surfaces
* will typically be rendered between the bottom and top layers.
* Fullscreen shell surfaces are typically rendered at the top layer.
* Multiple surfaces can share a single layer, and ordering within a
* single layer is undefined.
*/
enum zwlr_layer_shell_v1_layer {
ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND = 0,
ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM = 1,
ZWLR_LAYER_SHELL_V1_LAYER_TOP = 2,
ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY = 3,
};
#endif /* ZWLR_LAYER_SHELL_V1_LAYER_ENUM */
#define ZWLR_LAYER_SHELL_V1_GET_LAYER_SURFACE 0
#define ZWLR_LAYER_SHELL_V1_DESTROY 1
/**
* @ingroup iface_zwlr_layer_shell_v1
*/
#define ZWLR_LAYER_SHELL_V1_GET_LAYER_SURFACE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_layer_shell_v1
*/
#define ZWLR_LAYER_SHELL_V1_DESTROY_SINCE_VERSION 3
/** @ingroup iface_zwlr_layer_shell_v1 */
static inline void
zwlr_layer_shell_v1_set_user_data(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) zwlr_layer_shell_v1, user_data);
}
/** @ingroup iface_zwlr_layer_shell_v1 */
static inline void *
zwlr_layer_shell_v1_get_user_data(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1)
{
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_layer_shell_v1);
}
static inline uint32_t
zwlr_layer_shell_v1_get_version(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1)
{
return wl_proxy_get_version((struct wl_proxy *) zwlr_layer_shell_v1);
}
/**
* @ingroup iface_zwlr_layer_shell_v1
*
* Create a layer surface for an existing surface. This assigns the role of
* layer_surface, or raises a protocol error if another role is already
* assigned.
*
* Creating a layer surface from a wl_surface which has a buffer attached
* or committed is a client error, and any attempts by a client to attach
* or manipulate a buffer prior to the first layer_surface.configure call
* must also be treated as errors.
*
* After creating a layer_surface object and setting it up, the client
* must perform an initial commit without any buffer attached.
* The compositor will reply with a layer_surface.configure event.
* The client must acknowledge it and is then allowed to attach a buffer
* to map the surface.
*
* You may pass NULL for output to allow the compositor to decide which
* output to use. Generally this will be the one that the user most
* recently interacted with.
*
* Clients can specify a namespace that defines the purpose of the layer
* surface.
*/
static inline struct zwlr_layer_surface_v1 *
zwlr_layer_shell_v1_get_layer_surface(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1, struct wl_surface *surface, struct wl_output *output, uint32_t layer, const char *namespace)
{
struct wl_proxy *id;
id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_shell_v1,
ZWLR_LAYER_SHELL_V1_GET_LAYER_SURFACE, &zwlr_layer_surface_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_shell_v1), 0, NULL, surface, output, layer, namespace);
return (struct zwlr_layer_surface_v1 *) id;
}
/**
* @ingroup iface_zwlr_layer_shell_v1
*
* This request indicates that the client will not use the layer_shell
* object any more. Objects that have been created through this instance
* are not affected.
*/
static inline void
zwlr_layer_shell_v1_destroy(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_shell_v1,
ZWLR_LAYER_SHELL_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_shell_v1), WL_MARSHAL_FLAG_DESTROY);
}
#ifndef ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ENUM
#define ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ENUM
/**
* @ingroup iface_zwlr_layer_surface_v1
* types of keyboard interaction possible for a layer shell surface
*
* Types of keyboard interaction possible for layer shell surfaces. The
* rationale for this is twofold: (1) some applications are not interested
* in keyboard events and not allowing them to be focused can improve the
* desktop experience; (2) some applications will want to take exclusive
* keyboard focus.
*/
enum zwlr_layer_surface_v1_keyboard_interactivity {
/**
* no keyboard focus is possible
*
* This value indicates that this surface is not interested in
* keyboard events and the compositor should never assign it the
* keyboard focus.
*
* This is the default value, set for newly created layer shell
* surfaces.
*
* This is useful for e.g. desktop widgets that display information
* or only have interaction with non-keyboard input devices.
*/
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE = 0,
/**
* request exclusive keyboard focus
*
* Request exclusive keyboard focus if this surface is above the
* shell surface layer.
*
* For the top and overlay layers, the seat will always give
* exclusive keyboard focus to the top-most layer which has
* keyboard interactivity set to exclusive. If this layer contains
* multiple surfaces with keyboard interactivity set to exclusive,
* the compositor determines the one receiving keyboard events in
* an implementation- defined manner. In this case, no guarantee is
* made when this surface will receive keyboard focus (if ever).
*
* For the bottom and background layers, the compositor is allowed
* to use normal focus semantics.
*
* This setting is mainly intended for applications that need to
* ensure they receive all keyboard events, such as a lock screen
* or a password prompt.
*/
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE = 1,
/**
* request regular keyboard focus semantics
*
* This requests the compositor to allow this surface to be
* focused and unfocused by the user in an implementation-defined
* manner. The user should be able to unfocus this surface even
* regardless of the layer it is on.
*
* Typically, the compositor will want to use its normal mechanism
* to manage keyboard focus between layer shell surfaces with this
* setting and regular toplevels on the desktop layer (e.g. click
* to focus). Nevertheless, it is possible for a compositor to
* require a special interaction to focus or unfocus layer shell
* surfaces (e.g. requiring a click even if focus follows the mouse
* normally, or providing a keybinding to switch focus between
* layers).
*
* This setting is mainly intended for desktop shell components
* (e.g. panels) that allow keyboard interaction. Using this option
* can allow implementing a desktop shell that can be fully usable
* without the mouse.
* @since 4
*/
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND = 2,
};
/**
* @ingroup iface_zwlr_layer_surface_v1
*/
#define ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND_SINCE_VERSION 4
#endif /* ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ENUM */
#ifndef ZWLR_LAYER_SURFACE_V1_ERROR_ENUM
#define ZWLR_LAYER_SURFACE_V1_ERROR_ENUM
enum zwlr_layer_surface_v1_error {
/**
* provided surface state is invalid
*/
ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SURFACE_STATE = 0,
/**
* size is invalid
*/
ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE = 1,
/**
* anchor bitfield is invalid
*/
ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_ANCHOR = 2,
/**
* keyboard interactivity is invalid
*/
ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_KEYBOARD_INTERACTIVITY = 3,
};
#endif /* ZWLR_LAYER_SURFACE_V1_ERROR_ENUM */
#ifndef ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM
#define ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM
enum zwlr_layer_surface_v1_anchor {
/**
* the top edge of the anchor rectangle
*/
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP = 1,
/**
* the bottom edge of the anchor rectangle
*/
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM = 2,
/**
* the left edge of the anchor rectangle
*/
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT = 4,
/**
* the right edge of the anchor rectangle
*/
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT = 8,
};
#endif /* ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM */
/**
* @ingroup iface_zwlr_layer_surface_v1
* @struct zwlr_layer_surface_v1_listener
*/
struct zwlr_layer_surface_v1_listener {
/**
* suggest a surface change
*
* The configure event asks the client to resize its surface.
*
* Clients should arrange their surface for the new states, and
* then send an ack_configure request with the serial sent in this
* configure event at some point before committing the new surface.
*
* The client is free to dismiss all but the last configure event
* it received.
*
* The width and height arguments specify the size of the window in
* surface-local coordinates.
*
* The size is a hint, in the sense that the client is free to
* ignore it if it doesn't resize, pick a smaller size (to satisfy
* aspect ratio or resize in steps of NxM pixels). If the client
* picks a smaller size and is anchored to two opposite anchors
* (e.g. 'top' and 'bottom'), the surface will be centered on this
* axis.
*
* If the width or height arguments are zero, it means the client
* should decide its own window dimension.
*/
void (*configure)(void *data,
struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1,
uint32_t serial,
uint32_t width,
uint32_t height);
/**
* surface should be closed
*
* The closed event is sent by the compositor when the surface
* will no longer be shown. The output may have been destroyed or
* the user may have asked for it to be removed. Further changes to
* the surface will be ignored. The client should destroy the
* resource after receiving this event, and create a new surface if
* they so choose.
*/
void (*closed)(void *data,
struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1);
};
/**
* @ingroup iface_zwlr_layer_surface_v1
*/
static inline int
zwlr_layer_surface_v1_add_listener(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1,
const struct zwlr_layer_surface_v1_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) zwlr_layer_surface_v1,
(void (**)(void)) listener, data);
}
#define ZWLR_LAYER_SURFACE_V1_SET_SIZE 0
#define ZWLR_LAYER_SURFACE_V1_SET_ANCHOR 1
#define ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_ZONE 2
#define ZWLR_LAYER_SURFACE_V1_SET_MARGIN 3
#define ZWLR_LAYER_SURFACE_V1_SET_KEYBOARD_INTERACTIVITY 4
#define ZWLR_LAYER_SURFACE_V1_GET_POPUP 5
#define ZWLR_LAYER_SURFACE_V1_ACK_CONFIGURE 6
#define ZWLR_LAYER_SURFACE_V1_DESTROY 7
#define ZWLR_LAYER_SURFACE_V1_SET_LAYER 8
/**
* @ingroup iface_zwlr_layer_surface_v1
*/
#define ZWLR_LAYER_SURFACE_V1_CONFIGURE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_layer_surface_v1
*/
#define ZWLR_LAYER_SURFACE_V1_CLOSED_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_layer_surface_v1
*/
#define ZWLR_LAYER_SURFACE_V1_SET_SIZE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_layer_surface_v1
*/
#define ZWLR_LAYER_SURFACE_V1_SET_ANCHOR_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_layer_surface_v1
*/
#define ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_ZONE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_layer_surface_v1
*/
#define ZWLR_LAYER_SURFACE_V1_SET_MARGIN_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_layer_surface_v1
*/
#define ZWLR_LAYER_SURFACE_V1_SET_KEYBOARD_INTERACTIVITY_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_layer_surface_v1
*/
#define ZWLR_LAYER_SURFACE_V1_GET_POPUP_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_layer_surface_v1
*/
#define ZWLR_LAYER_SURFACE_V1_ACK_CONFIGURE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_layer_surface_v1
*/
#define ZWLR_LAYER_SURFACE_V1_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_layer_surface_v1
*/
#define ZWLR_LAYER_SURFACE_V1_SET_LAYER_SINCE_VERSION 2
/** @ingroup iface_zwlr_layer_surface_v1 */
static inline void
zwlr_layer_surface_v1_set_user_data(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) zwlr_layer_surface_v1, user_data);
}
/** @ingroup iface_zwlr_layer_surface_v1 */
static inline void *
zwlr_layer_surface_v1_get_user_data(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1)
{
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_layer_surface_v1);
}
static inline uint32_t
zwlr_layer_surface_v1_get_version(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1)
{
return wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1);
}
/**
* @ingroup iface_zwlr_layer_surface_v1
*
* Sets the size of the surface in surface-local coordinates. The
* compositor will display the surface centered with respect to its
* anchors.
*
* If you pass 0 for either value, the compositor will assign it and
* inform you of the assignment in the configure event. You must set your
* anchor to opposite edges in the dimensions you omit; not doing so is a
* protocol error. Both values are 0 by default.
*
* Size is double-buffered, see wl_surface.commit.
*/
static inline void
zwlr_layer_surface_v1_set_size(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t width, uint32_t height)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
ZWLR_LAYER_SURFACE_V1_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, width, height);
}
/**
* @ingroup iface_zwlr_layer_surface_v1
*
* Requests that the compositor anchor the surface to the specified edges
* and corners. If two orthogonal edges are specified (e.g. 'top' and
* 'left'), then the anchor point will be the intersection of the edges
* (e.g. the top left corner of the output); otherwise the anchor point
* will be centered on that edge, or in the center if none is specified.
*
* Anchor is double-buffered, see wl_surface.commit.
*/
static inline void
zwlr_layer_surface_v1_set_anchor(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t anchor)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
ZWLR_LAYER_SURFACE_V1_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, anchor);
}
/**
* @ingroup iface_zwlr_layer_surface_v1
*
* Requests that the compositor avoids occluding an area with other
* surfaces. The compositor's use of this information is
* implementation-dependent - do not assume that this region will not
* actually be occluded.
*
* A positive value is only meaningful if the surface is anchored to one
* edge or an edge and both perpendicular edges. If the surface is not
* anchored, anchored to only two perpendicular edges (a corner), anchored
* to only two parallel edges or anchored to all edges, a positive value
* will be treated the same as zero.
*
* A positive zone is the distance from the edge in surface-local
* coordinates to consider exclusive.
*
* Surfaces that do not wish to have an exclusive zone may instead specify
* how they should interact with surfaces that do. If set to zero, the
* surface indicates that it would like to be moved to avoid occluding
* surfaces with a positive exclusive zone. If set to -1, the surface
* indicates that it would not like to be moved to accommodate for other
* surfaces, and the compositor should extend it all the way to the edges
* it is anchored to.
*
* For example, a panel might set its exclusive zone to 10, so that
* maximized shell surfaces are not shown on top of it. A notification
* might set its exclusive zone to 0, so that it is moved to avoid
* occluding the panel, but shell surfaces are shown underneath it. A
* wallpaper or lock screen might set their exclusive zone to -1, so that
* they stretch below or over the panel.
*
* The default value is 0.
*
* Exclusive zone is double-buffered, see wl_surface.commit.
*/
static inline void
zwlr_layer_surface_v1_set_exclusive_zone(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, int32_t zone)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_ZONE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, zone);
}
/**
* @ingroup iface_zwlr_layer_surface_v1
*
* Requests that the surface be placed some distance away from the anchor
* point on the output, in surface-local coordinates. Setting this value
* for edges you are not anchored to has no effect.
*
* The exclusive zone includes the margin.
*
* Margin is double-buffered, see wl_surface.commit.
*/
static inline void
zwlr_layer_surface_v1_set_margin(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, int32_t top, int32_t right, int32_t bottom, int32_t left)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
ZWLR_LAYER_SURFACE_V1_SET_MARGIN, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, top, right, bottom, left);
}
/**
* @ingroup iface_zwlr_layer_surface_v1
*
* Set how keyboard events are delivered to this surface. By default,
* layer shell surfaces do not receive keyboard events; this request can
* be used to change this.
*
* This setting is inherited by child surfaces set by the get_popup
* request.
*
* Layer surfaces receive pointer, touch, and tablet events normally. If
* you do not want to receive them, set the input region on your surface
* to an empty region.
*
* Keyboard interactivity is double-buffered, see wl_surface.commit.
*/
static inline void
zwlr_layer_surface_v1_set_keyboard_interactivity(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t keyboard_interactivity)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
ZWLR_LAYER_SURFACE_V1_SET_KEYBOARD_INTERACTIVITY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, keyboard_interactivity);
}
/**
* @ingroup iface_zwlr_layer_surface_v1
*
* This assigns an xdg_popup's parent to this layer_surface. This popup
* should have been created via xdg_surface::get_popup with the parent set
* to NULL, and this request must be invoked before committing the popup's
* initial state.
*
* See the documentation of xdg_popup for more details about what an
* xdg_popup is and how it is used.
*/
static inline void
zwlr_layer_surface_v1_get_popup(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, struct xdg_popup *popup)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
ZWLR_LAYER_SURFACE_V1_GET_POPUP, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, popup);
}
/**
* @ingroup iface_zwlr_layer_surface_v1
*
* When a configure event is received, if a client commits the
* surface in response to the configure event, then the client
* must make an ack_configure request sometime before the commit
* request, passing along the serial of the configure event.
*
* If the client receives multiple configure events before it
* can respond to one, it only has to ack the last configure event.
*
* A client is not required to commit immediately after sending
* an ack_configure request - it may even ack_configure several times
* before its next surface commit.
*
* A client may send multiple ack_configure requests before committing, but
* only the last request sent before a commit indicates which configure
* event the client really is responding to.
*/
static inline void
zwlr_layer_surface_v1_ack_configure(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t serial)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
ZWLR_LAYER_SURFACE_V1_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, serial);
}
/**
* @ingroup iface_zwlr_layer_surface_v1
*
* This request destroys the layer surface.
*/
static inline void
zwlr_layer_surface_v1_destroy(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
ZWLR_LAYER_SURFACE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), WL_MARSHAL_FLAG_DESTROY);
}
/**
* @ingroup iface_zwlr_layer_surface_v1
*
* Change the layer that the surface is rendered on.
*
* Layer is double-buffered, see wl_surface.commit.
*/
static inline void
zwlr_layer_surface_v1_set_layer(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t layer)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_layer_surface_v1,
ZWLR_LAYER_SURFACE_V1_SET_LAYER, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1), 0, layer);
}
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,94 @@
/* Generated by wayland-scanner 1.26.0 */
/*
* Copyright © 2017 Drew DeVault
*
* Permission to use, copy, modify, distribute, and sell this
* software and its documentation for any purpose is hereby granted
* without fee, provided that the above copyright notice appear in
* all copies and that both that copyright notice and this permission
* notice appear in supporting documentation, and that the name of
* the copyright holders not be used in advertising or publicity
* pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include "wayland-util.h"
#ifndef __has_attribute
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
#else
#define WL_PRIVATE
#endif
extern const struct wl_interface wl_output_interface;
extern const struct wl_interface wl_surface_interface;
extern const struct wl_interface xdg_popup_interface;
extern const struct wl_interface zwlr_layer_surface_v1_interface;
static const struct wl_interface *wlr_layer_shell_unstable_v1_types[] = {
NULL,
NULL,
NULL,
NULL,
&zwlr_layer_surface_v1_interface,
&wl_surface_interface,
&wl_output_interface,
NULL,
NULL,
&xdg_popup_interface,
};
static const struct wl_message zwlr_layer_shell_v1_requests[] = {
{ "get_layer_surface", "no?ous", wlr_layer_shell_unstable_v1_types + 4 },
{ "destroy", "3", wlr_layer_shell_unstable_v1_types + 0 },
};
WL_PRIVATE const struct wl_interface zwlr_layer_shell_v1_interface = {
"zwlr_layer_shell_v1", 4,
2, zwlr_layer_shell_v1_requests,
0, NULL,
};
static const struct wl_message zwlr_layer_surface_v1_requests[] = {
{ "set_size", "uu", wlr_layer_shell_unstable_v1_types + 0 },
{ "set_anchor", "u", wlr_layer_shell_unstable_v1_types + 0 },
{ "set_exclusive_zone", "i", wlr_layer_shell_unstable_v1_types + 0 },
{ "set_margin", "iiii", wlr_layer_shell_unstable_v1_types + 0 },
{ "set_keyboard_interactivity", "u", wlr_layer_shell_unstable_v1_types + 0 },
{ "get_popup", "o", wlr_layer_shell_unstable_v1_types + 9 },
{ "ack_configure", "u", wlr_layer_shell_unstable_v1_types + 0 },
{ "destroy", "", wlr_layer_shell_unstable_v1_types + 0 },
{ "set_layer", "2u", wlr_layer_shell_unstable_v1_types + 0 },
};
static const struct wl_message zwlr_layer_surface_v1_events[] = {
{ "configure", "uuu", wlr_layer_shell_unstable_v1_types + 0 },
{ "closed", "", wlr_layer_shell_unstable_v1_types + 0 },
};
WL_PRIVATE const struct wl_interface zwlr_layer_surface_v1_interface = {
"zwlr_layer_surface_v1", 4,
9, zwlr_layer_surface_v1_requests,
2, zwlr_layer_surface_v1_events,
};

View file

@ -0,0 +1,270 @@
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client.h>
#include "xdg-shell-client.h"
/* EXPERIMENTAL CONTRACT: every popup owns distinct XDG objects. B's actual
* parent is A's xdg_surface; logs name object IDs as well as colors. */
struct surface {
const char *name;
struct wl_surface *wl;
struct xdg_surface *xdg;
struct xdg_popup *popup;
bool configured, mapped, done;
int width, height, anchor_x, anchor_y;
uint32_t color;
};
static struct wl_display *display;
static struct wl_compositor *compositor;
static struct wl_shm *shm;
static struct xdg_wm_base *wm;
static struct wl_seat *seat;
static struct wl_pointer *pointer;
static struct xdg_toplevel *top_role;
static struct surface top = {.name="TOPLEVEL", .width=420, .height=320, .color=0x002344cc};
static struct surface a = {.name="POPUP_A", .width=300, .height=170, .color=0x00e0c020};
static struct surface b = {.name="POPUP_B", .width=300, .height=130, .color=0x0020d0a0};
static volatile sig_atomic_t running = 1;
static struct surface *entered;
static void draw(struct surface *s) {
size_t bytes = (size_t)s->width * (size_t)s->height * 4;
char path[] = "/tmp/mosaic-nested-buffer-XXXXXX";
int fd = mkstemp(path);
if (fd < 0 || ftruncate(fd, (off_t)bytes) < 0) { perror("buffer"); exit(1); }
unlink(path);
uint32_t *pixels = mmap(NULL, bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (pixels == MAP_FAILED) { perror("mmap"); exit(1); }
for (size_t i=0; i<bytes/4; ++i) pixels[i] = s->color;
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, (int)bytes);
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, s->width, s->height,
s->width*4, WL_SHM_FORMAT_XRGB8888);
wl_surface_attach(s->wl, buffer, 0, 0);
wl_surface_damage_buffer(s->wl, 0, 0, s->width, s->height);
wl_surface_commit(s->wl);
wl_buffer_destroy(buffer);
wl_shm_pool_destroy(pool);
munmap(pixels, bytes);
close(fd);
s->mapped = true;
fprintf(stderr, "%s MAP wl=%u xdg=%u\n", s->name,
wl_proxy_get_id((struct wl_proxy *)s->wl), wl_proxy_get_id((struct wl_proxy *)s->xdg));
}
static void on_ping(void *data, struct xdg_wm_base *base, uint32_t serial) {
xdg_wm_base_pong(base, serial);
}
static const struct xdg_wm_base_listener wm_listener = {.ping=on_ping};
static void on_xdg_configure(void *data, struct xdg_surface *xdg, uint32_t serial) {
struct surface *s=data;
fprintf(stderr, "%s XDG_CONFIGURE serial=%u ack\n", s->name, serial);
xdg_surface_ack_configure(xdg, serial);
s->configured=true;
if (!s->mapped && !s->done) draw(s);
}
static const struct xdg_surface_listener xdg_listener = {.configure=on_xdg_configure};
static void on_top_configure(void *data, struct xdg_toplevel *role, int32_t w,
int32_t h, struct wl_array *states) {
fprintf(stderr, "TOPLEVEL ROLE_CONFIGURE w=%d h=%d\n", w,h);
}
static void on_top_close(void *data, struct xdg_toplevel *role) {
fprintf(stderr, "TOPLEVEL CLOSE_REQUEST\n"); running=0;
}
static const struct xdg_toplevel_listener top_listener = {
.configure=on_top_configure, .close=on_top_close,
};
static void on_popup_configure(void *data, struct xdg_popup *role, int32_t x,
int32_t y, int32_t w, int32_t h) {
struct surface *s=data;
fprintf(stderr, "%s ROLE_CONFIGURE x=%d y=%d w=%d h=%d\n",s->name,x,y,w,h);
}
static void on_popup_done(void *data, struct xdg_popup *role) {
struct surface *s=data;
s->done=true;
s->mapped=false;
fprintf(stderr, "%s POPUP_DONE role=%u\n",s->name,wl_proxy_get_id((struct wl_proxy *)role));
}
static const struct xdg_popup_listener popup_listener = {
.configure=on_popup_configure, .popup_done=on_popup_done,
};
/* WHY: XDG requires child-first destruction even if the compositor sends
* dismissals for multiple levels in one dispatch batch. */
static void destroy_popup(struct surface *s) {
if (!s->popup) return;
fprintf(stderr,"%s DESTROY role=%u xdg=%u wl=%u\n",s->name,
wl_proxy_get_id((struct wl_proxy *)s->popup),
wl_proxy_get_id((struct wl_proxy *)s->xdg),
wl_proxy_get_id((struct wl_proxy *)s->wl));
xdg_popup_destroy(s->popup);
xdg_surface_destroy(s->xdg);
wl_surface_destroy(s->wl);
s->popup=NULL; s->xdg=NULL; s->wl=NULL; s->mapped=false;
if (entered==s) entered=NULL;
}
static void collect_dismissals(void) {
if (b.done) destroy_popup(&b);
if (a.done) {
destroy_popup(&b);
destroy_popup(&a);
}
}
static void open_popup(struct surface *s, struct surface *parent, uint32_t serial) {
if (s->popup || !parent->mapped || !serial) return;
s->wl=wl_compositor_create_surface(compositor);
s->xdg=xdg_wm_base_get_xdg_surface(wm,s->wl);
xdg_surface_add_listener(s->xdg,&xdg_listener,s);
struct xdg_positioner *pos=xdg_wm_base_create_positioner(wm);
xdg_positioner_set_size(pos,s->width,s->height);
xdg_positioner_set_anchor_rect(pos,s->anchor_x,s->anchor_y,1,1);
xdg_positioner_set_anchor(pos,XDG_POSITIONER_ANCHOR_TOP_LEFT);
xdg_positioner_set_gravity(pos,XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
s->popup=xdg_surface_get_popup(s->xdg,parent->xdg,pos);
xdg_popup_add_listener(s->popup,&popup_listener,s);
fprintf(stderr,"%s CREATE wl=%u xdg=%u role=%u parent=%s parent_xdg=%u\n",
s->name,wl_proxy_get_id((struct wl_proxy *)s->wl),
wl_proxy_get_id((struct wl_proxy *)s->xdg),
wl_proxy_get_id((struct wl_proxy *)s->popup),parent->name,
wl_proxy_get_id((struct wl_proxy *)parent->xdg));
fprintf(stderr,"%s GRAB_REQUEST origin=wl_pointer.button target=%s serial=%u used=%u\n",
s->name,parent->name,serial,serial);
xdg_popup_grab(s->popup,seat,serial);
wl_surface_commit(s->wl); /* Initial bufferless commit requests configure. */
xdg_positioner_destroy(pos);
}
static struct surface *which(struct wl_surface *wl) {
if (wl==top.wl) return &top;
if (wl==a.wl && wl) return &a;
if (wl==b.wl && wl) return &b;
return NULL;
}
static void on_enter(void *data,struct wl_pointer *ptr,uint32_t serial,
struct wl_surface *wl,wl_fixed_t x,wl_fixed_t y) {
entered=which(wl);
fprintf(stderr,"POINTER_ENTER target=%s wl=%u serial=%u x=%.2f y=%.2f\n",
entered?entered->name:"OTHER",wl_proxy_get_id((struct wl_proxy *)wl),serial,
wl_fixed_to_double(x),wl_fixed_to_double(y));
}
static void on_leave(void *data,struct wl_pointer *ptr,uint32_t serial,
struct wl_surface *wl) {
struct surface *s=which(wl);
fprintf(stderr,"POINTER_LEAVE target=%s serial=%u\n",s?s->name:"OTHER",serial);
if (entered==s) entered=NULL;
}
static void on_motion(void *data,struct wl_pointer *ptr,uint32_t time,
wl_fixed_t x,wl_fixed_t y) {
fprintf(stderr,"POINTER_MOTION target=%s x=%.2f y=%.2f\n",
entered?entered->name:"OTHER",wl_fixed_to_double(x),wl_fixed_to_double(y));
}
static void on_button(void *data,struct wl_pointer *ptr,uint32_t serial,
uint32_t time,uint32_t button,uint32_t state) {
fprintf(stderr,"POINTER_BUTTON target=%s serial=%u button=%u state=%u\n",
entered?entered->name:"OTHER",serial,button,state);
if (state!=WL_POINTER_BUTTON_STATE_PRESSED || button!=272) return;
if (entered==&top && !a.popup) open_popup(&a,&top,serial);
else if (entered==&a && a.mapped && !b.popup) open_popup(&b,&a,serial);
}
static void on_axis(void *data,struct wl_pointer *ptr,uint32_t t,uint32_t axis,wl_fixed_t v) {}
static void on_frame(void *data,struct wl_pointer *ptr) {}
static const struct wl_pointer_listener pointer_listener={
.enter=on_enter,.leave=on_leave,.motion=on_motion,.button=on_button,
.axis=on_axis,.frame=on_frame,
};
static void on_caps(void *data,struct wl_seat *s,uint32_t caps) {
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !pointer) {
pointer=wl_seat_get_pointer(s);
wl_pointer_add_listener(pointer,&pointer_listener,NULL);
}
}
static void on_seat_name(void *data,struct wl_seat *s,const char *name) {}
static const struct wl_seat_listener seat_listener={.capabilities=on_caps,.name=on_seat_name};
static void on_global(void *data,struct wl_registry *reg,uint32_t name,
const char *interface,uint32_t version) {
if (!strcmp(interface,wl_compositor_interface.name))
compositor=wl_registry_bind(reg,name,&wl_compositor_interface,version<4?version:4);
else if (!strcmp(interface,wl_shm_interface.name))
shm=wl_registry_bind(reg,name,&wl_shm_interface,1);
else if (!strcmp(interface,xdg_wm_base_interface.name))
wm=wl_registry_bind(reg,name,&xdg_wm_base_interface,version<3?version:3);
else if (!strcmp(interface,wl_seat_interface.name)) {
seat=wl_registry_bind(reg,name,&wl_seat_interface,version<5?version:5);
wl_seat_add_listener(seat,&seat_listener,NULL);
}
}
static void on_removed(void *data,struct wl_registry *reg,uint32_t name) {}
static const struct wl_registry_listener registry_listener={.global=on_global,.global_remove=on_removed};
static void stop(int sig) { running=0; }
int main(int argc,char **argv) {
signal(SIGTERM,stop);
const char *placement=argc>1?argv[1]:"b_cross";
/* EXPERIMENTAL: only placement changes; serial/grab/lifecycle stay historical.
* Both 1x1 anchors lie inside their parent SHM window geometry. */
if (!strcmp(placement,"static_cross")) {
a.anchor_x=100; a.anchor_y=80; a.width=350;
b.anchor_x=100; b.anchor_y=60; b.width=300;
} else if (!strcmp(placement,"visible")) {
a.anchor_x=50; a.anchor_y=80; a.width=250;
b.anchor_x=20; b.anchor_y=60; b.width=170;
} else if (!strcmp(placement,"both_cross")) {
a.anchor_x=350; a.anchor_y=80; a.width=350;
b.anchor_x=160; b.anchor_y=60; b.width=300;
} else if (!strcmp(placement,"b_cross")) {
a.anchor_x=200; a.anchor_y=80; a.width=300;
b.anchor_x=220; b.anchor_y=60; b.width=300;
} else { fprintf(stderr,"invalid placement\n"); return 2; }
display=wl_display_connect(NULL);
if (!display) { fprintf(stderr,"cannot connect Wayland\n"); return 1; }
struct wl_registry *registry=wl_display_get_registry(display);
wl_registry_add_listener(registry,&registry_listener,NULL);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (!compositor || !shm || !wm || !seat || !pointer) {
fprintf(stderr,"missing Wayland globals/pointer\n"); return 1;
}
xdg_wm_base_add_listener(wm,&wm_listener,NULL);
top.wl=wl_compositor_create_surface(compositor);
top.xdg=xdg_wm_base_get_xdg_surface(wm,top.wl);
xdg_surface_add_listener(top.xdg,&xdg_listener,&top);
top_role=xdg_surface_get_toplevel(top.xdg);
xdg_toplevel_add_listener(top_role,&top_listener,NULL);
xdg_toplevel_set_title(top_role,"Mosaic Nested Grab Probe");
xdg_toplevel_set_app_id(top_role,"org.mosaic.NestedGrabProbe");
fprintf(stderr,"TOPLEVEL CREATE wl=%u xdg=%u role=%u placement=%s\n",
wl_proxy_get_id((struct wl_proxy *)top.wl),
wl_proxy_get_id((struct wl_proxy *)top.xdg),
wl_proxy_get_id((struct wl_proxy *)top_role),placement);
wl_surface_commit(top.wl);
while (running) {
wl_display_flush(display);
struct pollfd fds[2]={{.fd=wl_display_get_fd(display),.events=POLLIN},
{.fd=STDIN_FILENO,.events=POLLIN}};
int ready=poll(fds,2,500);
if (ready<0) { if (errno==EINTR) continue; perror("poll"); break; }
if (fds[0].revents & (POLLIN|POLLERR|POLLHUP)) {
if (wl_display_dispatch(display)<0) break;
collect_dismissals();
}
if (fds[1].revents & POLLIN) {
char cmd[64];
if (fgets(cmd,sizeof(cmd),stdin)) {
if (!strncmp(cmd,"close_b",7)) { destroy_popup(&b); fprintf(stderr,"COMMAND close_b DONE\n"); }
else if (!strncmp(cmd,"close_owner",11)) { fprintf(stderr,"COMMAND close_owner\n"); running=0; }
}
}
}
destroy_popup(&b); destroy_popup(&a);
fprintf(stderr,"TOPLEVEL DESTROY\n");
xdg_toplevel_destroy(top_role); xdg_surface_destroy(top.xdg); wl_surface_destroy(top.wl);
wl_display_flush(display);
/* WHY/CONTRACT/INVARIANT: exit0 does not prove protocol error0. */
fprintf(stderr,"EXIT error=%d\n",wl_display_get_error(display));
wl_display_disconnect(display);
return 0;
}

View file

@ -0,0 +1,57 @@
#define _DEFAULT_SOURCE
#include <linux/input-event-codes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wayland-client.h>
#include "virtual-pointer-client.h"
static struct zwlr_virtual_pointer_manager_v1 *manager;
static void global(void *data, struct wl_registry *registry, uint32_t name,
const char *interface, uint32_t version) {
if (!strcmp(interface, zwlr_virtual_pointer_manager_v1_interface.name))
manager=wl_registry_bind(registry,name,&zwlr_virtual_pointer_manager_v1_interface,
version < 2 ? version : 2);
}
static void removed(void *data, struct wl_registry *registry, uint32_t name) {}
int main(void) {
struct wl_display *display=wl_display_connect(NULL);
if (!display) return 1;
struct wl_registry *registry=wl_display_get_registry(display);
struct wl_registry_listener listener={global,removed};
wl_registry_add_listener(registry,&listener,NULL);
wl_display_roundtrip(display);
if (!manager) return 2;
struct zwlr_virtual_pointer_v1 *pointer=
zwlr_virtual_pointer_manager_v1_create_virtual_pointer(manager,NULL);
wl_display_roundtrip(display);
fprintf(stderr,"POINTER_READY\n");fflush(stderr);
char line[128];
uint32_t stamp=1000;
while (fgets(line,sizeof(line),stdin)) {
char action[16];int x,y;
unsigned width,height;
if (sscanf(line,"%15s %d %d %u %u",action,&x,&y,&width,&height)!=5 || !width || !height || x<0 || y<0 || (unsigned)x>=width || (unsigned)y>=height || (strcmp(action,"move") && strcmp(action,"left") && strcmp(action,"right"))) return 3;
fprintf(stderr,"INJECT %s %d,%d\n",action,x,y);fflush(stderr);
zwlr_virtual_pointer_v1_motion_absolute(pointer,stamp++,x,y,width,height);
zwlr_virtual_pointer_v1_frame(pointer);
wl_display_flush(display);usleep(250000);
if (!strcmp(action,"left") || !strcmp(action,"right")) {
uint32_t button=!strcmp(action,"left") ? BTN_LEFT : BTN_RIGHT;
zwlr_virtual_pointer_v1_button(pointer,stamp++,button,WL_POINTER_BUTTON_STATE_PRESSED);
zwlr_virtual_pointer_v1_frame(pointer);
wl_display_flush(display);usleep(80000);
zwlr_virtual_pointer_v1_button(pointer,stamp++,button,WL_POINTER_BUTTON_STATE_RELEASED);
zwlr_virtual_pointer_v1_frame(pointer);
wl_display_flush(display);
}
wl_display_roundtrip(display);
fprintf(stderr,"INJECT_DONE %s %d,%d\n",action,x,y);fflush(stderr);
}
zwlr_virtual_pointer_v1_destroy(pointer);
zwlr_virtual_pointer_manager_v1_destroy(manager);
wl_registry_destroy(registry);
wl_display_disconnect(display);
return 0;
}

View file

@ -0,0 +1,284 @@
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client.h>
#include "xdg-shell-client.h"
/* EXPERIMENTAL CONTRACT: every popup owns distinct XDG objects. B's actual
* parent is A's xdg_surface; logs name object IDs as well as colors. */
struct surface {
const char *name;
struct wl_surface *wl;
struct xdg_surface *xdg;
struct xdg_popup *popup;
bool configured, mapped, done;
int width, height, anchor_x, anchor_y;
uint32_t color;
};
static struct wl_display *display;
static struct wl_compositor *compositor;
static struct wl_shm *shm;
static struct xdg_wm_base *wm;
static struct wl_seat *seat;
static struct wl_pointer *pointer;
static struct xdg_toplevel *top_role;
static struct surface top = {.name="TOPLEVEL", .width=420, .height=320, .color=0x002344cc};
static struct surface a = {.name="POPUP_A", .width=300, .height=170, .color=0x00e0c020};
static struct surface b = {.name="POPUP_B", .width=300, .height=130, .color=0x0020d0a0};
static volatile sig_atomic_t running = 1;
static struct surface *entered;
static void draw(struct surface *s) {
size_t bytes = (size_t)s->width * (size_t)s->height * 4;
char path[] = "/tmp/mosaic-nested-buffer-XXXXXX";
int fd = mkstemp(path);
if (fd < 0 || ftruncate(fd, (off_t)bytes) < 0) { perror("buffer"); exit(1); }
unlink(path);
uint32_t *pixels = mmap(NULL, bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (pixels == MAP_FAILED) { perror("mmap"); exit(1); }
for (size_t i=0; i<bytes/4; ++i) pixels[i] = s->color;
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, (int)bytes);
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, s->width, s->height,
s->width*4, WL_SHM_FORMAT_XRGB8888);
wl_surface_attach(s->wl, buffer, 0, 0);
wl_surface_damage_buffer(s->wl, 0, 0, s->width, s->height);
wl_surface_commit(s->wl);
wl_buffer_destroy(buffer);
wl_shm_pool_destroy(pool);
munmap(pixels, bytes);
close(fd);
s->mapped = true;
fprintf(stderr, "%s MAP wl=%u xdg=%u\n", s->name,
wl_proxy_get_id((struct wl_proxy *)s->wl), wl_proxy_get_id((struct wl_proxy *)s->xdg));
}
static void on_ping(void *data, struct xdg_wm_base *base, uint32_t serial) {
xdg_wm_base_pong(base, serial);
}
static const struct xdg_wm_base_listener wm_listener = {.ping=on_ping};
static void on_xdg_configure(void *data, struct xdg_surface *xdg, uint32_t serial) {
struct surface *s=data;
fprintf(stderr, "%s XDG_CONFIGURE serial=%u ack\n", s->name, serial);
xdg_surface_ack_configure(xdg, serial);
s->configured=true;
if (!s->mapped && !s->done) draw(s);
}
static const struct xdg_surface_listener xdg_listener = {.configure=on_xdg_configure};
static void on_top_configure(void *data, struct xdg_toplevel *role, int32_t w,
int32_t h, struct wl_array *states) {
fprintf(stderr, "TOPLEVEL ROLE_CONFIGURE w=%d h=%d\n", w,h);
}
static void on_top_close(void *data, struct xdg_toplevel *role) {
fprintf(stderr, "TOPLEVEL CLOSE_REQUEST\n"); running=0;
}
static const struct xdg_toplevel_listener top_listener = {
.configure=on_top_configure, .close=on_top_close,
};
static void on_popup_configure(void *data, struct xdg_popup *role, int32_t x,
int32_t y, int32_t w, int32_t h) {
struct surface *s=data;
fprintf(stderr, "%s ROLE_CONFIGURE x=%d y=%d w=%d h=%d\n",s->name,x,y,w,h);
}
static void on_popup_done(void *data, struct xdg_popup *role) {
struct surface *s=data;
s->done=true;
s->mapped=false;
fprintf(stderr, "%s POPUP_DONE role=%u\n",s->name,wl_proxy_get_id((struct wl_proxy *)role));
}
static const struct xdg_popup_listener popup_listener = {
.configure=on_popup_configure, .popup_done=on_popup_done,
};
/* WHY: XDG requires child-first destruction even if the compositor sends
* dismissals for multiple levels in one dispatch batch. */
static void destroy_popup(struct surface *s) {
if (!s->popup) return;
fprintf(stderr,"%s DESTROY role=%u xdg=%u wl=%u\n",s->name,
wl_proxy_get_id((struct wl_proxy *)s->popup),
wl_proxy_get_id((struct wl_proxy *)s->xdg),
wl_proxy_get_id((struct wl_proxy *)s->wl));
xdg_popup_destroy(s->popup);
xdg_surface_destroy(s->xdg);
wl_surface_destroy(s->wl);
s->popup=NULL; s->xdg=NULL; s->wl=NULL; s->mapped=false;
if (entered==s) entered=NULL;
}
static void collect_dismissals(void) {
if (b.done) destroy_popup(&b);
if (a.done) {
destroy_popup(&b);
destroy_popup(&a);
}
}
static void open_popup(struct surface *s, struct surface *parent, uint32_t serial) {
if (s->popup || !parent->mapped || !serial) return;
s->wl=wl_compositor_create_surface(compositor);
s->xdg=xdg_wm_base_get_xdg_surface(wm,s->wl);
xdg_surface_add_listener(s->xdg,&xdg_listener,s);
struct xdg_positioner *pos=xdg_wm_base_create_positioner(wm);
xdg_positioner_set_size(pos,s->width,s->height);
xdg_positioner_set_anchor_rect(pos,s->anchor_x,s->anchor_y,1,1);
xdg_positioner_set_anchor(pos,XDG_POSITIONER_ANCHOR_TOP_LEFT);
xdg_positioner_set_gravity(pos,XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
s->popup=xdg_surface_get_popup(s->xdg,parent->xdg,pos);
xdg_popup_add_listener(s->popup,&popup_listener,s);
fprintf(stderr,"%s CREATE wl=%u xdg=%u role=%u parent=%s parent_xdg=%u\n",
s->name,wl_proxy_get_id((struct wl_proxy *)s->wl),
wl_proxy_get_id((struct wl_proxy *)s->xdg),
wl_proxy_get_id((struct wl_proxy *)s->popup),parent->name,
wl_proxy_get_id((struct wl_proxy *)parent->xdg));
fprintf(stderr,"%s GRAB_REQUEST origin=wl_pointer.button target=%s serial=%u used=%u\n",
s->name,parent->name,serial,serial);
xdg_popup_grab(s->popup,seat,serial);
wl_surface_commit(s->wl); /* Initial bufferless commit requests configure. */
xdg_positioner_destroy(pos);
}
static struct surface *which(struct wl_surface *wl) {
if (wl==top.wl) return &top;
if (wl==a.wl && wl) return &a;
if (wl==b.wl && wl) return &b;
return NULL;
}
static void on_enter(void *data,struct wl_pointer *ptr,uint32_t serial,
struct wl_surface *wl,wl_fixed_t x,wl_fixed_t y) {
entered=which(wl);
fprintf(stderr,"POINTER_ENTER target=%s wl=%u serial=%u x=%.2f y=%.2f\n",
entered?entered->name:"OTHER",wl_proxy_get_id((struct wl_proxy *)wl),serial,
wl_fixed_to_double(x),wl_fixed_to_double(y));
}
static void on_leave(void *data,struct wl_pointer *ptr,uint32_t serial,
struct wl_surface *wl) {
struct surface *s=which(wl);
fprintf(stderr,"POINTER_LEAVE target=%s serial=%u\n",s?s->name:"OTHER",serial);
if (entered==s) entered=NULL;
}
static void on_motion(void *data,struct wl_pointer *ptr,uint32_t time,
wl_fixed_t x,wl_fixed_t y) {
fprintf(stderr,"POINTER_MOTION target=%s x=%.2f y=%.2f\n",
entered?entered->name:"OTHER",wl_fixed_to_double(x),wl_fixed_to_double(y));
}
static void on_button(void *data,struct wl_pointer *ptr,uint32_t serial,
uint32_t time,uint32_t button,uint32_t state) {
fprintf(stderr,"POINTER_BUTTON target=%s serial=%u button=%u state=%u\n",
entered?entered->name:"OTHER",serial,button,state);
if (state!=WL_POINTER_BUTTON_STATE_PRESSED || button!=272) return;
if (entered==&top && !a.popup) open_popup(&a,&top,serial);
else if (entered==&a && a.mapped && !b.popup) open_popup(&b,&a,serial);
}
static void on_axis(void *data,struct wl_pointer *ptr,uint32_t t,uint32_t axis,wl_fixed_t v) {}
static void on_frame(void *data,struct wl_pointer *ptr) {}
static const struct wl_pointer_listener pointer_listener={
.enter=on_enter,.leave=on_leave,.motion=on_motion,.button=on_button,
.axis=on_axis,.frame=on_frame,
};
static void on_caps(void *data,struct wl_seat *s,uint32_t caps) {
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !pointer) {
pointer=wl_seat_get_pointer(s);
wl_pointer_add_listener(pointer,&pointer_listener,NULL);
}
}
static void on_seat_name(void *data,struct wl_seat *s,const char *name) {}
static const struct wl_seat_listener seat_listener={.capabilities=on_caps,.name=on_seat_name};
static void on_global(void *data,struct wl_registry *reg,uint32_t name,
const char *interface,uint32_t version) {
if (!strcmp(interface,wl_compositor_interface.name))
compositor=wl_registry_bind(reg,name,&wl_compositor_interface,version<4?version:4);
else if (!strcmp(interface,wl_shm_interface.name))
shm=wl_registry_bind(reg,name,&wl_shm_interface,1);
else if (!strcmp(interface,xdg_wm_base_interface.name))
wm=wl_registry_bind(reg,name,&xdg_wm_base_interface,version<3?version:3);
else if (!strcmp(interface,wl_seat_interface.name)) {
seat=wl_registry_bind(reg,name,&wl_seat_interface,version<5?version:5);
wl_seat_add_listener(seat,&seat_listener,NULL);
}
}
static void on_removed(void *data,struct wl_registry *reg,uint32_t name) {}
static const struct wl_registry_listener registry_listener={.global=on_global,.global_remove=on_removed};
static void stop(int sig) { running=0; }
int main(int argc,char **argv) {
signal(SIGTERM,stop);
const char *placement=argc>1?argv[1]:"b_cross";
if (!strcmp(placement,"visible")) {
a.anchor_x=50; a.anchor_y=80; a.width=250;
b.anchor_x=20; b.anchor_y=60; b.width=170;
} else if (!strcmp(placement,"both_cross")) {
a.anchor_x=350; a.anchor_y=80; a.width=350;
b.anchor_x=160; b.anchor_y=60; b.width=300;
} else if (!strcmp(placement,"b_cross")) {
a.anchor_x=200; a.anchor_y=80; a.width=300;
b.anchor_x=220; b.anchor_y=60; b.width=300;
} else { fprintf(stderr,"invalid placement\n"); return 2; }
display=wl_display_connect(NULL);
if (!display) { fprintf(stderr,"cannot connect Wayland\n"); return 1; }
struct wl_registry *registry=wl_display_get_registry(display);
wl_registry_add_listener(registry,&registry_listener,NULL);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (!compositor || !shm || !wm || !seat || !pointer) {
fprintf(stderr,"missing Wayland globals/pointer\n"); return 1;
}
xdg_wm_base_add_listener(wm,&wm_listener,NULL);
top.wl=wl_compositor_create_surface(compositor);
top.xdg=xdg_wm_base_get_xdg_surface(wm,top.wl);
xdg_surface_add_listener(top.xdg,&xdg_listener,&top);
top_role=xdg_surface_get_toplevel(top.xdg);
xdg_toplevel_add_listener(top_role,&top_listener,NULL);
xdg_toplevel_set_title(top_role,"Mosaic Nested Grab Probe");
xdg_toplevel_set_app_id(top_role,"org.mosaic.NestedGrabProbe");
fprintf(stderr,"TOPLEVEL CREATE wl=%u xdg=%u role=%u placement=%s\n",
wl_proxy_get_id((struct wl_proxy *)top.wl),
wl_proxy_get_id((struct wl_proxy *)top.xdg),
wl_proxy_get_id((struct wl_proxy *)top_role),placement);
wl_surface_commit(top.wl);
while (running) {
wl_display_flush(display);
struct pollfd fds[2]={{.fd=wl_display_get_fd(display),.events=POLLIN},
{.fd=STDIN_FILENO,.events=POLLIN}};
int ready=poll(fds,2,500);
if (ready<0) { if (errno==EINTR) continue; perror("poll"); break; }
if (fds[0].revents & (POLLIN|POLLERR|POLLHUP)) {
if (wl_display_dispatch(display)<0) break;
collect_dismissals();
}
if (fds[1].revents & POLLIN) {
char cmd[64];
if (fgets(cmd,sizeof(cmd),stdin)) {
/* WHY: isolate client content from scene geometry and input region.
* CONTRACT: same toplevel size/position; only private headless client.
* INVARIANT: region updates carry no new client buffer. */
if (!strncmp(cmd,"recolor_top",11)) {
top.color = 0x00d020c0;
draw(&top);
fprintf(stderr,"COMMAND recolor_top DONE color=%08x wl=%u\n",
top.color, wl_proxy_get_id((struct wl_proxy *)top.wl));
} else if (!strncmp(cmd,"region_empty_top",16) ||
!strncmp(cmd,"region_full_top",15)) {
bool empty = !strncmp(cmd,"region_empty_top",16);
struct wl_region *region = wl_compositor_create_region(compositor);
if (!empty) wl_region_add(region, 0, 0, top.width, top.height);
wl_surface_set_input_region(top.wl, region);
wl_surface_commit(top.wl);
wl_region_destroy(region);
fprintf(stderr,"COMMAND region_%s_top DONE wl=%u\n",
empty ? "empty" : "full",
wl_proxy_get_id((struct wl_proxy *)top.wl));
} else if (!strncmp(cmd,"close_b",7)) { destroy_popup(&b); fprintf(stderr,"COMMAND close_b DONE\n"); }
else if (!strncmp(cmd,"close_owner",11)) { fprintf(stderr,"COMMAND close_owner\n"); running=0; }
}
}
}
destroy_popup(&b); destroy_popup(&a);
fprintf(stderr,"TOPLEVEL DESTROY\n");
xdg_toplevel_destroy(top_role); xdg_surface_destroy(top.xdg); wl_surface_destroy(top.wl);
wl_display_flush(display);
/* WHY/CONTRACT/INVARIANT: exit0 does not prove protocol error0. */
fprintf(stderr,"EXIT error=%d\n",wl_display_get_error(display));
wl_display_disconnect(display);
return 0;
}

View file

@ -0,0 +1,224 @@
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
#include "xdg-shell-client.h"
/* WHY: the compositor must wait on a real, responsive XDG client.
* CONTRACT: hold only a received configure; ACK the latest received serial.
* INVARIANT: Wayland dispatch and ping replies continue while ACK is held. */
static struct wl_display *display;
static struct wl_compositor *compositor;
static struct wl_shm *shm;
static struct xdg_wm_base *wm;
static struct wl_surface *surface;
static struct xdg_surface *xdg;
static struct xdg_toplevel *toplevel;
static struct wl_seat *seat;
static struct wl_pointer *pointer;
static volatile sig_atomic_t running = 1;
static bool mapped, hold_next, held;
static uint32_t latest_serial;
static int32_t width = 420, height = 320, configured_width = 420, configured_height = 320;
static unsigned generation;
static long long stamp(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (long long)ts.tv_sec * 1000000000LL + ts.tv_nsec;
}
static void draw(void) {
if (configured_width > 0) width = configured_width;
if (configured_height > 0) height = configured_height;
if (width <= 0 || height <= 0 || width > 4096 || height > 4096) {
fprintf(stderr, "INVALID_SIZE %d %d\n", width, height);
exit(2);
}
size_t bytes = (size_t)width * (size_t)height * 4;
char name[] = "/tmp/mosaic-saved-shm-XXXXXX";
int fd = mkstemp(name);
if (fd < 0 || ftruncate(fd, (off_t)bytes) < 0) { perror("shm"); exit(2); }
unlink(name);
uint32_t *pixels = mmap(NULL, bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (pixels == MAP_FAILED) { perror("mmap"); exit(2); }
uint32_t color = (generation++ % 2) ? 0x0020d0a0 : 0x002344cc;
#ifdef STATIC_REFERENCE
/* CONTRACT: H2 uses one opaque SHM color across every real configure. */
color = 0x00303038;
#endif
for (size_t i = 0; i < bytes / 4; ++i) pixels[i] = color;
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, (int)bytes);
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height,
width * 4, WL_SHM_FORMAT_XRGB8888);
wl_surface_attach(surface, buffer, 0, 0);
wl_surface_damage_buffer(surface, 0, 0, width, height);
wl_surface_commit(surface);
fprintf(stderr, "COMMIT ns=%lld serial=%u size=%dx%d color=%08x\n",
stamp(), latest_serial, width, height, color);
wl_buffer_destroy(buffer);
wl_shm_pool_destroy(pool);
munmap(pixels, bytes);
close(fd);
mapped = true;
}
static void ack_and_commit(void) {
if (!latest_serial) return;
xdg_surface_ack_configure(xdg, latest_serial);
fprintf(stderr, "ACK ns=%lld serial=%u size=%dx%d\n",
stamp(), latest_serial, configured_width, configured_height);
draw();
held = false;
}
static void ping(void *data, struct xdg_wm_base *base, uint32_t serial) {
xdg_wm_base_pong(base, serial);
fprintf(stderr, "PONG ns=%lld serial=%u\n", stamp(), serial);
}
static const struct xdg_wm_base_listener wm_listener = {.ping = ping};
static void role_configure(void *data, struct xdg_toplevel *role,
int32_t w, int32_t h, struct wl_array *states) {
if (w > 0) configured_width = w;
if (h > 0) configured_height = h;
fprintf(stderr, "ROLE_CONFIGURE ns=%lld size=%dx%d\n", stamp(), w, h);
}
static void role_close(void *data, struct xdg_toplevel *role) { running = 0; }
static const struct xdg_toplevel_listener role_listener = {
.configure = role_configure, .close = role_close,
};
static void configure(void *data, struct xdg_surface *s, uint32_t serial) {
latest_serial = serial;
fprintf(stderr, "XDG_CONFIGURE ns=%lld serial=%u size=%dx%d hold=%d previous=%d\n",
stamp(), serial, configured_width, configured_height, hold_next || held, held);
if (hold_next || held) {
held = true;
hold_next = false;
fprintf(stderr, "HELD ns=%lld serial=%u\n", stamp(), serial);
} else {
ack_and_commit();
}
}
static const struct xdg_surface_listener surface_listener = {.configure = configure};
static void pointer_enter(void *data, struct wl_pointer *p, uint32_t serial,
struct wl_surface *s, wl_fixed_t x, wl_fixed_t y) {
fprintf(stderr, "ENTER ns=%lld x=%.2f y=%.2f\n", stamp(),
wl_fixed_to_double(x), wl_fixed_to_double(y));
}
static void pointer_leave(void *data, struct wl_pointer *p, uint32_t serial,
struct wl_surface *s) { fprintf(stderr, "LEAVE ns=%lld\n", stamp()); }
static void pointer_motion(void *data, struct wl_pointer *p, uint32_t time,
wl_fixed_t x, wl_fixed_t y) {
fprintf(stderr, "MOTION ns=%lld x=%.2f y=%.2f\n", stamp(),
wl_fixed_to_double(x), wl_fixed_to_double(y));
}
static void pointer_button(void *data, struct wl_pointer *p, uint32_t serial,
uint32_t time, uint32_t button, uint32_t state) {
fprintf(stderr, "BUTTON ns=%lld serial=%u button=%u state=%u\n",
stamp(), serial, button, state);
}
static void pointer_axis(void *data, struct wl_pointer *p, uint32_t t,
uint32_t axis, wl_fixed_t value) {}
static void pointer_frame(void *data, struct wl_pointer *p) {}
static const struct wl_pointer_listener pointer_listener = {
.enter = pointer_enter, .leave = pointer_leave, .motion = pointer_motion,
.button = pointer_button, .axis = pointer_axis, .frame = pointer_frame,
};
static void capabilities(void *data, struct wl_seat *s, uint32_t caps) {
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !pointer) {
pointer = wl_seat_get_pointer(s);
wl_pointer_add_listener(pointer, &pointer_listener, NULL);
}
}
static void seat_name(void *data, struct wl_seat *s, const char *name) {}
static const struct wl_seat_listener seat_listener = {
.capabilities = capabilities, .name = seat_name,
};
static void global(void *data, struct wl_registry *registry, uint32_t name,
const char *interface, uint32_t version) {
if (!strcmp(interface, wl_compositor_interface.name))
compositor = wl_registry_bind(registry, name, &wl_compositor_interface, version < 4 ? version : 4);
else if (!strcmp(interface, wl_shm_interface.name))
shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
else if (!strcmp(interface, xdg_wm_base_interface.name))
wm = wl_registry_bind(registry, name, &xdg_wm_base_interface, version < 3 ? version : 3);
else if (!strcmp(interface, wl_seat_interface.name)) {
seat = wl_registry_bind(registry, name, &wl_seat_interface, version < 5 ? version : 5);
wl_seat_add_listener(seat, &seat_listener, NULL);
}
}
static void global_remove(void *data, struct wl_registry *registry, uint32_t name) {}
static const struct wl_registry_listener registry_listener = {
.global = global, .global_remove = global_remove,
};
static void stop(int signum) { running = 0; }
int main(void) {
signal(SIGTERM, stop);
display = wl_display_connect(NULL);
if (!display) { fprintf(stderr, "CONNECT_FAILED\n"); return 1; }
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_roundtrip(display);
wl_display_roundtrip(display);
if (!compositor || !shm || !wm) { fprintf(stderr, "MISSING_GLOBAL\n"); return 2; }
xdg_wm_base_add_listener(wm, &wm_listener, NULL);
surface = wl_compositor_create_surface(compositor);
xdg = xdg_wm_base_get_xdg_surface(wm, surface);
xdg_surface_add_listener(xdg, &surface_listener, NULL);
toplevel = xdg_surface_get_toplevel(xdg);
xdg_toplevel_add_listener(toplevel, &role_listener, NULL);
#ifdef STATIC_REFERENCE
xdg_toplevel_set_title(toplevel, "Mosaic Static Reference");
xdg_toplevel_set_app_id(toplevel, "org.mosaic.StaticReference");
#else
xdg_toplevel_set_title(toplevel, "Mosaic Saved Buffer Probe");
xdg_toplevel_set_app_id(toplevel, "org.mosaic.SavedBufferProbe");
#endif
wl_surface_commit(surface);
fprintf(stderr, "READY ns=%lld\n", stamp());
while (running) {
wl_display_flush(display);
struct pollfd fds[2] = {
{.fd = wl_display_get_fd(display), .events = POLLIN},
{.fd = STDIN_FILENO, .events = POLLIN},
};
int ready = poll(fds, 2, 100);
if (ready < 0) { if (errno == EINTR) continue; perror("poll"); break; }
if (fds[0].revents & (POLLIN | POLLERR | POLLHUP)) {
if (wl_display_dispatch(display) < 0) break;
}
if (fds[1].revents & POLLIN) {
char cmd[64];
if (!fgets(cmd, sizeof(cmd), stdin)) break;
if (!strncmp(cmd, "hold_next_configure", 19)) {
hold_next = true;
fprintf(stderr, "COMMAND hold_next_configure DONE ns=%lld\n", stamp());
} else if (!strncmp(cmd, "release_configure", 17)) {
if (held) ack_and_commit();
fprintf(stderr, "COMMAND release_configure DONE ns=%lld\n", stamp());
} else if (!strncmp(cmd, "close", 5)) {
running = 0;
fprintf(stderr, "COMMAND close DONE ns=%lld\n", stamp());
}
}
}
fprintf(stderr, "EXIT ns=%lld mapped=%d held=%d error=%d\n",
stamp(), mapped, held, wl_display_get_error(display));
xdg_toplevel_destroy(toplevel);
xdg_surface_destroy(xdg);
wl_surface_destroy(surface);
wl_display_flush(display);
wl_display_disconnect(display);
return 0;
}

View file

@ -0,0 +1,5 @@
/* WHY: a deterministic H2 reference cannot contain Kitty's blinking cursor.
* CONTRACT: use the same real XDG configure/ACK loop with fixed opaque SHM.
* INVARIANT: once configured and stable, this client changes no pixels. */
#define STATIC_REFERENCE 1
#include "saved-client.c"

View file

@ -0,0 +1,280 @@
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client.h>
#include "xdg-shell-client.h"
/* EXPERIMENTAL CONTRACT: every popup owns distinct XDG objects. B's actual
* parent is A's xdg_surface; logs name object IDs as well as colors. */
struct surface {
const char *name;
struct wl_surface *wl;
struct xdg_surface *xdg;
struct xdg_popup *popup;
bool configured, mapped, done;
int width, height, anchor_x, anchor_y;
uint32_t color;
};
static struct wl_display *display;
static struct wl_compositor *compositor;
static struct wl_shm *shm;
static struct xdg_wm_base *wm;
static struct wl_seat *seat;
static struct wl_pointer *pointer;
static struct xdg_toplevel *top_role;
static struct surface top = {.name="TOPLEVEL", .width=420, .height=320, .color=0x002344cc};
static struct surface a = {.name="POPUP_A", .width=300, .height=170, .color=0x00e0c020};
static struct surface b = {.name="POPUP_B", .width=300, .height=130, .color=0x0020d0a0};
static volatile sig_atomic_t running = 1;
static struct surface *entered;
static void draw(struct surface *s) {
size_t bytes = (size_t)s->width * (size_t)s->height * 4;
char path[] = "/tmp/mosaic-nested-buffer-XXXXXX";
int fd = mkstemp(path);
if (fd < 0 || ftruncate(fd, (off_t)bytes) < 0) { perror("buffer"); exit(1); }
unlink(path);
uint32_t *pixels = mmap(NULL, bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (pixels == MAP_FAILED) { perror("mmap"); exit(1); }
for (size_t i=0; i<bytes/4; ++i) pixels[i] = s->color;
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, (int)bytes);
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, s->width, s->height,
s->width*4, WL_SHM_FORMAT_XRGB8888);
wl_surface_attach(s->wl, buffer, 0, 0);
wl_surface_damage_buffer(s->wl, 0, 0, s->width, s->height);
wl_surface_commit(s->wl);
wl_buffer_destroy(buffer);
wl_shm_pool_destroy(pool);
munmap(pixels, bytes);
close(fd);
s->mapped = true;
fprintf(stderr, "%s MAP wl=%u xdg=%u\n", s->name,
wl_proxy_get_id((struct wl_proxy *)s->wl), wl_proxy_get_id((struct wl_proxy *)s->xdg));
}
static void on_ping(void *data, struct xdg_wm_base *base, uint32_t serial) {
xdg_wm_base_pong(base, serial);
}
static const struct xdg_wm_base_listener wm_listener = {.ping=on_ping};
static void on_xdg_configure(void *data, struct xdg_surface *xdg, uint32_t serial) {
struct surface *s=data;
fprintf(stderr, "%s XDG_CONFIGURE serial=%u ack\n", s->name, serial);
xdg_surface_ack_configure(xdg, serial);
s->configured=true;
if (!s->mapped && !s->done) draw(s);
}
static const struct xdg_surface_listener xdg_listener = {.configure=on_xdg_configure};
static void on_top_configure(void *data, struct xdg_toplevel *role, int32_t w,
int32_t h, struct wl_array *states) {
fprintf(stderr, "TOPLEVEL ROLE_CONFIGURE w=%d h=%d\n", w,h);
}
static void on_top_close(void *data, struct xdg_toplevel *role) {
fprintf(stderr, "TOPLEVEL CLOSE_REQUEST\n"); running=0;
}
static const struct xdg_toplevel_listener top_listener = {
.configure=on_top_configure, .close=on_top_close,
};
static void on_popup_configure(void *data, struct xdg_popup *role, int32_t x,
int32_t y, int32_t w, int32_t h) {
struct surface *s=data;
fprintf(stderr, "%s ROLE_CONFIGURE x=%d y=%d w=%d h=%d\n",s->name,x,y,w,h);
}
static void on_popup_done(void *data, struct xdg_popup *role) {
struct surface *s=data;
s->done=true;
s->mapped=false;
fprintf(stderr, "%s POPUP_DONE role=%u\n",s->name,wl_proxy_get_id((struct wl_proxy *)role));
}
static const struct xdg_popup_listener popup_listener = {
.configure=on_popup_configure, .popup_done=on_popup_done,
};
/* WHY: XDG requires child-first destruction even if the compositor sends
* dismissals for multiple levels in one dispatch batch. */
static void destroy_popup(struct surface *s) {
if (!s->popup) return;
fprintf(stderr,"%s DESTROY role=%u xdg=%u wl=%u\n",s->name,
wl_proxy_get_id((struct wl_proxy *)s->popup),
wl_proxy_get_id((struct wl_proxy *)s->xdg),
wl_proxy_get_id((struct wl_proxy *)s->wl));
xdg_popup_destroy(s->popup);
xdg_surface_destroy(s->xdg);
wl_surface_destroy(s->wl);
s->popup=NULL; s->xdg=NULL; s->wl=NULL; s->mapped=false;
if (entered==s) entered=NULL;
}
static void collect_dismissals(void) {
if (b.done) destroy_popup(&b);
if (a.done) {
destroy_popup(&b);
destroy_popup(&a);
}
}
static void open_popup(struct surface *s, struct surface *parent, uint32_t serial) {
if (s->popup || !parent->mapped || !serial) return;
s->wl=wl_compositor_create_surface(compositor);
s->xdg=xdg_wm_base_get_xdg_surface(wm,s->wl);
xdg_surface_add_listener(s->xdg,&xdg_listener,s);
struct xdg_positioner *pos=xdg_wm_base_create_positioner(wm);
xdg_positioner_set_size(pos,s->width,s->height);
xdg_positioner_set_anchor_rect(pos,s->anchor_x,s->anchor_y,1,1);
xdg_positioner_set_anchor(pos,XDG_POSITIONER_ANCHOR_TOP_LEFT);
xdg_positioner_set_gravity(pos,XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
s->popup=xdg_surface_get_popup(s->xdg,parent->xdg,pos);
xdg_popup_add_listener(s->popup,&popup_listener,s);
fprintf(stderr,"%s CREATE wl=%u xdg=%u role=%u parent=%s parent_xdg=%u\n",
s->name,wl_proxy_get_id((struct wl_proxy *)s->wl),
wl_proxy_get_id((struct wl_proxy *)s->xdg),
wl_proxy_get_id((struct wl_proxy *)s->popup),parent->name,
wl_proxy_get_id((struct wl_proxy *)parent->xdg));
fprintf(stderr,"%s GRAB_REQUEST origin=wl_pointer.button target=%s serial=%u used=%u\n",
s->name,parent->name,serial,serial);
xdg_popup_grab(s->popup,seat,serial);
wl_surface_commit(s->wl); /* Initial bufferless commit requests configure. */
xdg_positioner_destroy(pos);
}
static struct surface *which(struct wl_surface *wl) {
if (wl==top.wl) return &top;
if (wl==a.wl && wl) return &a;
if (wl==b.wl && wl) return &b;
return NULL;
}
static void on_enter(void *data,struct wl_pointer *ptr,uint32_t serial,
struct wl_surface *wl,wl_fixed_t x,wl_fixed_t y) {
entered=which(wl);
fprintf(stderr,"POINTER_ENTER target=%s wl=%u serial=%u x=%.2f y=%.2f\n",
entered?entered->name:"OTHER",wl_proxy_get_id((struct wl_proxy *)wl),serial,
wl_fixed_to_double(x),wl_fixed_to_double(y));
}
static void on_leave(void *data,struct wl_pointer *ptr,uint32_t serial,
struct wl_surface *wl) {
struct surface *s=which(wl);
fprintf(stderr,"POINTER_LEAVE target=%s serial=%u\n",s?s->name:"OTHER",serial);
if (entered==s) entered=NULL;
}
static void on_motion(void *data,struct wl_pointer *ptr,uint32_t time,
wl_fixed_t x,wl_fixed_t y) {
fprintf(stderr,"POINTER_MOTION target=%s x=%.2f y=%.2f\n",
entered?entered->name:"OTHER",wl_fixed_to_double(x),wl_fixed_to_double(y));
}
static void on_button(void *data,struct wl_pointer *ptr,uint32_t serial,
uint32_t time,uint32_t button,uint32_t state) {
fprintf(stderr,"POINTER_BUTTON target=%s serial=%u button=%u state=%u\n",
entered?entered->name:"OTHER",serial,button,state);
if (state!=WL_POINTER_BUTTON_STATE_PRESSED || button!=272) return;
if (entered==&top && !a.popup) open_popup(&a,&top,serial);
else if (entered==&a && a.mapped && !b.popup) open_popup(&b,&a,serial);
}
static void on_axis(void *data,struct wl_pointer *ptr,uint32_t t,uint32_t axis,wl_fixed_t v) {}
static void on_frame(void *data,struct wl_pointer *ptr) {}
static const struct wl_pointer_listener pointer_listener={
.enter=on_enter,.leave=on_leave,.motion=on_motion,.button=on_button,
.axis=on_axis,.frame=on_frame,
};
static void on_caps(void *data,struct wl_seat *s,uint32_t caps) {
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !pointer) {
pointer=wl_seat_get_pointer(s);
wl_pointer_add_listener(pointer,&pointer_listener,NULL);
}
}
static void on_seat_name(void *data,struct wl_seat *s,const char *name) {}
static const struct wl_seat_listener seat_listener={.capabilities=on_caps,.name=on_seat_name};
static void on_global(void *data,struct wl_registry *reg,uint32_t name,
const char *interface,uint32_t version) {
if (!strcmp(interface,wl_compositor_interface.name))
compositor=wl_registry_bind(reg,name,&wl_compositor_interface,version<4?version:4);
else if (!strcmp(interface,wl_shm_interface.name))
shm=wl_registry_bind(reg,name,&wl_shm_interface,1);
else if (!strcmp(interface,xdg_wm_base_interface.name))
wm=wl_registry_bind(reg,name,&xdg_wm_base_interface,version<3?version:3);
else if (!strcmp(interface,wl_seat_interface.name)) {
seat=wl_registry_bind(reg,name,&wl_seat_interface,version<5?version:5);
wl_seat_add_listener(seat,&seat_listener,NULL);
}
}
static void on_removed(void *data,struct wl_registry *reg,uint32_t name) {}
static const struct wl_registry_listener registry_listener={.global=on_global,.global_remove=on_removed};
static void stop(int sig) { running=0; }
int main(int argc,char **argv) {
signal(SIGTERM,stop);
const char *placement=argc>1?argv[1]:"b_cross";
/* CONTRACT: vertical positioner anchors remain inside the toplevel's
* effective window geometry, which defaults to its buffer bounds. */
if (!strncmp(placement,"v_",2)) top.height=575;
if (!strcmp(placement,"visible")) {
a.anchor_x=50; a.anchor_y=80; a.width=250;
b.anchor_x=20; b.anchor_y=60; b.width=170;
} else if (!strcmp(placement,"v_visible")) {
a.anchor_x=50; a.anchor_y=80; a.width=250;
b.anchor_x=20; b.anchor_y=60; b.width=170;
} else if (!strcmp(placement,"v_b_cross")) {
/* WHY: only B's requested rectangle reaches below y=600 when the
* tiled toplevel content begins near y=25. Configure and pixels
* remain the authority for the actual crossing. */
a.anchor_x=50; a.anchor_y=350; a.width=250;
b.anchor_x=20; b.anchor_y=155; b.width=170;
} else if (!strcmp(placement,"v_both_cross")) {
a.anchor_x=50; a.anchor_y=480; a.width=250;
b.anchor_x=20; b.anchor_y=80; b.width=170;
} else if (!strcmp(placement,"both_cross")) {
a.anchor_x=350; a.anchor_y=80; a.width=350;
b.anchor_x=160; b.anchor_y=60; b.width=300;
} else if (!strcmp(placement,"b_cross")) {
a.anchor_x=200; a.anchor_y=80; a.width=300;
b.anchor_x=220; b.anchor_y=60; b.width=300;
} else { fprintf(stderr,"invalid placement\n"); return 2; }
display=wl_display_connect(NULL);
if (!display) { fprintf(stderr,"cannot connect Wayland\n"); return 1; }
struct wl_registry *registry=wl_display_get_registry(display);
wl_registry_add_listener(registry,&registry_listener,NULL);
wl_display_roundtrip(display); wl_display_roundtrip(display);
if (!compositor || !shm || !wm || !seat || !pointer) {
fprintf(stderr,"missing Wayland globals/pointer\n"); return 1;
}
xdg_wm_base_add_listener(wm,&wm_listener,NULL);
top.wl=wl_compositor_create_surface(compositor);
top.xdg=xdg_wm_base_get_xdg_surface(wm,top.wl);
xdg_surface_add_listener(top.xdg,&xdg_listener,&top);
top_role=xdg_surface_get_toplevel(top.xdg);
xdg_toplevel_add_listener(top_role,&top_listener,NULL);
xdg_toplevel_set_title(top_role,"Mosaic Nested Grab Probe");
xdg_toplevel_set_app_id(top_role,"org.mosaic.NestedGrabProbe");
fprintf(stderr,"TOPLEVEL CREATE wl=%u xdg=%u role=%u placement=%s\n",
wl_proxy_get_id((struct wl_proxy *)top.wl),
wl_proxy_get_id((struct wl_proxy *)top.xdg),
wl_proxy_get_id((struct wl_proxy *)top_role),placement);
wl_surface_commit(top.wl);
while (running) {
wl_display_flush(display);
struct pollfd fds[2]={{.fd=wl_display_get_fd(display),.events=POLLIN},
{.fd=STDIN_FILENO,.events=POLLIN}};
int ready=poll(fds,2,500);
if (ready<0) { if (errno==EINTR) continue; perror("poll"); break; }
if (fds[0].revents & (POLLIN|POLLERR|POLLHUP)) {
if (wl_display_dispatch(display)<0) break;
collect_dismissals();
}
if (fds[1].revents & POLLIN) {
char cmd[64];
if (fgets(cmd,sizeof(cmd),stdin)) {
if (!strncmp(cmd,"close_b",7)) { destroy_popup(&b); fprintf(stderr,"COMMAND close_b DONE\n"); }
else if (!strncmp(cmd,"close_owner",11)) { fprintf(stderr,"COMMAND close_owner\n"); running=0; }
}
}
}
destroy_popup(&b); destroy_popup(&a);
fprintf(stderr,"TOPLEVEL DESTROY\n");
xdg_toplevel_destroy(top_role); xdg_surface_destroy(top.xdg); wl_surface_destroy(top.wl);
wl_display_flush(display);
/* WHY/CONTRACT/INVARIANT: exit0 does not prove protocol error0. */
fprintf(stderr,"EXIT error=%d\n",wl_display_get_error(display));
wl_display_disconnect(display);
return 0;
}

View file

@ -0,0 +1,370 @@
/* Generated by wayland-scanner 1.26.0 */
#ifndef WLR_VIRTUAL_POINTER_UNSTABLE_V1_CLIENT_PROTOCOL_H
#define WLR_VIRTUAL_POINTER_UNSTABLE_V1_CLIENT_PROTOCOL_H
#include <stdint.h>
#include <stddef.h>
#include "wayland-client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @page page_wlr_virtual_pointer_unstable_v1 The wlr_virtual_pointer_unstable_v1 protocol
* @section page_ifaces_wlr_virtual_pointer_unstable_v1 Interfaces
* - @subpage page_iface_zwlr_virtual_pointer_v1 - virtual pointer
* - @subpage page_iface_zwlr_virtual_pointer_manager_v1 - virtual pointer manager
* @section page_copyright_wlr_virtual_pointer_unstable_v1 Copyright
* <pre>
*
* Copyright © 2019 Josef Gajdusek
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
* </pre>
*/
struct wl_output;
struct wl_seat;
struct zwlr_virtual_pointer_manager_v1;
struct zwlr_virtual_pointer_v1;
#ifndef ZWLR_VIRTUAL_POINTER_V1_INTERFACE
#define ZWLR_VIRTUAL_POINTER_V1_INTERFACE
/**
* @page page_iface_zwlr_virtual_pointer_v1 zwlr_virtual_pointer_v1
* @section page_iface_zwlr_virtual_pointer_v1_desc Description
*
* This protocol allows clients to emulate a physical pointer device. The
* requests are mostly mirror opposites of those specified in wl_pointer.
* @section page_iface_zwlr_virtual_pointer_v1_api API
* See @ref iface_zwlr_virtual_pointer_v1.
*/
/**
* @defgroup iface_zwlr_virtual_pointer_v1 The zwlr_virtual_pointer_v1 interface
*
* This protocol allows clients to emulate a physical pointer device. The
* requests are mostly mirror opposites of those specified in wl_pointer.
*/
extern const struct wl_interface zwlr_virtual_pointer_v1_interface;
#endif
#ifndef ZWLR_VIRTUAL_POINTER_MANAGER_V1_INTERFACE
#define ZWLR_VIRTUAL_POINTER_MANAGER_V1_INTERFACE
/**
* @page page_iface_zwlr_virtual_pointer_manager_v1 zwlr_virtual_pointer_manager_v1
* @section page_iface_zwlr_virtual_pointer_manager_v1_desc Description
*
* This object allows clients to create individual virtual pointer objects.
* @section page_iface_zwlr_virtual_pointer_manager_v1_api API
* See @ref iface_zwlr_virtual_pointer_manager_v1.
*/
/**
* @defgroup iface_zwlr_virtual_pointer_manager_v1 The zwlr_virtual_pointer_manager_v1 interface
*
* This object allows clients to create individual virtual pointer objects.
*/
extern const struct wl_interface zwlr_virtual_pointer_manager_v1_interface;
#endif
#ifndef ZWLR_VIRTUAL_POINTER_V1_ERROR_ENUM
#define ZWLR_VIRTUAL_POINTER_V1_ERROR_ENUM
enum zwlr_virtual_pointer_v1_error {
/**
* client sent invalid axis enumeration value
*/
ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS = 0,
/**
* client sent invalid axis source enumeration value
*/
ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS_SOURCE = 1,
};
#endif /* ZWLR_VIRTUAL_POINTER_V1_ERROR_ENUM */
#define ZWLR_VIRTUAL_POINTER_V1_MOTION 0
#define ZWLR_VIRTUAL_POINTER_V1_MOTION_ABSOLUTE 1
#define ZWLR_VIRTUAL_POINTER_V1_BUTTON 2
#define ZWLR_VIRTUAL_POINTER_V1_AXIS 3
#define ZWLR_VIRTUAL_POINTER_V1_FRAME 4
#define ZWLR_VIRTUAL_POINTER_V1_AXIS_SOURCE 5
#define ZWLR_VIRTUAL_POINTER_V1_AXIS_STOP 6
#define ZWLR_VIRTUAL_POINTER_V1_AXIS_DISCRETE 7
#define ZWLR_VIRTUAL_POINTER_V1_DESTROY 8
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*/
#define ZWLR_VIRTUAL_POINTER_V1_MOTION_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*/
#define ZWLR_VIRTUAL_POINTER_V1_MOTION_ABSOLUTE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*/
#define ZWLR_VIRTUAL_POINTER_V1_BUTTON_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*/
#define ZWLR_VIRTUAL_POINTER_V1_AXIS_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*/
#define ZWLR_VIRTUAL_POINTER_V1_FRAME_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*/
#define ZWLR_VIRTUAL_POINTER_V1_AXIS_SOURCE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*/
#define ZWLR_VIRTUAL_POINTER_V1_AXIS_STOP_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*/
#define ZWLR_VIRTUAL_POINTER_V1_AXIS_DISCRETE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*/
#define ZWLR_VIRTUAL_POINTER_V1_DESTROY_SINCE_VERSION 1
/** @ingroup iface_zwlr_virtual_pointer_v1 */
static inline void
zwlr_virtual_pointer_v1_set_user_data(struct zwlr_virtual_pointer_v1 *zwlr_virtual_pointer_v1, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) zwlr_virtual_pointer_v1, user_data);
}
/** @ingroup iface_zwlr_virtual_pointer_v1 */
static inline void *
zwlr_virtual_pointer_v1_get_user_data(struct zwlr_virtual_pointer_v1 *zwlr_virtual_pointer_v1)
{
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_virtual_pointer_v1);
}
static inline uint32_t
zwlr_virtual_pointer_v1_get_version(struct zwlr_virtual_pointer_v1 *zwlr_virtual_pointer_v1)
{
return wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_v1);
}
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*
* The pointer has moved by a relative amount to the previous request.
*
* Values are in the global compositor space.
*/
static inline void
zwlr_virtual_pointer_v1_motion(struct zwlr_virtual_pointer_v1 *zwlr_virtual_pointer_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_virtual_pointer_v1,
ZWLR_VIRTUAL_POINTER_V1_MOTION, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_v1), 0, time, dx, dy);
}
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*
* The pointer has moved in an absolute coordinate frame.
*
* Value of x can range from 0 to x_extent, value of y can range from 0
* to y_extent.
*/
static inline void
zwlr_virtual_pointer_v1_motion_absolute(struct zwlr_virtual_pointer_v1 *zwlr_virtual_pointer_v1, uint32_t time, uint32_t x, uint32_t y, uint32_t x_extent, uint32_t y_extent)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_virtual_pointer_v1,
ZWLR_VIRTUAL_POINTER_V1_MOTION_ABSOLUTE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_v1), 0, time, x, y, x_extent, y_extent);
}
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*
* A button was pressed or released.
*/
static inline void
zwlr_virtual_pointer_v1_button(struct zwlr_virtual_pointer_v1 *zwlr_virtual_pointer_v1, uint32_t time, uint32_t button, uint32_t state)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_virtual_pointer_v1,
ZWLR_VIRTUAL_POINTER_V1_BUTTON, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_v1), 0, time, button, state);
}
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*
* Scroll and other axis requests.
*/
static inline void
zwlr_virtual_pointer_v1_axis(struct zwlr_virtual_pointer_v1 *zwlr_virtual_pointer_v1, uint32_t time, uint32_t axis, wl_fixed_t value)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_virtual_pointer_v1,
ZWLR_VIRTUAL_POINTER_V1_AXIS, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_v1), 0, time, axis, value);
}
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*
* Indicates the set of events that logically belong together.
*/
static inline void
zwlr_virtual_pointer_v1_frame(struct zwlr_virtual_pointer_v1 *zwlr_virtual_pointer_v1)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_virtual_pointer_v1,
ZWLR_VIRTUAL_POINTER_V1_FRAME, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_v1), 0);
}
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*
* Source information for scroll and other axis.
*/
static inline void
zwlr_virtual_pointer_v1_axis_source(struct zwlr_virtual_pointer_v1 *zwlr_virtual_pointer_v1, uint32_t axis_source)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_virtual_pointer_v1,
ZWLR_VIRTUAL_POINTER_V1_AXIS_SOURCE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_v1), 0, axis_source);
}
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*
* Stop notification for scroll and other axes.
*/
static inline void
zwlr_virtual_pointer_v1_axis_stop(struct zwlr_virtual_pointer_v1 *zwlr_virtual_pointer_v1, uint32_t time, uint32_t axis)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_virtual_pointer_v1,
ZWLR_VIRTUAL_POINTER_V1_AXIS_STOP, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_v1), 0, time, axis);
}
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*
* Discrete step information for scroll and other axes.
*
* This event allows the client to extend data normally sent using the axis
* event with discrete value.
*/
static inline void
zwlr_virtual_pointer_v1_axis_discrete(struct zwlr_virtual_pointer_v1 *zwlr_virtual_pointer_v1, uint32_t time, uint32_t axis, wl_fixed_t value, int32_t discrete)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_virtual_pointer_v1,
ZWLR_VIRTUAL_POINTER_V1_AXIS_DISCRETE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_v1), 0, time, axis, value, discrete);
}
/**
* @ingroup iface_zwlr_virtual_pointer_v1
*/
static inline void
zwlr_virtual_pointer_v1_destroy(struct zwlr_virtual_pointer_v1 *zwlr_virtual_pointer_v1)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_virtual_pointer_v1,
ZWLR_VIRTUAL_POINTER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_v1), WL_MARSHAL_FLAG_DESTROY);
}
#define ZWLR_VIRTUAL_POINTER_MANAGER_V1_CREATE_VIRTUAL_POINTER 0
#define ZWLR_VIRTUAL_POINTER_MANAGER_V1_DESTROY 1
#define ZWLR_VIRTUAL_POINTER_MANAGER_V1_CREATE_VIRTUAL_POINTER_WITH_OUTPUT 2
/**
* @ingroup iface_zwlr_virtual_pointer_manager_v1
*/
#define ZWLR_VIRTUAL_POINTER_MANAGER_V1_CREATE_VIRTUAL_POINTER_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_virtual_pointer_manager_v1
*/
#define ZWLR_VIRTUAL_POINTER_MANAGER_V1_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_virtual_pointer_manager_v1
*/
#define ZWLR_VIRTUAL_POINTER_MANAGER_V1_CREATE_VIRTUAL_POINTER_WITH_OUTPUT_SINCE_VERSION 2
/** @ingroup iface_zwlr_virtual_pointer_manager_v1 */
static inline void
zwlr_virtual_pointer_manager_v1_set_user_data(struct zwlr_virtual_pointer_manager_v1 *zwlr_virtual_pointer_manager_v1, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) zwlr_virtual_pointer_manager_v1, user_data);
}
/** @ingroup iface_zwlr_virtual_pointer_manager_v1 */
static inline void *
zwlr_virtual_pointer_manager_v1_get_user_data(struct zwlr_virtual_pointer_manager_v1 *zwlr_virtual_pointer_manager_v1)
{
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_virtual_pointer_manager_v1);
}
static inline uint32_t
zwlr_virtual_pointer_manager_v1_get_version(struct zwlr_virtual_pointer_manager_v1 *zwlr_virtual_pointer_manager_v1)
{
return wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_manager_v1);
}
/**
* @ingroup iface_zwlr_virtual_pointer_manager_v1
*
* Creates a new virtual pointer. The optional seat is a suggestion to the
* compositor.
*/
static inline struct zwlr_virtual_pointer_v1 *
zwlr_virtual_pointer_manager_v1_create_virtual_pointer(struct zwlr_virtual_pointer_manager_v1 *zwlr_virtual_pointer_manager_v1, struct wl_seat *seat)
{
struct wl_proxy *id;
id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_virtual_pointer_manager_v1,
ZWLR_VIRTUAL_POINTER_MANAGER_V1_CREATE_VIRTUAL_POINTER, &zwlr_virtual_pointer_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_manager_v1), 0, seat, NULL);
return (struct zwlr_virtual_pointer_v1 *) id;
}
/**
* @ingroup iface_zwlr_virtual_pointer_manager_v1
*/
static inline void
zwlr_virtual_pointer_manager_v1_destroy(struct zwlr_virtual_pointer_manager_v1 *zwlr_virtual_pointer_manager_v1)
{
wl_proxy_marshal_flags((struct wl_proxy *) zwlr_virtual_pointer_manager_v1,
ZWLR_VIRTUAL_POINTER_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_manager_v1), WL_MARSHAL_FLAG_DESTROY);
}
/**
* @ingroup iface_zwlr_virtual_pointer_manager_v1
*
* Creates a new virtual pointer. The seat and the output arguments are
* optional. If the seat argument is set, the compositor should assign the
* input device to the requested seat. If the output argument is set, the
* compositor should map the input device to the requested output.
*/
static inline struct zwlr_virtual_pointer_v1 *
zwlr_virtual_pointer_manager_v1_create_virtual_pointer_with_output(struct zwlr_virtual_pointer_manager_v1 *zwlr_virtual_pointer_manager_v1, struct wl_seat *seat, struct wl_output *output)
{
struct wl_proxy *id;
id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_virtual_pointer_manager_v1,
ZWLR_VIRTUAL_POINTER_MANAGER_V1_CREATE_VIRTUAL_POINTER_WITH_OUTPUT, &zwlr_virtual_pointer_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_virtual_pointer_manager_v1), 0, seat, output, NULL);
return (struct zwlr_virtual_pointer_v1 *) id;
}
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,87 @@
/* Generated by wayland-scanner 1.26.0 */
/*
* Copyright © 2019 Josef Gajdusek
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include "wayland-util.h"
#ifndef __has_attribute
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
#else
#define WL_PRIVATE
#endif
extern const struct wl_interface wl_output_interface;
extern const struct wl_interface wl_seat_interface;
extern const struct wl_interface zwlr_virtual_pointer_v1_interface;
static const struct wl_interface *wlr_virtual_pointer_unstable_v1_types[] = {
NULL,
NULL,
NULL,
NULL,
NULL,
&wl_seat_interface,
&zwlr_virtual_pointer_v1_interface,
&wl_seat_interface,
&wl_output_interface,
&zwlr_virtual_pointer_v1_interface,
};
static const struct wl_message zwlr_virtual_pointer_v1_requests[] = {
{ "motion", "uff", wlr_virtual_pointer_unstable_v1_types + 0 },
{ "motion_absolute", "uuuuu", wlr_virtual_pointer_unstable_v1_types + 0 },
{ "button", "uuu", wlr_virtual_pointer_unstable_v1_types + 0 },
{ "axis", "uuf", wlr_virtual_pointer_unstable_v1_types + 0 },
{ "frame", "", wlr_virtual_pointer_unstable_v1_types + 0 },
{ "axis_source", "u", wlr_virtual_pointer_unstable_v1_types + 0 },
{ "axis_stop", "uu", wlr_virtual_pointer_unstable_v1_types + 0 },
{ "axis_discrete", "uufi", wlr_virtual_pointer_unstable_v1_types + 0 },
{ "destroy", "", wlr_virtual_pointer_unstable_v1_types + 0 },
};
WL_PRIVATE const struct wl_interface zwlr_virtual_pointer_v1_interface = {
"zwlr_virtual_pointer_v1", 2,
9, zwlr_virtual_pointer_v1_requests,
0, NULL,
};
static const struct wl_message zwlr_virtual_pointer_manager_v1_requests[] = {
{ "create_virtual_pointer", "?on", wlr_virtual_pointer_unstable_v1_types + 5 },
{ "destroy", "", wlr_virtual_pointer_unstable_v1_types + 0 },
{ "create_virtual_pointer_with_output", "2?o?on", wlr_virtual_pointer_unstable_v1_types + 7 },
};
WL_PRIVATE const struct wl_interface zwlr_virtual_pointer_manager_v1_interface = {
"zwlr_virtual_pointer_manager_v1", 2,
3, zwlr_virtual_pointer_manager_v1_requests,
0, NULL,
};

View file

@ -0,0 +1,390 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_layer_shell_unstable_v1">
<copyright>
Copyright © 2017 Drew DeVault
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<interface name="zwlr_layer_shell_v1" version="4">
<description summary="create surfaces that are layers of the desktop">
Clients can use this interface to assign the surface_layer role to
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
rendered with a defined z-depth respective to each other. They may also be
anchored to the edges and corners of a screen and specify input handling
semantics. This interface should be suitable for the implementation of
many desktop shell components, and a broad number of other applications
that interact with the desktop.
</description>
<request name="get_layer_surface">
<description summary="create a layer_surface from a surface">
Create a layer surface for an existing surface. This assigns the role of
layer_surface, or raises a protocol error if another role is already
assigned.
Creating a layer surface from a wl_surface which has a buffer attached
or committed is a client error, and any attempts by a client to attach
or manipulate a buffer prior to the first layer_surface.configure call
must also be treated as errors.
After creating a layer_surface object and setting it up, the client
must perform an initial commit without any buffer attached.
The compositor will reply with a layer_surface.configure event.
The client must acknowledge it and is then allowed to attach a buffer
to map the surface.
You may pass NULL for output to allow the compositor to decide which
output to use. Generally this will be the one that the user most
recently interacted with.
Clients can specify a namespace that defines the purpose of the layer
surface.
</description>
<arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
<arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
<arg name="namespace" type="string" summary="namespace for the layer surface"/>
</request>
<enum name="error">
<entry name="role" value="0" summary="wl_surface has another role"/>
<entry name="invalid_layer" value="1" summary="layer value is invalid"/>
<entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
</enum>
<enum name="layer">
<description summary="available layers for surfaces">
These values indicate which layers a surface can be rendered in. They
are ordered by z depth, bottom-most first. Traditional shell surfaces
will typically be rendered between the bottom and top layers.
Fullscreen shell surfaces are typically rendered at the top layer.
Multiple surfaces can share a single layer, and ordering within a
single layer is undefined.
</description>
<entry name="background" value="0"/>
<entry name="bottom" value="1"/>
<entry name="top" value="2"/>
<entry name="overlay" value="3"/>
</enum>
<!-- Version 3 additions -->
<request name="destroy" type="destructor" since="3">
<description summary="destroy the layer_shell object">
This request indicates that the client will not use the layer_shell
object any more. Objects that have been created through this instance
are not affected.
</description>
</request>
</interface>
<interface name="zwlr_layer_surface_v1" version="4">
<description summary="layer metadata interface">
An interface that may be implemented by a wl_surface, for surfaces that
are designed to be rendered as a layer of a stacked desktop-like
environment.
Layer surface state (layer, size, anchor, exclusive zone,
margin, interactivity) is double-buffered, and will be applied at the
time wl_surface.commit of the corresponding wl_surface is called.
Attaching a null buffer to a layer surface unmaps it.
Unmapping a layer_surface means that the surface cannot be shown by the
compositor until it is explicitly mapped again. The layer_surface
returns to the state it had right after layer_shell.get_layer_surface.
The client can re-map the surface by performing a commit without any
buffer attached, waiting for a configure event and handling it as usual.
</description>
<request name="set_size">
<description summary="sets the size of the surface">
Sets the size of the surface in surface-local coordinates. The
compositor will display the surface centered with respect to its
anchors.
If you pass 0 for either value, the compositor will assign it and
inform you of the assignment in the configure event. You must set your
anchor to opposite edges in the dimensions you omit; not doing so is a
protocol error. Both values are 0 by default.
Size is double-buffered, see wl_surface.commit.
</description>
<arg name="width" type="uint"/>
<arg name="height" type="uint"/>
</request>
<request name="set_anchor">
<description summary="configures the anchor point of the surface">
Requests that the compositor anchor the surface to the specified edges
and corners. If two orthogonal edges are specified (e.g. 'top' and
'left'), then the anchor point will be the intersection of the edges
(e.g. the top left corner of the output); otherwise the anchor point
will be centered on that edge, or in the center if none is specified.
Anchor is double-buffered, see wl_surface.commit.
</description>
<arg name="anchor" type="uint" enum="anchor"/>
</request>
<request name="set_exclusive_zone">
<description summary="configures the exclusive geometry of this surface">
Requests that the compositor avoids occluding an area with other
surfaces. The compositor's use of this information is
implementation-dependent - do not assume that this region will not
actually be occluded.
A positive value is only meaningful if the surface is anchored to one
edge or an edge and both perpendicular edges. If the surface is not
anchored, anchored to only two perpendicular edges (a corner), anchored
to only two parallel edges or anchored to all edges, a positive value
will be treated the same as zero.
A positive zone is the distance from the edge in surface-local
coordinates to consider exclusive.
Surfaces that do not wish to have an exclusive zone may instead specify
how they should interact with surfaces that do. If set to zero, the
surface indicates that it would like to be moved to avoid occluding
surfaces with a positive exclusive zone. If set to -1, the surface
indicates that it would not like to be moved to accommodate for other
surfaces, and the compositor should extend it all the way to the edges
it is anchored to.
For example, a panel might set its exclusive zone to 10, so that
maximized shell surfaces are not shown on top of it. A notification
might set its exclusive zone to 0, so that it is moved to avoid
occluding the panel, but shell surfaces are shown underneath it. A
wallpaper or lock screen might set their exclusive zone to -1, so that
they stretch below or over the panel.
The default value is 0.
Exclusive zone is double-buffered, see wl_surface.commit.
</description>
<arg name="zone" type="int"/>
</request>
<request name="set_margin">
<description summary="sets a margin from the anchor point">
Requests that the surface be placed some distance away from the anchor
point on the output, in surface-local coordinates. Setting this value
for edges you are not anchored to has no effect.
The exclusive zone includes the margin.
Margin is double-buffered, see wl_surface.commit.
</description>
<arg name="top" type="int"/>
<arg name="right" type="int"/>
<arg name="bottom" type="int"/>
<arg name="left" type="int"/>
</request>
<enum name="keyboard_interactivity">
<description summary="types of keyboard interaction possible for a layer shell surface">
Types of keyboard interaction possible for layer shell surfaces. The
rationale for this is twofold: (1) some applications are not interested
in keyboard events and not allowing them to be focused can improve the
desktop experience; (2) some applications will want to take exclusive
keyboard focus.
</description>
<entry name="none" value="0">
<description summary="no keyboard focus is possible">
This value indicates that this surface is not interested in keyboard
events and the compositor should never assign it the keyboard focus.
This is the default value, set for newly created layer shell surfaces.
This is useful for e.g. desktop widgets that display information or
only have interaction with non-keyboard input devices.
</description>
</entry>
<entry name="exclusive" value="1">
<description summary="request exclusive keyboard focus">
Request exclusive keyboard focus if this surface is above the shell surface layer.
For the top and overlay layers, the seat will always give
exclusive keyboard focus to the top-most layer which has keyboard
interactivity set to exclusive. If this layer contains multiple
surfaces with keyboard interactivity set to exclusive, the compositor
determines the one receiving keyboard events in an implementation-
defined manner. In this case, no guarantee is made when this surface
will receive keyboard focus (if ever).
For the bottom and background layers, the compositor is allowed to use
normal focus semantics.
This setting is mainly intended for applications that need to ensure
they receive all keyboard events, such as a lock screen or a password
prompt.
</description>
</entry>
<entry name="on_demand" value="2" since="4">
<description summary="request regular keyboard focus semantics">
This requests the compositor to allow this surface to be focused and
unfocused by the user in an implementation-defined manner. The user
should be able to unfocus this surface even regardless of the layer
it is on.
Typically, the compositor will want to use its normal mechanism to
manage keyboard focus between layer shell surfaces with this setting
and regular toplevels on the desktop layer (e.g. click to focus).
Nevertheless, it is possible for a compositor to require a special
interaction to focus or unfocus layer shell surfaces (e.g. requiring
a click even if focus follows the mouse normally, or providing a
keybinding to switch focus between layers).
This setting is mainly intended for desktop shell components (e.g.
panels) that allow keyboard interaction. Using this option can allow
implementing a desktop shell that can be fully usable without the
mouse.
</description>
</entry>
</enum>
<request name="set_keyboard_interactivity">
<description summary="requests keyboard events">
Set how keyboard events are delivered to this surface. By default,
layer shell surfaces do not receive keyboard events; this request can
be used to change this.
This setting is inherited by child surfaces set by the get_popup
request.
Layer surfaces receive pointer, touch, and tablet events normally. If
you do not want to receive them, set the input region on your surface
to an empty region.
Keyboard interactivity is double-buffered, see wl_surface.commit.
</description>
<arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
</request>
<request name="get_popup">
<description summary="assign this layer_surface as an xdg_popup parent">
This assigns an xdg_popup's parent to this layer_surface. This popup
should have been created via xdg_surface::get_popup with the parent set
to NULL, and this request must be invoked before committing the popup's
initial state.
See the documentation of xdg_popup for more details about what an
xdg_popup is and how it is used.
</description>
<arg name="popup" type="object" interface="xdg_popup"/>
</request>
<request name="ack_configure">
<description summary="ack a configure event">
When a configure event is received, if a client commits the
surface in response to the configure event, then the client
must make an ack_configure request sometime before the commit
request, passing along the serial of the configure event.
If the client receives multiple configure events before it
can respond to one, it only has to ack the last configure event.
A client is not required to commit immediately after sending
an ack_configure request - it may even ack_configure several times
before its next surface commit.
A client may send multiple ack_configure requests before committing, but
only the last request sent before a commit indicates which configure
event the client really is responding to.
</description>
<arg name="serial" type="uint" summary="the serial from the configure event"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the layer_surface">
This request destroys the layer surface.
</description>
</request>
<event name="configure">
<description summary="suggest a surface change">
The configure event asks the client to resize its surface.
Clients should arrange their surface for the new states, and then send
an ack_configure request with the serial sent in this configure event at
some point before committing the new surface.
The client is free to dismiss all but the last configure event it
received.
The width and height arguments specify the size of the window in
surface-local coordinates.
The size is a hint, in the sense that the client is free to ignore it if
it doesn't resize, pick a smaller size (to satisfy aspect ratio or
resize in steps of NxM pixels). If the client picks a smaller size and
is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
surface will be centered on this axis.
If the width or height arguments are zero, it means the client should
decide its own window dimension.
</description>
<arg name="serial" type="uint"/>
<arg name="width" type="uint"/>
<arg name="height" type="uint"/>
</event>
<event name="closed">
<description summary="surface should be closed">
The closed event is sent by the compositor when the surface will no
longer be shown. The output may have been destroyed or the user may
have asked for it to be removed. Further changes to the surface will be
ignored. The client should destroy the resource after receiving this
event, and create a new surface if they so choose.
</description>
</event>
<enum name="error">
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
<entry name="invalid_size" value="1" summary="size is invalid"/>
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
<entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
</enum>
<enum name="anchor" bitfield="true">
<entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
<entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
</enum>
<!-- Version 2 additions -->
<request name="set_layer" since="2">
<description summary="change the layer of the surface">
Change the layer that the surface is rendered on.
Layer is double-buffered, see wl_surface.commit.
</description>
<arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
</request>
</interface>
</protocol>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,184 @@
/* Generated by wayland-scanner 1.26.0 */
/*
* Copyright © 2008-2013 Kristian Høgsberg
* Copyright © 2013 Rafael Antognolli
* Copyright © 2013 Jasper St. Pierre
* Copyright © 2010-2013 Intel Corporation
* Copyright © 2015-2017 Samsung Electronics Co., Ltd
* Copyright © 2015-2017 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include "wayland-util.h"
#ifndef __has_attribute
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
#else
#define WL_PRIVATE
#endif
extern const struct wl_interface wl_output_interface;
extern const struct wl_interface wl_seat_interface;
extern const struct wl_interface wl_surface_interface;
extern const struct wl_interface xdg_popup_interface;
extern const struct wl_interface xdg_positioner_interface;
extern const struct wl_interface xdg_surface_interface;
extern const struct wl_interface xdg_toplevel_interface;
static const struct wl_interface *xdg_shell_types[] = {
NULL,
NULL,
NULL,
NULL,
&xdg_positioner_interface,
&xdg_surface_interface,
&wl_surface_interface,
&xdg_toplevel_interface,
&xdg_popup_interface,
&xdg_surface_interface,
&xdg_positioner_interface,
&xdg_toplevel_interface,
&wl_seat_interface,
NULL,
NULL,
NULL,
&wl_seat_interface,
NULL,
&wl_seat_interface,
NULL,
NULL,
&wl_output_interface,
&wl_seat_interface,
NULL,
&xdg_positioner_interface,
NULL,
};
static const struct wl_message xdg_wm_base_requests[] = {
{ "destroy", "", xdg_shell_types + 0 },
{ "create_positioner", "n", xdg_shell_types + 4 },
{ "get_xdg_surface", "no", xdg_shell_types + 5 },
{ "pong", "u", xdg_shell_types + 0 },
};
static const struct wl_message xdg_wm_base_events[] = {
{ "ping", "u", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_wm_base_interface = {
"xdg_wm_base", 7,
4, xdg_wm_base_requests,
1, xdg_wm_base_events,
};
static const struct wl_message xdg_positioner_requests[] = {
{ "destroy", "", xdg_shell_types + 0 },
{ "set_size", "ii", xdg_shell_types + 0 },
{ "set_anchor_rect", "iiii", xdg_shell_types + 0 },
{ "set_anchor", "u", xdg_shell_types + 0 },
{ "set_gravity", "u", xdg_shell_types + 0 },
{ "set_constraint_adjustment", "u", xdg_shell_types + 0 },
{ "set_offset", "ii", xdg_shell_types + 0 },
{ "set_reactive", "3", xdg_shell_types + 0 },
{ "set_parent_size", "3ii", xdg_shell_types + 0 },
{ "set_parent_configure", "3u", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_positioner_interface = {
"xdg_positioner", 7,
10, xdg_positioner_requests,
0, NULL,
};
static const struct wl_message xdg_surface_requests[] = {
{ "destroy", "", xdg_shell_types + 0 },
{ "get_toplevel", "n", xdg_shell_types + 7 },
{ "get_popup", "n?oo", xdg_shell_types + 8 },
{ "set_window_geometry", "iiii", xdg_shell_types + 0 },
{ "ack_configure", "u", xdg_shell_types + 0 },
};
static const struct wl_message xdg_surface_events[] = {
{ "configure", "u", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_surface_interface = {
"xdg_surface", 7,
5, xdg_surface_requests,
1, xdg_surface_events,
};
static const struct wl_message xdg_toplevel_requests[] = {
{ "destroy", "", xdg_shell_types + 0 },
{ "set_parent", "?o", xdg_shell_types + 11 },
{ "set_title", "s", xdg_shell_types + 0 },
{ "set_app_id", "s", xdg_shell_types + 0 },
{ "show_window_menu", "ouii", xdg_shell_types + 12 },
{ "move", "ou", xdg_shell_types + 16 },
{ "resize", "ouu", xdg_shell_types + 18 },
{ "set_max_size", "ii", xdg_shell_types + 0 },
{ "set_min_size", "ii", xdg_shell_types + 0 },
{ "set_maximized", "", xdg_shell_types + 0 },
{ "unset_maximized", "", xdg_shell_types + 0 },
{ "set_fullscreen", "?o", xdg_shell_types + 21 },
{ "unset_fullscreen", "", xdg_shell_types + 0 },
{ "set_minimized", "", xdg_shell_types + 0 },
};
static const struct wl_message xdg_toplevel_events[] = {
{ "configure", "iia", xdg_shell_types + 0 },
{ "close", "", xdg_shell_types + 0 },
{ "configure_bounds", "4ii", xdg_shell_types + 0 },
{ "wm_capabilities", "5a", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_toplevel_interface = {
"xdg_toplevel", 7,
14, xdg_toplevel_requests,
4, xdg_toplevel_events,
};
static const struct wl_message xdg_popup_requests[] = {
{ "destroy", "", xdg_shell_types + 0 },
{ "grab", "ou", xdg_shell_types + 22 },
{ "reposition", "3ou", xdg_shell_types + 24 },
};
static const struct wl_message xdg_popup_events[] = {
{ "configure", "iiii", xdg_shell_types + 0 },
{ "popup_done", "", xdg_shell_types + 0 },
{ "repositioned", "3u", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_popup_interface = {
"xdg_popup", 7,
3, xdg_popup_requests,
3, xdg_popup_events,
};

View file

@ -0,0 +1,8 @@
output HEADLESS-1 resolution 800x600 scale 1 transform normal position -800 0
output HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0
workspace 1 output HEADLESS-1
workspace 2 output HEADLESS-2
focus_follows_mouse no
seat seat0 hide_cursor 100
shadows disable
blur disable

View file

@ -0,0 +1,8 @@
output HEADLESS-1 resolution 800x600 scale 1 transform normal position -800 0
output HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0
workspace 1 output HEADLESS-1
workspace 2 output HEADLESS-2
focus_follows_mouse no
seat seat0 hide_cursor 100
shadows disable
blur disable

View file

@ -0,0 +1,8 @@
output HEADLESS-1 resolution 800x600 scale 1 transform normal position -800 0
output HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0
workspace 1 output HEADLESS-1
workspace 2 output HEADLESS-2
focus_follows_mouse no
seat seat0 hide_cursor 100
shadows disable
blur disable

View file

@ -0,0 +1,8 @@
output HEADLESS-1 resolution 1000x750 scale 1.25 transform normal position -800 0
output HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0
workspace 1 output HEADLESS-1
workspace 2 output HEADLESS-2
focus_follows_mouse no
seat seat0 hide_cursor 100
shadows disable
blur disable

View file

@ -0,0 +1,8 @@
output HEADLESS-1 resolution 750x1000 scale 1.25 transform 90 position -800 0
output HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0
workspace 1 output HEADLESS-1
workspace 2 output HEADLESS-2
focus_follows_mouse no
seat seat0 hide_cursor 100
shadows disable
blur disable

View file

@ -0,0 +1,8 @@
output HEADLESS-1 resolution 800x600 scale 1 transform normal position 0 0
output HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 600
workspace 1 output HEADLESS-1
workspace 2 output HEADLESS-2
focus_follows_mouse no
seat seat0 hide_cursor 100
shadows disable
blur disable

View file

@ -0,0 +1,8 @@
output HEADLESS-1 resolution 800x600 scale 1 transform normal position -800 0
output HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0
workspace 1 output HEADLESS-1
workspace 2 output HEADLESS-2
focus_follows_mouse no
seat seat0 hide_cursor 100
shadows disable
blur disable

View file

@ -0,0 +1,8 @@
output HEADLESS-1 resolution 800x600 scale 1 transform normal position -800 0
output HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0
workspace 1 output HEADLESS-1
workspace 2 output HEADLESS-2
focus_follows_mouse no
seat seat0 hide_cursor 100
shadows disable
blur disable

View file

@ -0,0 +1,8 @@
output HEADLESS-1 resolution 800x600 scale 1 transform normal position -800 0
output HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0
workspace 1 output HEADLESS-1
workspace 2 output HEADLESS-2
focus_follows_mouse no
seat seat0 hide_cursor 100
shadows disable
blur disable

View file

@ -0,0 +1,8 @@
output HEADLESS-1 resolution 800x600 scale 1 transform normal position -800 0
output HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0
workspace 1 output HEADLESS-1
workspace 2 output HEADLESS-2
focus_follows_mouse no
seat seat0 hide_cursor 100
shadows disable
blur disable

View file

@ -0,0 +1,8 @@
output HEADLESS-1 resolution 800x600 scale 1 transform normal position -800 0
output HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0
workspace 1 output HEADLESS-1
workspace 2 output HEADLESS-2
focus_follows_mouse no
seat seat0 hide_cursor 100
shadows disable
blur disable

View file

@ -0,0 +1,210 @@
"""Bounded owned headless session, usable only after the separate matrix review gate."""
import json,os,re,shutil,signal,subprocess,tempfile,threading,time
from pathlib import Path
from PIL import Image,ImageDraw
from geometry import *
from gate import LAB,sha,clean_environment,action_debit,BASE
ROOT=LAB
import sys
from ipc_predicates import nodes,workspace
from predicates import protocol,geometry,pair,dismissal
import validators
from lifecycle import opening_pair
from model import Provenance
COLORS={'TOPLEVEL':(35,68,204),'POPUP_A':(224,192,32),'POPUP_B':(32,208,160)}
class Session:
def __init__(self,spec,auth):
self.spec=spec;self.auth=auth;self.folder=LAB/'graphics/runs'/spec['id'];self.run=self.folder/'run';self.run.mkdir(parents=True)
self.runtime=Path(tempfile.mkdtemp(prefix='m20-',dir='/tmp'));self.runtime.chmod(0o700)
self.env=clean_environment();self.env.update(XDG_RUNTIME_DIR=str(self.runtime),WLR_BACKENDS='headless',WLR_HEADLESS_OUTPUTS='2')
self.outputs=[dict(o) for o in spec['outputs']];self.frames={o['name']:'logical' for o in self.outputs}
self.proc={};self.handles=[];self.started=time.monotonic();self.finished=threading.Event();self.expired=False;self.events=[];self.pointer=None
def start(self):
spec=self.spec;auth=self.auth
cfg='\n'.join(f"output {o['name']} resolution {o['mode'][0]}x{o['mode'][1]} scale {o['scale']} transform {o['transform']} position {o['logical'][0]} {o['logical'][1]}" for o in self.outputs)
cfg+='\nworkspace 1 output HEADLESS-1\nworkspace 2 output HEADLESS-2\nfocus_follows_mouse no\nseat seat0 hide_cursor 100\nshadows disable\nblur disable\n'
if spec['id'] in ('E2-fractional','E2-r90'):
marker=Image.new('RGB',(800,600),(30,30,46));draw=ImageDraw.Draw(marker)
for k,box in [('TL',(15,15,44,44)),('TR',(755,15,784,44)),('BL',(15,555,44,584)),('BR',(755,555,784,584)),('C',(385,285,414,314))]:draw.rectangle(box,fill=MARKERS[k])
marker.save(self.run/'marker-source.png');self.marker_source=self.run/'marker-source.png';self.record('generated-content-PNG',path=str(self.marker_source),counts_as_capture=False)
need(cfg==(BASE/'configs'/(spec['id']+'.conf')).read_text(),'sealed config bytes');(self.run/'config').write_text(cfg);self.watch=threading.Thread(target=self.watchdog,daemon=True);self.watch.start()
self.comp=self.spawn('compositor',[auth['binary'],'-d',*(['-Dtxn-timeout=5000'] if spec['id']=='E6' else []),'-c',self.run/'config'])
self.sock=self.wait(lambda:next(iter(self.runtime.glob('sway-ipc.*.sock')),None));display=self.wait(lambda:next((p for p in self.runtime.glob('wayland-*') if p.is_socket()),None))
self.env.update(WAYLAND_DISPLAY=display.name,SWAYSOCK=str(self.sock));need(Path(f'/proc/{self.comp.pid}/exe').resolve()==Path(auth['binary']).resolve() and sha(Path(f'/proc/{self.comp.pid}/exe'))==auth['binary_sha256'],'actual executable')
maps=Path(f'/proc/{self.comp.pid}/maps').read_text();(self.run/'proc-maps.txt').write_text(maps)
paths={s.split()[-1] for s in maps.splitlines() if len(s.split())>=6 and s.split()[-1].startswith('/')}
wl=[Path(p).resolve() for p in paths if 'libwlroots' in p];need(wl==[Path(auth['wlroots'])] and sha(wl[0])==auth['wlroots_sha256'],'mapped wlroots')
scene=[Path(p).resolve() for p in paths if 'libscenefx' in p]
if spec['binary']=='core':need(not scene,'static SceneFX')
else:need(scene==[Path(auth['pristine_scene'])] and sha(scene[0])==auth['pristine_scene_sha256'],'mapped pristine SceneFX')
render=[s for s in self.log('compositor').splitlines() if any(x in s for x in ('Creating scenefx FX renderer','GL renderer:','GL vendor:','Using OpenGL ES'))]
need(any('Creating scenefx FX renderer' in s for s in render) and any('GL renderer:' in s for s in render),'actual renderer')
self.record('actual-renderer',lines=render);self.snapshot('startup')
if spec['id'] in ('E2-fractional','E2-r90'):
self.spawn('marker-background',['swaybg','-o','HEADLESS-1','-i',self.marker_source,'-m','fill']);time.sleep(.35)
im=self.capture('marker-H1','HEADLESS-1',orientation_pending=True);m=marker_frame(im,self.outputs[0]['scale'],self.outputs[0]['transform']=='90');self.frames['HEADLESS-1']=m['frame'];self.record('marker-frame',**m)
self.pointer=self.spawn('pointer',[LAB/'graphics/clients/pointer-daemon']);self.wait(lambda:'POINTER_READY' in self.log('pointer'))
def record(self,kind,**fields):
e=dict(kind=kind,ns=time.monotonic_ns(),**fields);self.events.append(e)
with (self.run/'events.jsonl').open('a') as f:f.write(json.dumps(e)+'\n')
def save(self,name,value):
(self.run/(name+'.json')).write_text(json.dumps(value,indent=2)+'\n')
def debit(self,*kinds):action_debit(self.spec['id'],*kinds)
def spawn(self,name,argv,debug=False):
need(not self.expired,'expired before spawn');f=(self.run/(name+'.log')).open('w');self.handles.append(f)
p=subprocess.Popen(list(map(str,argv)),stdin=subprocess.PIPE,stdout=f,stderr=subprocess.STDOUT,text=True,start_new_session=True,env={**self.env,**({'WAYLAND_DEBUG':'1'} if debug else {})})
self.proc[name]=p;self.record('spawn',name=name,pid=p.pid,argv=list(map(str,argv)));return p
def log(self,name):return (self.run/(name+'.log')).read_text(errors='replace')
def wait(self,fn,seconds=5):
end=min(self.started+143,time.monotonic()+seconds)
while time.monotonic()<end:
need(not self.expired,'watchdog')
if hasattr(self,'comp'):need(self.comp.poll() is None,'unexpected compositor exit')
v=fn()
if v:return v
time.sleep(.02)
raise Ambiguous('bounded wait timeout')
def watchdog(self):
while not self.finished.wait(.1):
if time.monotonic()-self.started>143 or any(p.stat().st_size>32*1024*1024 for p in self.run.glob('*.log')):
self.expired=True
for p in list(self.proc.values()):
try:os.killpg(p.pid,signal.SIGTERM)
except ProcessLookupError:pass
return
def ipc(self,*args,strict=True):
need(self.sock.is_relative_to(self.runtime) and self.sock.is_socket(),'private socket')
self.record('ipc-request',args=args)
p=subprocess.run(['swaymsg','-s',str(self.sock),*args],env=self.env,capture_output=True,text=True,timeout=4)
self.record('ipc-reply',args=args,exit=p.returncode,stdout=p.stdout,stderr=p.stderr)
if strict:need(p.returncode==0,'IPC command failure')
return json.loads(p.stdout) if p.returncode==0 else None
def cmd(self,command,business=False):
self.debit('mutation' if business else 'configuration');reply=self.ipc(command);need(all(r['success'] for r in reply),'command rejected')
def snapshot(self,tag,validate=True):
snap=dict(tree=self.ipc('-t','get_tree'),outputs=self.ipc('-t','get_outputs'),workspaces=self.ipc('-t','get_workspaces'),ns=time.monotonic_ns())
if validate:expected_outputs(snap['outputs'],self.outputs)
self.save(tag+'-snapshot',snap);return snap
def find(self,name):
pid=self.proc[name].pid;rows=[n for n in nodes(self.ipc('-t','get_tree')) if n.get('pid')==pid];need(len(rows)<=1,'client owner ambiguity');return rows[0] if rows else None
def launch_client(self,name,client,*args):
p=self.spawn(name,[LAB/'graphics/clients'/client,*args],True);self.wait(lambda:self.find(name));time.sleep(.25);return p
def capture(self,tag,out,orientation_pending=False):
self.debit('capture');spec=next(o for o in self.outputs if o['name']==out);p=self.run/(tag+'.png');need(not p.exists(),'capture unique')
t=time.monotonic_ns();r=subprocess.run(['grim','-s',str(spec['scale']),'-o',out,str(p)],env=self.env,capture_output=True,text=True,timeout=4)
self.record('capture',tag=tag,output=out,start_ns=t,end_ns=time.monotonic_ns(),exit=r.returncode,path=str(p),sha256=sha(p) if p.exists() else None)
need(r.returncode==0,'capture failure');im=Image.open(p).convert('RGB')
logical=tuple(round(v*spec['scale']) for v in spec['logical'][2:]);physical=tuple(spec['mode'])
need(im.size==logical or (orientation_pending and im.size==physical) or (self.frames[out]=='physical90' and im.size==physical),'PNG size')
return im
def pair_capture(self,tag):return [self.capture(tag+'-H1','HEADLESS-1'),self.capture(tag+'-H2','HEADLESS-2')]
def inject(self,action,point,grab=False):
need(action in ('move','left','right'),'injection action');outs=self.ipc('-t','get_outputs');active=[o for o in outs if o['active']]
rects=[[o['rect'][k] for k in ('x','y','width','height')] for o in active];need(any(contains(r,point) for r in rects),'logical point outside output')
minx=min(r[0] for r in rects);miny=min(r[1] for r in rects);w=max(r[0]+r[2] for r in rects)-minx;h=max(r[1]+r[3] for r in rects)-miny
self.debit('move',*(['click'] if action!='move' else []),*(['grab'] if grab else []))
sent=[point[0]-minx,point[1]-miny];marker=f'INJECT_DONE {action} {sent[0]},{sent[1]}';before=self.log('pointer').count(marker)
self.pointer.stdin.write(f'{action} {sent[0]} {sent[1]} {w} {h}\n');self.pointer.stdin.flush();self.wait(lambda:self.log('pointer').count(marker)>before)
self.record('injection',action=action,global_logical=point,sent=sent,logical_extent=[w,h],grab=grab);time.sleep(.12)
def click(self,name,point,action='right',grab=False):
before={n:len(self.log(n)) for n in self.proc if n not in ('compositor','pointer','background')};t=time.monotonic_ns();self.inject(action,point,grab)
validators.window(t,time.monotonic_ns(),[e['ns'] for e in self.events if e['kind']=='injection' and e['ns']>=t]);tails={n:self.log(n)[i:] for n,i in before.items()};lines={n:[s for s in v.splitlines() if s.startswith(('POINTER_BUTTON ','BUTTON '))] for n,v in tails.items()}
self.record('click-observation',name=name,point=point,start_ns=t,tails=tails,buttons=lines)
return tails,lines
def delivery(self,name,rect,point,tails,lines,target,opening_serial=None):
need(not any(v for n,v in lines.items() if n!=name),'wrong client received popup button')
proof=opening_pair(lines[name],target,opening_serial) if opening_serial else pair(lines[name],target)
rows=re.findall(rf'POINTER_(?:ENTER|MOTION) target={target} .*?x=([\d.-]+) y=([\d.-]+)',tails[name]);need(rows,'fresh pointer local coordinates')
local=list(map(float,rows[-1]));validators.applied(rect[:2],rect[:2],[point[0]-local[0],point[1]-local[1]],'applied')
for e in proof:need(re.search(rf'wl_pointer#\d+\.button\({e[1]}, \d+, {e[2]}, {e[3]}\)',tails[name]) is not None,'button actual wire')
return proof
def owner(self,name):
snap=self.snapshot(name+'-owner')
rows=[n for n in nodes(snap['tree']) if n.get('pid')==self.proc[name].pid]
need(len(rows)==1,'owner identity/ambiguity');owner=rows[0]
ws=workspace(snap['tree'],'1')
path=__import__('ipc_predicates').ancestry(snap['tree'],owner['id'])
validators.owner(owner,owner['id'],ws['id'],path)
# WHY: standard_return changes layout after the initial campaign mode.
# CONTRACT: camera applies only to current Mosaic tiled descendants;
# floating branches and fullscreen group ancestry remain exempt.
# WHY/CONTRACT: root/output/workspace IPC descriptors may have floating=null.
# INVARIANT: null means no floating flag; branch membership still counts,
# and this geometry remains a pending candidate, never native-applied.
floating=any((n.get('floating') or 'auto_off').endswith('on') for n in path)
floating=floating or any(child in parent.get('floating_nodes',[]) for parent,child in zip(path,path[1:]))
# WHY/CONTRACT: workspace fullscreen_mode=1 is fixed IPC metadata,
# not the native current fullscreen-container pointer. Only con ancestry
# supplies actual container fullscreen exemption.
eligible=ws.get('layout')=='mosaic' and not floating and not any(n.get('type')=='con' and n.get('fullscreen_mode') for n in path)
origin=assigned_origin(owner);scene=exterior_ipc(owner)
if eligible:
scroll=int(ws['mosaic']['rows'][0]['scroll_x']);origin[0]-=scroll;scene[0]-=scroll
# INVARIANT: IPC never becomes native-applied without fresh paint and pointer corroboration.
raw='current-layout IPC candidate; native paint and fresh pointer-local corroboration required, generation NOT OBSERVABLE'
data=dict(geometry_status='IPC_PENDING_CANDIDATE; requires fresh paint/pointer after every mutation',owner=owner,workspace=ws,origin=origin,scene=scene,evidence=raw,snapshot=snap);self.save(name+'-geometry',data);return data
def calibrate(self,name):
state=self.owner(name)
if self.spec['mode']=='mosaic' and self.spec['axis']==0:
delta=int(state['origin'][0]+state['owner']['window_rect']['width']//2)
if delta:self.scroll(name,delta)
state=self.owner(name);need(crossing([*state['origin'],state['owner']['window_rect']['width'],state['owner']['window_rect']['height']],0,0),'owner real x0 crossing')
return state
def scroll(self,name,delta):
self.debit('camera');self.cmd(f'[con_id={self.find(name)["id"]}] layout mosaic scroll {int(delta)}');time.sleep(.35)
self.record('camera',owner=self.find(name)['id'],delta=int(delta))
def client_cmd(self,name,cmd,marker=None):
self.debit('configuration' if cmd.startswith('close') else 'mutation');before=len(self.log(name));self.record('client-request',name=name,command=cmd)
self.proc[name].stdin.write(cmd+'\n');self.proc[name].stdin.flush()
if marker:self.wait(lambda:marker in self.log(name)[before:])
def close_client(self,name,command='close_owner'):
self.client_cmd(name,command);code=self.proc[name].wait(timeout=3);from validators import client_exit
client_exit(self.log(name),code);self.wait(lambda:self.find(name) is None)
def cleanup(self,measurement_status,failure=None):
terminal=None;rescue=False
if hasattr(self,'comp') and self.comp.poll() is None and hasattr(self,'sock') and self.sock.is_socket():
try:
p=subprocess.run(['swaymsg','-s',str(self.sock),'exit'],env=self.env,capture_output=True,text=True,timeout=2)
terminal=dict(ipc_exit=p.returncode,stdout=p.stdout,stderr=p.stderr)
self.save('terminal-ipc',terminal)
except Exception as e:
terminal=dict(ipc_error=str(e));self.save('terminal-ipc',terminal)
if terminal is not None and 'ipc_exit' in terminal:
try:self.comp.wait(timeout=1)
except Exception as e:
terminal['wait_error']=str(e);self.save('terminal-ipc',terminal)
codes=[]
# One shared cleanup deadline prevents N owned clients multiplying waits.
cleanup_end=time.monotonic()+2
if self.pointer and self.pointer.poll() is None and self.pointer.stdin and not self.pointer.stdin.closed:self.pointer.stdin.close()
for name,p in reversed(list(self.proc.items())):
if p.poll() is None:
try:p.wait(timeout=max(.01,cleanup_end-time.monotonic()))
except subprocess.TimeoutExpired:pass
residual=[p for p in self.proc.values() if p.poll() is None]
if residual:
rescue=True
for p in residual:
try:os.killpg(p.pid,signal.SIGKILL)
except ProcessLookupError:pass
kill_end=time.monotonic()+1
for p in residual:p.wait(timeout=max(.01,kill_end-time.monotonic()))
for name,p in self.proc.items():
codes.append(dict(name=name,pid=p.pid,exit=p.returncode))
if p.stdin and not p.stdin.closed:p.stdin.close()
self.finished.set();self.watch.join(timeout=1)
for h in self.handles:h.close()
for p in self.proc.values():
try:os.killpg(p.pid,0)
except ProcessLookupError:pass
else:rescue=True;os.killpg(p.pid,signal.SIGKILL)
reaped=all(not Path('/proc',str(p.pid)).exists() for p in self.proc.values());need(reaped,'owned process residual');shutil.rmtree(self.runtime)
seconds=time.monotonic()-self.started
protocol_errors=[n for n in self.proc if n not in ('compositor','pointer') and re.search(r'wl_display[^\n]*\.error\(|protocol error|invalid_popup_parent|not_the_topmost_popup',self.log(n),re.I)]
cleanup_pass=bool(not rescue and reaped and seconds<=150 and not self.expired and not protocol_errors and all(p['exit']==0 for p in codes))
transport_pass=terminal is not None and terminal.get('ipc_exit') in (0,1) and 'wait_error' not in terminal and 'ipc_error' not in terminal
final_status=measurement_status if cleanup_pass and transport_pass else 'INCONCLUSIVE'
self.save('outcome',dict(status=final_status,measurements_pass=measurement_status,architecture_verdict='INCONCLUSIVE',evidence_provenance=self.auth,untested_matrix_cells=['ATOMIC_GENERATION','COMPLETE_DAMAGE','NATIVE_BUFFER_IDENTITY','GPU_LIFETIME'],scientific_cleanup='PASS' if cleanup_pass else 'INCONCLUSIVE',runtime_exit=codes,measurements_status=measurement_status,failure=failure,terminal=terminal,processes=codes,rescue=rescue,reaped=reaped,runtime_removed=not self.runtime.exists(),seconds=seconds,watchdog=self.expired,protocol_errors=protocol_errors,cleanup_pass=cleanup_pass,transport_status='PASS' if transport_pass else 'INCONCLUSIVE'))
return final_status

View file

@ -0,0 +1,148 @@
from gate import LAB
"""EXPERIMENTAL bounded FX campaign: 16 PNGs, no compositor implementation.
WHY: earlier whole-H1 equality aborted before normal client close. Differences
are evidence, never an exception preventing the lifetime measurement. Geometry
and ROIs are recorded prospectively; no anomaly-dependent mask is allowed.
"""
import hashlib
import sys
import time
from pathlib import Path
import fx_validators as v
import validators
def center_roi(s, name):
from geometry import assigned_origin, intersection, global_png, need
owner=s.find(name)
floating=owner.get('floating','auto_off').endswith('on')
origin=s.owner(name)['origin'] if s.spec['mode']=='mosaic' and not floating else assigned_origin(owner)
win=owner['window_rect']; rect=intersection([*origin,win['width'],win['height']],s.outputs[0]['logical'])
need(rect[2]>=80 and rect[3]>=80,'visible fixed FX center')
p=global_png([rect[0]+rect[2]//2,rect[1]+rect[3]//2],s.outputs[0],s.frames['HEADLESS-1'])
return [p[0]-32,p[1]-32,p[0]+32,p[1]+32]
def preset(s, name):
# CONTRACT: edges/outside are fixed before the first relevant image; center
# is chosen from fresh applied owner geometry, never from a pixel anomaly.
rois=dict(center=center_roi(s,name),left=[0,0,32,600],right=[768,0,800,600],
top=[0,0,800,32],bottom=[0,568,800,600],outside=[0,100,32,500],witness=[32,32,96,96])
return rois
def spawn_fx(s,name,mode):
old=s.env.get('MOSAIC_FX_SHM_DIR');s.env['MOSAIC_FX_SHM_DIR']=str(s.run)
try:
p=s.spawn(name,[LAB/'graphics/clients/fx-client',mode],True)
finally:
if old is None:s.env.pop('MOSAIC_FX_SHM_DIR',None)
else:s.env['MOSAIC_FX_SHM_DIR']=old
if mode!='background':s.wait(lambda:s.find(name));time.sleep(.25)
return p
def source_hash(s,mode):
rows=v.sources(s.run,s.log(mode))['rows']
v.require(rows,'SHM source recorded')
return rows[-1]['sha256']
def effects_verdict(closes, source, h2, witnessed, shadow_result):
# WHY: measured shadow spill previously did not affect the verdict.
# CONTRACT: the entire H2 RGB image must be exact, including shadow-on.
# INVARIANT: call only after all normal closes; retain numeric spill evidence.
passed=all(x['status']=='PASS' for x in closes.values())
passed=passed and source['ABA_byte_exact'] and h2['exact'] and witnessed and shadow_result['spill']['exact']
return 'PARTIALLY_FEASIBLE' if passed else 'INCONCLUSIVE'
def effects_campaign(s):
from cases import init_reference, stable_reference
from geometry import need
need(s.spec['campaign']=='FX','explicit FX campaign')
need(s.outputs[0]['logical']==[-800,0,800,600] and s.outputs[1]['logical']==[0,0,800,600]
and all(o['scale']==1 and o['transform']=='normal' for o in s.outputs),'fixed FX geometry')
s.cmd('output HEADLESS-1 bg #1e1e2e solid_color')
init_reference(s,0);baseline=s.capture('H2-reference1','HEADLESS-2');need(all(c==(48,48,56) for c in baseline.getdata()),'whole reference uniform')
bg=spawn_fx(s,'background','background')
s.wait(lambda:'BACKGROUND_CONFIGURED' in s.log('background'))
s.client_cmd('background','A','COMMAND A DONE');time.sleep(.2)
witness_box=[32,32,96,96]
s.save('fx-witness-roi',dict(roi=witness_box,established_ns=time.monotonic_ns()))
witness=None # 1 PNG
witnessed=False
s.save('fx-background-witness',dict(roi=witness_box,high_frequency_visible=witnessed,
source=v.sources(s.run,s.log('background')),zorder='background layer below transparent filler and glass'))
spawn_fx(s,'filler0','filler');s.cmd(f'[con_id={s.find("filler0")["id"]}] border none')
spawn_fx(s,'glass','glass');gid=s.find('glass')['id'];s.cmd(f'[con_id={gid}] border none')
s.cmd(f'[con_id={gid}] focus');s.calibrate('glass')
for cmd in ('gaps inner 24','blur_xray disable','blur_radius 5','blur_passes 3',
'blur_noise 0','blur_brightness 1','blur_contrast 1','blur_saturation 1',
'shadow_offset 20 0','shadow_blur_radius 16','shadow_color #000000A0'):
s.cmd(cmd)
s.save('fx-shadow-rois',preset(s,'glass'));s.owner('glass')
off=s.capture('FX-off-H1','HEADLESS-1');witnessed=v.adjacent(off,witness_box)>20
s.cmd(f'[con_id={gid}] shadows enable',True);time.sleep(.25)
shadow=s.capture('shadow-on-H1','HEADLESS-1');shadow_h2=s.capture('shadow-on-H2','HEADLESS-2') # 3 PNG
try:validators.shadow(off,shadow);shadow_status='EXERCISED'
except ValueError:shadow_status='SHADOW_NOT_EXERCISED'
shadow_result=dict(status=shadow_status,
actual_RGB=v.diff(off,shadow),spill=v.diff(baseline,shadow_h2),
meaning='pristine crossing spill control versus Mosaic sampled adjacent-output absence',
enabled_command=True,complete_shadow_geometry='NOT PROVEN')
s.cmd(f'[con_id={gid}] shadows disable',True)
s.cmd(f'[con_id={gid}] floating enable',True)
s.cmd(f'[con_id={gid}] resize set width 300 px height 200 px')
s.cmd(f'[con_id={gid}] move absolute position -650 200');time.sleep(.25)
classic_rois=preset(s,'glass');s.save('fx-classic-rois',classic_rois)
floating=s.find('glass');s.save('fx-classic-state',floating)
need(floating['rect']['x']>=-800 and floating['rect']['x']+floating['rect']['width']<=0,'classic wholly H1')
before=source_hash(s,'glass');classic_off=s.capture('classic-off-H1','HEADLESS-1')
trace_start=len(s.log('compositor'));s.cmd(f'[con_id={gid}] blur enable',True);time.sleep(.25)
classic_on=s.capture('classic-blur-H1','HEADLESS-1') # 2 PNG
native='NOT OBSERVABLE; CORE OPT instrumentation removed'
path=dict(selection='source floating overlap -> classic, blur_xray disabled',native=native,
nonempty_region=bool(v.diff(classic_off,classic_on,classic_rois['center'])['pixels']),
region_evidence='actual changed fixed interior RGB; native filter extents NOT OBSERVABLE')
classic_result=v.classic(classic_off,classic_on,classic_rois['center'],before==source_hash(s,'glass'),path,witnessed)
s.save('fx-classic',classic_result)
s.cmd(f'[con_id={gid}] floating disable',True);time.sleep(.25);s.calibrate('glass')
s.cmd(f'[con_id={gid}] blur disable',True);s.cmd(f'[con_id={gid}] blur enable',True);time.sleep(.25)
opt_rois=preset(s,'glass');s.save('fx-rois',dict(optimized=opt_rois,classic=classic_rois,
established_ns=time.monotonic_ns(),mask=None))
states=[];images=[]
for tag,command in (('optimized-A',None),('optimized-B','B'),('optimized-A-return','A')):
if command:s.client_cmd('background',command,'COMMAND '+command+' DONE');time.sleep(.25)
states.append(s.owner('glass'));s.save(tag+'-state',states[-1])
images.append(s.pair_capture(tag)) # 6 PNG
restoration=v.restoration(images[0][0],images[1][0],images[2][0],opt_rois)
# Whole-H1 failures deliberately do not abort normal close.
source=v.sources(s.run,s.log('background'))
opt=dict(restoration=restoration,source=source,native=dict(status='NOT OBSERVABLE',reason='CORE OPT instrumentation removed'),
source_selection='settled tiled owner -> optimized; animation path must be inspected in retained trace',
state_ids=[st['owner']['id'] for st in states],wholeH2=[v.diff(baseline,p[1]) for p in images])
s.save('fx-optimized',opt)
for pair in images:validators.rgb(baseline,pair[1])
close=[]
for name in ('glass','background','filler0'):
s.client_cmd(name,'close')
code=s.proc[name].wait(timeout=3)
if name!='background':s.wait(lambda:s.find(name) is None)
alive=s.comp.poll() is None
s.record('fx-normal-close',name=name,exit=code,compositor_alive=alive)
close.append((name,code,alive))
time.sleep(.2);final=s.pair_capture('final') # 2 PNG, total 16
h1_gone=all(c==(30,30,46) for c in final[0].getdata())
h2=v.diff(baseline,final[1])
closes={name:v.normalclose(s.log(name),code,alive,h1_gone) for name,code,alive in close}
result=dict(status=effects_verdict(closes,source,h2,witnessed,shadow_result),
classic=classic_result,optimized=opt,wholeH1=restoration['wholeH1'],wholeH2_final=h2,
damage=dict(status='NOT PROVEN',raw=[l for l in s.log('compositor').splitlines() if 'DAMAGE' in l],
complete_frames='NOT PROVEN'),normalclose=closes,shadow=shadow_result,
unknown_GPU_lifetime='NOT PROVEN',capture_count=14,
production='NOT YET SUPPORTED; experimental fixture only')
s.save('effects-evidence',result)
# Native OPT traces removed; no cache identity inference on CORE.
return result['status']

View file

@ -0,0 +1,172 @@
"""EXPERIMENTAL shared FX predicates; no compositor or graphics launcher.
The same functions validate synthetic negative tests, retained evidence and live
captures. Whole images are authoritative: fixed ROIs explain, never mask, defects.
Client SHM object IDs are connection-local and never output/cache identities.
"""
import hashlib
import re
import struct
import sys
from pathlib import Path
from PIL import Image, ImageChops
def require(ok, message):
if not ok:
raise ValueError(message)
def rgb(image):
return Image.open(image).convert('RGB') if isinstance(image, (str, Path)) else image.convert('RGB')
def diff(a, b, roi=None, mask=None):
require(mask is None, 'pixel masks are forbidden')
a, b = rgb(a), rgb(b)
require(a.size == b.size, 'different image dimensions')
if roi is not None:
require(len(roi) == 4 and all(type(v) is int for v in roi), 'ROI integers')
x, y, r, t = roi
require(0 <= x < r <= a.width and 0 <= y < t <= a.height, 'ROI outside image or empty')
a, b = a.crop(roi), b.crop(roi)
delta = ImageChops.difference(a, b)
hist = [c.histogram() for c in delta.split()]
data = delta.tobytes()
return dict(pixels=sum(any(data[i:i+3]) for i in range(0, len(data), 3)),
bbox=list(delta.getbbox()) if delta.getbbox() else None,
perchannel=[dict(changed=sum(h[1:]), total_abs=sum(i*n for i,n in enumerate(h)),
max_abs=max((i for i,n in enumerate(h) if n), default=0)) for h in hist],
size=list(a.size), roi=roi, exact=a.tobytes() == b.tobytes())
def adjacent(image, roi):
image = rgb(image)
diff(image, image, roi) # validates the prospective ROI without padding
im = image.crop(roi)
require(im.width > 1, 'adjacent metric ROI width')
total = sum(sum(abs(a-b) for a,b in zip(im.getpixel((x,y)), im.getpixel((x+1,y))))
for y in range(im.height) for x in range(im.width-1))
return total/(im.height*(im.width-1)*3)
def classic(off, on, roi, source_same, path, witness):
change = diff(off, on, roi)
before, after = adjacent(off, roi), adjacent(on, roi)
exercised = bool(source_same and path.get('nonempty_region') and witness
and change['pixels'] and after < before)
return dict(status='EXERCISED' if exercised else 'CLASSIC_BLUR_NOT_EXERCISED',
difference=change, adjacent_off=before, adjacent_on=after, source_same=source_same,
path=path, background_witness=witness,
meaning='same-source off/on distinguishes filtering from alpha compositing')
def restoration(a, b, returned, rois):
# CONTRACT: whole-H1 differences must survive even if center restoration passes.
return dict(wholeH1=diff(a, returned), changed_AB=diff(a,b),
rois={name:dict(AB=diff(a,b,box), AA=diff(a,returned,box)) for name,box in rois.items()},
status='EXACT' if diff(a, returned)['exact'] else 'WHOLE_H1_DIFFERENT')
def cache(trace):
rows=[l for l in trace.splitlines() if any(k in l for k in
('OPT_DIRTY_SET ', 'OPT_REGEN_ATTEMPT ', 'OPT_REGEN_RESULT ', 'OPT_DIRTY_CLEAR ', 'OPT_BLUR_USE '))]
episodes=[]; dirty={}; attempts={}; regenerated={}; cleared={}
for line in rows:
f=dict(re.findall(r'(\w+)=([^\s]+)',line));node=f.get('node');output=f.get('output')
if 'OPT_DIRTY_SET ' in line and f.get('enabled')=='1' and f.get('size')=='800x600':
dirty[node]=line
elif 'OPT_REGEN_ATTEMPT ' in line and node in dirty and f.get('dirty')=='1':
attempts[node]=(output,line)
elif 'OPT_REGEN_RESULT ' in line and node in attempts and f.get('result')=='1' and output==attempts[node][0]:
regenerated[node]=(output,line)
elif 'OPT_DIRTY_CLEAR ' in line and node in regenerated and f.get('dirty')=='0' and output==regenerated[node][0]:
cleared[output]=dict(node=node,dirty=dirty[node],attempt=attempts[node][1],
result=regenerated[node][1],clear=line)
elif 'OPT_BLUR_USE ' in line and output in cleared and f.get('optimized')=='1' and f.get('chosen') not in (None,'(nil)','0x0'):
ep={**cleared[output], 'use':line};episodes.append(ep)
return dict(status='NATIVE_OBSERVED' if episodes else 'CACHE_NOT_EXERCISED' if rows else 'NOT OBSERVABLE',
rows=rows, episodes=episodes,identity='node-correlated regeneration then output-correlated cache use; client IDs remain separate',
causal_dirty_regeneration_ABA='NOT PROVEN by aggregate trace presence')
def sources(run, log):
rows=[]
for line in log.splitlines():
if not line.startswith('SHM_SOURCE '):
continue
fields=dict(re.findall(r'(\w+)=([^\s]+)',line))
p=Path(fields['path'])
require(p.resolve().parent == Path(run).resolve(), 'SHM outside owned run')
raw=p.read_bytes()
require(len(raw)==int(fields['bytes']), 'SHM byte count')
stride=int(fields['stride'])
require(stride>0 and stride%4==0 and len(raw)%stride==0, 'SHM stride/size')
width,height=stride//4,len(raw)//stride
endian='<' if sys.byteorder=='little' else '>'
if fields['mode']=='background':
require(width==800 and height==600 and fields['format']=='XRGB8888', 'background format/geometry')
colors=[int(x,16) for x in fields['colors'].split(',')]
require(len(colors)==2, 'background palette')
row=b''.join(struct.pack(endian+'I',colors[x//8%2]) for x in range(width))
require(raw==row*height, 'background high-frequency source bytes')
elif fields['mode'] in ('glass','filler'):
color=0x88442010 if fields['mode']=='glass' else 0
require(fields['format']=='ARGB8888' and raw==struct.pack(endian+'I',color)*(width*height),
'glass/filler premultiplied source bytes')
fields.update(sha256=hashlib.sha256(raw).hexdigest(), actual_bytes=len(raw),
width=width,height=height,byteorder=sys.byteorder)
rows.append(fields)
bg=[r for r in rows if r['mode']=='background']
valid=(len(bg)==3 and bg[0]['sha256']==bg[2]['sha256'] and bg[0]['sha256']!=bg[1]['sha256'])
return dict(rows=rows, ABA_byte_exact=valid, format_endianness='native uint32_t; little-endian expected and separately recorded')
def normalclose(log, code, compositor_alive, gone):
lines=log.splitlines(); close=[(i,l) for i,l in enumerate(lines) if l.startswith('CLOSE_RECEIVED ')]
destroy=[(i,l) for i,l in enumerate(lines) if l.startswith('OBJECTS_DESTROYED ')]
exits=[(i,l) for i,l in enumerate(lines) if l.startswith('EXIT ')]
releases=[l for l in lines if l.startswith('RELEASE ')]
commit_after=bool(close and any(l.startswith('CLIENT_COMMIT ') for l in lines[close[0][0]+1:]))
ok=(code==0 and compositor_alive and gone and len(close)==len(destroy)==len(exits)==1
and close[0][0]<destroy[0][0]<exits[0][0] and 'error=0 ' in exits[0][1]
and 'normalclose=1 ' in exits[0][1] and bool(releases) and not commit_after)
return dict(status='PASS' if ok else 'FAIL', code=code, compositor_alive=compositor_alive,
pixels_gone=gone, command_received=[l for _,l in close], objects_destroyed=[l for _,l in destroy],
exits=[l for _,l in exits], release_callbacks=releases, commit_after_close=commit_after,
GPU_lifetime='NOT PROVEN; SHM releases and normal object teardown are separate observations')
def validate_run(run):
"""Post-run entry also usable on retained old runs; missing proof stays absent."""
import json
run=Path(run)
names=('optimized-A','optimized-B','optimized-A-return')
result={'schema':'experimental-fx-v1','historical':not (run/'fx-rois.json').exists()}
rois=json.loads((run/'fx-rois.json').read_text())['optimized'] if (run/'fx-rois.json').exists() else {'center':[300,250,364,314]}
if all((run/(name+'-H1.png')).exists() for name in names):
result['optimized']=restoration(*(run/(name+'-H1.png') for name in names),rois)
for output in ('H1','H2'):
a,b=run/('classic-off-'+output+'.png'),run/('classic-blur-'+output+'.png')
if a.exists() and b.exists():result['classic_'+output]=diff(a,b)
ref=run/'H2-reference1.png'
if ref.exists():result['wholeH2']={p.name:diff(ref,p) for p in sorted(run.glob('*-H2.png'))}
trace=(run/'compositor.log').read_text(errors='replace')
result['cache']=cache(trace)
result['clients']={name:normalclose((run/(name+'.log')).read_text(errors='replace'),
None,False,False) for name in ('glass','background') if (run/(name+'.log')).exists()}
result['normalclose']='NOT PROVEN offline without explicit recorded live-compositor/pixel proof'
if (run/'effects-evidence.json').exists() and (run/'events.jsonl').exists() and (run/'final-H1.png').exists():
events=[json.loads(l) for l in (run/'events.jsonl').read_text().splitlines()]
close={e['name']:e for e in events if e['kind']=='fx-normal-close'}
final=rgb(run/'final-H1.png');gone=all(c==(30,30,46) for c in final.getdata())
result['clients']={name:normalclose((run/(name+'.log')).read_text(errors='replace'),
e['exit'],e['compositor_alive'],gone) for name,e in close.items()}
result['normalclose']='PASS' if set(close)=={'glass','background','filler0'} and all(
r['status']=='PASS' for r in result['clients'].values()) else 'FAIL'
evidence=json.loads((run/'effects-evidence.json').read_text())
result['classic']=classic(run/'classic-off-H1.png',run/'classic-blur-H1.png',
json.loads((run/'fx-rois.json').read_text())['classic']['center'],
evidence['classic']['source_same'],evidence['classic']['path'],evidence['classic']['background_witness'])
result['sources']=sources(run,(run/'background.log').read_text(errors='replace'))
return result

View file

@ -0,0 +1,59 @@
"""WHY/CONTRACT/INVARIANT: ticket counters are durable, never refunded."""
import fcntl,hashlib,json,os,time,uuid
from pathlib import Path
LAB=Path(__file__).resolve().parent.parent
BASE=Path(__file__).resolve().parent
TICKET='SWAYFX_MOSAIC_STEP20C_MINIMAL_CORE_BUILD_RUNTIME_AND_GPU_FIELD_GATE_V1'
WL=Path('/usr/lib/libwlroots-0.20.so')
WL_SHA='7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837'
def sha(p):return hashlib.sha256(Path(p).read_bytes()).hexdigest()
def need(v,m):
if not v:raise ValueError(m)
def clean_environment():
e=os.environ.copy()
for k in list(e):
if k in ('DISPLAY','WAYLAND_DISPLAY','SWAYSOCK','XDG_RUNTIME_DIR','LD_PRELOAD','LD_LIBRARY_PATH') or k.startswith(('WLR_','PI_','OPT_','SAVED_')):e.pop(k,None)
return e
def durable(p,value,exclusive=False):
p=Path(p);tmp=p.with_name(p.name+'.tmp-'+uuid.uuid4().hex)
with tmp.open('x') as f:json.dump(value,f,indent=2);f.write('\n');f.flush();os.fsync(f.fileno())
if exclusive:
os.link(tmp,p);tmp.unlink()
else:os.replace(tmp,p)
fd=os.open(p.parent,os.O_DIRECTORY);os.fsync(fd);os.close(fd)
def transact(fn,path=LAB/'budget.json'):
# Lock inode stays stable while JSON replacement is atomic.
with Path(str(path)+'.lock').open('a') as lock:
fcntl.flock(lock,fcntl.LOCK_EX);d=json.loads(Path(path).read_text());need(d['ticket']==TICKET,'old ticket');r=fn(d);durable(path,d);return r
def validate_review(manifest,review):
need(review.get('decision')=='ACCEPTED' and review.get('independent') is True,'independent review absent')
need(review.get('manifest_sha256')==sha(manifest),'stale review')
m=json.loads(Path(manifest).read_text());need(m['ticket']==TICKET and m['lab']==str(LAB),'wrong lab/ticket')
for p,h in m['files'].items():need(sha(p)==h,'frozen bytes changed: '+p)
need(sha(WL)==WL_SHA,'wlroots changed')
return m
def begin(rid):
from plans import RUNS
m=validate_review(BASE/'pre-run-manifest.json',json.loads((BASE/'review.json').read_text()))
spec=RUNS[rid];auth=m['identities'][spec['binary']];need(sha(auth['binary'])==auth['binary_sha256'],'binary changed')
def reserve(d):
need(d['limits']['runs']==14 and d['limits']['captures']==180 and d['limits']['captures_per_run']==20 and d['limits']['duration_s']==150,'wrong ticket limits')
need(not any(r['id']==rid for r in d['runs']),'consumed run')
need(len(d['runs'])<14 and sum(r.get('capture_limit',r.get('captures',0)) for r in d['runs'])+spec['capture_limit']<=180,'global budget')
need(sum(r.get('campaign')=='E' for r in d['runs'])<11,'E run budget')
row=dict(id=rid,campaign='E',token=uuid.uuid4().hex,started_ns=time.monotonic_ns(),capture_limit=spec['capture_limit'],actions={},status='RESERVED')
d['runs'].append(row);return row
row=transact(reserve);durable(BASE/'runs'/rid/'reservation.json',row,True);return auth
def action_debit(rid,*kinds):
from plans import RUNS
def update(d):
rows=[r for r in d['runs'] if r['id']==rid];need(len(rows)==1,'reservation missing');r=rows[0]
for k in kinds:
limit=RUNS[rid]['capture_limit'] if k=='capture' else RUNS[rid]['action_limits'][k]
need(r['actions'].get(k,0)<limit,'local '+k+' exhausted');r['actions'][k]=r['actions'].get(k,0)+1
transact(update)

View file

@ -0,0 +1,62 @@
"""Explicit logical global, IPC assignment, physical mode and PNG-frame adapters."""
from dataclasses import dataclass
import re
from PIL import Image
class Ambiguous(ValueError):pass
def need(ok,msg):
if not ok:raise Ambiguous(msg)
def box_end(r):return [r[0]+r[2],r[1]+r[3]]
def contains(r,p):return r[0]<=p[0]<r[0]+r[2] and r[1]<=p[1]<r[1]+r[3]
def crossing(r,axis,boundary):return r[axis]<boundary<r[axis]+r[axis+2]
def intersection(a,b):
x=max(a[0],b[0]);y=max(a[1],b[1]);return [x,y,max(0,min(a[0]+a[2],b[0]+b[2])-x),max(0,min(a[1]+a[3],b[1]+b[3])-y)]
def assigned_origin(owner):
# IPC rect already excludes top decoration (ipc-json.c899–906).
return [owner['rect']['x']+owner['window_rect']['x'],owner['rect']['y']+owner['window_rect']['y']]
def exterior_ipc(owner):
r=owner['rect'];d=owner['deco_rect']['height'];return [r['x'],r['y']-d,r['width'],r['height']+d]
MARKERS={'TL':(255,32,32),'TR':(32,255,32),'BL':(32,32,255),'BR':(255,255,32),'C':(192,32,255)}
def marker_frame(image,scale,rotated):
positions={k:[] for k in MARKERS};lookup={v:k for k,v in MARKERS.items()}
for y in range(image.height):
for x in range(image.width):
k=lookup.get(image.getpixel((x,y)))
if k:positions[k].append((x,y))
need(all(len(v)>=100 for v in positions.values()),'marker color missing')
c={k:[sum(p[i] for p in v)/len(v) for i in (0,1)] for k,v in positions.items()}
logical=image.size==(round(800*scale),round(600*scale)) and c['TL'][0]<c['TR'][0] and c['TL'][1]<c['BL'][1]
physical=rotated and image.size==(round(600*scale),round(800*scale)) and c['TL'][0]>c['BL'][0] and c['TL'][1]<c['TR'][1]
need(logical or physical,'PNG orientation unestablished')
return dict(frame='logical' if logical else 'physical90',centers=c,counts={k:len(v) for k,v in positions.items()},size=list(image.size))
def png_global(px,output,frame):
scale=output['scale'];x,y=px;ox,oy,w,h=output['logical']
if frame=='physical90':x,y=y,round(h*scale)-1-x
need(frame in ('logical','physical90'),'unknown PNG frame')
# Sample integer logical centers only; resample at the chosen center later.
return [ox+int(x/scale),oy+int(y/scale)]
def global_png(p,output,frame):
x=(p[0]-output['logical'][0])*output['scale'];y=(p[1]-output['logical'][1])*output['scale']
if frame=='physical90':x,y=output['logical'][3]*output['scale']-1-y,x
return [round(x),round(y)]
def solid_point(image,color,output,frame,rectangle=None,margin=3):
for y in range(margin,image.height-margin,3):
for x in range(margin,image.width-margin,3):
if image.getpixel((x,y))!=color:continue
p=png_global((x,y),output,frame)
if rectangle is not None and not contains(rectangle,p):continue
pp=global_png(p,output,frame)
if not (margin<=pp[0]<image.width-margin and margin<=pp[1]<image.height-margin):continue
if all(image.getpixel((xx,yy))==color for yy in range(pp[1]-margin,pp[1]+margin+1) for xx in range(pp[0]-margin,pp[0]+margin+1)):
return p
raise Ambiguous('no measured solid logical interior')
def count(image,color):return sum(v==color for v in image.get_flattened_data())
def exact(a,b):need(a.size==b.size and a.tobytes()==b.tobytes(),'whole RGB differs');return dict(equal=True,pixels=a.width*a.height)
def expected_outputs(outputs,spec):
active=[o for o in outputs if o['active']];need(len(active)==2,'active output count')
for want in spec:
rows=[o for o in active if o['name']==want['name']];need(len(rows)==1,'output identity')
o=rows[0];need([o['rect'][k] for k in ('x','y','width','height')]==want['logical'],'logical output geometry')
need(o['scale']==want['scale'] and o['transform']==want['transform'],'scale/IPC transform string')
need([o['current_mode'][k] for k in ('width','height')]==want['mode'],'physical mode')
return active

View file

@ -0,0 +1,23 @@
"""Pure tree identity helpers only; IPC geometry is pending candidate."""
def nodes(node):
yield node
for child in node.get('nodes', []) + node.get('floating_nodes', []):
yield from nodes(child)
def workspace(tree, name):
rows = [n for n in nodes(tree) if n.get('type') == 'workspace' and n.get('name') == name]
assert len(rows) == 1, 'workspace identity'
return rows[0]
def ancestry(tree, con_id, path=()):
if tree.get('id') == con_id:
return path + (tree,)
for child in tree.get('nodes', []) + tree.get('floating_nodes', []):
found = ancestry(child, con_id, path + (tree,))
if found:
return found
return ()

View file

@ -0,0 +1,64 @@
"""Pure E2 input/lifecycle predicates shared with offline negative tests."""
import re
from model import require
from predicates import COLORS, paint, equal, pair, point
def opening_pair(lines,target,serial):
actual=[]
for line in lines:
m=re.fullmatch(r'POINTER_BUTTON target=(\w+) serial=(\d+) button=(\d+) state=(\d+)',line)
require(m is not None,'opening-button-format');actual.append([m[1],*map(int,m.groups()[1:])])
require(len(actual)==2 and all(e[0]==target for e in actual) and
[e[2:] for e in actual]==[[272,1],[272,0]] and actual[0][1]==serial and
0<actual[0][1]<actual[1][1]<=0xffffffff,'real-left-opening-pair')
return actual
def explicit_child_close(before,after,ids,request_ns,done_ns):
"""Client destroy must follow a recorded explicit request; no popup_done."""
require(type(request_ns) is int and request_ns<done_ns,'explicit-close-request-order')
require(after.startswith(before),'append-only-client-protocol')
tail=after[len(before):]
require('POPUP_DONE ' not in after,'close-must-not-be-dismissal')
b=ids['POPUP_B'];a=ids['POPUP_A']
destroy=f'POPUP_B DESTROY role={b[2]} xdg={b[1]} wl={b[0]}'
require(tail.count(destroy)==1 and tail.count('COMMAND close_b DONE')==1 and
tail.index(destroy)<tail.index('COMMAND close_b DONE'),'explicit-child-destroy-DONE')
for obj,stem in [(b[2],'xdg_popup'),(b[1],'xdg_surface'),(b[0],'wl_surface')]:
require(tail.count(f'{stem}#{obj}.destroy()')==1,'child-protocol-destroy')
require(after.count(f'POPUP_A MAP wl={a[0]} xdg={a[1]}')==1 and
'POPUP_A DESTROY ' not in after and 'TOPLEVEL DESTROY' not in after,'same-A-remains-mapped')
return dict(status='PASS',request_ns=request_ns,done_ns=done_ns,B=b,A=a,
meaning='explicit client close, no compositor popup_done')
def a_survival(image,before,after,ids,buttons,global_point,rect):
a=ids['POPUP_A']
require(paint(image,'POPUP_B')['count']==0 and paint(image,'POPUP_A')['count']>0,'child-zero-residual-A-painted')
require(before.startswith('') and after.startswith(before),'A-input-protocol-appended')
require('POPUP_A DESTROY ' not in after and 'POPUP_A POPUP_DONE ' not in after and
after.count(f'POPUP_A MAP wl={a[0]} xdg={a[1]}')==1,'same-A-live-after-input')
require(rect[0]<=global_point[0]<rect[0]+rect[2] and rect[1]<=global_point[1]<rect[1]+rect[3], 'A-recomputed-point-bounds')
return dict(status='PASS',pair=pair(buttons,'POPUP_A'),ids=a,point=global_point)
def normal_owner_close(before,after,ids,code):
require(code==0 and after.startswith(before),'normal-client-exit')
tail=after[len(before):]
require(tail.count('COMMAND close_owner')==1 and tail.count('TOPLEVEL DESTROY')==1 and
'POPUP_DONE ' not in tail,'normal-owner-close-command')
a=ids['POPUP_A'];top=ids['TOPLEVEL']
require(tail.count(f'POPUP_A DESTROY role={a[2]} xdg={a[1]} wl={a[0]}')==1 and
tail.index('COMMAND close_owner')<tail.index('POPUP_A DESTROY ')<tail.index('TOPLEVEL DESTROY'),
'normal-owner-child-first-destruction')
for obj,stem in [(a[2],'xdg_popup'),(a[1],'xdg_surface'),(a[0],'wl_surface'),
(top[2],'xdg_toplevel'),(top[1],'xdg_surface'),(top[0],'wl_surface')]:
require(tail.count(f'{stem}#{obj}.destroy()')==1,'owner-protocol-destroy')
return dict(status='PASS',client_exit=code,ids=ids)
def final_restoration(baseline,final):
cmp=equal(baseline,final)
require(cmp['equal'] and all(paint(final,n)['count']==0 for n in ('POPUP_A','POPUP_B')),'whole-H2-final-exact')
return dict(status='PASS',comparison=cmp)

View file

@ -0,0 +1,194 @@
"""EXPERIMENTAL typed half-open geometry. Pure: no processes or sockets.
Source contracts are recorded in the offline gate, not inferred from RGB.
Every conversion is explicit and keeps assignment/owner/generation provenance.
"""
from dataclasses import dataclass
from enum import Enum
class Invalid(ValueError):
pass
class Space(str, Enum):
NATIVE = 'NativeSHM'
CONFIGURE = 'XDGConfigure'
CONTENT = 'AppliedContent'
GLOBAL = 'SceneGlobal'
POPUP = 'PopupLocal'
VIEWPORT = 'GlobalViewport'
PNG = 'OutputPNG'
def require(value, contract):
if not value:
raise Invalid(contract)
@dataclass(frozen=True)
class Provenance:
owner: int
workspace: int
generation: str
evidence: str
state: str = 'applied'
@dataclass(frozen=True)
class Rect:
space: Space
x: int
y: int
width: int
height: int
provenance: Provenance
def __post_init__(self):
require(isinstance(self.space, Space), 'tag-required')
require(all(type(v) is int for v in (self.x, self.y, self.width, self.height)), 'integer-geometry')
require(self.width >= 0 and self.height >= 0, 'nonnegative-size')
require(bool(self.provenance.evidence), 'evidence-required')
@property
def end(self):
return (self.x + self.width, self.y + self.height)
def contains(self, xy):
return self.x <= xy[0] < self.end[0] and self.y <= xy[1] < self.end[1]
def values(self):
return [self.x, self.y, self.width, self.height]
@dataclass(frozen=True)
class Native:
rectangle: Rect
buffer_id: int
stride: int
format: int
commit_serial: int
@dataclass(frozen=True)
class Applied:
scene: Rect
window: Rect
configured: Rect
native: Native
border_left: int
border_top: int
titlebar: int
configure_ack: int
view_offset: tuple = (0, 0)
@dataclass(frozen=True)
class Output:
name: str
rectangle: Rect
scale: float
transform: str
def same(a, b):
require(a.owner == b.owner, 'owner-identity')
require(a.workspace == b.workspace, 'workspace-identity')
require(a.generation == b.generation, 'applied-generation')
require(a.state == b.state == 'applied', 'pending-not-applied')
def content(applied):
s, w, c, n = applied.scene, applied.window, applied.configured, applied.native
require(s.space is Space.GLOBAL and w.space is Space.CONTENT and c.space is Space.CONFIGURE,
'assignment-spaces')
require(n.rectangle.space is Space.NATIVE, 'native-space')
for r in (w, c, n.rectangle):
same(s.provenance, r.provenance)
require(n.buffer_id > 0 and n.stride == n.rectangle.width * 4 and n.format in (0, 1), 'native-buffer-identity')
require(0 < n.commit_serial <= 0xffffffff, 'commit-serial')
require(0 < applied.configure_ack <= 0xffffffff, 'assigned-configure-ack')
require((w.width, w.height) == (c.width, c.height), 'configure-assignment')
require(applied.border_left == w.x and applied.border_top == applied.titlebar + w.y,
'titlebar-content-offset')
require(w.width + 2 * applied.border_left == s.width and
w.height + applied.border_top + applied.border_left == s.height, 'assigned-destination')
return Rect(Space.GLOBAL, s.x + applied.border_left + applied.view_offset[0],
s.y + applied.border_top + applied.view_offset[1], w.width, w.height, s.provenance)
def intersect(rect, boundary):
require(rect.space is Space.GLOBAL and boundary.space in (Space.GLOBAL, Space.VIEWPORT), 'global-intersection')
same(rect.provenance, boundary.provenance)
x, y = max(rect.x, boundary.x), max(rect.y, boundary.y)
return Rect(Space.GLOBAL, x, y, max(0, min(rect.end[0], boundary.end[0]) - x),
max(0, min(rect.end[1], boundary.end[1]) - y), rect.provenance)
def to_png(rect, output):
require(rect.space is Space.GLOBAL and output.rectangle.space is Space.GLOBAL, 'global-to-png-spaces')
require(output.scale == 1.0, 'unmeasured-output-scale')
require(output.transform == 'normal', 'unmeasured-output-transform')
same(rect.provenance, output.rectangle.provenance)
visible = intersect(rect, output.rectangle)
return Rect(Space.PNG, min(max(visible.x - output.rectangle.x,0),output.rectangle.width), min(max(visible.y - output.rectangle.y,0),output.rectangle.height),
visible.width, visible.height, rect.provenance)
def from_png(rect, output):
require(rect.space is Space.PNG and output.rectangle.space is Space.GLOBAL, 'png-to-global-spaces')
require(output.scale == 1.0 and output.transform == 'normal', 'unmeasured-png-transform')
same(rect.provenance, output.rectangle.provenance)
require(0 <= rect.x <= rect.end[0] <= output.rectangle.width and
0 <= rect.y <= rect.end[1] <= output.rectangle.height, 'png-bounds')
return Rect(Space.GLOBAL, rect.x + output.rectangle.x, rect.y + output.rectangle.y,
rect.width, rect.height, rect.provenance)
def verify_paint(rect, output, bbox, count, clip=None):
expected = to_png(intersect(rect, clip) if clip is not None else rect, output)
actual = [] if bbox is None else list(bbox)
box=[expected.x, expected.y, *expected.end] if expected.width and expected.height else []
require(actual == box, 'paint-origin-intersection')
require(count == expected.width * expected.height, 'paint-area')
return expected
def popup(parent, configured, parent_xdg, expected_parent_xdg, ack, mapped):
require(parent.space is Space.GLOBAL and configured.space is Space.POPUP, 'popup-spaces')
same(parent.provenance, configured.provenance)
require(parent_xdg == expected_parent_xdg and expected_parent_xdg > 0, 'xdg-parent')
require(0 < ack <= 0xffffffff, 'popup-ack')
require(mapped is True, 'popup-map')
return Rect(Space.GLOBAL, parent.x + configured.x, parent.y + configured.y,
configured.width, configured.height, parent.provenance)
def crossing(rect, boundary_x):
require(rect.space is Space.GLOBAL, 'crossing-space')
return 'EXERCISED' if rect.x < boundary_x < rect.end[0] else 'NOT EXERCISED'
def local_clip_to_global(local, origin, provenance):
require(local.space is Space.CONTENT, 'clip-local-space')
same(local.provenance, provenance)
return Rect(Space.VIEWPORT, local.x + origin[0], local.y + origin[1],
local.width, local.height, provenance)
def pointer_local(xy, rect, actual):
require(rect.space is Space.GLOBAL and rect.contains(xy), 'pointer-global-bounds')
require(list(actual) == [xy[0] - rect.x, xy[1] - rect.y], 'pointer-local-correlation')
return list(actual)
def fresh_chain(old, new):
require(old['owner'] != new['owner'] and old['pid'] != new['pid'], 'fresh-E2-identity')
require(len(set(old['serials'] + new['serials'])) == 4 and
max(old['serials']) < min(new['serials']), 'fresh-E2-serials')
def fresh_standard(mosaic, standard):
require(mosaic.provenance.owner == standard.provenance.owner and
mosaic.provenance.workspace == standard.provenance.workspace, 'standard-owner')
require(mosaic.provenance.generation != standard.provenance.generation, 'fresh-standard-generation')
return standard
def constraints(flags, requested, configured):
require(type(flags) is int and 0 <= flags <= 63, 'positioner-flags')
require(flags != 0 or requested == configured, 'zero-flags-no-adjustment')
return {'flags': flags, 'request': requested, 'actual': configured, 'prediction_only': True}

View file

@ -0,0 +1,21 @@
"""Exact binary RGB masks with explicit output origin, clip and occlusion.
Screenshots prove color/visible geometry only, never native-buffer identity.
"""
from PIL import Image,ImageChops,ImageDraw
from model import *
COLORS=dict(TOPLEVEL=(35,68,204),POPUP_A=(224,192,32),POPUP_B=(32,208,160))
def color_geometry(im,target,rect,output,clip,occluders=()):
require(im.size==(output.rectangle.width,output.rectangle.height),'replay-output-size')
visible=intersect(rect,clip) if clip else rect
expected=Image.new('L',im.size,0);draw=ImageDraw.Draw(expected)
box=to_png(visible,output)
if box.width and box.height:draw.rectangle((box.x,box.y,box.end[0]-1,box.end[1]-1),fill=255)
for occ in occluders:
ob=to_png(occ,output)
if ob.width and ob.height:draw.rectangle((ob.x,ob.y,ob.end[0]-1,ob.end[1]-1),fill=0)
actual=Image.new('L',im.size);actual.putdata([255 if px==COLORS[target] else 0 for px in im.get_flattened_data()])
require(ImageChops.difference(expected,actual).getbbox() is None,'replay-paint-'+target)
return dict(count=sum(actual.histogram()[1:]),bbox=actual.getbbox())

View file

@ -0,0 +1,10 @@
"""Prospective fixed E matrix; no reruns or diagnostic tokens here."""
def out(name,logical,mode=None,scale=1,transform='normal'):
return dict(name=name,logical=logical,mode=mode or logical[2:],scale=scale,transform=transform)
H=[out('HEADLESS-1',[-800,0,800,600]),out('HEADLESS-2',[0,0,800,600])]
CAP={'E0-pristine':12,'E0-core':12,'E1':20,'E2-vertical':14,'E2-fractional':14,'E2-r90':14,'E3':18,'E4':14,'E5':12,'E6':10,'E7':14}
# WHY/CONTRACT/INVARIANT: maxima fixed before review and include two E1 chains.
RUNS={rid:dict(id=rid,binary='pristine' if rid=='E0-pristine' else 'core',mode='pristine' if rid.startswith('E0') else 'mosaic',outputs=H,campaign='FX' if rid=='E7' else rid,client='nested-client',placement='static_cross',axis=0,boundary=0,capture_limit=cap,action_limits=dict(configuration=60,mutation=24,move=20,click=12,grab=4,camera=6,chain=2)) for rid,cap in CAP.items()}
RUNS['E2-vertical'].update(outputs=[out('HEADLESS-1',[0,0,800,600]),out('HEADLESS-2',[0,600,800,600])],client='vertical-client',placement='v_both_cross',axis=1,boundary=600)
RUNS['E2-fractional']['outputs']=[out('HEADLESS-1',[-800,0,800,600],[1000,750],1.25),H[1]]
RUNS['E2-r90']['outputs']=[out('HEADLESS-1',[-800,0,800,600],[750,1000],1.25,'90'),H[1]]

View file

@ -0,0 +1,125 @@
"""Shared offline/live predicates: facts precede scientific classification.
CONTRACT: full RGB comparisons have no mask/tolerance; grabs require real logs.
INVARIANT: dismissal, client destruction, clipping and layout remain distinct.
"""
import re
from model import Rect,Space,Provenance,Output,popup,crossing,local_clip_to_global,require
from pixels import color_geometry,COLORS
from PIL import Image,ImageChops
class Defect(RuntimeError):
"""A directly measured scientific contract failure, distinct from fixture ambiguity."""
def paint(im,target):
im=im.convert('RGB');assert im.size==(800,600)
mask=Image.new('L',im.size);mask.putdata([255 if p==COLORS[target] else 0 for p in im.get_flattened_data()])
return dict(count=sum(mask.histogram()[1:]),bbox=mask.getbbox())
def point(im,target,origin):
box=paint(im,target)['bbox'];assert box,('no measured paint',target)
# WHY: A may be occluded by B; bbox center alone is not a valid hit point.
x1,y1,x2,y2=box
for y in range(y1+10,y2-10,5):
for x in range(x1+10,x2-10,5):
if all(im.getpixel((xx,yy))==COLORS[target] for yy in range(y-8,y+9) for xx in range(x-8,x+9)):
return [x+origin,y]
raise AssertionError('no solid measured interior '+target)
def equal(a,b):
assert a.size==b.size==(800,600)
rows=[(i%800,i//800,x,y) for i,(x,y) in enumerate(zip(a.get_flattened_data(),b.get_flattened_data())) if x!=y]
return dict(equal=not rows,changed=len(rows),bbox=ImageChops.difference(a,b).getbbox(),pixels=rows)
def reference(a,b):
result=equal(a,b)
assert result['equal'] and all(c==(48,48,56) for c in a.get_flattened_data()),'unstable reference'
return result
def configured(text,name):
rows=re.findall(rf'{name} ROLE_CONFIGURE x=(-?\d+) y=(-?\d+) w=(\d+) h=(\d+)',text)
assert rows,(name,'no configure')
return list(map(int,rows[-1]))
def geometry(text,origin,provenance=None):
a=configured(text,'POPUP_A');b=configured(text,'POPUP_B')
require(provenance is not None,'popup-owner-provenance-required')
p=provenance
top=Rect(Space.GLOBAL,*origin,0,0,p)
ids={n:list(map(int,re.search(rf'{n} CREATE wl=(\d+) xdg=(\d+) role=(\d+)',text).groups())) for n in ('TOPLEVEL','POPUP_A','POPUP_B')}
result={}
parent=top
for key,name,parent_name,c in [('A','POPUP_A','TOPLEVEL',a),('B','POPUP_B','POPUP_A',b)]:
parent_id=int(re.search(rf'{name} CREATE .*parent_xdg=(\d+)',text)[1])
ack=int(re.findall(rf'{name} XDG_CONFIGURE serial=(\d+) ack',text)[-1])
parent=popup(parent,Rect(Space.POPUP,*c,p),parent_id,ids[parent_name][1],ack,name+' MAP ' in text)
result[key]=parent.values()
result.update(configured_A=a,configured_B=b,origin=origin,provenance=dict(owner=p.owner,workspace=p.workspace,generation=p.generation,evidence=p.evidence,state=p.state),
crossing={key:crossing(Rect(Space.GLOBAL,*result[key],p),0) for key in ('A','B')})
return result
def mapped(text):
for name in ['POPUP_A','POPUP_B']:
assert text.count(name+' MAP ')==1 and name+' POPUP_DONE ' not in text and name+' DESTROY ' not in text,(name,'not mapped')
def protocol(text):
assert not re.search(r'wl_display[^\n]*\.error\(|protocol error|invalid_popup_parent|not_the_topmost_popup',text,re.I),'protocol error'
mapped(text)
ids={}
for name in ['TOPLEVEL','POPUP_A','POPUP_B']:
m=re.search(rf'{name} CREATE wl=(\d+) xdg=(\d+) role=(\d+)',text);assert m
ids[name]=list(map(int,m.groups()))
assert len({n for v in ids.values() for n in v})==9,'object identities reused'
grabs=[]
for name,parent in [('POPUP_A','TOPLEVEL'),('POPUP_B','POPUP_A')]:
create=re.search(rf'{name} CREATE .*parent={parent} parent_xdg=(\d+)',text);assert create
assert int(create[1])==ids[parent][1], 'wrong XDG parent'
debug_parent=re.search(rf'xdg_surface#{ids[name][1]}\.get_popup\(new id xdg_popup#{ids[name][2]}, xdg_surface#{ids[parent][1]}, xdg_positioner#\d+\)',text)
require(debug_parent is not None,'debug-parent-request')
m=re.search(rf'{name} GRAB_REQUEST origin=wl_pointer.button target={parent} serial=(\d+) used=(\d+)',text);assert m
serial,used=map(int,m.groups());assert serial==used and 0<serial<=0xffffffff
button=f'POINTER_BUTTON target={parent} serial={serial} button=272 state=1'
assert text.index(button)<m.start(),'grab serial not from earlier button'
debug_button=re.search(rf'wl_pointer#\d+\.button\({serial}, \d+, 272, 1\)',text)
require(debug_button is not None and debug_button.start()<m.start(),'debug-real-button-serial')
role=text.index(name+' ROLE_CONFIGURE ');cfg=re.search(rf'{name} XDG_CONFIGURE serial=(\d+) ack',text);assert cfg
mp=text.index(name+' MAP ')
assert m.start()<role<cfg.start()<mp,'configure ACK map order'
popup_role=ids[name][2];xdg=ids[name][1];wl=ids[name][0]
# WAYLAND_DEBUG is independent corroboration, not just fixture claims.
request=re.search(rf'xdg_popup#{popup_role}\.grab\(wl_seat#\d+, {serial}\)',text);assert request
ack=re.search(rf'xdg_surface#{xdg}\.ack_configure\({cfg[1]}\)',text);assert ack
attach=re.search(rf'wl_surface#{wl}\.attach\(',text);assert attach
commits=list(re.finditer(rf'wl_surface#{wl}\.commit\(\)',text));assert len(commits)>=2
assert request.start()<commits[0].start()<role<ack.start()<attach.start()<commits[1].start()<mp
actual=configured(text,name)
require(re.search(rf'xdg_popup#{popup_role}\.configure\({actual[0]}, {actual[1]}, {actual[2]}, {actual[3]}\)',text) is not None,'debug-popup-configure')
require(re.search(rf'xdg_surface#{xdg}\.configure\({cfg[1]}\)',text) is not None,'debug-xdg-configure')
native_attach=re.search(rf'wl_surface#{wl}\.attach\(wl_buffer#(\d+), 0, 0\)',text[ack.start():mp])
require(native_attach is not None,'popup-native-attach')
native_buffer=int(native_attach[1])
native_create=re.search(rf'create_buffer\(new id wl_buffer#{native_buffer}, 0, {actual[2]}, {actual[3]}, {actual[2]*4}, 1\)',text[role:mp])
require(native_create is not None,'popup-native-configured-size')
if parent=='POPUP_A':assert text.index('POPUP_A MAP ')<text.index(button)
grabs.append(dict(popup=name,parent=parent,serial=serial,used=used,origin='wl_pointer.button',ids=ids[name],ack=int(cfg[1]),native_buffer=native_buffer,native_size=actual[2:]))
assert grabs[0]['serial']<grabs[1]['serial'], 'distinct ordered serials'
return dict(ids=ids,grabs=grabs,hierarchy='TOPLEVEL -> POPUP_A -> POPUP_B',status='PASS')
def pair(lines,target):
events=[]
for line in lines:
m=re.fullmatch(r'POINTER_BUTTON target=(\w+) serial=(\d+) button=(\d+) state=(\d+)',line);assert m
events.append([m[1],*map(int,m.groups()[1:])])
if len(events)!=2 or any(e[0]!=target for e in events) or [e[2:] for e in events]!=[[273,1],[273,0]]:
raise Defect('wrong actual button recipient/pair: '+str(events))
assert 0<events[0][1]<events[1][1]<=0xffffffff
return events
def dismissal(text,buttons):
if any('target=POPUP_A ' in b or 'target=POPUP_B ' in b for b in buttons):
raise Defect('invisible popup received actual button')
done=re.findall(r'(POPUP_[AB]) POPUP_DONE ',text)
destroyed=re.findall(r'(POPUP_[AB]) DESTROY ',text)
assert done==['POPUP_B','POPUP_A'] and destroyed==['POPUP_B','POPUP_A'],('dismissal order',done,destroyed)
return dict(done=done,destroyed=destroyed,invisible_buttons=buttons,status='PASS',meaning='outside dismissal distinct from invisible hit')

View file

@ -0,0 +1,16 @@
#!/usr/bin/env python3
"""C17 builds only; no Wayland connections or compositor launches."""
import json,subprocess
from pathlib import Path
from gate import BASE,LAB,sha,durable,clean_environment,need
def prepare():
dest=BASE/'clients';commands=[]
flags=subprocess.run(['pkg-config','--cflags','--libs','wayland-client'],capture_output=True,text=True,timeout=10,check=True).stdout.split()
for n,proto in [('nested-client','xdg-shell'),('vertical-client','xdg-shell'),('saved-client','xdg-shell'),('static-reference','xdg-shell'),('pointer-daemon','virtual-pointer'),('fx-client','xdg-shell')]:
argv=['cc','-std=c17','-O2','-Wall','-Wextra',str(dest/(n+'.c')),str(dest/(proto+'-protocol.c')),*([str(dest/'layer-shell-protocol.c')] if n=='fx-client' else []),'-o',str(dest/n),*flags]
r=subprocess.run(argv,env=clean_environment(),capture_output=True,text=True,timeout=30);commands.append(dict(argv=argv,exit=r.returncode,stdout=r.stdout,stderr=r.stderr));need(r.returncode==0,'client compile '+n)
for rid in __import__('plans').RUNS:(BASE/'runs'/rid).mkdir(parents=True,exist_ok=True)
durable(BASE/'compile.json',dict(commands=commands,files={str(p):sha(p) for p in dest.iterdir() if p.is_file()},runtime='NOT RUN'),True)
if __name__=='__main__':prepare()

View file

@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""WHY/CONTRACT/INVARIANT: reserve once, reviewed bytes only, no retry."""
import argparse,time,re
from gate import LAB,begin,durable,BASE,need
from plans import RUNS
from engine import Session
import cases,validators
from geometry import *
from ipc_predicates import ancestry
def normal_remaining(s):
for n in list(s.proc):
if n not in ('owner','compositor','pointer') and s.proc[n].poll() is None:s.close_client(n,'close')
def standard_return(s,state,baseline,p,g):
oid=state['owner']['id'];s.cmd(f'[con_id={oid}] layout splith',True);time.sleep(.4)
state=s.owner('owner');prot=validators.grab(s.log('owner'));need(prot['ids']==p['ids'],'live second chain preserved')
geo=cases.geometry(s.log('owner'),state['origin'],cases.Provenance(oid,state['workspace']['id'],'NOT OBSERVABLE','pointer+paint corroboration'))
images=s.pair_capture('standard-live');need(any(count(images[1],COLORS[x]) for x in ('POPUP_A','POPUP_B')),'standard spill NOT EXERCISED')
for target,key,cmd in [('POPUP_B','B','close_b'),('POPUP_A','A',None)]:
im=images[0];xy=solid_point(im,COLORS[target],s.outputs[0],s.frames['HEADLESS-1'],geo[key]);tails,buttons=s.click('owner',xy);s.delivery('owner',geo[key],xy,tails,buttons,target)
if cmd:s.client_cmd('owner',cmd,'COMMAND close_b DONE');time.sleep(.2);images=[s.capture('B-closed-A-survives','HEADLESS-1'),images[1]];need(count(images[0],COLORS['POPUP_A'])>0,'A not alive')
s.close_client('owner');final=s.pair_capture('standard-final');validators.rgb(baseline,final[1])
return 'PARTIALLY_FEASIBLE'
def horizontal(s):
cases.init_reference(s);state,baseline,p,g,left,right=cases.open_chain(s)
xy=solid_point(left,COLORS['POPUP_B'],s.outputs[0],s.frames['HEADLESS-1'],g['B']);tails,buttons=s.click('owner',xy);s.delivery('owner',g['B'],xy,tails,buttons,'POPUP_B')
forbidden=intersection(g['B'],s.outputs[1]['logical']);need(forbidden[2]>6 and forbidden[3]>6,'forbidden interior');point=[forbidden[0]+forbidden[2]//2,forbidden[1]+forbidden[3]//2]
tails,buttons=s.click('owner',point);proof=cases.dismissal(tails['owner'],buttons['owner']);s.save('dismissal',proof);s.close_client('owner');s.save('first-chain-protocol-log',dict(raw=s.log('owner')))
# Two independent real chains; no resurrected dismissed popup.
state,baseline2,p,g,left,right=cases.open_chain(s);return standard_return(s,state,baseline2,p,g)
def fullscreen(s):
# WHY/CONTRACT/INVARIANT: fullscreen the actual nonleaf parent containing owner.
cases.init_reference(s);s.launch_client('owner','nested-client','static_cross');oid=s.find('owner')['id']
s.cmd(f'[con_id={oid}] focus');s.cmd('splitv');s.launch_client('nested-filler','static-reference');s.cmd(f'[con_id={oid}] focus')
before=s.owner('owner');baseline=cases.stable_reference(s);s.cmd('focus parent');s.cmd('fullscreen enable',True);time.sleep(.4)
state=s.owner('owner');path=ancestry(state['snapshot']['tree'],oid);anc=[n for n in path[:-1] if n.get('fullscreen_mode')]
s.save('fullscreen-current-ancestor',dict(before=before,path=path,ancestor_ids=[n['id'] for n in anc]))
if not anc:raise ValueError('current fullscreen ancestor NOT EXERCISED')
image=s.capture('fullscreen-owner','HEADLESS-1');xy=solid_point(image,COLORS['TOPLEVEL'],s.outputs[0],s.frames['HEADLESS-1'])
tails,buttons=s.click('owner',xy,'left',True);s.wait(lambda:'POPUP_A MAP ' in s.log('owner'));time.sleep(.2)
image=s.capture('fullscreen-A','HEADLESS-1');xyA=solid_point(image,COLORS['POPUP_A'],s.outputs[0],s.frames['HEADLESS-1'])
tailsA,buttonsA=s.click('owner',xyA,'left',True);s.wait(lambda:'POPUP_B MAP ' in s.log('owner'));time.sleep(.2)
prot=validators.grab(s.log('owner'));geo=cases.geometry(s.log('owner'),state['origin'],cases.Provenance(oid,state['workspace']['id'],'NOT OBSERVABLE','fullscreen paint+pointer'))
s.delivery('owner',[*state['origin'],state['owner']['window_rect']['width'],state['owner']['window_rect']['height']],xy,tails,buttons,'TOPLEVEL',prot['grabs'][0]['serial'])
s.delivery('owner',geo['A'],xyA,tailsA,buttonsA,'POPUP_A',prot['grabs'][1]['serial'])
images=s.pair_capture('fullscreen-nested');point=solid_point(images[0],COLORS['POPUP_B'],s.outputs[0],s.frames['HEADLESS-1'],geo['B']);tails,buttons=s.click('owner',point);s.delivery('owner',geo['B'],point,tails,buttons,'POPUP_B')
s.cmd(f'[con_id={anc[-1]["id"]}] fullscreen disable',True);time.sleep(.4);s.pair_capture('fullscreen-left')
s.cmd(f'[con_id={oid}] layout splith',True);time.sleep(.4);s.pair_capture('fullscreen-standard');s.close_client('owner');final=s.pair_capture('fullscreen-final');validators.rgb(baseline,final[1])
s.save('fullscreen-policy',dict(status='INCONCLUSIVE',reason='current fullscreen ancestor and nested grabs sampled; cross-output exemption policy has no comparable fullscreen standard control',atomic_generation='NOT OBSERVABLE'))
return 'INCONCLUSIVE'
COLORS=cases.COLORS
def main():
p=argparse.ArgumentParser();p.add_argument('run_id',choices=RUNS);rid=p.parse_args().run_id
auth=begin(rid);s=None;status='INCONCLUSIVE';failure=None
try:
s=Session(RUNS[rid],auth);s.start()
if rid=='E1':status=horizontal(s)
elif rid=='E3':status=cases.dynamic_campaign(s)
elif rid=='E4':status=fullscreen(s)
elif rid=='E5':status=cases.hidden_campaign(s)
elif rid=='E6':
from saved import run
status=run(s)
elif rid=='E7':
from fx import effects_campaign
status=effects_campaign(s)
elif rid.startswith('E0'):
cases.init_reference(s);state,baseline,p,g,left,right=cases.open_chain(s);status=standard_return(s,state,baseline,p,g)
else:status=cases.popup_campaign(s)
normal_remaining(s)
except Exception as e:failure=f'{type(e).__name__}: {e}'
finally:
if s is not None:status=s.cleanup(status,failure)
print(status,failure or '');return 0 if status in ('PASS','PARTIALLY_FEASIBLE') else 1
if __name__=='__main__':raise SystemExit(main())

View file

@ -0,0 +1,30 @@
"""One actual held→natural-timeout episode, exact binary, EXPERIMENTAL."""
import re,time
from pathlib import Path
import cases,engine
from geometry import need
from saved_validators import pending_geometry,held_configure,saved_identity,natural_retirement,rgb_exact,timeout_line
def run(s):
cases.init_reference(s,2);s.launch_client('owner','saved-client');s.cmd(f'[con_id={s.find("owner")["id"]}] focus');state=s.calibrate('owner');baseline=cases.stable_reference(s)
initial=s.snapshot('initial-applied');before=s.log('owner');acks=re.findall(r'ACK ns=\d+ serial=(\d+)',before);need(acks,'initial actual ACK');prior=int(acks[-1])
argv=Path(f'/proc/{s.comp.pid}/cmdline').read_bytes().split(b'\0');need(b'-Dtxn-timeout=5000' in argv,'actual startup timeout5000')
s.client_cmd('owner','hold_next_configure','COMMAND hold_next_configure DONE');cut=len(s.log('owner'));ccut=len(s.log('compositor'));s.cmd('workspace 1');request=time.monotonic_ns();s.cmd('gaps inner current set 30',True)
pending=s.snapshot('saved-requested-pending');s.wait(lambda:'HELD ' in s.log('owner')[cut:]);proof=pending_geometry(initial,pending,state['owner']['id'],state['workspace']['id'],before,s.log('owner')[cut:],prior,request)
held_start=time.monotonic_ns();held=s.pair_capture('held');held_end=time.monotonic_ns();need(held_start>proof['held']['ns'] and held_end<request+4_700_000_000,'held capture before expiration')
commits=re.findall(r'COMMIT ns=\d+ serial=\d+ size=\d+x\d+ color=([0-9a-f]+)',before);need(commits,'actual retained SHM color');value=int(commits[-1],16);color=((value>>16)&255,(value>>8)&255,value&255)
rgb_exact(baseline,held[1]);need(any(p==color for p in held[0].getdata()),'owner retained SHM color visible H1')
native=None
if False:native=saved_identity(s.log('compositor'),state['owner']['id'],request)
s.wait(lambda:re.search(r'Transaction .*timed out \(\d*[1-9]\d* waiting\)',s.log('compositor')[ccut:]),seconds=6)
observed=time.monotonic_ns();need(s.proc['owner'].poll() is None,'owner alive natural timeout');held_configure(before,s.log('owner')[cut:],prior)
timeout=timeout_line(s.log('compositor')[ccut:],-1,True,request,observed,2**63)
after_start=time.monotonic_ns();after=s.pair_capture('natural-timeout');rgb_exact(baseline,after[1]);s.client_cmd('owner','release_configure','COMMAND release_configure DONE');s.wait(lambda:re.search(r'ACK ns=\d+ serial='+str(proof['held']['serial'])+r'\b',s.log('owner')[cut:]));live=s.pair_capture('restored-live');rgb_exact(baseline,live[1])
if native:
native=saved_identity(s.log('compositor'),state['owner']['id'],request,True)
retirement=natural_retirement(s.log('compositor'),native,after_start,time.monotonic_ns(),True,before,s.log('owner')[cut:],prior)
else:retirement={'status':'NOT OBSERVABLE; CORE has no native saved identity'}
# WHY CONTRACT INVARIANT: CPU destruction is measured separately from GPU
# completion, and process exit0 alone never qualifies Wayland cleanup.
s.save('saved-evidence',dict(request_ns=request,held_start_ns=held_start,held_end_ns=held_end,timeout_observed_ns=observed,timeout_native=timeout,geometry=proof,native=native,retirement=retirement,held_wire=held_configure(before,s.log('owner')[cut:],prior),gpu_completion='NOT PROVEN',native_generation='NOT PROVEN',binary=s.auth))
s.close_client('owner','close');final=s.pair_capture('final');rgb_exact(baseline,final[1]);return 'PASS'

View file

@ -0,0 +1,42 @@
"""Local pure CORE saved evidence; no diagnostic/historical import."""
import re
from geometry import need,intersection
from ipc_predicates import workspace,nodes
from PIL import ImageChops
def held_configure(before,after,prior_serial):
need(type(prior_serial)is int and prior_serial>0,'prior serial required')
need(re.search(rf'ACK ns=\d+ serial={prior_serial}\b',before) and re.search(rf'ack_configure\({prior_serial}\)',before),'initial actual ACK required')
need('wl_shm_pool#' in before and '.create_buffer(' in before and 'COMMIT ns=' in before,'real initial SHM required')
held=re.findall(r'HELD ns=(\d+) serial=(\d+)',after);need(len(held)==1,'unique real HELD configure')
ns,serial=map(int,held[0]);need(serial!=prior_serial,'new held serial')
need(re.search(rf'xdg_surface#\d+\.configure\({serial}\)',after),'received actual configure wire')
need(re.search(rf'XDG_CONFIGURE ns=\d+ serial={serial}\b.*hold=1',after),'client received hold state')
old=re.findall(r'ACK ns=\d+ serial='+str(prior_serial)+r' size=(\d+)x(\d+)',before);new=re.findall(r'XDG_CONFIGURE ns=\d+ serial='+str(serial)+r' size=(\d+)x(\d+)',after)
need(old and new and old[-1]!=new[-1],'new geometry configure required')
need(not re.search(rf'ack_configure\({serial}\)|ACK ns=\d+ serial={serial}\b',after),'held serial must remain unACKed')
return dict(ns=ns,serial=serial,prior_serial=prior_serial,initial_size=list(map(int,old[-1])),requested_size=list(map(int,new[-1])),status='HELD_WIRE_VALIDATED',native_saved_identity='NOT OBSERVABLE')
def pending_geometry(initial,pending,owner_id,workspace_id,before,after,prior,request_ns):
oldws=workspace(initial['tree'],'1');ws=workspace(pending['tree'],'1')
need(oldws['id']==ws['id']==workspace_id and ws['output']==oldws['output']=='HEADLESS-1' and ws['layout']==oldws['layout']=='mosaic','same requested workspace')
need('gaps' not in ws,'IPC gaps field is absent in audited source')
old=next(n for n in nodes(oldws) if n['id']==owner_id)
owners=[n for n in nodes(ws) if n['id']==owner_id];need(len(owners)==1,'same owner ancestry')
target=owners[0];need(target['pid']==old['pid'] and target['app_id']==old['app_id']=='org.mosaic.SavedBufferProbe','same actual client')
a=oldws['rect'];b=ws['rect'];need([b[k] for k in ('x','y','width','height')]==[a['x']+30,a['y']+30,a['width']-60,a['height']-60],'requested workspace rect changed by runtime gaps30')
proof=held_configure(before,after,prior)
need(proof['ns']>request_ns and proof['requested_size']==[target['window_rect']['width'],target['window_rect']['height']],'held wire after command matches requested dimensions')
need(proof['initial_size']==[old['window_rect']['width'],old['window_rect']['height']],'initial applied dimensions')
return dict(held=proof,workspace=ws,owner=target)
def rgb_exact(a,b):
need(a.mode==b.mode=='RGB' and a.size==b.size,'whole RGB contract')
need(ImageChops.difference(a,b).getbbox() is None,'whole RGB equal; no masked channel/ROI');return True
def timeout_line(text,activation_line,client_alive,request_ns,observed_ns,cleanup_ns):
need(client_alive and request_ns+4_700_000_000<=observed_ns<cleanup_ns,'natural timeout window/client alive')
matches=[(i,l) for i,l in enumerate(text.splitlines()) if i>activation_line and re.search(r'Transaction .*timed out \(\d*[1-9]\d* waiting\)',l)]
need(len(matches)==1,'native natural timeout required');return matches[0]
def saved_identity(*args,**kwargs):raise ValueError("CORE native saved identity NOT OBSERVABLE")
def natural_retirement(*args,**kwargs):raise ValueError("CORE retirement NOT OBSERVABLE")

View file

@ -0,0 +1,30 @@
#!/usr/bin/env python3
"""Seal exact final closure; independent review still required afterwards."""
import argparse,json,shutil,sys
from pathlib import Path
from gate import *
def seal(identity):
identities=json.loads(Path(identity).read_text());need(set(identities)=={'core','pristine'},'both exact identities required')
files={str(p):sha(p) for p in BASE.rglob('*') if p.is_file() and p.suffix in ('.py','.c','.h','.xml','.txt','.json','.conf','.md') and p.name not in ('review.json','pre-run-manifest.json') and 'runs' not in p.parts}
files.update(json.loads((BASE/'compile.json').read_text())['files'])
source=LAB/'audit/candidate-source-freeze.json';files[str(source)]=sha(source)
for p,h in json.loads(source.read_text())['files'].items():
path=LAB/p;need(sha(path)==h,'source freeze changed');files[str(path)]=h
for name,a in identities.items():
need(name in ('core','pristine'),'identity name');need(sha(a['binary'])==a['binary_sha256'],'exact binary');files[a['binary']]=a['binary_sha256']
need(a['wlroots']==str(WL) and a['wlroots_sha256']==WL_SHA,'required wlroots');files[str(WL)]=WL_SHA
if name=='pristine':
need(a.get('source_parent')=='fd71a6bdc061bd633b488ae7b83e8a6981d22f86','pristine parent');need(a.get('pristine_scene') and a.get('source_proof'),'pristine comparability proof required');files[a['pristine_scene']]=a['pristine_scene_sha256'];proof=json.loads(Path(a['source_proof']).read_text());need(proof.get('status')=='PASS' and proof.get('pinned_parent')==a['source_parent'],'pristine source proof unproven');files[a['source_proof']]=sha(a['source_proof'])
for row in proof['files']:
path=Path(proof['source'])/row['path'];need(row['match'] and sha(path)==row['expected'],'pristine source changed');files[str(path)]=row['expected']
for p in (LAB/'build-audit').glob('*.json'):files[str(p)]=sha(p)
for t in ('swaymsg','grim','swaybg'):
path=Path(shutil.which(t)).resolve();files[str(path)]=sha(path)
import PIL
for p in Path(PIL.__file__).parent.rglob('*'):
if p.is_file() and p.suffix in ('.py','.so'):files[str(p)]=sha(p)
files[str(Path(sys.executable).resolve())]=sha(Path(sys.executable).resolve())
durable(BASE/'pre-run-manifest.json',dict(ticket=TICKET,lab=str(LAB),status='SEALED_REVIEW_PENDING',identities=identities,files=files),True)
print(sha(BASE/'pre-run-manifest.json'))
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('identities');seal(p.parse_args().identities)

View file

@ -0,0 +1,49 @@
"""New bounded synthetic delta tests; no native runtime qualification."""
import ast
import inspect
import unittest
from types import SimpleNamespace
from engine import Session
from fx import effects_verdict, effects_campaign
class OwnerDelta(unittest.TestCase):
def state(self,layout='mosaic',floating=False,group_fullscreen=0,owner_fullscreen=0,initial='mosaic'):
owner=dict(id=3,type='con',pid=42,rect=dict(x=120,y=30,width=200,height=100),window_rect=dict(x=2,y=3,width=196,height=97),deco_rect=dict(height=5),fullscreen_mode=owner_fullscreen)
group=dict(id=2,type='con',nodes=[owner],fullscreen_mode=group_fullscreen)
ws=dict(id=1,type='workspace',name='1',layout=layout,nodes=[] if floating else [group],floating_nodes=[group] if floating else [])
if layout=='mosaic':ws['mosaic']=dict(rows=[dict(scroll_x=70)])
s=Session.__new__(Session);s.spec=dict(mode=initial);s.proc={'owner':SimpleNamespace(pid=42)}
s.snapshot=lambda tag:dict(tree=ws);s.save=lambda *args:None
# No Session initialization, IPC, processes, captures or ledger writes.
return s.owner('owner')
def check(self,state,x):
self.assertEqual(state['origin'],[x,33]);self.assertEqual(state['scene'],[x-2,25,200,105])
self.assertTrue(state['geometry_status'].startswith('IPC_PENDING_CANDIDATE'))
self.assertIn('fresh pointer-local corroboration',state['evidence'])
def test_standard_return_missing_mosaic_key(self):self.check(self.state(layout='splith'),122)
def test_current_mosaic_overrides_initial_standard(self):self.check(self.state(initial='standard'),52)
def test_current_mosaic_tiled_camera(self):self.check(self.state(),52)
def test_floating_group_exempt(self):self.check(self.state(floating=True),122)
def test_current_fullscreen_group_exempt(self):self.check(self.state(group_fullscreen=1),122)
def test_current_fullscreen_owner_exempt(self):self.check(self.state(owner_fullscreen=1),122)
class FXDelta(unittest.TestCase):
def test_shadow_exact_false_nonpass_after_all_closes(self):
closed=[];closes={}
for name in ('glass','background','filler0'):
closed.append(name);closes[name]=dict(status='PASS')
self.assertEqual(closed,['glass','background','filler0'])
spill=dict(exact=False,pixels=1,max_delta=1)
self.assertEqual(effects_verdict(closes,dict(ABA_byte_exact=True),dict(exact=True),True,dict(spill=spill)),'INCONCLUSIVE')
self.assertEqual(spill,dict(exact=False,pixels=1,max_delta=1))
def test_exact_shadow_preserves_bounded_verdict(self):
self.assertEqual(effects_verdict({n:dict(status='PASS') for n in ('glass','background','filler0')},dict(ABA_byte_exact=True),dict(exact=True),True,dict(spill=dict(exact=True))),'PARTIALLY_FEASIBLE')
def test_real_campaign_verdict_after_close_and_final_capture(self):
tree=ast.parse(inspect.getsource(effects_campaign))
calls=[n for n in ast.walk(tree) if isinstance(n,ast.Call)]
verdict=next(n.lineno for n in calls if isinstance(n.func,ast.Name) and n.func.id=='effects_verdict')
close=next(n.lineno for n in calls if isinstance(n.func,ast.Attribute) and n.func.attr=='record' and n.args and isinstance(n.args[0],ast.Constant) and n.args[0].value=='fx-normal-close')
final=next(n.lineno for n in calls if isinstance(n.func,ast.Attribute) and n.func.attr=='pair_capture' and n.args[0].value=='final')
self.assertGreater(verdict,close);self.assertGreater(verdict,final)
if __name__=='__main__':unittest.main(verbosity=2)

View file

@ -0,0 +1,61 @@
"""R2 nullable IPC adapter regressions; offline, no native qualification."""
import copy
import json
from pathlib import Path
from types import SimpleNamespace
import unittest
from unittest.mock import patch
from engine import Session
from ipc_predicates import nodes, ancestry
from geometry import assigned_origin, exterior_ipc
BASE=Path(__file__).resolve().parent
class NullableAncestor(unittest.TestCase):
def call_owner(self,snapshot):
owners=[n for n in nodes(snapshot['tree']) if n.get('app_id')=='org.mosaic.NestedGrabProbe']
self.assertEqual(len(owners),1)
owner=owners[0]
session=Session.__new__(Session)
session.proc={'owner':SimpleNamespace(pid=owner['pid'])}
session.snapshot=lambda tag:snapshot
session.save=lambda *args:None
with patch('engine.subprocess.Popen',side_effect=AssertionError('offline only')),patch('engine.subprocess.run',side_effect=AssertionError('offline only')):
result=session.owner('owner')
self.assertTrue(result['geometry_status'].startswith('IPC_PENDING_CANDIDATE'))
self.assertIn('fresh pointer-local corroboration',result['evidence'])
return owner,result
def test_retained_null_ancestors_actual_owner(self):
for case in ('E0-pristine','E0-core','E2-vertical'):
with self.subTest(case=case):
snap=json.loads((BASE/'runs'/case/'run/owner-owner-snapshot.json').read_text())
owner=next(n for n in nodes(snap['tree']) if n.get('app_id')=='org.mosaic.NestedGrabProbe')
path=ancestry(snap['tree'],owner['id'])
for kind in ('root','output','workspace'):
rows=[n for n in path if n['type']==kind]
self.assertEqual(len(rows),1)
self.assertIn('floating',rows[0]);self.assertIsNone(rows[0]['floating'])
owner,result=self.call_owner(snap)
origin=assigned_origin(owner);scene=exterior_ipc(owner)
ws=result['workspace']
if ws.get('layout')=='mosaic':
scroll=int(ws['mosaic']['rows'][0]['scroll_x']);origin[0]-=scroll;scene[0]-=scroll
self.assertEqual(result['origin'],origin);self.assertEqual(result['scene'],scene)
def test_valid_strings_and_floating_membership_preserved(self):
original=json.loads((BASE/'runs/E0-core/run/owner-owner-snapshot.json').read_text())
for flag,membership in (('auto_on',False),('user_on',False),('auto_off',True),('user_off',False)):
with self.subTest(flag=flag,membership=membership):
snap=copy.deepcopy(original)
owner=next(n for n in nodes(snap['tree']) if n.get('app_id')=='org.mosaic.NestedGrabProbe')
path=ancestry(snap['tree'],owner['id']);owner['floating']=flag
ws=next(n for n in path if n['type']=='workspace')
ws['layout']='mosaic';ws['mosaic']={'rows':[{'scroll_x':70}]}
if membership:
parent=path[-2];parent['nodes'].remove(owner);parent['floating_nodes'].append(owner)
owner,result=self.call_owner(snap);origin=assigned_origin(owner)
if flag.endswith('off') and not membership:origin[0]-=70
self.assertEqual(result['origin'],origin)
if __name__=='__main__':unittest.main(verbosity=2)

View file

@ -0,0 +1,67 @@
"""Mutation tests on modules actually imported by run/engine/cases."""
import copy,json,tempfile,unittest
from pathlib import Path
from unittest.mock import patch
from PIL import Image
import run,engine,cases,gate,validators as v
from plans import RUNS
class Tests(unittest.TestCase):
def test_imported_identity(self):
self.assertIs(run.validators,v);self.assertIs(engine.validators,v);self.assertIs(cases.validators,v)
def test_source_compile(self):
for p in gate.BASE.glob('*.py'):compile(p.read_text(),str(p),'exec')
def test_whole_h2(self):
a=Image.new('RGB',(800,600),(48,48,56));v.rgb(a,a)
b=a.copy();b.putpixel((799,599),(48,48,55))
with self.assertRaises(ValueError):v.rgb(a,b)
with self.assertRaises(ValueError):v.rgb(a,a,True)
def test_wrong_output(self):
spec=RUNS['E2-r90']['outputs'];rows=[dict(active=True,name=o['name'],rect=dict(zip(('x','y','width','height'),o['logical'])),scale=o['scale'],transform=o['transform'],current_mode=dict(zip(('width','height'),o['mode']))) for o in spec]
v.expected_outputs(rows,spec);rows[0]['name']='HEADLESS-3'
with self.assertRaises(ValueError):v.expected_outputs(rows,spec)
def test_owner(self):
p=[dict(type='workspace',id=4)];v.owner(dict(id=5),5,4,p)
with self.assertRaises(ValueError):v.owner(dict(id=6),5,4,p)
with self.assertRaises(ValueError):v.owner(dict(id=5),5,8,p)
def test_real_grab_mutations(self):
raw=(gate.BASE/'wire-fixture.txt').read_text();p=v.grab(raw);self.assertEqual(len(p['grabs']),2)
for bad in (raw.replace('origin=wl_pointer.button','origin=fake'),raw.replace(' used='+str(p['grabs'][0]['serial']),' used=999999'),raw.replace('parent_xdg='+str(p['ids']['TOPLEVEL'][1]),'parent_xdg=999999'),raw.replace('wl_pointer#','fake_pointer#')):
with self.assertRaises((ValueError,AssertionError)):v.grab(bad)
def test_ambiguous_window(self):
v.window(1,4,[2])
for bad in ([0],[2,3],[]):
with self.assertRaises(ValueError):v.window(1,4,bad)
def test_no_crossing(self):
v.crosses([-10,0,20,10],0,0)
with self.assertRaises(ValueError):v.crosses([0,0,20,10],0,0)
def test_shadow(self):
a=Image.new('RGB',(3,3));b=a.copy();b.putpixel((0,0),(1,0,0));v.shadow(a,b)
with self.assertRaises(ValueError):v.shadow(a,a)
def test_wrong_binary_saved(self):
with self.assertRaises(ValueError):v.saved_identity(dict(binary_sha256='old',native=True),'core')
with self.assertRaises(ValueError):v.saved_identity(dict(binary_sha256='core',native=False),'core')
def test_pending_not_applied(self):
v.applied([1,2],[1,2],[1,2],'applied')
with self.assertRaises(ValueError):v.applied([1,2],[1,2],[1,2],'pending')
with self.assertRaises(ValueError):v.applied([1,2],[1,2],[2,2],'applied')
def test_code0_error_nonzero(self):
v.client_exit('EXIT error=0',0)
for log,code in [('EXIT error=7',0),('EXIT error=0',2),('',0)]:
with self.assertRaises(ValueError):v.client_exit(log,code)
def test_stale_review(self):
with tempfile.TemporaryDirectory() as d:
p=Path(d)/'manifest';p.write_text('{}')
with self.assertRaises(ValueError):gate.validate_review(p,dict(decision='ACCEPTED',independent=True,manifest_sha256='stale'))
def test_consumed_budget_no_refund(self):
with tempfile.TemporaryDirectory() as d:
p=Path(d)/'budget';gate.durable(p,dict(ticket=gate.TICKET,limits={},runs=[dict(id='E1')],builds=[],stages=[]))
def reject(b):gate.need(not any(r['id']=='E1' for r in b['runs']),'consumed')
before=p.read_bytes()
with self.assertRaises(ValueError):gate.transact(reject,p)
self.assertEqual(p.read_bytes(),before)
p.write_text(json.dumps(dict(ticket='historical',runs=[])))
with self.assertRaises(ValueError):gate.transact(lambda b:None,p)
def test_limits(self):
self.assertEqual(len(RUNS),11);self.assertEqual(sum(s['capture_limit'] for s in RUNS.values()),154)
self.assertTrue(all(s['capture_limit']<=20 for s in RUNS.values()))
if __name__=='__main__':unittest.main(verbosity=2)

View file

@ -0,0 +1,78 @@
"""R2B nullable/workspace-marker IPC adapter regressions; offline, no native qualification."""
import copy
import json
from pathlib import Path
from types import SimpleNamespace
import unittest
from unittest.mock import patch
from engine import Session
from ipc_predicates import nodes, ancestry
from geometry import assigned_origin, exterior_ipc
BASE=Path(__file__).resolve().parent
class NullableAncestor(unittest.TestCase):
def call_owner(self,snapshot):
owners=[n for n in nodes(snapshot['tree']) if n.get('app_id')=='org.mosaic.NestedGrabProbe']
self.assertEqual(len(owners),1)
owner=owners[0]
session=Session.__new__(Session)
session.proc={'owner':SimpleNamespace(pid=owner['pid'])}
session.snapshot=lambda tag:snapshot
session.save=lambda *args:None
with patch('engine.subprocess.Popen',side_effect=AssertionError('offline only')),patch('engine.subprocess.run',side_effect=AssertionError('offline only')):
result=session.owner('owner')
self.assertTrue(result['geometry_status'].startswith('IPC_PENDING_CANDIDATE'))
self.assertIn('fresh pointer-local corroboration',result['evidence'])
return owner,result
def test_retained_null_ancestors_actual_owner(self):
for case in ('E0-pristine','E0-core','E2-vertical'):
with self.subTest(case=case):
snap=json.loads((BASE/'runs'/case/'run/owner-owner-snapshot.json').read_text())
owner=next(n for n in nodes(snap['tree']) if n.get('app_id')=='org.mosaic.NestedGrabProbe')
path=ancestry(snap['tree'],owner['id'])
for kind in ('root','output','workspace'):
rows=[n for n in path if n['type']==kind]
self.assertEqual(len(rows),1)
self.assertIn('floating',rows[0]);self.assertIsNone(rows[0]['floating'])
owner,result=self.call_owner(snap)
origin=assigned_origin(owner);scene=exterior_ipc(owner)
ws=result['workspace']
if ws.get('layout')=='mosaic':
scroll=int(ws['mosaic']['rows'][0]['scroll_x']);origin[0]-=scroll;scene[0]-=scroll
self.assertEqual(result['origin'],origin);self.assertEqual(result['scene'],scene)
def test_valid_strings_and_floating_membership_preserved(self):
original=json.loads((BASE/'runs/E0-core/run/owner-owner-snapshot.json').read_text())
for flag,membership in (('auto_on',False),('user_on',False),('auto_off',True),('user_off',False)):
with self.subTest(flag=flag,membership=membership):
snap=copy.deepcopy(original)
owner=next(n for n in nodes(snap['tree']) if n.get('app_id')=='org.mosaic.NestedGrabProbe')
path=ancestry(snap['tree'],owner['id']);owner['floating']=flag
ws=next(n for n in path if n['type']=='workspace')
ws['layout']='mosaic';ws['mosaic']={'rows':[{'scroll_x':70}]}
if membership:
parent=path[-2];parent['nodes'].remove(owner);parent['floating_nodes'].append(owner)
owner,result=self.call_owner(snap);origin=assigned_origin(owner)
if flag.endswith('off') and not membership:origin[0]-=70
self.assertEqual(result['origin'],origin)
def test_true_fullscreen_container_ancestry(self):
original=json.loads((BASE/'runs/E0-core/run/owner-owner-snapshot.json').read_text())
for level in ('leaf','parent'):
with self.subTest(level=level):
snap=copy.deepcopy(original)
owner=next(n for n in nodes(snap['tree']) if n.get('app_id')=='org.mosaic.NestedGrabProbe')
path=ancestry(snap['tree'],owner['id']);ws=next(n for n in path if n['type']=='workspace')
ws['layout']='mosaic';ws['mosaic']={'rows':[{'scroll_x':70}]}
owner['floating']='auto_off'
if level=='leaf':owner['fullscreen_mode']=1
else:
parent=path[-2];index=parent['nodes'].index(owner)
parent['nodes'][index]={'id':987654,'type':'con','fullscreen_mode':1,'floating':None,'nodes':[owner],'floating_nodes':[]}
owner,result=self.call_owner(snap)
self.assertEqual(result['origin'],assigned_origin(owner))
self.assertEqual(result['scene'],exterior_ipc(owner))
if __name__=='__main__':unittest.main(verbosity=2)

View file

@ -0,0 +1,23 @@
"""Imported live/offline scientific predicates, not a parallel test model."""
import re
from geometry import need,exact,crossing,expected_outputs
from predicates import protocol
def client_exit(log,code):
need(code==0,'client nonzero');errors=re.findall(r'EXIT[^\n]*error=(\d+)',log)
need(bool(errors) and all(e=='0' for e in errors),'client error/nonobservable exit')
need(not re.search(r'protocol error|wl_display[^\n]*\.error\(',log,re.I),'wire error')
def window(start,end,events):
need(start<end and len(events)==1 and start<=events[0]<=end,'ambiguous event window')
def owner(node,owner_id,workspace_id,path):
need(node['id']==owner_id and [n['id'] for n in path if n['type']=='workspace']==[workspace_id],'wrong owner ancestry')
def rgb(reference,actual,masked=False):
need(not masked,'H2 occlusion/mask forbidden');return exact(reference,actual)
def grab(log):return protocol(log)
def applied(geometry,paint_origin,pointer_origin,state):
need(state=='applied' and geometry==paint_origin==pointer_origin,'pending/uncorroborated origin')
def crosses(rect,axis,boundary):need(crossing(rect,axis,boundary),'crossing not exercised')
def shadow(before,after):need(before.tobytes()!=after.tobytes(),'shadow not exercised')
def saved_identity(identity,binary_sha):
need(identity['binary_sha256']==binary_sha,'wrong binary saved identity')
need(identity.get('native') is True,'saved identity not observable')

View file

@ -0,0 +1,221 @@
[22:46:38.282527] {Default Queue} -> wl_display#1.get_registry(new id wl_registry#2)
[22:46:38.282546] {Default Queue} -> wl_display#1.sync(new id wl_callback#3)
[22:46:38.282664] {Display Queue} wl_display#1.delete_id(3)
[22:46:38.282677] {Default Queue} wl_registry#2.global(1, "wl_fixes", 1)
[22:46:38.282685] {Default Queue} wl_registry#2.global(2, "wl_shm", 2)
[22:46:38.282692] {Default Queue} -> wl_registry#2.bind(2, "wl_shm", 1, new id [unknown]#4)
[22:46:38.282699] {Default Queue} wl_registry#2.global(3, "zwp_linux_dmabuf_v1", 5)
[22:46:38.282703] {Default Queue} wl_registry#2.global(4, "wp_linux_drm_syncobj_manager_v1", 1)
[22:46:38.282708] {Default Queue} wl_registry#2.global(5, "wl_compositor", 6)
[22:46:38.282712] {Default Queue} -> wl_registry#2.bind(5, "wl_compositor", 4, new id [unknown]#5)
[22:46:38.282716] {Default Queue} wl_registry#2.global(6, "wl_subcompositor", 1)
[22:46:38.282720] {Default Queue} wl_registry#2.global(7, "wl_data_device_manager", 3)
[22:46:38.282724] {Default Queue} wl_registry#2.global(8, "zwlr_gamma_control_manager_v1", 1)
[22:46:38.282728] {Default Queue} wl_registry#2.global(9, "zxdg_output_manager_v1", 3)
[22:46:38.282732] {Default Queue} wl_registry#2.global(10, "ext_idle_notifier_v1", 2)
[22:46:38.282736] {Default Queue} wl_registry#2.global(11, "zwp_idle_inhibit_manager_v1", 1)
[22:46:38.282740] {Default Queue} wl_registry#2.global(12, "zwlr_layer_shell_v1", 5)
[22:46:38.282744] {Default Queue} wl_registry#2.global(13, "xdg_wm_base", 5)
[22:46:38.282751] {Default Queue} -> wl_registry#2.bind(13, "xdg_wm_base", 3, new id [unknown]#6)
[22:46:38.282756] {Default Queue} wl_registry#2.global(14, "zwp_tablet_manager_v2", 1)
[22:46:38.282760] {Default Queue} wl_registry#2.global(15, "org_kde_kwin_server_decoration_manager", 1)
[22:46:38.282764] {Default Queue} wl_registry#2.global(16, "zxdg_decoration_manager_v1", 1)
[22:46:38.282768] {Default Queue} wl_registry#2.global(17, "zwp_relative_pointer_manager_v1", 1)
[22:46:38.282772] {Default Queue} wl_registry#2.global(18, "zwp_pointer_constraints_v1", 1)
[22:46:38.282776] {Default Queue} wl_registry#2.global(19, "wp_presentation", 2)
[22:46:38.282780] {Default Queue} wl_registry#2.global(20, "wp_alpha_modifier_v1", 1)
[22:46:38.282784] {Default Queue} wl_registry#2.global(21, "zwlr_output_manager_v1", 4)
[22:46:38.282788] {Default Queue} wl_registry#2.global(22, "zwlr_output_power_manager_v1", 1)
[22:46:38.282792] {Default Queue} wl_registry#2.global(23, "zwp_input_method_manager_v2", 1)
[22:46:38.282796] {Default Queue} wl_registry#2.global(24, "zwp_text_input_manager_v3", 1)
[22:46:38.282799] {Default Queue} wl_registry#2.global(25, "ext_foreign_toplevel_list_v1", 1)
[22:46:38.282803] {Default Queue} wl_registry#2.global(26, "zwlr_foreign_toplevel_manager_v1", 3)
[22:46:38.282807] {Default Queue} wl_registry#2.global(27, "ext_session_lock_manager_v1", 1)
[22:46:38.282812] {Default Queue} wl_registry#2.global(28, "ext_workspace_manager_v1", 1)
[22:46:38.282816] {Default Queue} wl_registry#2.global(29, "zwlr_export_dmabuf_manager_v1", 1)
[22:46:38.282820] {Default Queue} wl_registry#2.global(30, "zwlr_screencopy_manager_v1", 3)
[22:46:38.282824] {Default Queue} wl_registry#2.global(31, "ext_image_copy_capture_manager_v1", 1)
[22:46:38.282828] {Default Queue} wl_registry#2.global(32, "ext_output_image_capture_source_manager_v1", 1)
[22:46:38.282832] {Default Queue} wl_registry#2.global(33, "zwlr_data_control_manager_v1", 2)
[22:46:38.282836] {Default Queue} wl_registry#2.global(34, "ext_data_control_manager_v1", 1)
[22:46:38.282840] {Default Queue} wl_registry#2.global(35, "wp_security_context_manager_v1", 1)
[22:46:38.282844] {Default Queue} wl_registry#2.global(36, "wp_viewporter", 1)
[22:46:38.282848] {Default Queue} wl_registry#2.global(37, "wp_single_pixel_buffer_manager_v1", 1)
[22:46:38.282852] {Default Queue} wl_registry#2.global(38, "wp_content_type_manager_v1", 1)
[22:46:38.282856] {Default Queue} wl_registry#2.global(39, "wp_fractional_scale_manager_v1", 1)
[22:46:38.282860] {Default Queue} wl_registry#2.global(40, "ext_foreign_toplevel_image_capture_source_manager_v1", 1)
[22:46:38.282872] {Default Queue} wl_registry#2.global(41, "wp_tearing_control_manager_v1", 1)
[22:46:38.282886] {Default Queue} wl_registry#2.global(42, "zxdg_exporter_v1", 1)
[22:46:38.282890] {Default Queue} wl_registry#2.global(43, "zxdg_importer_v1", 1)
[22:46:38.282894] {Default Queue} wl_registry#2.global(44, "zxdg_exporter_v2", 1)
[22:46:38.282898] {Default Queue} wl_registry#2.global(45, "zxdg_importer_v2", 1)
[22:46:38.282903] {Default Queue} wl_registry#2.global(46, "xdg_activation_v1", 1)
[22:46:38.282907] {Default Queue} wl_registry#2.global(47, "xdg_toplevel_tag_manager_v1", 1)
[22:46:38.282911] {Default Queue} wl_registry#2.global(48, "wp_cursor_shape_manager_v1", 2)
[22:46:38.282915] {Default Queue} wl_registry#2.global(49, "wp_color_representation_manager_v1", 1)
[22:46:38.282919] {Default Queue} wl_registry#2.global(50, "zwp_virtual_keyboard_manager_v1", 1)
[22:46:38.282923] {Default Queue} wl_registry#2.global(51, "zwlr_virtual_pointer_manager_v1", 2)
[22:46:38.282927] {Default Queue} wl_registry#2.global(52, "zwp_keyboard_shortcuts_inhibit_manager_v1", 1)
[22:46:38.282931] {Default Queue} wl_registry#2.global(53, "zwp_pointer_gestures_v1", 3)
[22:46:38.282935] {Default Queue} wl_registry#2.global(54, "ext_transient_seat_manager_v1", 1)
[22:46:38.282939] {Default Queue} wl_registry#2.global(55, "wl_seat", 9)
[22:46:38.282944] {Default Queue} -> wl_registry#2.bind(55, "wl_seat", 5, new id [unknown]#7)
[22:46:38.282948] {Default Queue} wl_registry#2.global(57, "zwp_primary_selection_device_manager_v1", 1)
[22:46:38.282953] {Default Queue} wl_registry#2.global(58, "wl_output", 4)
[22:46:38.282957] {Default Queue} wl_registry#2.global(59, "wl_output", 4)
[22:46:38.282961] {Default Queue} wl_callback#3.done(16)
[22:46:38.282966] {Default Queue} -> wl_display#1.sync(new id wl_callback#3)
[22:46:38.283205] {Display Queue} wl_display#1.delete_id(3)
[22:46:38.283217] {Default Queue} discarded wl_shm#4.format(0)
[22:46:38.283222] {Default Queue} discarded wl_shm#4.format(1)
[22:46:38.283227] {Default Queue} discarded wl_shm#4.format(875709016)
[22:46:38.283231] {Default Queue} discarded wl_shm#4.format(875708993)
[22:46:38.283235] {Default Queue} discarded wl_shm#4.format(875710274)
[22:46:38.283239] {Default Queue} discarded wl_shm#4.format(842094674)
[22:46:38.283242] {Default Queue} discarded wl_shm#4.format(842088786)
[22:46:38.283246] {Default Queue} discarded wl_shm#4.format(892426322)
[22:46:38.283249] {Default Queue} discarded wl_shm#4.format(892420434)
[22:46:38.283253] {Default Queue} discarded wl_shm#4.format(909199186)
[22:46:38.283257] {Default Queue} discarded wl_shm#4.format(808665688)
[22:46:38.283260] {Default Queue} discarded wl_shm#4.format(808665665)
[22:46:38.283264] {Default Queue} discarded wl_shm#4.format(1213351746)
[22:46:38.283268] {Default Queue} discarded wl_shm#4.format(1211384408)
[22:46:38.283271] {Default Queue} discarded wl_shm#4.format(1211384385)
[22:46:38.283275] {Default Queue} discarded wl_shm#4.format(942950210)
[22:46:38.283279] {Default Queue} discarded wl_shm#4.format(942948952)
[22:46:38.283282] {Default Queue} discarded wl_shm#4.format(942948929)
[22:46:38.283286] {Default Queue} wl_seat#7.name("seat0")
[22:46:38.283291] {Default Queue} wl_seat#7.capabilities(1)
[22:46:38.283296] {Default Queue} -> wl_seat#7.get_pointer(new id wl_pointer#8)
[22:46:38.283300] {Default Queue} wl_callback#3.done(16)
[22:46:38.283305] {Default Queue} -> wl_compositor#5.create_surface(new id wl_surface#3)
[22:46:38.283310] {Default Queue} -> xdg_wm_base#6.get_xdg_surface(new id xdg_surface#9, wl_surface#3)
[22:46:38.283319] {Default Queue} -> xdg_surface#9.get_toplevel(new id xdg_toplevel#10)
[22:46:38.283324] {Default Queue} -> xdg_toplevel#10.set_title("Mosaic Nested Grab Probe")
[22:46:38.283328] {Default Queue} -> xdg_toplevel#10.set_app_id("org.mosaic.NestedGrabProbe")
TOPLEVEL CREATE wl=3 xdg=9 role=10 placement=v_both_cross
[22:46:38.283338] {Default Queue} -> wl_surface#3.commit()
[22:46:38.283679] {Default Queue} xdg_wm_base#6.ping(17)
[22:46:38.283698] {Default Queue} -> xdg_wm_base#6.pong(17)
[22:46:38.283708] {Default Queue} xdg_toplevel#10.configure(0, 0, array[0])
TOPLEVEL ROLE_CONFIGURE w=0 h=0
[22:46:38.283716] {Default Queue} xdg_surface#9.configure(18)
TOPLEVEL XDG_CONFIGURE serial=18 ack
[22:46:38.283724] {Default Queue} -> xdg_surface#9.ack_configure(18)
[22:46:38.284502] {Default Queue} -> wl_shm#4.create_pool(new id wl_shm_pool#11, fd 5, 966000)
[22:46:38.284513] {Default Queue} -> wl_shm_pool#11.create_buffer(new id wl_buffer#12, 0, 420, 575, 1680, 1)
[22:46:38.284520] {Default Queue} -> wl_surface#3.attach(wl_buffer#12, 0, 0)
[22:46:38.284525] {Default Queue} -> wl_surface#3.damage_buffer(0, 0, 420, 575)
[22:46:38.284530] {Default Queue} -> wl_surface#3.commit()
[22:46:38.284534] {Default Queue} -> wl_buffer#12.destroy()
[22:46:38.284538] {Default Queue} -> wl_shm_pool#11.destroy()
TOPLEVEL MAP wl=3 xdg=9
[22:46:38.285508] discarded [unknown]#12.[event 0](0 fd, 8 byte)
[22:46:38.285514] {Display Queue} wl_display#1.delete_id(12)
[22:46:38.285519] {Display Queue} wl_display#1.delete_id(11)
[22:46:38.285524] {Default Queue} xdg_toplevel#10.configure(636, 573, array[20])
TOPLEVEL ROLE_CONFIGURE w=636 h=573
[22:46:38.285532] {Default Queue} xdg_surface#9.configure(19)
TOPLEVEL XDG_CONFIGURE serial=19 ack
[22:46:38.285539] {Default Queue} -> xdg_surface#9.ack_configure(19)
[22:46:38.583097] {Default Queue} wl_pointer#8.enter(24, wl_surface#3, 238.00000000, 275.00000000)
POINTER_ENTER target=TOPLEVEL wl=3 serial=24 x=238.00 y=275.00
[22:46:38.583124] {Default Queue} wl_pointer#8.frame()
[22:46:38.583129] {Default Queue} wl_pointer#8.leave(25, wl_surface#3)
POINTER_LEAVE target=TOPLEVEL serial=25
[22:46:38.583137] {Default Queue} wl_pointer#8.frame()
[22:46:39.867771] {Default Queue} wl_pointer#8.enter(30, wl_surface#3, 3.00000000, 5.00000000)
POINTER_ENTER target=TOPLEVEL wl=3 serial=30 x=3.00 y=5.00
[22:46:39.867787] {Default Queue} wl_pointer#8.frame()
[22:46:39.867792] {Default Queue} wl_pointer#8.frame()
[22:46:39.968604] {Default Queue} wl_pointer#8.leave(31, wl_surface#3)
POINTER_LEAVE target=TOPLEVEL serial=31
[22:46:39.968649] {Default Queue} wl_pointer#8.frame()
[22:46:40.117908] {Default Queue} wl_pointer#8.enter(32, wl_surface#3, 3.00000000, 5.00000000)
POINTER_ENTER target=TOPLEVEL wl=3 serial=32 x=3.00 y=5.00
[22:46:40.117939] {Default Queue} wl_pointer#8.frame()
[22:46:40.117943] {Default Queue} wl_pointer#8.button(33, 1002, 272, 1)
POINTER_BUTTON target=TOPLEVEL serial=33 button=272 state=1
[22:46:40.117951] {Default Queue} -> wl_compositor#5.create_surface(new id wl_surface#11)
[22:46:40.117956] {Default Queue} -> xdg_wm_base#6.get_xdg_surface(new id xdg_surface#12, wl_surface#11)
[22:46:40.117960] {Default Queue} -> xdg_wm_base#6.create_positioner(new id xdg_positioner#13)
[22:46:40.117964] {Default Queue} -> xdg_positioner#13.set_size(250, 170)
[22:46:40.117967] {Default Queue} -> xdg_positioner#13.set_anchor_rect(50, 480, 1, 1)
[22:46:40.117970] {Default Queue} -> xdg_positioner#13.set_anchor(5)
[22:46:40.117973] {Default Queue} -> xdg_positioner#13.set_gravity(8)
[22:46:40.117976] {Default Queue} -> xdg_surface#12.get_popup(new id xdg_popup#14, xdg_surface#9, xdg_positioner#13)
POPUP_A CREATE wl=11 xdg=12 role=14 parent=TOPLEVEL parent_xdg=9
POPUP_A GRAB_REQUEST origin=wl_pointer.button target=TOPLEVEL serial=33 used=33
[22:46:40.117984] {Default Queue} -> xdg_popup#14.grab(wl_seat#7, 33)
[22:46:40.117988] {Default Queue} -> wl_surface#11.commit()
[22:46:40.117991] {Default Queue} -> xdg_positioner#13.destroy()
[22:46:40.117994] {Default Queue} wl_pointer#8.frame()
[22:46:40.118126] {Display Queue} wl_display#1.delete_id(13)
[22:46:40.118129] {Default Queue} xdg_popup#14.configure(50, 480, 250, 170)
POPUP_A ROLE_CONFIGURE x=50 y=480 w=250 h=170
[22:46:40.118135] {Default Queue} xdg_surface#12.configure(34)
POPUP_A XDG_CONFIGURE serial=34 ack
[22:46:40.118140] {Default Queue} -> xdg_surface#12.ack_configure(34)
[22:46:40.118286] {Default Queue} -> wl_shm#4.create_pool(new id wl_shm_pool#13, fd 5, 170000)
[22:46:40.118298] {Default Queue} -> wl_shm_pool#13.create_buffer(new id wl_buffer#15, 0, 250, 170, 1000, 1)
[22:46:40.118313] {Default Queue} -> wl_surface#11.attach(wl_buffer#15, 0, 0)
[22:46:40.118317] {Default Queue} -> wl_surface#11.damage_buffer(0, 0, 250, 170)
[22:46:40.118320] {Default Queue} -> wl_surface#11.commit()
[22:46:40.118322] {Default Queue} -> wl_buffer#15.destroy()
[22:46:40.118325] {Default Queue} -> wl_shm_pool#13.destroy()
POPUP_A MAP wl=11 xdg=12
[22:46:40.118959] discarded [unknown]#15.[event 0](0 fd, 8 byte)
[22:46:40.118962] {Display Queue} wl_display#1.delete_id(15)
[22:46:40.118965] {Display Queue} wl_display#1.delete_id(13)
[22:46:40.197365] {Default Queue} wl_pointer#8.button(35, 1003, 272, 0)
POINTER_BUTTON target=TOPLEVEL serial=35 button=272 state=0
[22:46:40.197412] {Default Queue} wl_pointer#8.frame()
[22:46:40.298184] {Default Queue} wl_pointer#8.leave(36, wl_surface#3)
POINTER_LEAVE target=TOPLEVEL serial=36
[22:46:40.298225] {Default Queue} wl_pointer#8.frame()
[22:46:40.585174] {Default Queue} wl_pointer#8.enter(37, wl_surface#3, 3.00000000, 5.00000000)
POINTER_ENTER target=TOPLEVEL wl=3 serial=37 x=3.00 y=5.00
[22:46:40.585201] {Default Queue} wl_pointer#8.frame()
[22:46:40.585208] {Default Queue} wl_pointer#8.leave(38, wl_surface#3)
POINTER_LEAVE target=TOPLEVEL serial=38
[22:46:40.585221] {Default Queue} wl_pointer#8.frame()
[22:46:40.585227] {Default Queue} wl_pointer#8.enter(39, wl_surface#11, 4.00000000, 5.00000000)
POINTER_ENTER target=POPUP_A wl=11 serial=39 x=4.00 y=5.00
[22:46:40.585240] {Default Queue} wl_pointer#8.frame()
[22:46:40.585246] {Default Queue} wl_pointer#8.frame()
[22:46:40.685838] {Default Queue} wl_pointer#8.leave(40, wl_surface#11)
POINTER_LEAVE target=POPUP_A serial=40
[22:46:40.685909] {Default Queue} wl_pointer#8.frame()
[22:46:40.835646] {Default Queue} wl_pointer#8.enter(41, wl_surface#11, 4.00000000, 5.00000000)
POINTER_ENTER target=POPUP_A wl=11 serial=41 x=4.00 y=5.00
[22:46:40.835675] {Default Queue} wl_pointer#8.frame()
[22:46:40.835682] {Default Queue} wl_pointer#8.button(42, 1005, 272, 1)
POINTER_BUTTON target=POPUP_A serial=42 button=272 state=1
[22:46:40.835696] {Default Queue} -> wl_compositor#5.create_surface(new id wl_surface#13)
[22:46:40.835704] {Default Queue} -> xdg_wm_base#6.get_xdg_surface(new id xdg_surface#15, wl_surface#13)
[22:46:40.835712] {Default Queue} -> xdg_wm_base#6.create_positioner(new id xdg_positioner#16)
[22:46:40.835719] {Default Queue} -> xdg_positioner#16.set_size(170, 130)
[22:46:40.835726] {Default Queue} -> xdg_positioner#16.set_anchor_rect(20, 80, 1, 1)
[22:46:40.835733] {Default Queue} -> xdg_positioner#16.set_anchor(5)
[22:46:40.835738] {Default Queue} -> xdg_positioner#16.set_gravity(8)
[22:46:40.835745] {Default Queue} -> xdg_surface#15.get_popup(new id xdg_popup#17, xdg_surface#12, xdg_positioner#16)
POPUP_B CREATE wl=13 xdg=15 role=17 parent=POPUP_A parent_xdg=12
POPUP_B GRAB_REQUEST origin=wl_pointer.button target=POPUP_A serial=42 used=42
[22:46:40.835761] {Default Queue} -> xdg_popup#17.grab(wl_seat#7, 42)
[22:46:40.835768] {Default Queue} -> wl_surface#13.commit()
[22:46:40.835775] {Default Queue} -> xdg_positioner#16.destroy()
[22:46:40.835781] {Default Queue} wl_pointer#8.frame()
[22:46:40.835930] {Display Queue} wl_display#1.delete_id(16)
[22:46:40.835937] {Default Queue} xdg_popup#17.configure(20, 80, 170, 130)
POPUP_B ROLE_CONFIGURE x=20 y=80 w=170 h=130
[22:46:40.835949] {Default Queue} xdg_surface#15.configure(43)
POPUP_B XDG_CONFIGURE serial=43 ack
[22:46:40.835960] {Default Queue} -> xdg_surface#15.ack_configure(43)
[22:46:40.836118] {Default Queue} -> wl_shm#4.create_pool(new id wl_shm_pool#16, fd 5, 88400)
[22:46:40.836128] {Default Queue} -> wl_shm_pool#16.create_buffer(new id wl_buffer#18, 0, 170, 130, 680, 1)
[22:46:40.836135] {Default Queue} -> wl_surface#13.attach(wl_buffer#18, 0, 0)
[22:46:40.836142] {Default Queue} -> wl_surface#13.damage_buffer(0, 0, 170, 130)
[22:46:40.836149] {Default Queue} -> wl_surface#13.commit()
[22:46:40.836155] {Default Queue} -> wl_buffer#18.destroy()
[22:46:40.836168] {Default Queue} -> wl_shm_pool#16.destroy()
POPUP_B MAP wl=13 xdg=15

View file

@ -0,0 +1,637 @@
{
"status": "EXPERIMENTAL; archive of consumed campaign, not runtime authorization",
"lab": "/home/fy59/swayfx-mosaic-minimal-core-2l7riqt_",
"baseline": "c4bdfb46aadaa4e30a9a05a3238702f56651eec6",
"files_copied_exactly": [
{
"published_path": "c-tests-fixed/extract.py",
"lab_path": "c-tests-fixed/extract.py",
"sha256": "022ed6c86cb5df1ac80ae8c1fe9caeccaa20637bb10d8d052eb2e4b5cdf559f1",
"bytes": 2165,
"byte_equivalent": true
},
{
"published_path": "c-tests-fixed/run.py",
"lab_path": "c-tests-fixed/run.py",
"sha256": "38cd49d2fe92d6b2152ecd4dd7fa7924cfff1bb3189856c21cd7119e42f676f4",
"bytes": 2721,
"byte_equivalent": true
},
{
"published_path": "c-tests-fixed/geometry.c",
"lab_path": "c-tests-fixed/geometry.c",
"sha256": "5c8f60751715b256ebaabd79e86519f26d65cdad44948628076465eb523d76cb",
"bytes": 7793,
"byte_equivalent": true
},
{
"published_path": "c-tests-fixed/bindings.json",
"lab_path": "c-tests-fixed/bindings.json",
"sha256": "df85d775308b8b4f34b474b5567ae1546438cb1c9e609a67d41bc3ea559c9d4a",
"bytes": 1718,
"byte_equivalent": true
},
{
"published_path": "c-tests-fixed/handoff.json",
"lab_path": "c-tests-fixed/handoff.json",
"sha256": "9dc4553ff47bb89335d928369f3babaddc17bf660560207fc9756ad9abaf7b51",
"bytes": 277,
"byte_equivalent": true
},
{
"published_path": "c-tests-lifecycle/extract.py",
"lab_path": "c-tests-lifecycle/extract.py",
"sha256": "875cb96f4c20c7d1a2bc0546d79d1f117b1d441fedacce7968b5f5c6613b5461",
"bytes": 1181,
"byte_equivalent": true
},
{
"published_path": "c-tests-lifecycle/run.py",
"lab_path": "c-tests-lifecycle/run.py",
"sha256": "899913d93b1cacd8bd4b54481faf9c3b2efddeaa6c63d65d86480c7ceeb99b7c",
"bytes": 2018,
"byte_equivalent": true
},
{
"published_path": "c-tests-lifecycle/lifecycle.c",
"lab_path": "c-tests-lifecycle/lifecycle.c",
"sha256": "0cd8ee40696279dc2c0c8a60f093402ac8313dd3a7e99a2f44e3425392646c13",
"bytes": 7662,
"byte_equivalent": true
},
{
"published_path": "c-tests-lifecycle/scene.c",
"lab_path": "c-tests-lifecycle/scene.c",
"sha256": "073ce0399c7ef0cb248597c8f6f4a3b80ca805f8871b7714da3ed6672ac29d8a",
"bytes": 1165,
"byte_equivalent": true
},
{
"published_path": "c-tests-lifecycle/support.h",
"lab_path": "c-tests-lifecycle/support.h",
"sha256": "6fcda6fea770236eaa5e0f5371104b7848a659d555851fe43a79c3cceb6c6047",
"bytes": 3438,
"byte_equivalent": true
},
{
"published_path": "c-tests-lifecycle/bindings.json",
"lab_path": "c-tests-lifecycle/bindings.json",
"sha256": "8c7acce06a1e6e0382b72cf5745a84896f41da0a53706271aa40322140f7eac8",
"bytes": 2074,
"byte_equivalent": true
},
{
"published_path": "c-tests-lifecycle/handoff.json",
"lab_path": "c-tests-lifecycle/handoff.json",
"sha256": "8745e6b68c98d6644ec3e4d66b25ca2976948b02ce413d0f7be12dcba38bd52c",
"bytes": 570,
"byte_equivalent": true
},
{
"published_path": "c-tests-lifecycle/README.md",
"lab_path": "c-tests-lifecycle/README.md",
"sha256": "6cf3ec73d5feb6122c1f58d4ace9b22f0f0ad7dccea120d585ab16a758c5d377",
"bytes": 5594,
"byte_equivalent": true
},
{
"published_path": "graphics/cases.py",
"lab_path": "graphics/cases.py",
"sha256": "c3811fcad57f319500aad3643f000b704c25a89b44ac1ed05ec5f05d8c750589",
"bytes": 11306,
"byte_equivalent": true
},
{
"published_path": "graphics/engine.py",
"lab_path": "graphics/engine.py",
"sha256": "b8abebd09659be1868ab0f66f3e33bb6d38d50b163d2754900d7a324343673bd",
"bytes": 17940,
"byte_equivalent": true
},
{
"published_path": "graphics/fx.py",
"lab_path": "graphics/fx.py",
"sha256": "0a016c0f97c96344e2c62bf24a5533d315c6cc16d628cd840887d50362ffb8cf",
"bytes": 8648,
"byte_equivalent": true
},
{
"published_path": "graphics/fx_validators.py",
"lab_path": "graphics/fx_validators.py",
"sha256": "3a46ff7ab5dd1e45941c0c30d944ff0763e4426e70cf762571b1f06c75f2e8f0",
"bytes": 9903,
"byte_equivalent": true
},
{
"published_path": "graphics/gate.py",
"lab_path": "graphics/gate.py",
"sha256": "d37118314d022b0049df748d4ed4ec127c238dd8fd2f1340faee21ddc20bc3ab",
"bytes": 3210,
"byte_equivalent": true
},
{
"published_path": "graphics/geometry.py",
"lab_path": "graphics/geometry.py",
"sha256": "76a80e98887d3a3707ee942b48a49c1850cdc4aeeb28237d9fea33fa3470fb12",
"bytes": 3948,
"byte_equivalent": true
},
{
"published_path": "graphics/ipc_predicates.py",
"lab_path": "graphics/ipc_predicates.py",
"sha256": "6d5a25c225685adf24978a8bd9b4a6de44a369af6620d10db95accacc892b6f8",
"bytes": 691,
"byte_equivalent": true
},
{
"published_path": "graphics/lifecycle.py",
"lab_path": "graphics/lifecycle.py",
"sha256": "df0c11027b3b8b0ef371ddb6ec4bcfd28dac09835644d0c3ce6e273d5e565e92",
"bytes": 3702,
"byte_equivalent": true
},
{
"published_path": "graphics/model.py",
"lab_path": "graphics/model.py",
"sha256": "a578fc589fb092dae3ccdcf8c4eec12a97e515f721decc4c968078dedcb2367a",
"bytes": 7601,
"byte_equivalent": true
},
{
"published_path": "graphics/pixels.py",
"lab_path": "graphics/pixels.py",
"sha256": "d315ddad978238ffbf1c8e4d712d4608ba23564703332d814f627f57f7b6e7f5",
"bytes": 1132,
"byte_equivalent": true
},
{
"published_path": "graphics/plans.py",
"lab_path": "graphics/plans.py",
"sha256": "fbacf2fde781b53a8a5c5fd1b7475c310621f1cd635fe7e397d048ea5a5268ea",
"bytes": 1242,
"byte_equivalent": true
},
{
"published_path": "graphics/predicates.py",
"lab_path": "graphics/predicates.py",
"sha256": "d1d3505d9553444a3c6ec4b6c47eed817adac0451db1518ee15e33e0683c12fb",
"bytes": 7711,
"byte_equivalent": true
},
{
"published_path": "graphics/prepare.py",
"lab_path": "graphics/prepare.py",
"sha256": "e6094b2f720962b83106717c1c76ac23098051778c4df43064e6c0147645d321",
"bytes": 1266,
"byte_equivalent": true
},
{
"published_path": "graphics/run.py",
"lab_path": "graphics/run.py",
"sha256": "4503434f912ff8623d02ef897b0f702b61510ff7b3cc5a464734b09240ab8b8a",
"bytes": 5867,
"byte_equivalent": true
},
{
"published_path": "graphics/saved.py",
"lab_path": "graphics/saved.py",
"sha256": "ea70128504805b780bf0d59c5157c6fbb03fe7597b67062dda90103fb42109e6",
"bytes": 3470,
"byte_equivalent": true
},
{
"published_path": "graphics/saved_validators.py",
"lab_path": "graphics/saved_validators.py",
"sha256": "02c848d8eb8e2b7ffae66362040ce7378bbcf0d94db2457e769e0286edb01eea",
"bytes": 3673,
"byte_equivalent": true
},
{
"published_path": "graphics/seal.py",
"lab_path": "graphics/seal.py",
"sha256": "8203b28c17b7244e9ec4fd8989c3207b99532faa963b3421aebd87a6f9e00f11",
"bytes": 2460,
"byte_equivalent": true
},
{
"published_path": "graphics/test_fixture_repair_R1.py",
"lab_path": "graphics/test_fixture_repair_R1.py",
"sha256": "56d5c7410ee5de522f183225fc72eef2859d004a171632adefc8479686cc4b7b",
"bytes": 3595,
"byte_equivalent": true
},
{
"published_path": "graphics/test_fixture_repair_R2.py",
"lab_path": "graphics/test_fixture_repair_R2.py",
"sha256": "75a4963c6a8509dce197034d6bf01d46ef9bf8d5a3e30414d8bd0203c56b7fc0",
"bytes": 3405,
"byte_equivalent": true
},
{
"published_path": "graphics/test_graphics.py",
"lab_path": "graphics/test_graphics.py",
"sha256": "45ba9eb7229bfb7f87742733853e43c12b6d54d5e3ffbda14d4a76111601194b",
"bytes": 3933,
"byte_equivalent": true
},
{
"published_path": "graphics/test_workspace_marker_R2B.py",
"lab_path": "graphics/test_workspace_marker_R2B.py",
"sha256": "7af7ab259a372f4df8a21719abe5a0c197c6641175aeac40671bd3df3be1d9d5",
"bytes": 4529,
"byte_equivalent": true
},
{
"published_path": "graphics/validators.py",
"lab_path": "graphics/validators.py",
"sha256": "9834bc25e7891c341a714852a569d8c5f04c01e56b4fca6f1605713f7e9ac94b",
"bytes": 1418,
"byte_equivalent": true
},
{
"published_path": "graphics/configs/E0-core.conf",
"lab_path": "graphics/configs/E0-core.conf",
"sha256": "caf7cdff074714cb5b2ba35827558ce5a7defda125de034a7d42296f03b0528b",
"bytes": 292,
"byte_equivalent": true
},
{
"published_path": "graphics/configs/E0-pristine.conf",
"lab_path": "graphics/configs/E0-pristine.conf",
"sha256": "caf7cdff074714cb5b2ba35827558ce5a7defda125de034a7d42296f03b0528b",
"bytes": 292,
"byte_equivalent": true
},
{
"published_path": "graphics/configs/E1.conf",
"lab_path": "graphics/configs/E1.conf",
"sha256": "caf7cdff074714cb5b2ba35827558ce5a7defda125de034a7d42296f03b0528b",
"bytes": 292,
"byte_equivalent": true
},
{
"published_path": "graphics/configs/E2-fractional.conf",
"lab_path": "graphics/configs/E2-fractional.conf",
"sha256": "c9f351c52645e2cb502693da4611830db556dad40e8505e148bc716b5f89bfec",
"bytes": 296,
"byte_equivalent": true
},
{
"published_path": "graphics/configs/E2-r90.conf",
"lab_path": "graphics/configs/E2-r90.conf",
"sha256": "4affb6b0ad280d7166f63239708847bf184fadb492de50ce3155d2f66c3b99c9",
"bytes": 292,
"byte_equivalent": true
},
{
"published_path": "graphics/configs/E2-vertical.conf",
"lab_path": "graphics/configs/E2-vertical.conf",
"sha256": "d3e22dc4565af0e21e6180fa4a37a35b92a0f8260d5873719359f04fc7933e95",
"bytes": 291,
"byte_equivalent": true
},
{
"published_path": "graphics/configs/E3.conf",
"lab_path": "graphics/configs/E3.conf",
"sha256": "caf7cdff074714cb5b2ba35827558ce5a7defda125de034a7d42296f03b0528b",
"bytes": 292,
"byte_equivalent": true
},
{
"published_path": "graphics/configs/E4.conf",
"lab_path": "graphics/configs/E4.conf",
"sha256": "caf7cdff074714cb5b2ba35827558ce5a7defda125de034a7d42296f03b0528b",
"bytes": 292,
"byte_equivalent": true
},
{
"published_path": "graphics/configs/E5.conf",
"lab_path": "graphics/configs/E5.conf",
"sha256": "caf7cdff074714cb5b2ba35827558ce5a7defda125de034a7d42296f03b0528b",
"bytes": 292,
"byte_equivalent": true
},
{
"published_path": "graphics/configs/E6.conf",
"lab_path": "graphics/configs/E6.conf",
"sha256": "caf7cdff074714cb5b2ba35827558ce5a7defda125de034a7d42296f03b0528b",
"bytes": 292,
"byte_equivalent": true
},
{
"published_path": "graphics/configs/E7.conf",
"lab_path": "graphics/configs/E7.conf",
"sha256": "caf7cdff074714cb5b2ba35827558ce5a7defda125de034a7d42296f03b0528b",
"bytes": 292,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/fx-client.c",
"lab_path": "graphics/clients/fx-client.c",
"sha256": "f1abd428f6b35cea4ad3a82f30e39d79314324ce9652556e11118666ad2453a4",
"bytes": 13287,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/fx-clients.c",
"lab_path": "graphics/clients/fx-clients.c",
"sha256": "cc913ff83ce20e430d17eda99504278c8cacafb56b81cb44d7bd99ad3f2cf31a",
"bytes": 11551,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/layer-shell-client.h",
"lab_path": "graphics/clients/layer-shell-client.h",
"sha256": "bd359658282daf42b2f3270f2f670ae7d1d045ea47e3410bd3fdd012c8b47237",
"bytes": 26443,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/layer-shell-protocol.c",
"lab_path": "graphics/clients/layer-shell-protocol.c",
"sha256": "699dabe913a95113f56f82c87f59b3748cdfd959c17c45f39073b53cb2af998e",
"bytes": 3439,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/nested-client.c",
"lab_path": "graphics/clients/nested-client.c",
"sha256": "a11c9b3c6599e5c08b8e11917d85b7bfa0457f1fea0161ce8230faa9922d0c6c",
"bytes": 12653,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/pointer-daemon.c",
"lab_path": "graphics/clients/pointer-daemon.c",
"sha256": "8e4eaad95882d0d73866f801899007240b01cb6e15a712e820b3e3f71a78bbe5",
"bytes": 2759,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/region-client.c",
"lab_path": "graphics/clients/region-client.c",
"sha256": "51564a0eebf8b99ea1947e2699bdd5b6073c3f37ce3b4165ba500171783a30b6",
"bytes": 13613,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/saved-client.c",
"lab_path": "graphics/clients/saved-client.c",
"sha256": "f4d462b9eb9406e336f5613bdc347fe9cf9c28c06aae28dad95b6b9e556b1e3f",
"bytes": 9705,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/static-reference.c",
"lab_path": "graphics/clients/static-reference.c",
"sha256": "e346a85ded3345b232cd6edca5a1ac57aebfd548a60e12b3c47a5cf515e6c50e",
"bytes": 283,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/vertical-client.c",
"lab_path": "graphics/clients/vertical-client.c",
"sha256": "f56151d4c34c577a9a5e82dda2bfccfc0febbce1cda741eea32d9658904905f9",
"bytes": 13207,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/virtual-pointer-client.h",
"lab_path": "graphics/clients/virtual-pointer-client.h",
"sha256": "0dcb6107cf948a007dcef0a6b2192e4f7b9166c0f57b499ca49b7aaf48adc56c",
"bytes": 13757,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/virtual-pointer-protocol.c",
"lab_path": "graphics/clients/virtual-pointer-protocol.c",
"sha256": "a72e9b38dd5ed0c49fae22aa2f01883aea76075ad33f6399d9b2033df4330675",
"bytes": 3325,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/wlr-layer-shell-unstable-v1.xml",
"lab_path": "graphics/clients/wlr-layer-shell-unstable-v1.xml",
"sha256": "996b8c66e1c2276c443336d4cb174634e8e5948823779042f244930525b79cc2",
"bytes": 18462,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/xdg-shell-client.h",
"lab_path": "graphics/clients/xdg-shell-client.h",
"sha256": "a09726137ea9fe1a23d58c95f1d33ab77a3fc692c63726bc82fbc91ab8e5c6fc",
"bytes": 86409,
"byte_equivalent": true
},
{
"published_path": "graphics/clients/xdg-shell-protocol.c",
"lab_path": "graphics/clients/xdg-shell-protocol.c",
"sha256": "9e12fcd5676d8c6b6cfd233c2eba563163c0ea7e586cb6e27f202a328323c7f0",
"bytes": 5999,
"byte_equivalent": true
},
{
"published_path": "gpu-field/validate.py",
"lab_path": "gpu-field/validate.py",
"sha256": "158a820a2f170cabce37f7b8965ce23c3887cfbced32fecaea4914ea6f423ef7",
"bytes": 10826,
"byte_equivalent": true
},
{
"published_path": "gpu-field/analysis.py",
"lab_path": "gpu-field/analysis.py",
"sha256": "5341909a7fa4010f52bfc909cd76f2288cfa5c4da9126b7258c47b4ad987fa3d",
"bytes": 6713,
"byte_equivalent": true
},
{
"published_path": "gpu-field/test_fixture.py",
"lab_path": "gpu-field/test_fixture.py",
"sha256": "824c9fa022871076bef8b53357f6a25394706180a3eb29b1a31e993d46ef9ca1",
"bytes": 10004,
"byte_equivalent": true
},
{
"published_path": "gpu-field/test_native_hash.py",
"lab_path": "gpu-field/test_native_hash.py",
"sha256": "0b863ebaab99faaf07256d24f363c76b717f46969596769d6c433f1b66bc6546",
"bytes": 1413,
"byte_equivalent": true
},
{
"published_path": "evidence/budget-final.json",
"lab_path": "budget.json",
"sha256": "177665e5cc799e0979ea8f64fafbac403baa1ada1361db9ae1841544c8dc01d7",
"bytes": 5876,
"byte_equivalent": true
},
{
"published_path": "evidence/core-evidence-delta-review.json",
"lab_path": "reviews/core-evidence-delta-review.json",
"sha256": "0e2de194d3831bd86f425a581cfd1ee87aefa9c656317e90e5fbae038742f629",
"bytes": 2434,
"byte_equivalent": true
},
{
"published_path": "evidence/G1-rejection.md",
"lab_path": "audit/gpu-rejection/G1-rejection.md",
"sha256": "1f8d3d5d8a2577a7d0ace6b8f945efc8adc418bff9d38059bc10dc485913d284",
"bytes": 5102,
"byte_equivalent": true
},
{
"published_path": "evidence/modifier-layout-audit.json",
"lab_path": "audit/gpu-rejection/modifier-layout-audit.json",
"sha256": "a08457bfe2e2a325c0f22fb6eba6fa3cccd4cf3fbaf82515104f6569d0465fce",
"bytes": 1014,
"byte_equivalent": true
},
{
"published_path": "evidence/core-abi-provider-summary.json",
"lab_path": "audit/core-abi-provider-summary.json",
"sha256": "352f1abb458a4432def0725f8b27c1fd2c76b86cc3178c276d806162760edd28",
"bytes": 2032,
"byte_equivalent": true
},
{
"published_path": "evidence/r2-count-discrepancy.json",
"lab_path": "gpu-execution/r2-count-discrepancy.json",
"sha256": "b361cda3cf7e6826b5e716b0fc9124b82d75224390b17cc8302807a02cd5cc5b",
"bytes": 592,
"byte_equivalent": true
},
{
"published_path": "graphics/wire-fixture.txt",
"lab_path": "graphics/wire-fixture.txt",
"sha256": "c6c9fc072f78fbcf9cfdeebcc5becdbdfb61824352495a0e767d7d390267f640",
"bytes": 16513,
"byte_equivalent": true
}
],
"private_references": [
{
"path": "ticket.md",
"sha256": "b21c0bdddcdce522a706f3cb1e3af1e17a1f0354934588b69eb4cf8069910c62",
"bytes": 43024
},
{
"path": "audit/source-replay.json",
"sha256": "fdd5027e897765f040ed268f46ecc8a2c5bea07e5327807319fde84cf97c9c29",
"bytes": 127335
},
{
"path": "audit/parent-provenance.json",
"sha256": "18b95508687513f4966d3fe951f2450f2a52c53d2411b83b9ce8ecd69364c547",
"bytes": 4300
},
{
"path": "audit/candidate-source-freeze.json",
"sha256": "f2f5c4f4823c0dd33bc2a9085d5eaa114256a2501add0ffd417f8d796728d787",
"bytes": 57598
},
{
"path": "audit/historical-verification.json",
"sha256": "679b3afdc07ece6de92f8533c95edc1fc86f8bbc7b6c04de68b4607ae2970a1e",
"bytes": 93995
},
{
"path": "c-tests/manifest.json",
"sha256": "48f3deea6026b4982c4bda1753301f78c60cb23a2cabac1136ad4762f9a20ce5",
"bytes": 7240
},
{
"path": "c-tests-fixed/manifest.json",
"sha256": "db8996995f62d42e490c9dc3a9fe69943bf15c33b1ab2c2ba90ff632dba5b3d1",
"bytes": 9702
},
{
"path": "c-tests-lifecycle/manifest.json",
"sha256": "435cc242f2f8a2db00b9f14e929b606c2eb89470ca70d90eef3532838e837978",
"bytes": 7125
},
{
"path": "build-audit/handoff.json",
"sha256": "63fe2d66d65f51c682a3cd3a0901609403c0a07c1d6c90094c4c220abcc296a0",
"bytes": 14654
},
{
"path": "build-audit/critical-providers.json",
"sha256": "ada6f7436dfc7f4ab0914d303ec701e00efbc61d4d595a00cd160e47ef09e9ef",
"bytes": 8904
},
{
"path": "graphics/pre-run-manifest.json",
"sha256": "5e49909975ea6776a677280a4e444d5014699bf36b68c68fba3345626b196fd3",
"bytes": 174059
},
{
"path": "graphics/review.json",
"sha256": "0e7525b69fdd3e3f75b5c62fb0c3c833557024c6153c3ee033c2d9eebe9d66d0",
"bytes": 593
},
{
"path": "graphics-execution/summary.json",
"sha256": "13068c152004500a173d633d0f1896debb0601ff8c136f4419158cd2ad22158c",
"bytes": 7644
},
{
"path": "graphics-execution-R2/summary.json",
"sha256": "2ff842a7eaa0d49c63d99a3e2b41c81b0297844de680797ebbca2ecb3ecb6b9d",
"bytes": 76214
},
{
"path": "gpu-design/HANDOFF.md",
"sha256": "bf1a51a6d7b373f82a2b8d9c8639513405069f2743c2044e1bc0eb6619a1d223",
"bytes": 24234
},
{
"path": "gpu-design/provenance-and-budget.json",
"sha256": "0a7d1a7836fc17919d9a70507d3a262d59de32d8228b185c0d4911dbb73691e7",
"bytes": 11206
},
{
"path": "gpu-field/source-freeze.json",
"sha256": "58535958c0c55b3bee758bfda413df594302122f9a9da2e1a4aedde3889b9f5f",
"bytes": 177911
},
{
"path": "gpu-field/source-review.json",
"sha256": "c65c5c9a61d82aa6e22705457797d466aa0f004ff97377c38a46233c7e83aa8e",
"bytes": 382
},
{
"path": "gpu-field/runtime-review.json",
"sha256": "12b4f56e4166aae421e9655d9f66e13b08d7664130b4c26c697076e2ec9741a7",
"bytes": 615
},
{
"path": "gpu-field/build-identity.json",
"sha256": "cd723e95be468e1b479e489ce84b1ed6f160bc1431aa43fafb5992deb9f305ab",
"bytes": 6566
},
{
"path": "gpu-execution/G1-summary.json",
"sha256": "fe48671c95a06210768c3addf08b61dbf67efa80953d49b389e3b851687c3d47",
"bytes": 13085
},
{
"path": "fixture-repair-R2/compiled-source-equivalence.json",
"sha256": "fbba2c862e9a22be0580e8cf40b652d9307dba5d624c28131406acaa1bf06fe1",
"bytes": 94102
}
],
"core_binary_sha256": "29baf31aa3eeed259825c87d0799fa5d3dc83710afb967154604eb1c6657f965",
"gpu_binary_sha256": "1905a20fa72ebcc7ec47d1584a4653654280288096a20005199eb75673ac0383",
"runs_consumed": 12,
"output_capture_debits": 88,
"png_files": 90,
"excluded": [
"private functional core patches/sources",
"generated extracted core .inc bodies",
"binaries",
"build trees",
"raw captures/readbacks",
"large logs",
"generated metadata manifests of complete private trees"
]
}