test(mosaic): record minimal core build and partial runtime GPU gates
This commit is contained in:
parent
c4bdfb46aa
commit
70954e26f6
84 changed files with 11093 additions and 0 deletions
19
CHANGELOG.md
19
CHANGELOG.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
98
docs/design/STEP20C_GPU_FULL_FIELD_FRESHNESS_FOLLOWUP.md
Normal file
98
docs/design/STEP20C_GPU_FULL_FIELD_FRESHNESS_FOLLOWUP.md
Normal 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.
|
||||
|
|
@ -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.
|
||||
232
docs/design/STEP20C_MINIMAL_CORE_BUILD_AND_C_FAILURE_PATHS.md
Normal file
232
docs/design/STEP20C_MINIMAL_CORE_BUILD_AND_C_FAILURE_PATHS.md
Normal 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.
|
||||
128
docs/design/STEP20C_MINIMAL_CORE_GRAPHICS_QUALIFICATION.md
Normal file
128
docs/design/STEP20C_MINIMAL_CORE_GRAPHICS_QUALIFICATION.md
Normal 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.
|
||||
101
docs/design/STEP20C_MINIMAL_CORE_LIFETIME_DAMAGE_AND_TXN.md
Normal file
101
docs/design/STEP20C_MINIMAL_CORE_LIFETIME_DAMAGE_AND_TXN.md
Normal 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.
|
||||
99
tooling/probes/step20c-minimal-core-gate/README.md
Normal file
99
tooling/probes/step20c-minimal-core-gate/README.md
Normal 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.
|
||||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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')
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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')
|
||||
|
|
@ -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.
|
||||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
|
|
@ -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')
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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;}
|
||||
|
|
@ -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))
|
||||
|
|
@ -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);}
|
||||
|
|
@ -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
|
||||
|
|
@ -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/`.
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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."
|
||||
}
|
||||
|
|
@ -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')
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)))
|
||||
151
tooling/probes/step20c-minimal-core-gate/gpu-field/validate.py
Normal file
151
tooling/probes/step20c-minimal-core-gate/gpu-field/validate.py
Normal 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')
|
||||
112
tooling/probes/step20c-minimal-core-gate/graphics/cases.py
Normal file
112
tooling/probes/step20c-minimal-core-gate/graphics/cases.py
Normal 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'
|
||||
|
||||
|
|
@ -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, ®istry_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;
|
||||
}
|
||||
|
|
@ -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, ®istry_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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
@ -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 ⊤
|
||||
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,®istry_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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 ⊤
|
||||
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,®istry_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;
|
||||
}
|
||||
|
|
@ -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, ®istry_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;
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
@ -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 ⊤
|
||||
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,®istry_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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
@ -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
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
210
tooling/probes/step20c-minimal-core-gate/graphics/engine.py
Normal file
210
tooling/probes/step20c-minimal-core-gate/graphics/engine.py
Normal 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
|
||||
148
tooling/probes/step20c-minimal-core-gate/graphics/fx.py
Normal file
148
tooling/probes/step20c-minimal-core-gate/graphics/fx.py
Normal 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']
|
||||
|
|
@ -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
|
||||
59
tooling/probes/step20c-minimal-core-gate/graphics/gate.py
Normal file
59
tooling/probes/step20c-minimal-core-gate/graphics/gate.py
Normal 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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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 ()
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
194
tooling/probes/step20c-minimal-core-gate/graphics/model.py
Normal file
194
tooling/probes/step20c-minimal-core-gate/graphics/model.py
Normal 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}
|
||||
21
tooling/probes/step20c-minimal-core-gate/graphics/pixels.py
Normal file
21
tooling/probes/step20c-minimal-core-gate/graphics/pixels.py
Normal 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())
|
||||
10
tooling/probes/step20c-minimal-core-gate/graphics/plans.py
Normal file
10
tooling/probes/step20c-minimal-core-gate/graphics/plans.py
Normal 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]]
|
||||
125
tooling/probes/step20c-minimal-core-gate/graphics/predicates.py
Normal file
125
tooling/probes/step20c-minimal-core-gate/graphics/predicates.py
Normal 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')
|
||||
|
||||
16
tooling/probes/step20c-minimal-core-gate/graphics/prepare.py
Normal file
16
tooling/probes/step20c-minimal-core-gate/graphics/prepare.py
Normal 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()
|
||||
79
tooling/probes/step20c-minimal-core-gate/graphics/run.py
Normal file
79
tooling/probes/step20c-minimal-core-gate/graphics/run.py
Normal 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())
|
||||
30
tooling/probes/step20c-minimal-core-gate/graphics/saved.py
Normal file
30
tooling/probes/step20c-minimal-core-gate/graphics/saved.py
Normal 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'
|
||||
|
|
@ -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")
|
||||
30
tooling/probes/step20c-minimal-core-gate/graphics/seal.py
Normal file
30
tooling/probes/step20c-minimal-core-gate/graphics/seal.py
Normal 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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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')
|
||||
|
|
@ -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
|
||||
637
tooling/probes/step20c-minimal-core-gate/provenance.json
Normal file
637
tooling/probes/step20c-minimal-core-gate/provenance.json
Normal 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"
|
||||
]
|
||||
}
|
||||
Loading…
Reference in a new issue