From 70954e26f62fb3df25995e31b0c615762ff85a60 Mon Sep 17 00:00:00 2001 From: fy59 Date: Wed, 14 Oct 2026 19:28:20 +0200 Subject: [PATCH] test(mosaic): record minimal core build and partial runtime GPU gates --- CHANGELOG.md | 19 + docs/ARCHITECTURE.md | 35 + docs/KNOWN_LIMITATIONS.md | 34 + docs/README.md | 30 + docs/ROADMAP.md | 28 + ...EP20C_GPU_FULL_FIELD_FRESHNESS_FOLLOWUP.md | 98 + ...RE_ABI_PACKAGE_AND_INTEGRATION_DECISION.md | 182 ++ ..._MINIMAL_CORE_BUILD_AND_C_FAILURE_PATHS.md | 232 ++ ...20C_MINIMAL_CORE_GRAPHICS_QUALIFICATION.md | 128 + ...0C_MINIMAL_CORE_LIFETIME_DAMAGE_AND_TXN.md | 101 + .../step20c-minimal-core-gate/README.md | 99 + .../c-tests-fixed/bindings.json | 45 + .../c-tests-fixed/extract.py | 36 + .../c-tests-fixed/geometry.c | 160 ++ .../c-tests-fixed/handoff.json | 9 + .../c-tests-fixed/run.py | 41 + .../c-tests-lifecycle/README.md | 96 + .../c-tests-lifecycle/bindings.json | 52 + .../c-tests-lifecycle/extract.py | 16 + .../c-tests-lifecycle/handoff.json | 21 + .../c-tests-lifecycle/lifecycle.c | 56 + .../c-tests-lifecycle/run.py | 27 + .../c-tests-lifecycle/scene.c | 18 + .../c-tests-lifecycle/support.h | 53 + .../evidence/G1-rejection.md | 93 + .../evidence/budget-final.json | 242 ++ .../evidence/core-abi-provider-summary.json | 54 + .../evidence/core-evidence-delta-review.json | 35 + .../evidence/modifier-layout-audit.json | 20 + .../evidence/r2-count-discrepancy.json | 20 + .../gpu-field/analysis.py | 96 + .../gpu-field/test_fixture.py | 146 + .../gpu-field/test_native_hash.py | 20 + .../gpu-field/validate.py | 151 ++ .../graphics/cases.py | 112 + .../graphics/clients/fx-client.c | 333 +++ .../graphics/clients/fx-clients.c | 303 +++ .../graphics/clients/layer-shell-client.h | 706 +++++ .../graphics/clients/layer-shell-protocol.c | 94 + .../graphics/clients/nested-client.c | 270 ++ .../graphics/clients/pointer-daemon.c | 57 + .../graphics/clients/region-client.c | 284 ++ .../graphics/clients/saved-client.c | 224 ++ .../graphics/clients/static-reference.c | 5 + .../graphics/clients/vertical-client.c | 280 ++ .../graphics/clients/virtual-pointer-client.h | 370 +++ .../clients/virtual-pointer-protocol.c | 87 + .../clients/wlr-layer-shell-unstable-v1.xml | 390 +++ .../graphics/clients/xdg-shell-client.h | 2392 +++++++++++++++++ .../graphics/clients/xdg-shell-protocol.c | 184 ++ .../graphics/configs/E0-core.conf | 8 + .../graphics/configs/E0-pristine.conf | 8 + .../graphics/configs/E1.conf | 8 + .../graphics/configs/E2-fractional.conf | 8 + .../graphics/configs/E2-r90.conf | 8 + .../graphics/configs/E2-vertical.conf | 8 + .../graphics/configs/E3.conf | 8 + .../graphics/configs/E4.conf | 8 + .../graphics/configs/E5.conf | 8 + .../graphics/configs/E6.conf | 8 + .../graphics/configs/E7.conf | 8 + .../graphics/engine.py | 210 ++ .../step20c-minimal-core-gate/graphics/fx.py | 148 + .../graphics/fx_validators.py | 172 ++ .../graphics/gate.py | 59 + .../graphics/geometry.py | 62 + .../graphics/ipc_predicates.py | 23 + .../graphics/lifecycle.py | 64 + .../graphics/model.py | 194 ++ .../graphics/pixels.py | 21 + .../graphics/plans.py | 10 + .../graphics/predicates.py | 125 + .../graphics/prepare.py | 16 + .../step20c-minimal-core-gate/graphics/run.py | 79 + .../graphics/saved.py | 30 + .../graphics/saved_validators.py | 42 + .../graphics/seal.py | 30 + .../graphics/test_fixture_repair_R1.py | 49 + .../graphics/test_fixture_repair_R2.py | 61 + .../graphics/test_graphics.py | 67 + .../graphics/test_workspace_marker_R2B.py | 78 + .../graphics/validators.py | 23 + .../graphics/wire-fixture.txt | 221 ++ .../step20c-minimal-core-gate/provenance.json | 637 +++++ 84 files changed, 11093 insertions(+) create mode 100644 docs/design/STEP20C_GPU_FULL_FIELD_FRESHNESS_FOLLOWUP.md create mode 100644 docs/design/STEP20C_MINIMAL_CORE_ABI_PACKAGE_AND_INTEGRATION_DECISION.md create mode 100644 docs/design/STEP20C_MINIMAL_CORE_BUILD_AND_C_FAILURE_PATHS.md create mode 100644 docs/design/STEP20C_MINIMAL_CORE_GRAPHICS_QUALIFICATION.md create mode 100644 docs/design/STEP20C_MINIMAL_CORE_LIFETIME_DAMAGE_AND_TXN.md create mode 100644 tooling/probes/step20c-minimal-core-gate/README.md create mode 100644 tooling/probes/step20c-minimal-core-gate/c-tests-fixed/bindings.json create mode 100644 tooling/probes/step20c-minimal-core-gate/c-tests-fixed/extract.py create mode 100644 tooling/probes/step20c-minimal-core-gate/c-tests-fixed/geometry.c create mode 100644 tooling/probes/step20c-minimal-core-gate/c-tests-fixed/handoff.json create mode 100644 tooling/probes/step20c-minimal-core-gate/c-tests-fixed/run.py create mode 100644 tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/README.md create mode 100644 tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/bindings.json create mode 100644 tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/extract.py create mode 100644 tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/handoff.json create mode 100644 tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/lifecycle.c create mode 100644 tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/run.py create mode 100644 tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/scene.c create mode 100644 tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/support.h create mode 100644 tooling/probes/step20c-minimal-core-gate/evidence/G1-rejection.md create mode 100644 tooling/probes/step20c-minimal-core-gate/evidence/budget-final.json create mode 100644 tooling/probes/step20c-minimal-core-gate/evidence/core-abi-provider-summary.json create mode 100644 tooling/probes/step20c-minimal-core-gate/evidence/core-evidence-delta-review.json create mode 100644 tooling/probes/step20c-minimal-core-gate/evidence/modifier-layout-audit.json create mode 100644 tooling/probes/step20c-minimal-core-gate/evidence/r2-count-discrepancy.json create mode 100644 tooling/probes/step20c-minimal-core-gate/gpu-field/analysis.py create mode 100644 tooling/probes/step20c-minimal-core-gate/gpu-field/test_fixture.py create mode 100644 tooling/probes/step20c-minimal-core-gate/gpu-field/test_native_hash.py create mode 100644 tooling/probes/step20c-minimal-core-gate/gpu-field/validate.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/cases.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/fx-client.c create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/fx-clients.c create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/layer-shell-client.h create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/layer-shell-protocol.c create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/nested-client.c create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/pointer-daemon.c create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/region-client.c create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/saved-client.c create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/static-reference.c create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/vertical-client.c create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/virtual-pointer-client.h create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/virtual-pointer-protocol.c create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/wlr-layer-shell-unstable-v1.xml create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/xdg-shell-client.h create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/clients/xdg-shell-protocol.c create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/configs/E0-core.conf create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/configs/E0-pristine.conf create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/configs/E1.conf create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/configs/E2-fractional.conf create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/configs/E2-r90.conf create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/configs/E2-vertical.conf create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/configs/E3.conf create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/configs/E4.conf create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/configs/E5.conf create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/configs/E6.conf create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/configs/E7.conf create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/engine.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/fx.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/fx_validators.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/gate.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/geometry.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/ipc_predicates.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/lifecycle.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/model.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/pixels.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/plans.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/predicates.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/prepare.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/run.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/saved.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/saved_validators.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/seal.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/test_fixture_repair_R1.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/test_fixture_repair_R2.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/test_graphics.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/test_workspace_marker_R2B.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/validators.py create mode 100644 tooling/probes/step20c-minimal-core-gate/graphics/wire-fixture.txt create mode 100644 tooling/probes/step20c-minimal-core-gate/provenance.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 4060cd0..1ed94e4 100644 --- a/CHANGELOG.md +++ b/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 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 07585c6..2e1a05c 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -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. diff --git a/docs/KNOWN_LIMITATIONS.md b/docs/KNOWN_LIMITATIONS.md index d4e7429..65d5359 100644 --- a/docs/KNOWN_LIMITATIONS.md +++ b/docs/KNOWN_LIMITATIONS.md @@ -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. diff --git a/docs/README.md b/docs/README.md index 3e60372..1f7bf84 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index e76658c..12a6544 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -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. diff --git a/docs/design/STEP20C_GPU_FULL_FIELD_FRESHNESS_FOLLOWUP.md b/docs/design/STEP20C_GPU_FULL_FIELD_FRESHNESS_FOLLOWUP.md new file mode 100644 index 0000000..d1c2b51 --- /dev/null +++ b/docs/design/STEP20C_GPU_FULL_FIELD_FRESHNESS_FOLLOWUP.md @@ -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. diff --git a/docs/design/STEP20C_MINIMAL_CORE_ABI_PACKAGE_AND_INTEGRATION_DECISION.md b/docs/design/STEP20C_MINIMAL_CORE_ABI_PACKAGE_AND_INTEGRATION_DECISION.md new file mode 100644 index 0000000..3b449bc --- /dev/null +++ b/docs/design/STEP20C_MINIMAL_CORE_ABI_PACKAGE_AND_INTEGRATION_DECISION.md @@ -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. diff --git a/docs/design/STEP20C_MINIMAL_CORE_BUILD_AND_C_FAILURE_PATHS.md b/docs/design/STEP20C_MINIMAL_CORE_BUILD_AND_C_FAILURE_PATHS.md new file mode 100644 index 0000000..4e91949 --- /dev/null +++ b/docs/design/STEP20C_MINIMAL_CORE_BUILD_AND_C_FAILURE_PATHS.md @@ -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. diff --git a/docs/design/STEP20C_MINIMAL_CORE_GRAPHICS_QUALIFICATION.md b/docs/design/STEP20C_MINIMAL_CORE_GRAPHICS_QUALIFICATION.md new file mode 100644 index 0000000..6d5ef63 --- /dev/null +++ b/docs/design/STEP20C_MINIMAL_CORE_GRAPHICS_QUALIFICATION.md @@ -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//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 `; 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. diff --git a/docs/design/STEP20C_MINIMAL_CORE_LIFETIME_DAMAGE_AND_TXN.md b/docs/design/STEP20C_MINIMAL_CORE_LIFETIME_DAMAGE_AND_TXN.md new file mode 100644 index 0000000..6d700b1 --- /dev/null +++ b/docs/design/STEP20C_MINIMAL_CORE_LIFETIME_DAMAGE_AND_TXN.md @@ -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. diff --git a/tooling/probes/step20c-minimal-core-gate/README.md b/tooling/probes/step20c-minimal-core-gate/README.md new file mode 100644 index 0000000..2f4a5e4 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/README.md @@ -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 ` +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. diff --git a/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/bindings.json b/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/bindings.json new file mode 100644 index 0000000..5fc1017 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/bindings.json @@ -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" + } + ] +} diff --git a/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/extract.py b/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/extract.py new file mode 100644 index 0000000..47daae3 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/extract.py @@ -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') diff --git a/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/geometry.c b/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/geometry.c new file mode 100644 index 0000000..ca06cbc --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/geometry.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +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=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; +} diff --git a/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/handoff.json b/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/handoff.json new file mode 100644 index 0000000..cac6a25 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/handoff.json @@ -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" +} diff --git a/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/run.py b/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/run.py new file mode 100644 index 0000000..b6ea9ec --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/c-tests-fixed/run.py @@ -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') diff --git a/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/README.md b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/README.md new file mode 100644 index 0000000..4c26a5d --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/README.md @@ -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. diff --git a/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/bindings.json b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/bindings.json new file mode 100644 index 0000000..68e5953 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/bindings.json @@ -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" + } +] diff --git a/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/extract.py b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/extract.py new file mode 100644 index 0000000..00a1d22 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/extract.py @@ -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') diff --git a/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/handoff.json b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/handoff.json new file mode 100644 index 0000000..74c9ac8 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/handoff.json @@ -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" +} diff --git a/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/lifecycle.c b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/lifecycle.c new file mode 100644 index 0000000..e7aa3c3 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/lifecycle.c @@ -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;ilength;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;iboxes[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;} diff --git a/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/run.py b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/run.py new file mode 100644 index 0000000..4ad7fd0 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/run.py @@ -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)) diff --git a/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/scene.c b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/scene.c new file mode 100644 index 0000000..aaa5bd2 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/scene.c @@ -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);} diff --git a/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/support.h b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/support.h new file mode 100644 index 0000000..9d596bc --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/c-tests-lifecycle/support.h @@ -0,0 +1,53 @@ +#ifndef SUPPORT_H +#define SUPPORT_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// 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 diff --git a/tooling/probes/step20c-minimal-core-gate/evidence/G1-rejection.md b/tooling/probes/step20c-minimal-core-gate/evidence/G1-rejection.md new file mode 100644 index 0000000..c85aa84 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/evidence/G1-rejection.md @@ -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/`. diff --git a/tooling/probes/step20c-minimal-core-gate/evidence/budget-final.json b/tooling/probes/step20c-minimal-core-gate/evidence/budget-final.json new file mode 100644 index 0000000..3147529 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/evidence/budget-final.json @@ -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 + } + ] +} diff --git a/tooling/probes/step20c-minimal-core-gate/evidence/core-abi-provider-summary.json b/tooling/probes/step20c-minimal-core-gate/evidence/core-abi-provider-summary.json new file mode 100644 index 0000000..9c36ba5 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/evidence/core-abi-provider-summary.json @@ -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" + ] +} diff --git a/tooling/probes/step20c-minimal-core-gate/evidence/core-evidence-delta-review.json b/tooling/probes/step20c-minimal-core-gate/evidence/core-evidence-delta-review.json new file mode 100644 index 0000000..81c1a53 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/evidence/core-evidence-delta-review.json @@ -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 +} diff --git a/tooling/probes/step20c-minimal-core-gate/evidence/modifier-layout-audit.json b/tooling/probes/step20c-minimal-core-gate/evidence/modifier-layout-audit.json new file mode 100644 index 0000000..488dfeb --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/evidence/modifier-layout-audit.json @@ -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 +} diff --git a/tooling/probes/step20c-minimal-core-gate/evidence/r2-count-discrepancy.json b/tooling/probes/step20c-minimal-core-gate/evidence/r2-count-discrepancy.json new file mode 100644 index 0000000..5a4e88a --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/evidence/r2-count-discrepancy.json @@ -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." +} diff --git a/tooling/probes/step20c-minimal-core-gate/gpu-field/analysis.py b/tooling/probes/step20c-minimal-core-gate/gpu-field/analysis.py new file mode 100644 index 0000000..0c48242 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/gpu-field/analysis.py @@ -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') diff --git a/tooling/probes/step20c-minimal-core-gate/gpu-field/test_fixture.py b/tooling/probes/step20c-minimal-core-gate/gpu-field/test_fixture.py new file mode 100644 index 0000000..0a53dde --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/gpu-field/test_fixture.py @@ -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('\n#include \n#include \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))) diff --git a/tooling/probes/step20c-minimal-core-gate/gpu-field/validate.py b/tooling/probes/step20c-minimal-core-gate/gpu-field/validate.py new file mode 100644 index 0000000..fb488c8 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/gpu-field/validate.py @@ -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=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 begin0,'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' + diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/fx-client.c b/tooling/probes/step20c-minimal-core-gate/graphics/clients/fx-client.c new file mode 100644 index 0000000..f09bc3a --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/fx-client.c @@ -0,0 +1,333 @@ +#define _POSIX_C_SOURCE 200809L +#include "layer-shell-client.h" +#include "xdg-shell-client.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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; +} diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/fx-clients.c b/tooling/probes/step20c-minimal-core-gate/graphics/clients/fx-clients.c new file mode 100644 index 0000000..2c67fb4 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/fx-clients.c @@ -0,0 +1,303 @@ +#define _POSIX_C_SOURCE 200809L +#include "layer-shell-client.h" +#include "xdg-shell-client.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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; +} diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/layer-shell-client.h b/tooling/probes/step20c-minimal-core-gate/graphics/clients/layer-shell-client.h new file mode 100644 index 0000000..4415c07 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/layer-shell-client.h @@ -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 +#include +#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 + *
+ *
+ * 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.
+ * 
+ */ +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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/layer-shell-protocol.c b/tooling/probes/step20c-minimal-core-gate/graphics/clients/layer-shell-protocol.c new file mode 100644 index 0000000..f74b898 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/layer-shell-protocol.c @@ -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 +#include +#include +#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, +}; + diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/nested-client.c b/tooling/probes/step20c-minimal-core-gate/graphics/clients/nested-client.c new file mode 100644 index 0000000..09d93ec --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/nested-client.c @@ -0,0 +1,270 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; icolor; + 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; +} diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/pointer-daemon.c b/tooling/probes/step20c-minimal-core-gate/graphics/clients/pointer-daemon.c new file mode 100644 index 0000000..9c85433 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/pointer-daemon.c @@ -0,0 +1,57 @@ +#define _DEFAULT_SOURCE +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/region-client.c b/tooling/probes/step20c-minimal-core-gate/graphics/clients/region-client.c new file mode 100644 index 0000000..71af452 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/region-client.c @@ -0,0 +1,284 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; icolor; + 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; +} diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/saved-client.c b/tooling/probes/step20c-minimal-core-gate/graphics/clients/saved-client.c new file mode 100644 index 0000000..c5aa1de --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/saved-client.c @@ -0,0 +1,224 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/static-reference.c b/tooling/probes/step20c-minimal-core-gate/graphics/clients/static-reference.c new file mode 100644 index 0000000..c6818ba --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/static-reference.c @@ -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" diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/vertical-client.c b/tooling/probes/step20c-minimal-core-gate/graphics/clients/vertical-client.c new file mode 100644 index 0000000..f26fbcd --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/vertical-client.c @@ -0,0 +1,280 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; icolor; + 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; +} diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/virtual-pointer-client.h b/tooling/probes/step20c-minimal-core-gate/graphics/clients/virtual-pointer-client.h new file mode 100644 index 0000000..c09a707 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/virtual-pointer-client.h @@ -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 +#include +#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 + *
+ *
+ * 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.
+ * 
+ */ +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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/virtual-pointer-protocol.c b/tooling/probes/step20c-minimal-core-gate/graphics/clients/virtual-pointer-protocol.c new file mode 100644 index 0000000..7b0236d --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/virtual-pointer-protocol.c @@ -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 +#include +#include +#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, +}; + diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/wlr-layer-shell-unstable-v1.xml b/tooling/probes/step20c-minimal-core-gate/graphics/clients/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 0000000..d62fd51 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,390 @@ + + + + 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. + + + + + 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. + + + + + 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. + + + + + + + + + + + + + + + + + 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. + + + + + + + + + + + + + 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. + + + + + + + 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. + + + + + 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. + + + + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + + + + 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. + + + + + 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. + + + + + 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. + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + This request destroys the layer surface. + + + + + + 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. + + + + + + + + + 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. + + + + + + + + + + + + + + + + + + + + + + Change the layer that the surface is rendered on. + + Layer is double-buffered, see wl_surface.commit. + + + + + diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/xdg-shell-client.h b/tooling/probes/step20c-minimal-core-gate/graphics/clients/xdg-shell-client.h new file mode 100644 index 0000000..64470e4 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/xdg-shell-client.h @@ -0,0 +1,2392 @@ +/* Generated by wayland-scanner 1.26.0 */ + +#ifndef XDG_SHELL_CLIENT_PROTOCOL_H +#define XDG_SHELL_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_xdg_shell The xdg_shell protocol + * @section page_ifaces_xdg_shell Interfaces + * - @subpage page_iface_xdg_wm_base - create desktop-style surfaces + * - @subpage page_iface_xdg_positioner - child surface positioner + * - @subpage page_iface_xdg_surface - desktop user interface surface base interface + * - @subpage page_iface_xdg_toplevel - toplevel surface + * - @subpage page_iface_xdg_popup - short-lived, popup surfaces for menus + * @section page_copyright_xdg_shell Copyright + *
+ *
+ * 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.
+ * 
+ */ +struct wl_output; +struct wl_seat; +struct wl_surface; +struct xdg_popup; +struct xdg_positioner; +struct xdg_surface; +struct xdg_toplevel; +struct xdg_wm_base; + +#ifndef XDG_WM_BASE_INTERFACE +#define XDG_WM_BASE_INTERFACE +/** + * @page page_iface_xdg_wm_base xdg_wm_base + * @section page_iface_xdg_wm_base_desc Description + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + * @section page_iface_xdg_wm_base_api API + * See @ref iface_xdg_wm_base. + */ +/** + * @defgroup iface_xdg_wm_base The xdg_wm_base interface + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + */ +extern const struct wl_interface xdg_wm_base_interface; +#endif +#ifndef XDG_POSITIONER_INTERFACE +#define XDG_POSITIONER_INTERFACE +/** + * @page page_iface_xdg_positioner xdg_positioner + * @section page_iface_xdg_positioner_desc Description + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an invalid_positioner error. + * @section page_iface_xdg_positioner_api API + * See @ref iface_xdg_positioner. + */ +/** + * @defgroup iface_xdg_positioner The xdg_positioner interface + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an invalid_positioner error. + */ +extern const struct wl_interface xdg_positioner_interface; +#endif +#ifndef XDG_SURFACE_INTERFACE +#define XDG_SURFACE_INTERFACE +/** + * @page page_iface_xdg_surface xdg_surface + * @section page_iface_xdg_surface_desc Description + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_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 xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up (e.g. by sending + * the title, app ID, size constraints, parent, etc), the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with initial wl_surface state such as + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure + * event. The client must acknowledge it and is then allowed to attach a + * buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed, i.e. the client must perform the initial commit + * again before attaching a buffer. + * @section page_iface_xdg_surface_api API + * See @ref iface_xdg_surface. + */ +/** + * @defgroup iface_xdg_surface The xdg_surface interface + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_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 xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up (e.g. by sending + * the title, app ID, size constraints, parent, etc), the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with initial wl_surface state such as + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure + * event. The client must acknowledge it and is then allowed to attach a + * buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed, i.e. the client must perform the initial commit + * again before attaching a buffer. + */ +extern const struct wl_interface xdg_surface_interface; +#endif +#ifndef XDG_TOPLEVEL_INTERFACE +#define XDG_TOPLEVEL_INTERFACE +/** + * @page page_iface_xdg_toplevel xdg_toplevel + * @section page_iface_xdg_toplevel_desc Description + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * An xdg_toplevel by default is responsible for providing the full intended + * visual representation of the toplevel, which depending on the window + * state, may mean things like a title bar, window controls and drop shadow. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by performing a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + * @section page_iface_xdg_toplevel_api API + * See @ref iface_xdg_toplevel. + */ +/** + * @defgroup iface_xdg_toplevel The xdg_toplevel interface + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * An xdg_toplevel by default is responsible for providing the full intended + * visual representation of the toplevel, which depending on the window + * state, may mean things like a title bar, window controls and drop shadow. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by performing a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + */ +extern const struct wl_interface xdg_toplevel_interface; +#endif +#ifndef XDG_POPUP_INTERFACE +#define XDG_POPUP_INTERFACE +/** + * @page page_iface_xdg_popup xdg_popup + * @section page_iface_xdg_popup_desc Description + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + * @section page_iface_xdg_popup_api API + * See @ref iface_xdg_popup. + */ +/** + * @defgroup iface_xdg_popup The xdg_popup interface + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + */ +extern const struct wl_interface xdg_popup_interface; +#endif + +#ifndef XDG_WM_BASE_ERROR_ENUM +#define XDG_WM_BASE_ERROR_ENUM +enum xdg_wm_base_error { + /** + * given wl_surface has another role + */ + XDG_WM_BASE_ERROR_ROLE = 0, + /** + * xdg_wm_base was destroyed before children + */ + XDG_WM_BASE_ERROR_DEFUNCT_SURFACES = 1, + /** + * the client tried to map or destroy a non-topmost popup + */ + XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP = 2, + /** + * the client specified an invalid popup parent surface + */ + XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT = 3, + /** + * the client provided an invalid surface state + */ + XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE = 4, + /** + * the client provided an invalid positioner + */ + XDG_WM_BASE_ERROR_INVALID_POSITIONER = 5, + /** + * the client didn’t respond to a ping event in time + */ + XDG_WM_BASE_ERROR_UNRESPONSIVE = 6, +}; +#endif /* XDG_WM_BASE_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_wm_base + * @struct xdg_wm_base_listener + */ +struct xdg_wm_base_listener { + /** + * check if the client is alive + * + * The ping event asks the client if it's still alive. Pass the + * serial specified in the event back to the compositor by sending + * a "pong" request back with the specified serial. See + * xdg_wm_base.pong. + * + * Compositors can use this to determine if the client is still + * alive. It's unspecified what will happen if the client doesn't + * respond to the ping request, or in what timeframe. Clients + * should try to respond in a reasonable amount of time. The + * “unresponsive” error is provided for compositors that wish + * to disconnect unresponsive clients. + * + * A compositor is free to ping in any way it wants, but a client + * must always respond to any xdg_wm_base object it created. + * @param serial pass this to the pong request + */ + void (*ping)(void *data, + struct xdg_wm_base *xdg_wm_base, + uint32_t serial); +}; + +/** + * @ingroup iface_xdg_wm_base + */ +static inline int +xdg_wm_base_add_listener(struct xdg_wm_base *xdg_wm_base, + const struct xdg_wm_base_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_wm_base, + (void (**)(void)) listener, data); +} + +#define XDG_WM_BASE_DESTROY 0 +#define XDG_WM_BASE_CREATE_POSITIONER 1 +#define XDG_WM_BASE_GET_XDG_SURFACE 2 +#define XDG_WM_BASE_PONG 3 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PING_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_CREATE_POSITIONER_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_GET_XDG_SURFACE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PONG_SINCE_VERSION 1 + +/** @ingroup iface_xdg_wm_base */ +static inline void +xdg_wm_base_set_user_data(struct xdg_wm_base *xdg_wm_base, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_wm_base, user_data); +} + +/** @ingroup iface_xdg_wm_base */ +static inline void * +xdg_wm_base_get_user_data(struct xdg_wm_base *xdg_wm_base) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_wm_base); +} + +static inline uint32_t +xdg_wm_base_get_version(struct xdg_wm_base *xdg_wm_base) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_wm_base); +} + +/** + * @ingroup iface_xdg_wm_base + * + * Destroy this xdg_wm_base object. + * + * Destroying a bound xdg_wm_base object while there are surfaces + * still alive created by this xdg_wm_base object instance is illegal + * and will result in a defunct_surfaces error. + */ +static inline void +xdg_wm_base_destroy(struct xdg_wm_base *xdg_wm_base) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_wm_base + * + * Create a positioner object. A positioner object is used to position + * surfaces relative to some parent surface. See the interface description + * and xdg_surface.get_popup for details. + */ +static inline struct xdg_positioner * +xdg_wm_base_create_positioner(struct xdg_wm_base *xdg_wm_base) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_CREATE_POSITIONER, &xdg_positioner_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL); + + return (struct xdg_positioner *) id; +} + +/** + * @ingroup iface_xdg_wm_base + * + * This creates an xdg_surface for the given surface. An xdg_surface is + * used as basis to define a role to a given surface, such as xdg_toplevel + * or xdg_popup. It also manages functionality shared between xdg_surface + * based surface roles. + * + * While xdg_surface itself is not a role, the corresponding surface may + * only be assigned a role extending xdg_surface, such as xdg_toplevel or + * xdg_popup. It is illegal to create an xdg_surface for a wl_surface which + * already has anassigned role and this will result in a role error. + * + * See the documentation of xdg_surface for more details about what an + * xdg_surface is and how it is used. + */ +static inline struct xdg_surface * +xdg_wm_base_get_xdg_surface(struct xdg_wm_base *xdg_wm_base, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_GET_XDG_SURFACE, &xdg_surface_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL, surface); + + return (struct xdg_surface *) id; +} + +/** + * @ingroup iface_xdg_wm_base + * + * A client must respond to a ping event with a pong request or + * the client may be deemed unresponsive. See xdg_wm_base.ping + * and xdg_wm_base.error.unresponsive. + */ +static inline void +xdg_wm_base_pong(struct xdg_wm_base *xdg_wm_base, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, serial); +} + +#ifndef XDG_POSITIONER_ERROR_ENUM +#define XDG_POSITIONER_ERROR_ENUM +enum xdg_positioner_error { + /** + * invalid input provided + */ + XDG_POSITIONER_ERROR_INVALID_INPUT = 0, +}; +#endif /* XDG_POSITIONER_ERROR_ENUM */ + +#ifndef XDG_POSITIONER_ANCHOR_ENUM +#define XDG_POSITIONER_ANCHOR_ENUM +enum xdg_positioner_anchor { + XDG_POSITIONER_ANCHOR_NONE = 0, + XDG_POSITIONER_ANCHOR_TOP = 1, + XDG_POSITIONER_ANCHOR_BOTTOM = 2, + XDG_POSITIONER_ANCHOR_LEFT = 3, + XDG_POSITIONER_ANCHOR_RIGHT = 4, + XDG_POSITIONER_ANCHOR_TOP_LEFT = 5, + XDG_POSITIONER_ANCHOR_BOTTOM_LEFT = 6, + XDG_POSITIONER_ANCHOR_TOP_RIGHT = 7, + XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_ANCHOR_ENUM */ + +#ifndef XDG_POSITIONER_GRAVITY_ENUM +#define XDG_POSITIONER_GRAVITY_ENUM +enum xdg_positioner_gravity { + XDG_POSITIONER_GRAVITY_NONE = 0, + XDG_POSITIONER_GRAVITY_TOP = 1, + XDG_POSITIONER_GRAVITY_BOTTOM = 2, + XDG_POSITIONER_GRAVITY_LEFT = 3, + XDG_POSITIONER_GRAVITY_RIGHT = 4, + XDG_POSITIONER_GRAVITY_TOP_LEFT = 5, + XDG_POSITIONER_GRAVITY_BOTTOM_LEFT = 6, + XDG_POSITIONER_GRAVITY_TOP_RIGHT = 7, + XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_GRAVITY_ENUM */ + +#ifndef XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +#define XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +/** + * @ingroup iface_xdg_positioner + * constraint adjustments + * + * The constraint adjustment value define ways the compositor will adjust + * the position of the surface, if the unadjusted position would result + * in the surface being partly constrained. + * + * Whether a surface is considered 'constrained' is left to the compositor + * to determine. For example, the surface may be partly outside the + * compositor's defined 'work area', thus necessitating the child surface's + * position be adjusted until it is entirely inside the work area. + * + * The adjustments can be combined, according to a defined precedence: 1) + * Flip, 2) Slide, 3) Resize. + */ +enum xdg_positioner_constraint_adjustment { + /** + * don't move the child surface when constrained + * + * Don't alter the surface position even if it is constrained on + * some axis, for example partially outside the edge of an output. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE = 0, + /** + * move along the x axis until unconstrained + * + * Slide the surface along the x axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the x + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the x axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1, + /** + * move along the y axis until unconstrained + * + * Slide the surface along the y axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the y + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the y axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2, + /** + * invert the anchor and gravity on the x axis + * + * Invert the anchor and gravity on the x axis if the surface is + * constrained on the x axis. For example, if the left edge of the + * surface is constrained, the gravity is 'left' and the anchor is + * 'left', change the gravity to 'right' and the anchor to 'right'. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_x adjustment will be the one + * before the adjustment. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X = 4, + /** + * invert the anchor and gravity on the y axis + * + * Invert the anchor and gravity on the y axis if the surface is + * constrained on the y axis. For example, if the bottom edge of + * the surface is constrained, the gravity is 'bottom' and the + * anchor is 'bottom', change the gravity to 'top' and the anchor + * to 'top'. + * + * The adjusted position is calculated given the original anchor + * rectangle and offset, but with the new flipped anchor and + * gravity values. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_y adjustment will be the one + * before the adjustment. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8, + /** + * horizontally resize the surface + * + * Resize the surface horizontally so that it is completely + * unconstrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16, + /** + * vertically resize the surface + * + * Resize the surface vertically so that it is completely + * unconstrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32, +}; +#endif /* XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM */ + +#define XDG_POSITIONER_DESTROY 0 +#define XDG_POSITIONER_SET_SIZE 1 +#define XDG_POSITIONER_SET_ANCHOR_RECT 2 +#define XDG_POSITIONER_SET_ANCHOR 3 +#define XDG_POSITIONER_SET_GRAVITY 4 +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT 5 +#define XDG_POSITIONER_SET_OFFSET 6 +#define XDG_POSITIONER_SET_REACTIVE 7 +#define XDG_POSITIONER_SET_PARENT_SIZE 8 +#define XDG_POSITIONER_SET_PARENT_CONFIGURE 9 + + +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_RECT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_GRAVITY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3 + +/** @ingroup iface_xdg_positioner */ +static inline void +xdg_positioner_set_user_data(struct xdg_positioner *xdg_positioner, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_positioner, user_data); +} + +/** @ingroup iface_xdg_positioner */ +static inline void * +xdg_positioner_get_user_data(struct xdg_positioner *xdg_positioner) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_positioner); +} + +static inline uint32_t +xdg_positioner_get_version(struct xdg_positioner *xdg_positioner) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_positioner); +} + +/** + * @ingroup iface_xdg_positioner + * + * Notify the compositor that the xdg_positioner will no longer be used. + */ +static inline void +xdg_positioner_destroy(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the size of the surface that is to be positioned with the positioner + * object. The size is in surface-local coordinates and corresponds to the + * window geometry. See xdg_surface.set_window_geometry. + * + * If a zero or negative size is set the invalid_input error is raised. + */ +static inline void +xdg_positioner_set_size(struct xdg_positioner *xdg_positioner, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, width, height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify the anchor rectangle within the parent surface that the child + * surface will be placed relative to. The rectangle is relative to the + * window geometry as defined by xdg_surface.set_window_geometry of the + * parent surface. + * + * When the xdg_positioner object is used to position a child surface, the + * anchor rectangle may not extend outside the window geometry of the + * positioned child's parent surface. + * + * If a negative size is set the invalid_input error is raised. + */ +static inline void +xdg_positioner_set_anchor_rect(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR_RECT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y, width, height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Defines the anchor point for the anchor rectangle. The specified anchor + * is used to derive an anchor point that the child surface will be + * positioned relative to. If a corner anchor is set (e.g. 'top_left' or + * 'bottom_right'), the anchor point will be at the specified corner; + * otherwise, the derived anchor point will be centered on the specified + * edge, or in the center of the anchor rectangle if no edge is specified. + */ +static inline void +xdg_positioner_set_anchor(struct xdg_positioner *xdg_positioner, uint32_t anchor) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, anchor); +} + +/** + * @ingroup iface_xdg_positioner + * + * Defines in what direction a surface should be positioned, relative to + * the anchor point of the parent surface. If a corner gravity is + * specified (e.g. 'bottom_right' or 'top_left'), then the child surface + * will be placed towards the specified gravity; otherwise, the child + * surface will be centered over the anchor point on any axis that had no + * gravity specified. If the gravity is not in the ‘gravity’ enum, an + * invalid_input error is raised. + */ +static inline void +xdg_positioner_set_gravity(struct xdg_positioner *xdg_positioner, uint32_t gravity) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_GRAVITY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, gravity); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify how the window should be positioned if the originally intended + * position caused the surface to be constrained, meaning at least + * partially outside positioning boundaries set by the compositor. The + * adjustment is set by constructing a bitmask describing the adjustment to + * be made when the surface is constrained on that axis. + * + * If no bit for one axis is set, the compositor will assume that the child + * surface should not change its position on that axis when constrained. + * + * If more than one bit for one axis is set, the order of how adjustments + * are applied is specified in the corresponding adjustment descriptions. + * + * The default adjustment is none. + */ +static inline void +xdg_positioner_set_constraint_adjustment(struct xdg_positioner *xdg_positioner, uint32_t constraint_adjustment) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, constraint_adjustment); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify the surface position offset relative to the position of the + * anchor on the anchor rectangle and the anchor on the surface. For + * example if the anchor of the anchor rectangle is at (x, y), the surface + * has the gravity bottom|right, and the offset is (ox, oy), the calculated + * surface position will be (x + ox, y + oy). The offset position of the + * surface is the one used for constraint testing. See + * set_constraint_adjustment. + * + * An example use case is placing a popup menu on top of a user interface + * element, while aligning the user interface element of the parent surface + * with some user interface element placed somewhere in the popup surface. + */ +static inline void +xdg_positioner_set_offset(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y); +} + +/** + * @ingroup iface_xdg_positioner + * + * When set reactive, the surface is reconstrained if the conditions used + * for constraining changed, e.g. the parent window moved. + * + * If the conditions changed and the popup was reconstrained, an + * xdg_popup.configure event is sent with updated geometry, followed by an + * xdg_surface.configure event. + */ +static inline void +xdg_positioner_set_reactive(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_REACTIVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the parent window geometry the compositor should use when + * positioning the popup. The compositor may use this information to + * determine the future state the popup should be constrained using. If + * this doesn't match the dimension of the parent the popup is eventually + * positioned against, the behavior is undefined. + * + * The arguments are given in the surface-local coordinate space. + */ +static inline void +xdg_positioner_set_parent_size(struct xdg_positioner *xdg_positioner, int32_t parent_width, int32_t parent_height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, parent_width, parent_height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the serial of an xdg_surface.configure event this positioner will be + * used in response to. The compositor may use this information together + * with set_parent_size to determine what future state the popup should be + * constrained using. + */ +static inline void +xdg_positioner_set_parent_configure(struct xdg_positioner *xdg_positioner, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, serial); +} + +#ifndef XDG_SURFACE_ERROR_ENUM +#define XDG_SURFACE_ERROR_ENUM +enum xdg_surface_error { + /** + * Surface was not fully constructed + */ + XDG_SURFACE_ERROR_NOT_CONSTRUCTED = 1, + /** + * Surface was already constructed + */ + XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED = 2, + /** + * Attaching a buffer to an unconfigured surface + */ + XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER = 3, + /** + * Invalid serial number when acking a configure event + */ + XDG_SURFACE_ERROR_INVALID_SERIAL = 4, + /** + * Width or height was zero or negative + */ + XDG_SURFACE_ERROR_INVALID_SIZE = 5, + /** + * Surface was destroyed before its role object + */ + XDG_SURFACE_ERROR_DEFUNCT_ROLE_OBJECT = 6, +}; +#endif /* XDG_SURFACE_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_surface + * @struct xdg_surface_listener + */ +struct xdg_surface_listener { + /** + * suggest a surface change + * + * The configure event marks the end of a configure sequence. A + * configure sequence is a set of one or more events configuring + * the state of the xdg_surface, including the final + * xdg_surface.configure event. + * + * Where applicable, xdg_surface surface roles will during a + * configure sequence extend this event as a latched state sent as + * events before the xdg_surface.configure event. Such events + * should be considered to make up a set of atomically applied + * configuration states, where the xdg_surface.configure commits + * the accumulated state. + * + * 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. + * + * If the client receives multiple configure events before it can + * respond to one, it is free to discard all but the last event it + * received. + * @param serial serial of the configure event + */ + void (*configure)(void *data, + struct xdg_surface *xdg_surface, + uint32_t serial); +}; + +/** + * @ingroup iface_xdg_surface + */ +static inline int +xdg_surface_add_listener(struct xdg_surface *xdg_surface, + const struct xdg_surface_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_surface, + (void (**)(void)) listener, data); +} + +#define XDG_SURFACE_DESTROY 0 +#define XDG_SURFACE_GET_TOPLEVEL 1 +#define XDG_SURFACE_GET_POPUP 2 +#define XDG_SURFACE_SET_WINDOW_GEOMETRY 3 +#define XDG_SURFACE_ACK_CONFIGURE 4 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_CONFIGURE_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_TOPLEVEL_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_POPUP_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_ACK_CONFIGURE_SINCE_VERSION 1 + +/** @ingroup iface_xdg_surface */ +static inline void +xdg_surface_set_user_data(struct xdg_surface *xdg_surface, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_surface, user_data); +} + +/** @ingroup iface_xdg_surface */ +static inline void * +xdg_surface_get_user_data(struct xdg_surface *xdg_surface) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_surface); +} + +static inline uint32_t +xdg_surface_get_version(struct xdg_surface *xdg_surface) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_surface); +} + +/** + * @ingroup iface_xdg_surface + * + * Destroy the xdg_surface object. An xdg_surface must only be destroyed + * after its role object has been destroyed, otherwise + * a defunct_role_object error is raised. + */ +static inline void +xdg_surface_destroy(struct xdg_surface *xdg_surface) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_surface + * + * This creates an xdg_toplevel object for the given xdg_surface and gives + * the associated wl_surface the xdg_toplevel role. + * + * See the documentation of xdg_toplevel for more details about what an + * xdg_toplevel is and how it is used. + */ +static inline struct xdg_toplevel * +xdg_surface_get_toplevel(struct xdg_surface *xdg_surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_TOPLEVEL, &xdg_toplevel_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL); + + return (struct xdg_toplevel *) id; +} + +/** + * @ingroup iface_xdg_surface + * + * This creates an xdg_popup object for the given xdg_surface and gives + * the associated wl_surface the xdg_popup role. + * + * If null is passed as a parent, a parent surface must be specified using + * some other protocol, before committing the initial state. + * + * See the documentation of xdg_popup for more details about what an + * xdg_popup is and how it is used. + */ +static inline struct xdg_popup * +xdg_surface_get_popup(struct xdg_surface *xdg_surface, struct xdg_surface *parent, struct xdg_positioner *positioner) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_POPUP, &xdg_popup_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL, parent, positioner); + + return (struct xdg_popup *) id; +} + +/** + * @ingroup iface_xdg_surface + * + * The window geometry of a surface is its "visible bounds" from the + * user's perspective. Client-side decorations often have invisible + * portions like drop-shadows which should be ignored for the + * purposes of aligning, placing and constraining windows. Note that + * in some situations, compositors may clip rendering to the window + * geometry, so the client should avoid putting functional elements + * outside of it. + * + * The window geometry is double-buffered state, see wl_surface.commit. + * + * When maintaining a position, the compositor should treat the (x, y) + * coordinate of the window geometry as the top left corner of the window. + * A client changing the (x, y) window geometry coordinate should in + * general not alter the position of the window. + * + * Once the window geometry of the surface is set, it is not possible to + * unset it, and it will remain the same until set_window_geometry is + * called again, even if a new subsurface or buffer is attached. + * + * If never set, the value is the full bounds of the surface, + * including any subsurfaces. This updates dynamically on every + * commit. This unset is meant for extremely simple clients. + * + * The arguments are given in the surface-local coordinate space of + * the wl_surface associated with this xdg_surface, and may extend outside + * of the wl_surface itself to mark parts of the subsurface tree as part of + * the window geometry. + * + * When applied, the effective window geometry will be the set window + * geometry clamped to the bounding rectangle of the combined + * geometry of the surface of the xdg_surface and the associated + * subsurfaces. + * + * The effective geometry will not be recalculated unless a new call to + * set_window_geometry is done and the new pending surface state is + * subsequently applied. + * + * The width and height of the effective window geometry must be + * greater than zero. Setting an invalid size will raise an + * invalid_size error. + */ +static inline void +xdg_surface_set_window_geometry(struct xdg_surface *xdg_surface, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_SET_WINDOW_GEOMETRY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, x, y, width, height); +} + +/** + * @ingroup iface_xdg_surface + * + * 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. + * + * For instance, for toplevel surfaces the compositor might use this + * information to move a surface to the top left only when the client has + * drawn itself for the maximized or fullscreen state. + * + * If the client receives multiple configure events before it + * can respond to one, it only has to ack the last configure event. + * Acking a configure event that was never sent raises an invalid_serial + * error. + * + * 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. + * + * Sending an ack_configure request consumes the serial number sent with + * the request, as well as serial numbers sent by all configure events + * sent on this xdg_surface prior to the configure event referenced by + * the committed serial. + * + * It is an error to issue multiple ack_configure requests referencing a + * serial from the same configure event, or to issue an ack_configure + * request referencing a serial from a configure event issued before the + * event identified by the last ack_configure request for the same + * xdg_surface. Doing so will raise an invalid_serial error. + */ +static inline void +xdg_surface_ack_configure(struct xdg_surface *xdg_surface, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, serial); +} + +#ifndef XDG_TOPLEVEL_ERROR_ENUM +#define XDG_TOPLEVEL_ERROR_ENUM +enum xdg_toplevel_error { + /** + * provided value is not a valid variant of the resize_edge enum + */ + XDG_TOPLEVEL_ERROR_INVALID_RESIZE_EDGE = 0, + /** + * invalid parent toplevel + */ + XDG_TOPLEVEL_ERROR_INVALID_PARENT = 1, + /** + * client provided an invalid min or max size + */ + XDG_TOPLEVEL_ERROR_INVALID_SIZE = 2, +}; +#endif /* XDG_TOPLEVEL_ERROR_ENUM */ + +#ifndef XDG_TOPLEVEL_RESIZE_EDGE_ENUM +#define XDG_TOPLEVEL_RESIZE_EDGE_ENUM +/** + * @ingroup iface_xdg_toplevel + * edge values for resizing + * + * These values are used to indicate which edge of a surface + * is being dragged in a resize operation. + */ +enum xdg_toplevel_resize_edge { + XDG_TOPLEVEL_RESIZE_EDGE_NONE = 0, + XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2, + XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT = 5, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT = 6, + XDG_TOPLEVEL_RESIZE_EDGE_RIGHT = 8, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT = 9, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT = 10, +}; +#endif /* XDG_TOPLEVEL_RESIZE_EDGE_ENUM */ + +#ifndef XDG_TOPLEVEL_STATE_ENUM +#define XDG_TOPLEVEL_STATE_ENUM +/** + * @ingroup iface_xdg_toplevel + * types of state on the surface + * + * The different state values used on the surface. This is designed for + * state values like maximized, fullscreen. It is paired with the + * configure event to ensure that both the client and the compositor + * setting the state can be synchronized. + * + * States set in this way are double-buffered, see wl_surface.commit. + */ +enum xdg_toplevel_state { + /** + * the surface is maximized + * the surface is maximized + * + * The surface is maximized. The window geometry specified in the + * configure event must be obeyed by the client, or the + * xdg_wm_base.invalid_surface_state error is raised. + * + * The client should draw without shadow or other decoration + * outside of the window geometry. + */ + XDG_TOPLEVEL_STATE_MAXIMIZED = 1, + /** + * the surface is fullscreen + * the surface is fullscreen + * + * The surface is fullscreen. The window geometry specified in + * the configure event is a maximum; the client cannot resize + * beyond it. For a surface to cover the whole fullscreened area, + * the geometry dimensions must be obeyed by the client. For more + * details, see xdg_toplevel.set_fullscreen. + */ + XDG_TOPLEVEL_STATE_FULLSCREEN = 2, + /** + * the surface is being resized + * the surface is being resized + * + * The surface is being resized. The window geometry specified in + * the configure event is a maximum; the client cannot resize + * beyond it. Clients that have aspect ratio or cell sizing + * configuration can use a smaller size, however. + */ + XDG_TOPLEVEL_STATE_RESIZING = 3, + /** + * the surface is now activated + * the surface is now activated + * + * Client window decorations should be painted as if the window + * is active. Do not assume this means that the window actually has + * keyboard or pointer focus. + */ + XDG_TOPLEVEL_STATE_ACTIVATED = 4, + /** + * the surface’s left edge is tiled + * + * The window is currently in a tiled layout and the left edge is + * considered to be adjacent to another part of the tiling grid. + * + * The client should draw without shadow or other decoration + * outside of the window geometry on the left edge. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_LEFT = 5, + /** + * the surface’s right edge is tiled + * + * The window is currently in a tiled layout and the right edge + * is considered to be adjacent to another part of the tiling grid. + * + * The client should draw without shadow or other decoration + * outside of the window geometry on the right edge. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_RIGHT = 6, + /** + * the surface’s top edge is tiled + * + * The window is currently in a tiled layout and the top edge is + * considered to be adjacent to another part of the tiling grid. + * + * The client should draw without shadow or other decoration + * outside of the window geometry on the top edge. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_TOP = 7, + /** + * the surface’s bottom edge is tiled + * + * The window is currently in a tiled layout and the bottom edge + * is considered to be adjacent to another part of the tiling grid. + * + * The client should draw without shadow or other decoration + * outside of the window geometry on the bottom edge. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_BOTTOM = 8, + /** + * surface repaint is suspended + * + * The surface is currently not ordinarily being repainted; for + * example because its content is occluded by another window, or + * its outputs are switched off due to screen locking. + * @since 6 + */ + XDG_TOPLEVEL_STATE_SUSPENDED = 9, + /** + * the surface’s left edge is constrained + * + * The left edge of the window is currently constrained, meaning + * it shouldn't attempt to resize from that edge. It can for + * example mean it's tiled next to a monitor edge on the + * constrained side of the window. + * @since 7 + */ + XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT = 10, + /** + * the surface’s right edge is constrained + * + * The right edge of the window is currently constrained, meaning + * it shouldn't attempt to resize from that edge. It can for + * example mean it's tiled next to a monitor edge on the + * constrained side of the window. + * @since 7 + */ + XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT = 11, + /** + * the surface’s top edge is constrained + * + * The top edge of the window is currently constrained, meaning + * it shouldn't attempt to resize from that edge. It can for + * example mean it's tiled next to a monitor edge on the + * constrained side of the window. + * @since 7 + */ + XDG_TOPLEVEL_STATE_CONSTRAINED_TOP = 12, + /** + * the surface’s bottom edge is constrained + * + * The bottom edge of the window is currently constrained, + * meaning it shouldn't attempt to resize from that edge. It can + * for example mean it's tiled next to a monitor edge on the + * constrained side of the window. + * @since 7 + */ + XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM = 13, +}; +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_TOP_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_BOTTOM_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION 6 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION 7 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT_SINCE_VERSION 7 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_CONSTRAINED_TOP_SINCE_VERSION 7 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM_SINCE_VERSION 7 +#endif /* XDG_TOPLEVEL_STATE_ENUM */ + +#ifndef XDG_TOPLEVEL_WM_CAPABILITIES_ENUM +#define XDG_TOPLEVEL_WM_CAPABILITIES_ENUM +enum xdg_toplevel_wm_capabilities { + /** + * show_window_menu is available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU = 1, + /** + * set_maximized and unset_maximized are available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE = 2, + /** + * set_fullscreen and unset_fullscreen are available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN = 3, + /** + * set_minimized is available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE = 4, +}; +#endif /* XDG_TOPLEVEL_WM_CAPABILITIES_ENUM */ + +/** + * @ingroup iface_xdg_toplevel + * @struct xdg_toplevel_listener + */ +struct xdg_toplevel_listener { + /** + * suggest a surface change + * + * This configure event asks the client to resize its toplevel + * surface or to change its state. The configured state should not + * be applied immediately. See xdg_surface.configure for details. + * + * The width and height arguments specify a hint to the window + * about how its surface should be resized in window geometry + * coordinates. See set_window_geometry. + * + * If the width or height arguments are zero, it means the client + * should decide its own window dimension. This may happen when the + * compositor needs to configure the state of the surface but + * doesn't have any information about any previous or expected + * dimension. + * + * The states listed in the event specify how the width/height + * arguments should be interpreted, and possibly how it should be + * drawn. + * + * The states are sent as an array of 32-bit unsigned integers in + * native endianness. State values are defined in the state enum. + * + * Clients must send an ack_configure in response to this event. + * See xdg_surface.configure and xdg_surface.ack_configure for + * details. + * @param width suggested width of window + * @param height suggested height of window + * @param states suggested states of the window + */ + void (*configure)(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array *states); + /** + * surface wants to be closed + * + * The close event is sent by the compositor when the user wants + * the surface to be closed. This should be equivalent to the user + * clicking the close button in client-side decorations, if your + * application has any. + * + * This is only a request that the user intends to close the + * window. The client may choose to ignore this request, or show a + * dialog to ask the user to save their data, etc. + */ + void (*close)(void *data, + struct xdg_toplevel *xdg_toplevel); + /** + * recommended window geometry bounds + * + * The configure_bounds event may be sent prior to a + * xdg_toplevel.configure event to communicate the bounds a window + * geometry size is recommended to constrain to. + * + * The passed width and height are in surface coordinate space. If + * width and height are 0, it means bounds is unknown and + * equivalent to as if no configure_bounds event was ever sent for + * this surface. + * + * The bounds can for example correspond to the size of a monitor + * excluding any panels or other shell components, so that a + * surface isn't created in a way that it cannot fit. + * + * The bounds may change at any point, and in such a case, a new + * xdg_toplevel.configure_bounds will be sent, followed by + * xdg_toplevel.configure and xdg_surface.configure. + * @param width suggested maximum width of surface + * @param height suggested maximum height of surface + * @since 4 + */ + void (*configure_bounds)(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height); + /** + * compositor capabilities + * + * This event advertises the capabilities supported by the + * compositor. If a capability isn't supported, clients should hide + * or disable the UI elements that expose this functionality. For + * instance, if the compositor doesn't advertise support for + * minimized toplevels, a button triggering the set_minimized + * request should not be displayed. + * + * The compositor will ignore requests it doesn't support. For + * instance, a compositor which doesn't advertise support for + * minimized will ignore set_minimized requests. + * + * Compositors must send this event once before the first + * xdg_surface.configure event. When the capabilities change, + * compositors must send this event again and then send an + * xdg_surface.configure event. + * + * The configured state should not be applied immediately. See + * xdg_surface.configure for details. + * + * The capabilities are sent as an array of 32-bit unsigned + * integers in native endianness. Capability values are defined in + * the wm_capabilities enum. + * @param capabilities array of 32-bit capabilities + * @since 5 + */ + void (*wm_capabilities)(void *data, + struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities); +}; + +/** + * @ingroup iface_xdg_toplevel + */ +static inline int +xdg_toplevel_add_listener(struct xdg_toplevel *xdg_toplevel, + const struct xdg_toplevel_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_toplevel, + (void (**)(void)) listener, data); +} + +#define XDG_TOPLEVEL_DESTROY 0 +#define XDG_TOPLEVEL_SET_PARENT 1 +#define XDG_TOPLEVEL_SET_TITLE 2 +#define XDG_TOPLEVEL_SET_APP_ID 3 +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU 4 +#define XDG_TOPLEVEL_MOVE 5 +#define XDG_TOPLEVEL_RESIZE 6 +#define XDG_TOPLEVEL_SET_MAX_SIZE 7 +#define XDG_TOPLEVEL_SET_MIN_SIZE 8 +#define XDG_TOPLEVEL_SET_MAXIMIZED 9 +#define XDG_TOPLEVEL_UNSET_MAXIMIZED 10 +#define XDG_TOPLEVEL_SET_FULLSCREEN 11 +#define XDG_TOPLEVEL_UNSET_FULLSCREEN 12 +#define XDG_TOPLEVEL_SET_MINIMIZED 13 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CLOSE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION 4 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION 5 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_PARENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_TITLE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_APP_ID_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_MOVE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_RESIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAX_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MIN_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MINIMIZED_SINCE_VERSION 1 + +/** @ingroup iface_xdg_toplevel */ +static inline void +xdg_toplevel_set_user_data(struct xdg_toplevel *xdg_toplevel, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_toplevel, user_data); +} + +/** @ingroup iface_xdg_toplevel */ +static inline void * +xdg_toplevel_get_user_data(struct xdg_toplevel *xdg_toplevel) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_toplevel); +} + +static inline uint32_t +xdg_toplevel_get_version(struct xdg_toplevel *xdg_toplevel) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_toplevel); +} + +/** + * @ingroup iface_xdg_toplevel + * + * This request destroys the role surface and unmaps the surface; + * see "Unmapping" behavior in interface section for details. + */ +static inline void +xdg_toplevel_destroy(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set the "parent" of this surface. This surface should be stacked + * above the parent surface and all other ancestor surfaces. + * + * Parent surfaces should be set on dialogs, toolboxes, or other + * "auxiliary" surfaces, so that the parent is raised when the dialog + * is raised. + * + * Setting a null parent for a child surface unsets its parent. Setting + * a null parent for a surface which currently has no parent is a no-op. + * + * Only mapped surfaces can have child surfaces. Setting a parent which + * is not mapped is equivalent to setting a null parent. If a surface + * becomes unmapped, its children's parent is set to the parent of + * the now-unmapped surface. If the now-unmapped surface has no parent, + * its children's parent is unset. If the now-unmapped surface becomes + * mapped again, its parent-child relationship is not restored. + * + * The parent toplevel must not be one of the child toplevel's + * descendants, and the parent must be different from the child toplevel, + * otherwise the invalid_parent protocol error is raised. + */ +static inline void +xdg_toplevel_set_parent(struct xdg_toplevel *xdg_toplevel, struct xdg_toplevel *parent) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_PARENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, parent); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a short title for the surface. + * + * This string may be used to identify the surface in a task bar, + * window list, or other user interface elements provided by the + * compositor. + * + * The string must be encoded in UTF-8. + */ +static inline void +xdg_toplevel_set_title(struct xdg_toplevel *xdg_toplevel, const char *title) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, title); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set an application identifier for the surface. + * + * The app ID identifies the general class of applications to which + * the surface belongs. The compositor can use this to group multiple + * surfaces together, or to determine how to launch a new application. + * + * For D-Bus activatable applications, the app ID is used as the D-Bus + * service name. + * + * The compositor shell will try to group application surfaces together + * by their app ID. As a best practice, it is suggested to select app + * ID's that match the basename of the application's .desktop file. + * For example, "org.freedesktop.FooViewer" where the .desktop file is + * "org.freedesktop.FooViewer.desktop". + * + * Like other properties, a set_app_id request can be sent after the + * xdg_toplevel has been mapped to update the property. + * + * See the desktop-entry specification [0] for more details on + * application identifiers and how they relate to well-known D-Bus + * names and .desktop files. + * + * [0] https://standards.freedesktop.org/desktop-entry-spec/ + */ +static inline void +xdg_toplevel_set_app_id(struct xdg_toplevel *xdg_toplevel, const char *app_id) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_APP_ID, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, app_id); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Clients implementing client-side decorations might want to show + * a context menu when right-clicking on the decorations, giving the + * user a menu that they can use to maximize or minimize the window. + * + * This request asks the compositor to pop up such a window menu at + * the given position, relative to the local surface coordinates of + * the parent surface. There are no guarantees as to what menu items + * the window menu contains, or even if a window menu will be drawn + * at all. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. + */ +static inline void +xdg_toplevel_show_window_menu(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SHOW_WINDOW_MENU, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, x, y); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Start an interactive, user-driven move of the surface. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. The passed + * serial is used to determine the type of interactive move (touch, + * pointer, etc). + * + * The server may ignore move requests depending on the state of + * the surface (e.g. fullscreen or maximized), or if the passed serial + * is no longer valid. + * + * If triggered, the surface will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the move. It is up to the + * compositor to visually indicate that the move is taking place, such as + * updating a pointer cursor, during the move. There is no guarantee + * that the device focus will return when the move is completed. + */ +static inline void +xdg_toplevel_move(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Start a user-driven, interactive resize of the surface. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. The passed + * serial is used to determine the type of interactive resize (touch, + * pointer, etc). + * + * The server may ignore resize requests depending on the state of + * the surface (e.g. fullscreen or maximized). + * + * If triggered, the client will receive configure events with the + * "resize" state enum value and the expected sizes. See the "resize" + * enum value for more details about what is required. The client + * must also acknowledge configure events using "ack_configure". After + * the resize is completed, the client will receive another "configure" + * event without the resize state. + * + * If triggered, the surface also will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the resize. It is up to the + * compositor to visually indicate that the resize is taking place, + * such as updating a pointer cursor, during the resize. There is no + * guarantee that the device focus will return when the resize is + * completed. + * + * The edges parameter specifies how the surface should be resized, and + * is one of the values of the resize_edge enum. Values not matching + * a variant of the enum will cause the invalid_resize_edge protocol error. + * The compositor may use this information to update the surface position + * for example when dragging the top left corner. The compositor may also + * use this information to adapt its behavior, e.g. choose an appropriate + * cursor image. + */ +static inline void +xdg_toplevel_resize(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, uint32_t edges) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, edges); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a maximum size for the window. + * + * The client can specify a maximum size so that the compositor does + * not try to configure the window beyond this size. + * + * The width and height arguments are in window geometry coordinates. + * See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered, see wl_surface.commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the maximum + * size. The compositor may decide to ignore the values set by the + * client and request a larger size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected maximum size in the given dimension. + * As a result, a client wishing to reset the maximum size + * to an unspecified state can use zero for width and height in the + * request. + * + * Requesting a maximum size to be smaller than the minimum size of + * a surface is illegal and will result in an invalid_size error. + * + * The width and height must be greater than or equal to zero. Using + * strictly negative values for width or height will result in an + * invalid_size error. + */ +static inline void +xdg_toplevel_set_max_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAX_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a minimum size for the window. + * + * The client can specify a minimum size so that the compositor does + * not try to configure the window below this size. + * + * The width and height arguments are in window geometry coordinates. + * See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered, see wl_surface.commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the minimum + * size. The compositor may decide to ignore the values set by the + * client and request a smaller size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected minimum size in the given dimension. + * As a result, a client wishing to reset the minimum size + * to an unspecified state can use zero for width and height in the + * request. + * + * Requesting a minimum size to be larger than the maximum size of + * a surface is illegal and will result in an invalid_size error. + * + * The width and height must be greater than or equal to zero. Using + * strictly negative values for width and height will result in an + * invalid_size error. + */ +static inline void +xdg_toplevel_set_min_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MIN_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Maximize the surface. + * + * After requesting that the surface should be maximized, the compositor + * will respond by emitting a configure event. Whether this configure + * actually sets the window maximized is subject to compositor policies. + * The client must then update its content, drawing in the configured + * state. The client must also acknowledge the configure when committing + * the new content (see ack_configure). + * + * It is up to the compositor to decide how and where to maximize the + * surface, for example which output and what region of the screen should + * be used. + * + * If the surface was already maximized, the compositor will still emit + * a configure event with the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no direct + * effect. It may alter the state the surface is returned to when + * unmaximized unless overridden by the compositor. + */ +static inline void +xdg_toplevel_set_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Unmaximize the surface. + * + * After requesting that the surface should be unmaximized, the compositor + * will respond by emitting a configure event. Whether this actually + * un-maximizes the window is subject to compositor policies. + * If available and applicable, the compositor will include the window + * geometry dimensions the window had prior to being maximized in the + * configure event. The client must then update its content, drawing it in + * the configured state. The client must also acknowledge the configure + * when committing the new content (see ack_configure). + * + * It is up to the compositor to position the surface after it was + * unmaximized; usually the position the surface had before maximizing, if + * applicable. + * + * If the surface was already not maximized, the compositor will still + * emit a configure event without the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no direct + * effect. It may alter the state the surface is returned to when + * unmaximized unless overridden by the compositor. + */ +static inline void +xdg_toplevel_unset_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Make the surface fullscreen. + * + * After requesting that the surface should be fullscreened, the + * compositor will respond by emitting a configure event. Whether the + * client is actually put into a fullscreen state is subject to compositor + * policies. The client must also acknowledge the configure when + * committing the new content (see ack_configure). + * + * The output passed by the request indicates the client's preference as + * to which display it should be set fullscreen on. If this value is NULL, + * it's up to the compositor to choose which display will be used to map + * this surface. + * + * If the surface doesn't cover the whole output, the compositor will + * position the surface in the center of the output and compensate with + * border fill covering the rest of the output. The content of the + * border fill is undefined, but should be assumed to be in some way that + * attempts to blend into the surrounding area (e.g. solid black). + * + * If the fullscreened surface is not opaque, the compositor must make + * sure that other screen content not part of the same surface tree (made + * up of subsurfaces, popups or similarly coupled surfaces) are not + * visible below the fullscreened surface. + */ +static inline void +xdg_toplevel_set_fullscreen(struct xdg_toplevel *xdg_toplevel, struct wl_output *output) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, output); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Make the surface no longer fullscreen. + * + * After requesting that the surface should be unfullscreened, the + * compositor will respond by emitting a configure event. + * Whether this actually removes the fullscreen state of the client is + * subject to compositor policies. + * + * Making a surface unfullscreen sets states for the surface based on the following: + * * the state(s) it may have had before becoming fullscreen + * * any state(s) decided by the compositor + * * any state(s) requested by the client while the surface was fullscreen + * + * The compositor may include the previous window geometry dimensions in + * the configure event, if applicable. + * + * The client must also acknowledge the configure when committing the new + * content (see ack_configure). + */ +static inline void +xdg_toplevel_unset_fullscreen(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Request that the compositor minimize your surface. There is no + * way to know if the surface is currently minimized, nor is there + * any way to unset minimization on this surface. + * + * If you are looking to throttle redrawing when minimized, please + * instead use the wl_surface.frame event for this, as this will + * also work with live previews on windows in Alt-Tab, Expose or + * similar compositor features. + */ +static inline void +xdg_toplevel_set_minimized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +#ifndef XDG_POPUP_ERROR_ENUM +#define XDG_POPUP_ERROR_ENUM +enum xdg_popup_error { + /** + * tried to grab after being mapped + */ + XDG_POPUP_ERROR_INVALID_GRAB = 0, +}; +#endif /* XDG_POPUP_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_popup + * @struct xdg_popup_listener + */ +struct xdg_popup_listener { + /** + * configure the popup surface + * + * This event asks the popup surface to configure itself given + * the configuration. The configured state should not be applied + * immediately. See xdg_surface.configure for details. + * + * The x and y arguments represent the position the popup was + * placed at given the xdg_positioner rule, relative to the upper + * left corner of the window geometry of the parent surface. + * + * For version 2 or older, the configure event for an xdg_popup is + * only ever sent once for the initial configuration. Starting with + * version 3, it may be sent again if the popup is setup with an + * xdg_positioner with set_reactive requested, or in response to + * xdg_popup.reposition requests. + * @param x x position relative to parent surface window geometry + * @param y y position relative to parent surface window geometry + * @param width window geometry width + * @param height window geometry height + */ + void (*configure)(void *data, + struct xdg_popup *xdg_popup, + int32_t x, + int32_t y, + int32_t width, + int32_t height); + /** + * popup interaction is done + * + * The popup_done event is sent out when a popup is dismissed by + * the compositor. The client should destroy the xdg_popup object + * at this point. + */ + void (*popup_done)(void *data, + struct xdg_popup *xdg_popup); + /** + * signal the completion of a repositioned request + * + * The repositioned event is sent as part of a popup + * configuration sequence, together with xdg_popup.configure and + * lastly xdg_surface.configure to notify the completion of a + * reposition request. + * + * The repositioned event is to notify about the completion of a + * xdg_popup.reposition request. The token argument is the token + * passed in the xdg_popup.reposition request. + * + * Immediately after this event is emitted, xdg_popup.configure and + * xdg_surface.configure will be sent with the updated size and + * position, as well as a new configure serial. + * + * The client should optionally update the content of the popup, + * but must acknowledge the new popup configuration for the new + * position to take effect. See xdg_surface.ack_configure for + * details. + * @param token reposition request token + * @since 3 + */ + void (*repositioned)(void *data, + struct xdg_popup *xdg_popup, + uint32_t token); +}; + +/** + * @ingroup iface_xdg_popup + */ +static inline int +xdg_popup_add_listener(struct xdg_popup *xdg_popup, + const struct xdg_popup_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_popup, + (void (**)(void)) listener, data); +} + +#define XDG_POPUP_DESTROY 0 +#define XDG_POPUP_GRAB 1 +#define XDG_POPUP_REPOSITION 2 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_GRAB_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITION_SINCE_VERSION 3 + +/** @ingroup iface_xdg_popup */ +static inline void +xdg_popup_set_user_data(struct xdg_popup *xdg_popup, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_popup, user_data); +} + +/** @ingroup iface_xdg_popup */ +static inline void * +xdg_popup_get_user_data(struct xdg_popup *xdg_popup) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_popup); +} + +static inline uint32_t +xdg_popup_get_version(struct xdg_popup *xdg_popup) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_popup); +} + +/** + * @ingroup iface_xdg_popup + * + * This destroys the popup. Explicitly destroying the xdg_popup + * object will also dismiss the popup, and unmap the surface. + * + * If this xdg_popup is not the "topmost" popup, the + * xdg_wm_base.not_the_topmost_popup protocol error will be sent. + */ +static inline void +xdg_popup_destroy(struct xdg_popup *xdg_popup) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_popup + * + * This request makes the created popup take an explicit grab. An explicit + * grab will be dismissed when the user dismisses the popup, or when the + * client destroys the xdg_popup. This can be done by the user clicking + * outside the surface, using the keyboard, or even locking the screen + * through closing the lid or a timeout. + * + * If the compositor denies the grab, the popup will be immediately + * dismissed. + * + * This request must be used in response to some sort of user action like a + * button press, key press, or touch down event. The serial number of the + * event should be passed as 'serial'. + * + * The parent of a grabbing popup must either be an xdg_toplevel surface or + * another xdg_popup with an explicit grab. If the parent is another + * xdg_popup it means that the popups are nested, with this popup now being + * the topmost popup. + * + * Nested popups must be destroyed in the reverse order they were created + * in, e.g. the only popup you are allowed to destroy at all times is the + * topmost one. + * + * When compositors choose to dismiss a popup, they may dismiss every + * nested grabbing popup as well. When a compositor dismisses popups, it + * will follow the same dismissing order as required from the client. + * + * If the topmost grabbing popup is destroyed, the grab will be returned to + * the parent of the popup, if that parent previously had an explicit grab. + * + * If the parent is a grabbing popup which has already been dismissed, this + * popup will be immediately dismissed. If the parent is a popup that did + * not take an explicit grab, an error will be raised. + * + * During a popup grab, the client owning the grab will receive pointer + * and touch events for all their surfaces as normal (similar to an + * "owner-events" grab in X11 parlance), while the top most grabbing popup + * will always have keyboard focus. + */ +static inline void +xdg_popup_grab(struct xdg_popup *xdg_popup, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_GRAB, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, seat, serial); +} + +/** + * @ingroup iface_xdg_popup + * + * Reposition an already-mapped popup. The popup will be placed given the + * details in the passed xdg_positioner object, and a + * xdg_popup.repositioned followed by xdg_popup.configure and + * xdg_surface.configure will be emitted in response. Any parameters set + * by the previous positioner will be discarded. + * + * The passed token will be sent in the corresponding + * xdg_popup.repositioned event. The new popup position will not take + * effect until the corresponding configure event is acknowledged by the + * client. See xdg_popup.repositioned for details. The token itself is + * opaque, and has no other special meaning. + * + * If multiple reposition requests are sent, the compositor may skip all + * but the last one. + * + * If the popup is repositioned in response to a configure event for its + * parent, the client should send an xdg_positioner.set_parent_configure + * and possibly an xdg_positioner.set_parent_size request to allow the + * compositor to properly constrain the popup. + * + * If the popup is repositioned together with a parent that is being + * resized, but not in response to a configure event, the client should + * send an xdg_positioner.set_parent_size request. + */ +static inline void +xdg_popup_reposition(struct xdg_popup *xdg_popup, struct xdg_positioner *positioner, uint32_t token) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_REPOSITION, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, positioner, token); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/clients/xdg-shell-protocol.c b/tooling/probes/step20c-minimal-core-gate/graphics/clients/xdg-shell-protocol.c new file mode 100644 index 0000000..33881aa --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/clients/xdg-shell-protocol.c @@ -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 +#include +#include +#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, +}; + diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/configs/E0-core.conf b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E0-core.conf new file mode 100644 index 0000000..7cdba56 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E0-core.conf @@ -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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/configs/E0-pristine.conf b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E0-pristine.conf new file mode 100644 index 0000000..7cdba56 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E0-pristine.conf @@ -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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/configs/E1.conf b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E1.conf new file mode 100644 index 0000000..7cdba56 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E1.conf @@ -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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/configs/E2-fractional.conf b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E2-fractional.conf new file mode 100644 index 0000000..882f25d --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E2-fractional.conf @@ -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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/configs/E2-r90.conf b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E2-r90.conf new file mode 100644 index 0000000..128bcf7 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E2-r90.conf @@ -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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/configs/E2-vertical.conf b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E2-vertical.conf new file mode 100644 index 0000000..ed583f9 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E2-vertical.conf @@ -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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/configs/E3.conf b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E3.conf new file mode 100644 index 0000000..7cdba56 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E3.conf @@ -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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/configs/E4.conf b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E4.conf new file mode 100644 index 0000000..7cdba56 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E4.conf @@ -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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/configs/E5.conf b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E5.conf new file mode 100644 index 0000000..7cdba56 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E5.conf @@ -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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/configs/E6.conf b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E6.conf new file mode 100644 index 0000000..7cdba56 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E6.conf @@ -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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/configs/E7.conf b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E7.conf new file mode 100644 index 0000000..7cdba56 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/configs/E7.conf @@ -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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/engine.py b/tooling/probes/step20c-minimal-core-gate/graphics/engine.py new file mode 100644 index 0000000..0b01859 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/engine.py @@ -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()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 diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/fx.py b/tooling/probes/step20c-minimal-core-gate/graphics/fx.py new file mode 100644 index 0000000..3dc89ee --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/fx.py @@ -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'] diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/fx_validators.py b/tooling/probes/step20c-minimal-core-gate/graphics/fx_validators.py new file mode 100644 index 0000000..df9229a --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/fx_validators.py @@ -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]=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['BL'][0] and c['TL'][1]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]= 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} diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/pixels.py b/tooling/probes/step20c-minimal-core-gate/graphics/pixels.py new file mode 100644 index 0000000..e5660b6 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/pixels.py @@ -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()) diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/plans.py b/tooling/probes/step20c-minimal-core-gate/graphics/plans.py new file mode 100644 index 0000000..a7f321d --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/plans.py @@ -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]] diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/predicates.py b/tooling/probes/step20c-minimal-core-gate/graphics/predicates.py new file mode 100644 index 0000000..da957e4 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/predicates.py @@ -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=2 + assert request.start() 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 00,'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()) diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/saved.py b/tooling/probes/step20c-minimal-core-gate/graphics/saved.py new file mode 100644 index 0000000..f9a047a --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/saved.py @@ -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>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' diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/saved_validators.py b/tooling/probes/step20c-minimal-core-gate/graphics/saved_validators.py new file mode 100644 index 0000000..189cc33 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/saved_validators.py @@ -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_nsactivation_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") diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/seal.py b/tooling/probes/step20c-minimal-core-gate/graphics/seal.py new file mode 100644 index 0000000..2c9715a --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/seal.py @@ -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) diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/test_fixture_repair_R1.py b/tooling/probes/step20c-minimal-core-gate/graphics/test_fixture_repair_R1.py new file mode 100644 index 0000000..557619b --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/test_fixture_repair_R1.py @@ -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) diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/test_fixture_repair_R2.py b/tooling/probes/step20c-minimal-core-gate/graphics/test_fixture_repair_R2.py new file mode 100644 index 0000000..3938593 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/test_fixture_repair_R2.py @@ -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) diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/test_graphics.py b/tooling/probes/step20c-minimal-core-gate/graphics/test_graphics.py new file mode 100644 index 0000000..a816341 --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/test_graphics.py @@ -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) diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/test_workspace_marker_R2B.py b/tooling/probes/step20c-minimal-core-gate/graphics/test_workspace_marker_R2B.py new file mode 100644 index 0000000..e6f490a --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/test_workspace_marker_R2B.py @@ -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) diff --git a/tooling/probes/step20c-minimal-core-gate/graphics/validators.py b/tooling/probes/step20c-minimal-core-gate/graphics/validators.py new file mode 100644 index 0000000..529523a --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/graphics/validators.py @@ -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 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 diff --git a/tooling/probes/step20c-minimal-core-gate/provenance.json b/tooling/probes/step20c-minimal-core-gate/provenance.json new file mode 100644 index 0000000..18b011f --- /dev/null +++ b/tooling/probes/step20c-minimal-core-gate/provenance.json @@ -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" + ] +}