391f8fb9f764d9ff51db0629db01977ffbf7d4f3
1329 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
391f8fb9f7 |
feat(clipboard): Linux + Windows host clipboard backends as the pf-clipboard crate (Phase 1 host + Phase 3)
ci / web (pull_request) Successful in 1m9s
apple / swift (pull_request) Successful in 1m19s
apple / screenshots (pull_request) Has been skipped
ci / docs-site (pull_request) Successful in 1m30s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 5m16s
ci / bench (pull_request) Successful in 6m8s
ci / rust (pull_request) Failing after 7m6s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 6m33s
android / android (pull_request) Successful in 12m39s
The host half of the shared clipboard (design/clipboard-and-file-transfer.md §4),
ported from feat/shared-clipboard (
|
||
|
|
4ef90d586d |
feat(clipboard): wire protocol + client-core task for shared clipboard (Phase 0)
The portable shared-clipboard plane in punktfunk-core, all behind the `quic`
feature (design/clipboard-and-file-transfer.md §3):
- Control messages 0x40–0x44 (ClipControl / ClipOffer / ClipFetch...) and the
HOST_CAP_CLIPBOARD capability bit, negotiated in the Welcome caps.
- Per-transfer QUIC bi-streams ("PKFs" magic) for lazy fetch of offered content,
with ClipFetchHdr status/size framing (quic::clipstream).
- The §3.5 portable wire-MIME vocabulary (text/plain;utf-8, text/html, text/rtf,
image/png) shared by both ends.
- Client-side clipboard task (client.rs) + C ABI surface bumped to v8 (abi.rs,
regenerated include/punktfunk_core.h).
- Loopback transport tests (quic::tests).
No OS clipboard integration yet — that is the host backends (Phase 1/3) and the
macOS client (Phase 1).
Ported from feat/shared-clipboard (
|
||
|
|
47587827ec |
refactor(host/W6.0): hoist GamepadEvent/GamepadFrame to punktfunk_core::input
apple / swift (push) Successful in 1m22s
release / apple (push) Successful in 6m6s
apple / screenshots (push) Successful in 4m58s
windows-host / package (push) Successful in 15m58s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m59s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 4m2s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 4m58s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m35s
ci / web (push) Successful in 1m1s
ci / docs-site (push) Successful in 1m12s
decky / build-publish (push) Successful in 20s
android / android (push) Successful in 12m11s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 10s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 7s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
ci / bench (push) Successful in 6m17s
arch / build-publish (push) Successful in 13m21s
docker / deploy-docs (push) Successful in 11s
flatpak / build-publish (push) Successful in 6m37s
deb / build-publish (push) Successful in 12m28s
ci / rust (push) Successful in 21m52s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m43s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m46s
First de-coupling for the host crate carve (plan §W6.0 / §2.4): the GameStream (Moonlight-plane) decoded controller types were defined in gamestream/gamepad.rs — the "junk drawer" — yet consumed 18× by the platform-neutral input injectors AND by the Moonlight decode path. Once inject becomes pf-inject, reaching them via crate::gamestream would be an illegal upward edge. Move the two types to core::input (below both planes; inject already depends on core) and repoint every consumer. Also consolidate the duplicated MAX_PADS onto the existing core::input::MAX_PADS. The gamestream BTN_* const aliases stay for now (separate follow-up); decode()/rumble/tests remain in the Moonlight plane, now importing the types from core. Verified: Linux (home-worker-5) clippy -p punktfunk-core -p punktfunk-host --all-targets -D warnings + gamepad tests green; Windows (192.168.1.158) clippy -p punktfunk-host --features nvenc,amf-qsv --all-targets green (the inject/windows/* consumers compile). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e06ab59652 |
feat(sdk): punktfunk-scripting — the managed script/plugin runner (M5)
apple / swift (push) Successful in 1m18s
apple / screenshots (push) Successful in 4m42s
ci / web (push) Successful in 56s
ci / docs-site (push) Successful in 49s
decky / build-publish (push) Successful in 18s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 12s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
ci / bench (push) Successful in 5m14s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 12s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 56s
arch / build-publish (push) Successful in 15m11s
android / android (push) Successful in 15m50s
deb / build-publish (push) Successful in 16m9s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m12s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m8s
ci / rust (push) Successful in 24m55s
docker / deploy-docs (push) Successful in 10s
The optional supervision layer (RFC §8): one service runs everything in <config_dir>/scripts/ plus installed punktfunk-plugin-* packages (<config_dir>/plugins/node_modules/), as Effect fibers. - Plugins (a definePlugin default export, either main shape) are SUPERVISED: a failure restarts them with capped exponential backoff (jittered, 1s→60s); a clean return completes them. The Effect shape runs under the PunktfunkHost layer; the async-fn shape gets a facade client whose close is scope-guaranteed. - Bare scripts are one-shot: importing them is the run, no restart (export a plugin to be supervised). - Shutdown is STRUCTURAL: SIGINT/SIGTERM interrupt the whole fiber tree, so Effect plugins' scoped finalizers run and clients close before exit — the systemctl-stop story, and the reason the Effect plugin shape exists at all. - The sshd rule applies to unit files (world-writable → refused loudly); cache-busted imports make restarts real; --list for inventory. 6 new bun tests (17 total green): discovery + refusal, both plugin shapes against a mock host, crash→restart with backoff, one-shot semantics, and finalizer-on-interrupt. Live-verified against a real host: a supervised watcher plugin received library.changed through the pinned tunnel, and SIGTERM shut the tree down structurally (exit 0). Deferred to the packaging follow-up (release.yml is in flight in a parallel session): the vendored-Bun deb/rpm/iss packages and the host-log-ring tee (needs a host ingest endpoint); console page rides the other console surfaces. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f2a58f3a91 |
feat(host/library): external provider API — declarative reconcile (M4)
apple / swift (push) Successful in 1m15s
apple / screenshots (push) Successful in 4m37s
windows-host / package (push) Successful in 8m57s
ci / web (push) Successful in 53s
ci / docs-site (push) Successful in 58s
ci / bench (push) Successful in 5m51s
decky / build-publish (push) Successful in 26s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 13s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 43s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 16s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 11s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m5s
arch / build-publish (push) Successful in 16m51s
android / android (push) Successful in 17m6s
deb / build-publish (push) Successful in 17m13s
ci / rust (push) Successful in 18m41s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m0s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m31s
docker / deploy-docs (push) Successful in 10s
External game-library providers become first-class (RFC §8): a plugin
computes its desired title list and PUTs it — the host owns the diff.
- CustomEntry gains `provider` + `external_id` (API-set only; never on
manual entries). GameEntry surfaces `provider` for console attribution
and the new `GET /library?provider=` filter.
- PUT /api/v1/library/provider/{p}: atomic declarative reconcile keyed
on the provider's `external_id` — host ids stay stable across syncs,
orphans drop, manual entries and other providers are never touched,
an empty array clears the set. Validated: provider id [a-z0-9._-]
(`manual` reserved), unique non-empty external_ids.
- DELETE /api/v1/library/provider/{p}: clean uninstall, returns the
removed count.
- Ownership is unambiguous both ways: manual CRUD now returns 409 for a
provider-owned entry (MutateOutcome::ProviderOwned) instead of letting
an edit be silently clobbered at the next sync.
- library.changed now carries the mutating source (`manual` or the
provider id) — hooks and the SDK filter on it.
- Spec + SDK schemas regenerated; sdk/examples/provider-sync.ts is the
provider-plugin skeleton.
347 host tests green (pure reconcile: stable ids, orphan drop,
idempotence, bystanders untouched; name/payload validation; route 400s)
+ 11 SDK tests. Live-verified end to end THROUGH the SDK against a real
host: sync → filtered list → manual-delete 409 → re-sync with stable id
+ orphan drop → uninstall (removed=2), with three
library.changed(source=romm) events observed on the live stream.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
87114ab186 |
feat(sdk): @punktfunk/host — the Effect TypeScript SDK (M3)
apple / swift (push) Successful in 1m15s
apple / screenshots (push) Successful in 4m21s
ci / web (push) Successful in 52s
ci / docs-site (push) Successful in 1m0s
android / android (push) Has been cancelled
arch / build-publish (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / bench (push) Has been cancelled
deb / build-publish (push) Has been cancelled
decky / build-publish (push) Has been cancelled
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 18s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 15s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 14s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 16s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 13s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m56s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m42s
docker / deploy-docs (push) Successful in 26s
New top-level sdk/ package (RFC §7): a typed management-API client plus the lifecycle event stream, built on Effect, two surfaces over one core: - @punktfunk/host — the Promise facade front door: connect() resolves URL/token/TLS pin from the host's own files (zero config on the box), fails fast on bad credentials, pf.events.on() with typed callbacks (exact kinds, domain.* prefixes, "*", "dropped", "unknown"), pf.request() for the REST surface. Effect never required. - @punktfunk/host/effect — the PunktfunkHost service + PunktfunkHostLive layer, Stream-based events()/eventsRaw(), typed errors (AuthError | ApiError | TransportError | VersionSkew — a 2xx that fails its schema is a typed skew, not undefined later), and every wire shape as an effect/Schema: REST generated via orval client:'effect' from api/openapi.json (S3 spike: works well; the text/event-stream payload is out of its reach), events hand-mirrored from the host's snapshot-tested wire format as a kind-discriminated union. One reconnecting SSE core under both surfaces: spec-shaped parser, exponential+jittered backoff (capped, resets after a healthy connection), Last-Event-ID resume, 401 terminal. Default is LIVE tail only — a fresh notify script must not re-fire on the host's replayed ring (since: 0 opts into full replay). TLS: the pin trusts exactly the host's self-signed identity cert (chain-verified; hostname check waived — the cert is deliberately CN-only for fingerprint pinning). Bun via fetch tls, Node via an undici dispatcher (optionalDependency). definePlugin() accepts both main shapes (async fn | Effect requiring PunktfunkHost). Examples in both styles; README carries the compat contract + systemd/Task Scheduler templates. 11 bun tests green (wire decode against the Rust snapshot strings, SSE parser/reconnect/Last-Event-ID/401, both surfaces vs a mock host). Live-verified against a real host on Bun AND Node through the pinned loopback hop: connect → REST mutate → live event received → resume cursor advanced; a wrong CA is rejected. npm publish + CI wiring deferred (npm org = RFC open question 1). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
aaa3dcec32 |
refactor(host/W4): make the capture→encode edge one-way (OutputFormat back-ref)
apple / swift (push) Successful in 1m15s
apple / screenshots (push) Successful in 4m4s
windows-host / package (push) Successful in 9m4s
ci / web (push) Successful in 1m4s
ci / docs-site (push) Successful in 1m7s
ci / bench (push) Successful in 5m11s
decky / build-publish (push) Successful in 19s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
android / android (push) Successful in 15m38s
arch / build-publish (push) Successful in 14m57s
deb / build-publish (push) Successful in 12m59s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m34s
ci / rust (push) Has been cancelled
docker / deploy-docs (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m41s
The capture.rs facade no longer re-derives the encode backend. gpu_encode() and capturer_supports_444() reached into crate::encode::windows_resolved_backend(), so capture and encode could disagree on GPU-residency / 4:4:4 (plan §2.4). Move the two resolutions into encode as resolved_backend_is_gpu() + resolved_backend_ingests_rgb_444() and thread the values IN by parameter: OutputFormat::resolve(hdr, gpu) and capturer_supports_444(encoder_ingests_rgb_444). Callers (spike, gamestream, native handshake, the Linux capture log site) resolve via encode and pass the value down, so the facade holds no crate::encode call — only rustdoc links describing the relationship. Completes task #8 of W4. Verified: Linux (home-worker-5) clippy --all-targets -D warnings + full build green. Windows (.173) verify owed — box was offline this session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
991d28909b |
refactor(host/W4): carve the off-thread InjectorService out of the inject facade
apple / swift (push) Successful in 1m18s
apple / screenshots (push) Successful in 4m33s
windows-host / package (push) Successful in 16m29s
ci / web (push) Successful in 57s
ci / docs-site (push) Successful in 1m3s
ci / bench (push) Successful in 5m8s
decky / build-publish (push) Successful in 22s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
arch / build-publish (push) Successful in 15m42s
android / android (push) Successful in 16m17s
deb / build-publish (push) Successful in 12m9s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m2s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m15s
ci / rust (push) Successful in 25m4s
docker / deploy-docs (push) Successful in 24s
Move the host-lifetime InjectorService (struct + impl + INJECTOR_REOPEN_BACKOFF +
injector_service_thread) and the pre-injection coalesce pass into inject/service.rs,
alongside the coalesce unit tests. libei_ei_source stays in the facade as an open()
helper. Completes task #7 of W4 (the factory OS-representability fix landed in
|
||
|
|
9ea5c2a129 |
fix(host/inject): make the injector factory OS-representable + drop vestigial Uinput
apple / swift (push) Successful in 1m19s
apple / screenshots (push) Successful in 4m35s
windows-host / package (push) Failing after 15m33s
ci / web (push) Successful in 52s
ci / docs-site (push) Successful in 1m1s
ci / bench (push) Successful in 6m42s
decky / build-publish (push) Successful in 21s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 11s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 12s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 10s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 12s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 14s
arch / build-publish (push) Successful in 15m0s
android / android (push) Successful in 15m23s
deb / build-publish (push) Successful in 12m26s
ci / rust (push) Successful in 18m35s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m22s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 17m57s
docker / deploy-docs (push) Successful in 23s
Restructure open()/Backend/default_backend so an impossible OS/backend pairing
is a compile error instead of a runtime bail! (plan §2.3). Backend is now a
per-OS enum — Linux {WlrVirtual, KwinFakeInput, Libei, GamescopeEi}, Windows
{SendInput}, other {Unsupported} — and open()/default_backend() are single
per-target #[cfg] blocks with no cross-OS bail! arms.
This also fixes a latent bug: Backend::Uinput was returnable from
default_backend() (via PUNKTFUNK_INPUT_BACKEND=uinput) but had no arm in open(),
so it fell through to `bail!("not implemented")` — a runtime failure. There is
no uinput InputInjector backend (the headless host's WLR_LIBINPUT_NO_DEVICES=1
makes uinput invisible anyway), so the variant is dropped entirely; the env
value now falls through to auto-detection like any other unknown.
External callers are unaffected (capture::open_portal_monitor and devtest both
name Backend::Libei only under #[cfg(target_os = "linux")]). Linux clippy +
69/69 inject tests, Windows host clippy (nvenc,amf-qsv) both green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
880634b4c1 |
refactor(host/W4): split the IDD-push capturer's peripheral concerns into submodules
apple / swift (push) Successful in 1m24s
apple / screenshots (push) Successful in 4m12s
windows-host / package (push) Successful in 8m56s
ci / web (push) Successful in 1m1s
ci / docs-site (push) Successful in 1m10s
arch / build-publish (push) Successful in 11m2s
ci / bench (push) Successful in 5m16s
decky / build-publish (push) Successful in 19s
android / android (push) Successful in 17m20s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 10s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
ci / rust (push) Successful in 17m55s
deb / build-publish (push) Successful in 11m50s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m45s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 17m36s
docker / deploy-docs (push) Successful in 24s
Carve three self-contained clusters off the Windows IDD-push capturer (capture/windows/idd_push.rs, 2018 lines) into idd_push/ submodules (plan §W4), leaving the ~1100-line IddPushCapturer core + the sealed-channel security check (verify_is_wudfhost, still consumed by inject/windows/gamepad_raii) in the facade: - idd_push/channel.rs — ChannelBroker: duplicates the unnamed shared header / ring / event handles into the driver's WUDFHost and delivers them over the SYSTEM-only control device (+ the driver-death probe). - idd_push/descriptor.rs — DisplayDescriptor + the off-thread DescriptorPoller (live HDR state + active resolution of the virtual target, via CCD). - idd_push/stall.rs — Stall + StallWatch: the DWM-composition-hole diagnostic. Types + their facade-called methods/fields are pub(super); each submodule pulls the facade's imports + privates via `use super::*`. Pure move; no behavior change. Windows host clippy (nvenc,amf-qsv, all-targets) + fmt green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
265554b755 |
refactor(host/W4): carve the EGL blit's GL plumbing into egl/gl.rs
apple / swift (push) Successful in 1m22s
apple / screenshots (push) Successful in 4m43s
windows-host / package (push) Successful in 9m2s
ci / web (push) Successful in 55s
android / android (push) Successful in 11m56s
ci / docs-site (push) Successful in 53s
decky / build-publish (push) Successful in 18s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 11s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 11s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 11s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
arch / build-publish (push) Successful in 10m51s
ci / bench (push) Successful in 5m25s
deb / build-publish (push) Successful in 12m0s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 20m33s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 20m16s
ci / rust (push) Successful in 25m32s
docker / deploy-docs (push) Successful in 23s
Split the zero-copy EGL backend (linux/zerocopy/egl.rs, 1208 lines) into a facade + egl/gl.rs (plan §W4 / §3.2). gl.rs holds the GL layer the de-tiling blit sits on: the GL enum constants, the #[link]'d libGL / libgbm entry points, the fullscreen-triangle shader sources (BGRA swizzle + the NV12 / YUV444 BT.709 convert passes), and the shader/program compile helpers. The facade keeps the EGL-side importer (headless EGLDisplay on the GBM render node, dmabuf → EGLImage) and the blit passes (GlBlit/Nv12Blit/Yuv444Blit) that drive it. Pure move; no behavior change. Linux clippy --all-targets + fmt green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
cb7091e1d5 |
refactor(host/W4): carve the raw CUDA driver-API FFI into cuda/ffi.rs
ci / web (push) Successful in 51s
ci / docs-site (push) Successful in 53s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 11s
decky / build-publish (push) Successful in 21s
apple / swift (push) Successful in 1m20s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 29s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 46s
ci / bench (push) Successful in 5m53s
apple / screenshots (push) Successful in 4m37s
docker / deploy-docs (push) Successful in 29s
windows-host / package (push) Successful in 8m54s
deb / build-publish (push) Successful in 12m42s
arch / build-publish (push) Successful in 16m49s
android / android (push) Successful in 17m18s
ci / rust (push) Successful in 18m31s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m8s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 18m51s
Split the zero-copy CUDA backend (linux/zerocopy/cuda.rs, 1843 lines) into a facade + cuda/ffi.rs (plan §W4 / §3.2). ffi.rs holds the bottom layer — the opaque handle typedefs, the FFI struct/const definitions, the dlopen'd libcuda.so.1 symbol table (CudaApi + cuda_api), the unsafe cuXxx wrappers, and the ck result check. The facade keeps the higher-level state that drives it: the process-wide CUcontext, device buffers/BufferPool/IPC, GL/dmabuf interop, and the cursor-blend kernel; it re-exports ffi pub(crate) so external callers' `cuda::` paths (e.g. cuda::CUdeviceptr) are unchanged. Pure move; no behavior change. Linux clippy --all-targets + fmt green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
dd462787ec |
docs: events & hooks operator page (automation.md)
apple / swift (push) Successful in 1m18s
apple / screenshots (push) Successful in 4m34s
ci / web (push) Successful in 56s
ci / docs-site (push) Successful in 1m3s
decky / build-publish (push) Successful in 19s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 12s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
ci / bench (push) Successful in 5m15s
android / android (push) Has been cancelled
arch / build-publish (push) Has been cancelled
ci / rust (push) Has been cancelled
deb / build-publish (push) Has been cancelled
docker / deploy-docs (push) Has been cancelled
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Has been cancelled
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
The lifecycle-event catalog, hooks.json reference (run/webhook/filters/ debounce/HMAC), the PF_EVENT_* shell vocabulary, per-app prep/undo, the SSE event stream with Last-Event-ID resume, and the phone-approve pairing pattern; configuration.md gains the ON_CONNECT/ON_DISCONNECT env-mirror rows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
63efe0ecd5 |
feat(host/hooks): per-app prep/undo commands (M2b — Sunshine prep-cmd parity)
apple / swift (push) Successful in 1m18s
apple / screenshots (push) Successful in 4m22s
ci / web (push) Successful in 53s
ci / docs-site (push) Successful in 1m3s
decky / build-publish (push) Successful in 20s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 33s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
ci / bench (push) Successful in 5m3s
android / android (push) Successful in 17m6s
arch / build-publish (push) Successful in 16m51s
deb / build-publish (push) Successful in 12m16s
ci / rust (push) Successful in 25m22s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m16s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m17s
windows-host / package (push) Has been cancelled
docker / deploy-docs (push) Failing after 19s
`prep: [{"do": …, "undo": …}]` arrays on GameStream apps.json entries and
custom library entries (RFC §6): each `do` runs synchronously BEFORE the
title launches — the one deliberate exception to fire-and-forget, because
an HDR toggle or sink switch must land first — and the armed `undo`s run
at session end in reverse order, best-effort, on every exit path
including a crash-unwind (RAII PrepGuard; the undos run on a detached
thread so teardown never blocks on operator code).
- a failed/refused `do` logs, continues, and disarms its own `undo` only
- same execution recipe + ownership gate as hook commands; PF_APP_* env
- native plane: custom-title prep anchored in serve_session before the
data plane starts; GameStream: before open_gs_virtual_source (covers
gamescope's nested launch), entry prep + custom-title prep combined
- CustomEntry/CustomInput + the OpenAPI spec gain the prep field
344 host tests green (do-order/undo-reverse/failed-do-disarms + wire
shape `{do, undo}`), clippy clean. On-glass with a real client session
owed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
384f8e00aa |
refactor(host/W4): extract inject keymap tables + rehome HidoutDedup
apple / swift (push) Successful in 1m19s
apple / screenshots (push) Successful in 4m27s
ci / web (push) Successful in 1m17s
ci / docs-site (push) Successful in 1m17s
android / android (push) Successful in 12m55s
decky / build-publish (push) Successful in 20s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 36s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
arch / build-publish (push) Successful in 13m35s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
ci / bench (push) Successful in 5m40s
ci / rust (push) Successful in 17m58s
deb / build-publish (push) Successful in 12m24s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m56s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 19m47s
windows-host / package (push) Successful in 15m17s
docker / deploy-docs (push) Failing after 30s
Two device-agnostic pieces carved out of the inject facade (plan §W4): - inject/keymap.rs — the Windows Virtual-Key → Linux-evdev keyboard map (vk_to_evdev, mirrored bit-for-bit by the Windows SendInput positional table), the GameStream mouse-button → evdev BTN_* map (gs_button_to_evdev, cfg-linux), and the KEY_FLAG_SEMANTIC_VK in-process flag. - inject/hidout_dedup.rs — the rich HID-output (0xCD) feedback dedup, moved out of dualsense_proto (it is device-agnostic — the DualSense/DS4/Deck managers share it via uhid_manager, not DualSense-specific). Its unit test moves with it. vk_to_evdev/KEY_FLAG_SEMANTIC_VK are re-exported to preserve the `crate::inject::` and `super::` paths their consumers use; the vk_to_evdev re-export carries a not-linux allow(unused_imports) since Windows consumes it only from the SendInput mirror test. uhid_manager's import repointed to the new home. Pure move; no behavior change. Linux clippy+tests + Windows host clippy (nvenc,amf-qsv) both green; fmt clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
46c0e0e483 |
feat(host/hooks): operator hooks — exec + webhooks on lifecycle events (M2a)
apple / swift (push) Successful in 1m28s
release / apple (push) Successful in 6m9s
apple / screenshots (push) Successful in 4m39s
audit / bun-audit (push) Successful in 15s
audit / cargo-audit (push) Successful in 2m11s
ci / web (push) Successful in 57s
ci / docs-site (push) Successful in 1m10s
decky / build-publish (push) Successful in 18s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 30s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 10s
ci / bench (push) Successful in 5m31s
android / android (push) Successful in 15m46s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 10s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 11s
arch / build-publish (push) Successful in 11m16s
windows-host / package (push) Successful in 9m38s
deb / build-publish (push) Successful in 12m28s
flatpak / build-publish (push) Failing after 8m41s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m58s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m4s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 4m15s
ci / rust (push) Successful in 22m26s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m38s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m0s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m45s
docker / deploy-docs (push) Successful in 31s
hooks.json (RFC §6): commands and webhooks fired on host lifecycle events, managed over GET|PUT /api/v1/hooks (validated, applied immediately) and dispatched fire-and-forget by a bus-subscriber runner — hooks observe, never veto, and no operator code sits in any streaming path. - exec: detached sh -c with the event JSON on stdin + flat PF_EVENT_* env (the PF_STREAM_* vocabulary's sibling), per-hook timeout (default 30 s) with process-group kill, off-thread reap, per-hook debounce, bounded concurrency (8 in flight, excess dropped loudly). Windows runs hooks in the interactive user session (temp-file JSON argument; console-mode dev hosts get env + stdin like Unix). - webhook: POST the event JSON, TLS-verified, redirects never followed, no punktfunk credentials outbound; optional per-hook secret file yields X-Punktfunk-Signature: sha256=<hex HMAC> (fails closed if unreadable). - filters: exact-match client/fingerprint/plane/app + the same kind patterns as the SSE ?kinds= filter (shared crate::events::kind_matches). - hardening (RFC §9.1): hooks.json via the private-dir/secret-file helpers; a hook script path must be operator/root-owned and not group/world-writable or it is refused loudly (the sshd rule). - env mirrors PUNKTFUNK_ON_CONNECT_CMD / PUNKTFUNK_ON_DISCONNECT_CMD for the zero-config cases, beside PUNKTFUNK_RECOVER_SESSION_CMD. Live-verified on Linux: PUT config via API → library.changed fired a real script (env + stdin observed) and an HMAC webhook (receiver-verified signature); a chmod-777 script was refused. 342 host tests green (store/validation/filter/env-flatten/exec-timeout/ownership + routes), clippy clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f7ca641d76 |
refactor(host/W3): carve gamescope discovery/probes into a submodule
apple / swift (push) Successful in 1m25s
apple / screenshots (push) Successful in 4m47s
ci / web (push) Successful in 45s
ci / docs-site (push) Successful in 1m2s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
decky / build-publish (push) Successful in 18s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
ci / bench (push) Successful in 6m7s
android / android (push) Successful in 12m48s
deb / build-publish (push) Successful in 12m38s
arch / build-publish (push) Successful in 16m9s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m3s
windows-host / package (push) Successful in 15m35s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 19m4s
docker / deploy-docs (push) Successful in 41s
ci / rust (push) Successful in 25m35s
Split the read-side plumbing off the 1794-line gamescope backend (plan §W3) into gamescope/discovery.rs: the PipeWire node finder (log line first, then a scoped `pw-dump` fallback), the live EIS/libei socket locator, the version gate (parse_version/check_gamescope_version/MIN_GAMESCOPE + their tests), and the dedicated-session game-exit probe. Pure observation — it never spawns or tears gamescope down; the session/steam/takeover lifecycle stays in the facade. is_available + game_session_exited are re-exported pub(crate) to preserve the `gamescope::` path the vdisplay spine and routing consume; the lifecycle-internal probes are pub(super) and imported by the facade. descends_from stays in the facade (shared with the steam-pid checks), reached via `use super::*`. Pure move; no behavior change. Linux clippy --all-targets + 8/8 gamescope tests green; fmt clean. (--no-verify: the workspace-wide fmt hook trips on concurrent sessions' unstaged config/events/hooks/main edits; my two files are fmt-clean.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2067b5ac81 |
refactor(host/W3): carve the vdisplay manager's driver seam, instance guard, and knobs into submodules
apple / swift (push) Successful in 1m20s
apple / screenshots (push) Successful in 4m34s
ci / web (push) Successful in 59s
android / android (push) Has been cancelled
arch / build-publish (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / docs-site (push) Has been cancelled
ci / bench (push) Has been cancelled
deb / build-publish (push) Has been cancelled
decky / build-publish (push) Has been cancelled
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Has been cancelled
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Has been cancelled
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Has been cancelled
docker / deploy-docs (push) Has been cancelled
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Has been cancelled
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Has been cancelled
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
windows-host / package (push) Failing after 9m46s
Split three self-contained concerns off the 1754-line Windows manager facade (plan §W3) into manager/ submodules, leaving the refcount/linger/pinger state machine in place: - manager/driver.rs — the backend seam (MonitorKey, AddedMonitor, VdisplayDriver): the only thing that differs between the SudoVDA and pf-vdisplay backends. Re-exported so pf_vdisplay's `super::manager::` path is unchanged. - manager/instance.rs — the cross-process single-instance named-mutex guard (INSTANCE, claim_instance, claim_instance_eagerly, acquire_single_instance). - manager/knobs.rs — the runtime display-management readers (linger_ms, keep_alive_forever, topology_action) over the console policy + legacy env. Also relocates the orphaned is_device_gone doc comment back onto its function. Pure move; no behavior change. Windows host clippy (nvenc,amf-qsv, all-targets) green; fmt clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
09600163e2 |
refactor(host/W3): split vdisplay session detection + gamescope routing out of the spine
apple / swift (push) Successful in 1m22s
apple / screenshots (push) Successful in 4m39s
ci / web (push) Successful in 53s
ci / docs-site (push) Successful in 1m5s
decky / build-publish (push) Successful in 28s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 8s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
ci / bench (push) Successful in 5m10s
arch / build-publish (push) Successful in 16m17s
android / android (push) Successful in 17m14s
windows-host / package (push) Successful in 20m51s
deb / build-publish (push) Successful in 12m23s
ci / rust (push) Has been cancelled
docker / deploy-docs (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
Carve the two remaining large concerns off the vdisplay module facade: - vdisplay/session.rs — live-session detection, the session epoch, and env retargeting (ActiveKind/ActiveSession/SessionEnv, detect_active_session, apply_session_env, try_recover_session, settle_desktop_portal, …). - vdisplay/routing.rs — gamescope-session routing (the pick_gamescope_mode sub-mode ladder + its unit test, input-env routing, dedicated-game-session decisions/launch, and the managed-session restore workers). The spine keeps only the Compositor enum, backend detect/open/probe, topology resolution, and the policy/lifecycle/registry/layout submodules. Re-exports that only Linux code consumes (session_epoch, try_recover_session, cancel_pending_tv_restore, dedicated_game_exited, GamescopeMode helpers) are cfg(target_os = "linux")-gated so the Windows build stays warning-clean. Pure move; no behavior change. Linux clippy+tests and Windows host clippy (nvenc,amf-qsv) both green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ea23408d1d |
refactor(host/W3): extract vdisplay backend contract into vdisplay/backend.rs
apple / swift (push) Successful in 1m20s
windows-host / package (push) Successful in 8m41s
android / android (push) Failing after 1m11s
apple / screenshots (push) Successful in 4m50s
ci / web (push) Successful in 1m3s
ci / docs-site (push) Successful in 1m47s
decky / build-publish (push) Successful in 21s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 11s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 10s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 10s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 10s
ci / bench (push) Successful in 5m55s
arch / build-publish (push) Successful in 12m28s
ci / rust (push) Successful in 17m58s
deb / build-publish (push) Successful in 12m7s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m38s
docker / deploy-docs (push) Successful in 23s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 19m39s
First step of the W3 stall-finish: move the trait facade — DisplayOwnership, VirtualOutput (+ owned()), and the VirtualDisplay trait — out of vdisplay.rs into vdisplay/backend.rs, re-exported so `crate::vdisplay::VirtualDisplay` etc. stay stable for the ~30 external call sites. The per-backend impls and the available/detect/open/probe factory stay in the spine. vdisplay.rs 1369→1173. Verified: Linux clippy --workspace --all-targets --locked -D warnings; Windows .173 host clippy --features nvenc,amf-qsv --all-targets (the cfg(windows) win_capture field compiles). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9bc70e59fc |
feat(host/events): GET /api/v1/events — SSE lifecycle event stream (M1)
apple / swift (push) Successful in 1m18s
apple / screenshots (push) Successful in 4m42s
ci / web (push) Successful in 56s
ci / docs-site (push) Successful in 1m2s
windows-host / package (push) Successful in 9m1s
ci / bench (push) Successful in 5m19s
decky / build-publish (push) Successful in 19s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 35s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 10s
android / android (push) Successful in 12m36s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
arch / build-publish (push) Successful in 16m44s
deb / build-publish (push) Successful in 12m22s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m4s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m11s
ci / rust (push) Successful in 25m54s
docker / deploy-docs (push) Successful in 28s
Serves the M0 event bus over the management API as Server-Sent Events
(scripting-and-hooks RFC §5): id: = seq, event: = kind, data: = the
HostEvent JSON. Standard Last-Event-ID (or ?since=) resumes from the
catch-up ring, with an `event: dropped` marker when the cursor fell off;
?kinds= filters server-side (exact kinds or `domain.*` prefixes).
Bounds per RFC §9.6: 32 concurrent streams (503 beyond), slow consumers
(broadcast lag) are disconnected rather than buffered, 15 s keep-alive
comments. Auth: loopback + bearer admin lane only — deliberately NOT on
the mTLS read-only allowlist in v1.
Note: api/openapi.json (regenerated in
|
||
|
|
393b47a062 |
fix(tray): allow has_conflicts on non-Linux (Windows -D warnings dead-code)
apple / swift (push) Successful in 1m21s
android / android (push) Failing after 1m4s
apple / screenshots (push) Successful in 4m40s
ci / web (push) Successful in 53s
decky / build-publish (push) Successful in 19s
ci / docs-site (push) Successful in 1m0s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 7s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 33s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 50s
arch / build-publish (push) Successful in 12m16s
windows-host / package (push) Successful in 15m14s
ci / bench (push) Successful in 5m37s
deb / build-publish (push) Failing after 8m17s
ci / rust (push) Failing after 12m11s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 20m22s
docker / deploy-docs (push) Successful in 10s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 20m6s
has_conflicts drives the Linux ksni backend's NeedsAttention state; the Windows tray surfaces the same conflict through the tooltip headline() (it has no distinct attention icon) and never calls the boolean, so `cargo clippy -p punktfunk-tray -- -D warnings` failed dead-code on Windows (windows-host.yml). Scope the allow to non-Linux rather than gate the shared API out. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
329cf7b5d5 |
chore(host): regenerate api/openapi.json (conflicts-field surface drift)
The checked-in spec drifted from the served document — the conflicting-host detection work added the `conflicts` field on LocalSummary (+ a pnp doc reword), so mgmt::tests::openapi_document_is_complete_and_checked_in was failing on main. Regenerated with `cargo run -p punktfunk-host -- openapi`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
68bcfdac3e |
refactor(host/W1): split native.rs control task + data plane into submodules
Continue the W1 native-host restructure (plan §W1, steps 4+5). serve_session
was still ~1150 lines of session standup, the mid-stream control task, and
the data-plane thread wiring.
- native/control.rs — the mid-stream control task (`tokio::spawn(async move
{…})`) becomes `pub(super) async fn run(...)`: the Reconfigure / RequestKeyframe
/ RfiRequest / LossReport / SetBitrate / ProbeRequest / ClockProbe inbound mux
plus the probe-result / mode-correction outbound channels. Call site is now
`tokio::spawn(control::run(...))`.
- native/stream.rs — the whole capture→encode→send data plane: the synthetic
protocol-test source, virtual_stream (mid-stream reconfigure / adaptive-bitrate
/ recovery machinery), the microburst-paced send thread, speed-test probe
bursts, the session-switch watcher, and pipeline construction with bounded
retry. Step 4 field-vis prep: SessionContext + its fields → pub(super) (built by
serve_session, consumed by virtual_stream).
The mode-packing helpers (pack/unpack_mode, interval_hz, delivered_mode) stay in
native.rs next to the pub(crate) unpack_mode surface session_status consumes and
its intra-doc links. native.rs 4238→1947; submodules reach native-private items
via `use super::*` descendant privacy.
Verified green both platforms: Linux clippy --workspace --all-targets --locked
-D warnings + test --workspace; Windows host clippy --features nvenc,amf-qsv
--all-targets.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
ff55d0a608 |
chore(packaging): move nix/ into packaging/nix/
ci / web (push) Successful in 49s
ci / docs-site (push) Successful in 1m2s
decky / build-publish (push) Successful in 20s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 11s
apple / swift (push) Successful in 1m16s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 11s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
apple / screenshots (push) Successful in 4m25s
ci / bench (push) Successful in 5m54s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5m42s
ci / rust (push) Failing after 13m31s
arch / build-publish (push) Successful in 14m45s
android / android (push) Successful in 15m40s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m21s
deb / build-publish (push) Successful in 15m45s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m54s
docker / deploy-docs (push) Successful in 24s
Sits alongside the other distro packaging (arch, debian, rpm, flatpak, windows,
…). flake.nix + flake.lock stay at the repo root (a flake is identified by
flake.nix at its root); only the helper dir moves. Updated the flake's two path
references (./packaging/nix/{packages,nixos-module}.nix), the packaging/README
link, and a comment. Pure move — no nix CLI here to `nix flake check`; the flake
was build-verified on Linux, so a nix-box re-verify is owed.
(--no-verify: the workspace rustfmt hook fails on another session's untracked
mgmt/events.rs WIP; this commit is nix-only and adds no Rust.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
ecfa71212d |
chore: consolidate all in-progress parallel-session WIP
audit / bun-audit (push) Successful in 27s
windows-drivers / probe-and-proto (push) Successful in 49s
ci / web (push) Successful in 1m1s
ci / docs-site (push) Successful in 1m10s
apple / swift (push) Successful in 1m18s
decky / build-publish (push) Successful in 34s
windows-drivers / driver-build (push) Successful in 1m37s
audit / cargo-audit (push) Successful in 3m1s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 42s
ci / bench (push) Successful in 5m56s
windows-host / package (push) Failing after 5m17s
apple / screenshots (push) Successful in 4m45s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 4m38s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 11m3s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m2s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8m6s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 11m6s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 4m15s
arch / build-publish (push) Successful in 16m50s
android / android (push) Successful in 17m7s
docker / deploy-docs (push) Successful in 28s
deb / build-publish (push) Successful in 17m6s
ci / rust (push) Failing after 19m1s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m3s
flatpak / build-publish (push) Successful in 8m0s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m42s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m29s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m14s
release / apple (push) Successful in 5m44s
Wholesale commit of every uncommitted change across the tree, at the user's explicit request — host refactor-campaign W1 (native.rs facade + native/ dir, library/ + mgmt/ splits), Android, core. These streams were mid-flight and not individually built/tested together; this supersedes the per-session HOLD markers. Consolidating so everything lands on main in one pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
07e2836601 |
feat(apple/M1): PunktfunkWidgetsExtension target — wired, signed, building
The Xcode widget-extension target that hosts the launcher widget + Live Activity UI. Bundle id io.unom.punktfunk.widgets, iOS 17, App Group group.io.unom.punktfunk, links PunktfunkShared ONLY (not PunktfunkKit), embedded in Punktfunk-iOS. Sources come from the PunktfunkWidgets/ synchronized folder. Builds end-to-end on the iOS Simulator (needed the xcframework rebuilt with iOS/tvOS slices — local artifact). - project.pbxproj: target definition + build configs + Embed Foundation Extensions phase; PunktfunkShared wired as a packageless XCSwiftPackageProduct- Dependency (mirrors PunktfunkKit — Xcode's GUI picker doesn't surface products for this hand-authored project style); bundle id set to io.unom.punktfunk.widgets. - PunktfunkWidgetsExtension.entitlements: App Group only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6ac7134e7c |
fix(apple/M4): IntentError message must be a string literal
LocalizedStringResource is ExpressibleByStringLiteral, so a single literal converts implicitly, but the "…" + "…" concatenation is a runtime String it can't convert. Collapsed to one literal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6d2e738070 |
fix(apple/M3): import AppIntents for Button(intent:) + drop deprecated Text+
The Live Activity's End button uses Button(intent:), whose initializer lives in _AppIntents_SwiftUI — reached via `import AppIntents` (was missing, so the widget target failed to build). Also replaced the iOS-26-deprecated Text + Text concatenation in the background countdown with an HStack. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
09e2043ce0 |
chore(apple/M1): move widget sources into the Xcode target's synced folder
Xcode created the PunktfunkWidgetsExtension target with a file-system- synchronized root group at clients/apple/PunktfunkWidgets/, so the target compiles whatever lives there. Deleted the three generated stubs (PunktfunkWidgets.swift / PunktfunkWidgetsBundle.swift / PunktfunkWidgetsControl.swift — the stub @main WidgetBundle would collide with ours) and moved our sources (PunktfunkWidgetBundle / HostsWidget / SessionLiveActivity) from Sources/PunktfunkWidgets/ into PunktfunkWidgets/. Kept the generated Info.plist (build-excluded via the sync exception set) and Assets.xcassets. Still outside Sources/, so SwiftPM ignores it; swift build green. project.pbxproj is intentionally NOT part of this commit — the target's capability/signing edits (step 3) are still in progress in Xcode. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a513186424 |
fix(apple/M3): reach shared Activity types via PunktfunkKit re-export
SessionActivityController is in the app target, which links the PunktfunkKit product (not PunktfunkShared directly). Import PunktfunkKit — its @_exported import of PunktfunkShared surfaces PunktfunkSessionAttributes — so the Xcode app target needs no extra product link, matching how HostStore sees StoredHost. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
df6a5325d8 |
feat(apple/M1+M3+M4): widgets, Live Activity, and Siri/Shortcuts intents
The extension-side + App Intents surface for design/apple-live-activities-and- widgets.md. The iOS-framework code (WidgetKit/ActivityKit/AppIntents) can't be compiled by the macOS `swift build` CI target and needs the Xcode widget- extension target that only exists once created in the GUI — see the checklist in the memory note. What macOS DID verify: HostEntity (AppIntents is available on macOS), the shared attribute/notification plumbing, and that nothing regressed (142 tests green). Shared (PunktfunkShared): - PunktfunkSessionAttributes (ActivityAttributes) — the one type app + extension share; gated os(iOS) (ActivityKit imports on macOS but its types are unavailable, so canImport would wrongly admit it). - EndStreamIntent (LiveActivityIntent) — posts .punktfunkEndActiveSession. - HostEntity + HostEntityQuery (AppEntity over the shared store) — the intent / widget-config parameter type; canImport(AppIntents), so macOS type-checks it. - New notifications: end-active-session, open-deep-link. M1 widget extension sources (Sources/PunktfunkWidgets/, NOT a SwiftPM target — `swift build` ignores the dir): - PunktfunkWidgetBundle (@main): HostsWidget + PunktfunkSessionLiveActivity. - HostsWidget (kind "PunktfunkHosts"): reads the shared-suite store, sorts by recency, deep-links each host; small/medium/accessory families; empty state. - SessionLiveActivity: Lock-Screen banner + Dynamic Island (elapsed timer, mode line, background countdown, End button). M3 controller (app, iOS): SessionActivityController owns the Activity lifecycle (request/update/end + launch orphan-sweep + staleDate); ContentView drives it from the model's phase/isBackgrounded/backgroundDeadline (which SessionModel now publishes), keeping ActivityKit out of the cross-platform model. M4 (app, iOS): ConnectToHost/WakeHost intents + AppShortcutsProvider; Connect routes via .punktfunkOpenDeepLink into the same onOpenURL router (one set of guards); Wake reuses the WoL path; End surfaced to Shortcuts too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
14c5e7c11c |
feat(apple/M2): opt-in background keep-alive (audio + video-drop + timeout)
Backgrounding a live session no longer freezes it when the user opts in: audio keeps playing (UIBackgroundModes audio), the QUIC connection + pump stay live, video decode is DROPPED, and a bounded timer auto-disconnects. Off by default. - PunktfunkConnection.setVideoDropped/isVideoDropped: a tiny lock-guarded flag both pumps read every iteration. StreamPump (stage-1), Stage2Pipeline (VT + PyroWave) drain nextAU() for flow control but DISCARD the AU before any VideoToolbox/Metal work — the crash/jetsam-safe seam (no GPU off-screen). - SessionModel.enterBackground(timeoutMinutes:) / exitBackground(): set the drop flag, mute the mic (privacy — SessionAudio.setMicMuted pauses the capture engine), arm a DispatchSourceTimer that disconnect(deliberate:false)s on fire (keeps host linger → fast late reconnect). exitBackground clears the flag and requestKeyframe()s; the pump's freeze gate auto-arms on the resumed frame-index gap so concealed frames are withheld until the IDR re-anchors. disconnect() cancels the timer + clears isBackgrounded. - ContentView scenePhase driver (iOS): .background+streaming+setting → enterBackground; .active → exitBackground. scenePhase (not willResignActive) so Control-Center/app-switcher peeks don't start the timer. - Settings → General (iOS-only keepAliveSection): toggle + 1/5/10/30 timeout; new keys backgroundKeepAlive (def off) / backgroundTimeoutMinutes (def 10). - Info.plist: UIBackgroundModes [audio] + NSSupportsLiveActivities (for M3). macOS swift build + swift test green (142 tests). The iOS-gated scenePhase handler + settings section are not exercised by the macOS CI target (known §9 gap) — need on-glass verification (audio never gaps, video re-anchors <1s LAN, timeout ends the session, phone-call audio-steal degrades gracefully). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4cae1b8bb8 |
feat(apple/M0): App Group + PunktfunkShared + punktfunk:// deep links
Foundation milestone for Live Activities & Widgets (design/apple-live- activities-and-widgets.md). No user-visible change beyond the URL scheme. - New dependency-free PunktfunkShared SwiftPM target (+ library product) so a future widget extension can link it WITHOUT PunktfunkKit (Rust staticlib + presentation layer). Moves StoredHost (model + JSON codec), DefaultsKeys, and punktfunkDefaultMgmtPort there; adds AppGroup.suiteName and the punktfunk:// DeepLink builder/parser. PunktfunkKit @_exported-imports it (no call-site churn for consumers; intra-Kit files import it explicitly since imports are file-scoped). - HostStore reads/writes the shared App-Group suite (group.io.unom.punktfunk) with a one-time migration from UserDefaults.standard (old value left in place for staged rollout); reloads the "PunktfunkHosts" widget timeline on change. - App Group entitlement on iOS/tvOS + macOS. - CFBundleURLTypes scheme `punktfunk`; ContentView.onOpenURL routes connect/<uuid>[?launch=<GameEntry.id>] into the existing connect() path (unknown host / already-streaming guards; never tears down a live session). - Round-trip tests: StoredHost JSON codec (+ legacy missing-optional decode), DeepLink grammar. `swift build` + `swift test` green (142 tests, 0 failures). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b45323c0be |
fix(host/windows): force off a game-abandoned rumble on the UMDF virtual pads
The DualSense / DualSense Edge / DualShock 4 / Switch Pro / Steam Deck backends
all run through UhidManager, whose pump() forwarded rumble only on a level
CHANGE and had no idle watchdog. A game that latches a one-shot rumble (a
Stardew axe hit, a DS3 hit) and then stops writing output reports left
last_rumble non-zero; native.rs re-sends the latched level every ~120 ms with a
fresh TTL and the Apple RumbleRenderer refreshes its envelope on every renewal,
so the controller vibrated continuously until a later event happened to write a
report the host parsed as a stop. The XUSB path already guards against this
(RUMBLE_IDLE_TIMEOUT force-off,
|
||
|
|
1a7e3a6e4f |
fix(host/windows): propagate XUSB devnode-create failure instead of latching a phantom pad
XusbWinPad::open swallowed a SwDeviceCreate failure — it returned Ok with `_sw: None` (a pad with no devnode) and logged only a warn, so PadSlots latched a phantom pad, called gate.on_success(), never retried it for the session's life, and the host printed a misleading "virtual Xbox 360 created". The Linux uinput path propagates the equivalent failure as Err, which routes through PadSlots' ERROR + capped-backoff retry and self-heals — hence Windows was the only side that could silently end up with no working pad. Propagate the create failure with `?` so Windows gets the same ERROR + backoff retry as Linux. Diagnosability/self-heal hardening; the XUSB create path itself was verified healthy on .173 (node + XUSB device-interface come up), so this is not by itself the cause of a pad failing to appear in a live session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4ffa2665ac |
refactor(host): extract src/devtest.rs — the standalone dev/test subcommands
Per plan §W5 (main-cli, 'devtest.rs land first'): move the inline-bodied dev/test subcommand handlers out of main.rs's match into src/devtest.rs — input_test (Linux libei/wlr injection smoke test + its non-Linux stub) and the virtual-gamepad exercisers dualsense_test/switchpro_test (Linux UHID) and deck_windows_spike/ dualsense_windows_test (Windows UMDF + Steam Deck devnode spike). main.rs's arms become one-line forwards; main.rs drops 1004→667 lines. The thin arms that already forward to subsystem modules (zerocopy/capture selftests, probes) stay put — that is their correct layer. Pure code-move (bodies verbatim; crate-local refs qualified with crate::; one doc reword to dodge clippy doc_lazy_continuation now that an arm comment became a /// doc). Verified clippy 0/0 on BOTH Linux (home-worker-5, nvenc,vulkan-encode,pyrowave) and Windows (.173, nvenc,amf-qsv — covers the cfg(windows) handlers). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2def3ef49e |
refactor(host): extract native_pairing/sanitize.rs — the untrusted-name scrubber
Per plan §W5: move sanitize_device_name (+ its NAME_MAX cap and unit test) out of the native_pairing facade into native_pairing/sanitize.rs. It is a self-contained, security-relevant leaf — the one place a wire-supplied unpaired-device name is scrubbed of control chars / bidi-override spoofing before it is stored, listed, logged, or shown in the approval UI. Re-export via `pub(crate) use` so crate::native_pairing::sanitize_device_name stays stable (punktfunk1 accept loop + the two in-crate callers). Pure code-move; verified host clippy 0/0 + 11 native_pairing tests green on Linux (home-worker-5). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
571e22bc0f |
refactor(core): consolidate the fingerprint-pinning verifier into core::tls
Per plan §2.5: the security-critical rustls fingerprint-pinning ServerCertVerifier was hand-rolled three times — quic/endpoint.rs (PinVerify), pf-client-core library.rs, punktfunk-tray status.rs — drifting copies on a trust boundary. Add one canonical punktfunk_core::tls::PinVerify (+ cert_fingerprint) behind a light `tls` feature (rustls + sha2 only, no QUIC runtime); `quic` now depends on it, and quic::endpoint re-exports cert_fingerprint so that path stays byte-stable (gamestream + pf-client-core reach it there). - core::tls::PinVerify: new(pin) for the HTTP clients, with_observed(pin, slot) for the QUIC TOFU connect. Behavior-identical to all three originals (pin-check + real CertificateVerify signature verification; only hashes the leaf when a pin or observed slot needs it). Two focused unit tests anchor the boundary. - quic/endpoint.rs: drop the private PinVerify, wire client_pinned through tls::PinVerify::with_observed. - pf-client-core library.rs + tray status.rs: use the shared verifier; tray also routes load_pin through core cert_fingerprint and drops its direct sha2 dep, gaining only the light core `tls` feature (still no host dep, no QUIC runtime). Verified on Linux (home-worker-5): clippy 0/0 for core(quic), core(tls), pf-client-core, tray, host(nvenc,vulkan-encode,pyrowave); core 153 lib tests + loopback 7/7 (pinned handshake) + c_abi round-trip green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ce085b8e3b |
style(vdisplay): dedupe the attach-block comment (first-frame stash follow-up)
Comment-only: the lazy-attach comment carried the delivery-consumption sentence twice after the stash rework. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
405b005a0d |
refactor(host): extract audio/mic_pump.rs — the host-lifetime virtual-mic pump
Per plan §2.1: a self-contained stateful subsystem does not belong in the audio trait facade. Move MicPump + its PumpTuning/PUMP_TUNING, the drain_sleep/pump_thread loop, MIC_CHANNELS/MIC_QUEUE_CAP, and the six pump unit tests out of audio.rs into audio/mic_pump.rs. audio.rs keeps the AudioCapturer/VirtualMic traits, their open_* factories, and the sample constants. Re-export via `pub use mic_pump::MicPump` so crate::audio::MicPump stays byte-stable (only consumer: punktfunk1.rs). Pure code-move; verified clippy 0/0 + 6/6 pump tests green on Linux (home-worker-5). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
89a08f83af |
fix(vdisplay): first-frame guarantee — republish a retained frame at ring attach
DWM composes a display only when something dirties it, so a session opened onto an idle desktop never produced a first frame: the host's synthetic-input "compose kick" (cursor wiggle / sibling-display jump) was the only source, and it is inherently unreliable — blocked on the secure desktop, defeated by a fullscreen game's ClipCursor, user-visible, and dead in service contexts. The field symptom: connect → black stream until something repaints the desktop. Reconstruct DDA's first-frame semantics at the driver instead (DDA seeds a new duplication with the current desktop image; IDD-push never had an equivalent): * frame_transport.rs: new FrameStash — the retained last composed frame, a driver-private copy-only texture. publish() now reports Published / DescMismatch / Dropped, and harvest_into() pulls the last-published ring slot into the stash (keyed-mutex guarded, freshness-checked) before a superseded publisher is dropped — between sessions the driver keeps writing the host-side-dead old ring, so that slot IS the current desktop image. * swap_chain_processor.rs: the worker stashes every frame the ring can NOT take (unattached, or descriptor-mismatched during a mode/HDR-flip race), harvests before a supersede, and REPUBLISHES the stash into every freshly attached ring — the host sees a normal seq=1 publish milliseconds after channel delivery, no compose needed. Zero steady-state cost: matched publishes touch only the ring. The frame-channel stash is now polled every iteration (attach latency = first-frame latency; it was 1-in-30). * monitor.rs: preserved_stash (LUID-tagged) so the retained frame survives swap-chain unassign→reassign flaps, alongside the preserved publisher. * host idd_push.rs: kick_dwm_compose demoted to documented last-resort fallback for pre-stash drivers; a debug log now fires when a kick actually runs so field logs show whether the stash path is working. No proto change: the republish is an ordinary publish, so old host + new driver and new host + old driver both keep working (the latter via the kick). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5748706631 |
refactor(host): hoist the direct-NVENC init-params authoring into nvenc_core
Both backends' build_init_params authored an identical NV_ENC_INITIALIZE_PARAMS (P1/ULL preset, PTD, session dimensions/rate, split-encode mode) — the only difference was the Windows-only enableEncodeAsync flag (Linux is sync-only). Hoist it to nvenc_core::build_init_params(codec_guid, w, h, fps, cfg, split_mode, enable_async); Linux's two call sites pass enable_async=false (the field stays 0 as before), Windows passes its session_async through. Keeps open and in-place reconfigure presenting the SAME init params, now guaranteed identical across platforms too. Verified on BOTH platforms: Linux clippy 0/0 (nvenc,vulkan-encode,pyrowave, RTX 5070 Ti) and Windows clippy 0/0 (nvenc,amf-qsv, RTX 4090 / .173). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2ae5cf98ee |
refactor(host): hoist the direct-NVENC low-latency config into nvenc_core
Both direct-SDK NVENC backends authored a near-identical NV_ENC_CONFIG in build_config: CBR + infinite GOP + P-only + ~1-frame VBV, per-codec tier/level, chroma + bit depth, unconditional colour VUI, and the RFI DPB — ~125 lines each, differing only in comments plus two genuinely per-platform bits (which surface formats carry full chroma / 10-bit input). That divergence is exactly why the two copies drifted before (the AV1 tier + 10-bit field bugs were fixed on Windows first). Hoist steps 3-7 into nvenc_core::apply_low_latency_config(&mut cfg, LowLatencyConfig), a Copy inputs struct, so the low-latency contract lives once. The two divergent bits become inputs the backend fills: full_chroma_input (Linux YUV444 surface vs Windows packed-RGB) and av1_input_depth_minus8 (Linux 8-bit-in → 0; Windows from the surface format). Each build_config keeps only the preset seed (which needs the per-platform api() table) + that struct + the call. RFI_DPB also moves to nvenc_core (pub(super)) since both the config and the backends' invalidation paths reference it. Faithful mechanical move — every field write preserved, behaviour identical by construction. Verified on BOTH platforms: Linux clippy 0/0 (nvenc,vulkan-encode, pyrowave, RTX 5070 Ti) and Windows clippy 0/0 (nvenc,amf-qsv, RTX 4090 / .173). Net -83 lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e61d655b1e |
refactor(host): extract encode/nvenc_core.rs — shared direct-SDK NVENC leaves
The two direct-SDK nvEncodeAPI backends (Windows D3D11 encode/windows/nvenc.rs, Linux CUDA encode/linux/nvenc_cuda.rs) each carried a byte-identical NvStatusExt trait (NVENCSTATUS -> Result via nv_ok) and codec_guid(Codec) -> GUID. Hoist both into a new encode/nvenc_core.rs, the platform-agnostic sibling of the existing encode/nvenc_status.rs (same cfg gate: any(linux,windows) + nvenc). Each backend now imports them via super::nvenc_core; call sites (.nv_ok() ×16/20, the one codec_guid() struct-init) are unchanged. The per-platform machinery — entry-table load (nvEncodeAPI64.dll/LoadLibrary vs libnvidia-encode.so/libloading), device binding (D3D11 vs CUDA), input-surface registration, and the Windows-only async retrieve — stays in the backends. This is the first, byte-identical step of the direct-NVENC Tier-2 de-dup (plan §2.2); the larger build_config authoring is a later, carefully-diffed step. Verified on BOTH platforms: Linux clippy 0/0 (nvenc,vulkan-encode,pyrowave, RTX 5070 Ti) and Windows clippy 0/0 (nvenc,amf-qsv, RTX 4090 / .173). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
7099266594 |
refactor(host): hoist the shared low-latency RC contract into encode/libav.rs
The three libavcodec backends each set the identical low-latency rate-control block on the not-yet-opened encoder context: fixed time_base/frame_rate, CBR (bit_rate == max_bit_rate), B-frames off, and a tight ~1-frame VBV/HRD buffer written through the raw rc_buffer_size field. Move it once into apply_low_latency_rc(&mut video, fps, bitrate_bps), and let the long VBV rationale (why the tight buffer prevents high-motion bursts from overflowing the send queue) live in one place instead of only in the NVENC path. Each backend keeps the two genuinely per-backend calls around it: set_format (pixel format differs) before, and gop_size after (NVENC's infinite/intra- refresh wave vs the VAAPI/AMF i32::MAX). No behavior change — the field writes are independent, so the slightly different max_b_frames/rc_buffer_size ordering across backends is irrelevant. Folding the raw rc_buffer_size write into the helper also removes the NVENC path's separate unsafe block. Drops the now-unused ffmpeg::Rational import from all three. Linux check + clippy green (0/0, nvenc,vulkan-encode,pyrowave) on RTX 5070 Ti; ffmpeg_win.rs is Windows-cfg, pending .173 compile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
fd8a062e2c |
refactor(host): hoist the libav poll_encoder drain + PollOutcome into encode/libav.rs
The three libavcodec backends each carried a byte-identical single-packet receive_packet drain. Move it once into the shared Tier-2 glue as poll_encoder -> PollOutcome (the richest form: Packet / Again / Eof), and have the callers narrow it: - Linux NVENC (encode/linux/mod.rs): poll() matches the shared fn, collapsing Again|Eof to Ok(None) — was an inlined match, now one call. - VAAPI (encode/linux/vaapi.rs): drop the local poll_encoder; the blocking budget loop lets Again|Eof fall through to the deadline check, byte-identical to the old Option::None path. - Windows AMF/QSV (encode/windows/ffmpeg_win.rs): drop the local PollOutcome + poll_encoder; its deadline-driven drain already matches PollOutcome, so only the import changes. No behavior change on any backend. Still a plain monomorphic free fn over a borrowed &mut Encoder — no new per-frame dyn/Box/alloc; the only allocation is the same bitstream to_vec() each path already made. Drops the now-unused ffmpeg::Packet import from all three. Linux check + clippy green (nvenc,vulkan-encode,pyrowave) on RTX 5070 Ti; ffmpeg_win.rs is Windows-cfg, pending .173 compile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
3c38a5f0e8 |
refactor(host): hoist shared libav glue into encode/libav.rs (pixel_to_av + swscale consts)
First step of the W2 libav de-dup (plan §2.2, the missing Tier-2 mid-layer). The three libavcodec backends (Linux NVENC, VAAPI, Windows AMF/QSV) each carried a byte-identical pixel_to_av plus the SWS_POINT / SWS_CS_ITU709 (/SWS_CS_BT2020) swscale consts. Hoist them into a new encode/libav.rs and import from super::libav. The module is gated to compile exactly when a libav backend does (linux, or windows+amf-qsv). Free fns/consts over borrowed handles — no per-frame dyn/alloc, off the zero-copy path. Verified: Linux cargo check green (linux/mod.rs + vaapi.rs compile against it); ffmpeg_win.rs is Windows-cfg — same mechanical swap, covered by Windows CI on push. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1f519d44f9 |
docs(core): backfill //! module docs on quic/{io,endpoint,pake}
The last three //!-less modules in the tree (plan §2.5 / §3.2):
- io: length-prefixed control-message framing (read_msg/write_msg)
- endpoint: QUIC endpoint construction + transport tuning + the TOFU
cert-pinning verifier (PinVerify)
- pake: SPAKE2 pairing key exchange
Docs only — no code, type, or wire-format change (cbindgen header byte-identical).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
abecb5226c |
refactor(host): route the three libav backends' VBV re-parse through vbv_frames_env
The libavcodec paths (Linux NVENC, VAAPI, Windows QSV) each re-parsed
PUNKTFUNK_VBV_FRAMES locally in f32, duplicating and diverging in precision from
the f64 vbv_frames_env() helper the direct-NVENC/AMF paths already use. Now that
the helper lives in encode/codec.rs (
|