09600163e2159c74cbda48b3c6ddf37744d8b855
1311 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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 (
|
||
|
|
11045a0f70 |
chore: consolidate parallel-session WIP (HOLD — do not push)
Local snapshot of intermingled in-flight work, committed to unblock the encode
refactor (a clean ffmpeg_win.rs for the vbv-dedup follow-on). These hunks span
the same files and can't be cleanly split here; the commit bundles three
distinct workstreams that each belong in their own PR:
- logging rework (~43 files: level re-tiering, structured fields, `?e`,
hot-path flood latches)
- conflicting-host detection (detect.rs + detect/{linux,windows}.rs + wiring
in main.rs/mgmt.rs/Cargo.toml/docs/packaging)
- standby-sink DWM-stall attribution (windows/display_events.rs + capture/
vdisplay wiring)
NOT verified as a combination. NOT to be pushed until the refactor is done and
these are re-verified and reorganized into their proper per-workstream PRs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
d466e3e2b2 |
chore(nix): NixOS flake snapshot — host+client packages, module, devShell (WIP)
Local snapshot of the in-flight NixOS support: flake.nix + flake.lock + nix/ (crane host and client packages, services.punktfunk module, devShell). Standalone — nothing in the Rust/Cargo tree references it. Held from push pending its owning session's finalization (Skia-under-Nix follow-up + intended per-workstream PR). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
532b313b8c |
refactor(host): extract encode/codec.rs — the encoder contract
Move the Tier-1 encoder contract out of the stuffed encode.rs facade into a new encode/codec.rs submodule (plan §7 / W2): EncodedFrame, Codec (all methods except host_wire_caps), ChromaFormat, EncoderCaps, the Encoder trait, validate_dimensions, vbv_frames_env, and the dimension + wire-roundtrip contract tests. host_wire_caps stays in encode.rs alongside the backend-selection probes it depends on; CodecSupport and its wire-mask test stay too. encode.rs gains `mod codec;` + `pub(crate) use codec::*;` so every existing crate::encode::X path — crate::encode::vbv_frames_env, ::Codec, ::Encoder, … — stays byte-stable. Pure relocation: no call sites touched. Verified: dev-Mac type-check of both files clean; Linux `cargo check -p punktfunk-host --features nvenc,vulkan-encode,pyrowave` green (all encode backends compile against the relocated contract); contract unit tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d381cdf7f4 |
fix(host): NVENC open-failure resilience — backoff, failed-open hygiene, self-diagnosis
ci / web (push) Successful in 49s
ci / docs-site (push) Successful in 51s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 8s
apple / swift (push) Successful in 1m9s
decky / build-publish (push) Successful in 18s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 7s
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 (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
ci / bench (push) Successful in 5m48s
docker / deploy-docs (push) Successful in 24s
apple / screenshots (push) Successful in 5m18s
windows-host / package (push) Successful in 9m16s
arch / build-publish (push) Successful in 10m53s
android / android (push) Successful in 11m58s
deb / build-publish (push) Successful in 18m7s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 19m19s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m28s
ci / rust (push) Successful in 24m56s
Field report (Linux direct NVENC): after a codec switch, every session open failed with NV_ENC_ERR_INVALID_VERSION until the host process was restarted — so the poisoned state is per-process, not a driver install issue. On-hardware investigation (RTX 5070 Ti, 610.43.03) could not reproduce it with clean codec cycles, dirty teardowns, or open/destroy storms, but established the failure class: the driver enforces a per-process concurrent-session cap (12 there, status INCOMPATIBLE_CLIENT_KEY; other branches report differently) whose exhaustion is exactly this signature — persistent open failures healed only by a process restart. Harden every path that can feed or mask that state: * Rebuild backoff: the in-place encoder-rebuild retries slept one frame interval, so all 5 attempts burned within ~40 ms at 120 Hz — no driver-side transient (deferred teardown of the previous session, engine reset) can clear that fast. Exponential backoff 100 ms → 1.6 s (~3 s total) so transients heal instead of killing the session. * Destroy-on-failed-open (Linux + Windows, all four open sites): the NVENC docs require NvEncDestroyEncoder even when OpenEncodeSessionEx FAILS — the driver may have allocated the session slot before erroring. Without it a retry burst against a transient leaks slots toward the cap, converting the transient into permanent exhaustion. * Teardown: a destroy_encoder failure (a session slot the driver may keep) is now logged with its status instead of silently discarded. * One-shot self-diagnosis on a failed session open (Linux): retry the raw open on a fresh dedicated CUDA context and log which of the three causes applies — shared-context poisoned (fresh works), driver-level skew/exhaustion/GPU loss (fresh fails the same way), or CUDA itself unhealthy (no fresh context) — so the next field report pinpoints the root cause with zero reporter effort. On-hardware regression tests (RTX box .21, all green): codec-switch reopen cycle (H265→AV1→H265→H264→H265), dirty teardown with in-flight encodes, and the full open-failure→diagnosis→in-place-recovery path via real session-cap exhaustion. Existing RFI/reconfigure/4:4:4 smokes still pass; clippy clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f901bedf22 |
fix(host): actionable NVENC error logging — drop misleading "(no NVIDIA GPU?)"
ci / web (push) Successful in 53s
ci / docs-site (push) Successful in 1m2s
apple / swift (push) Successful in 1m10s
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 (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 (., web/Dockerfile, punktfunk-web) (push) Successful in 40s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 56s
apple / screenshots (push) Successful in 5m24s
ci / bench (push) Successful in 7m32s
docker / deploy-docs (push) Successful in 28s
arch / build-publish (push) Successful in 11m51s
windows-host / package (push) Successful in 14m53s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m52s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m54s
android / android (push) Successful in 17m12s
deb / build-publish (push) Successful in 18m12s
ci / rust (push) Successful in 24m52s
Every NVENC entry-point failure was annotated "(no NVIDIA GPU?)", which
misled triage: the direct-NVENC path only loads on a machine that HAS an
NVIDIA GPU. A Linux user hit NV_ENC_ERR_INVALID_VERSION at
open_encode_session_ex (past the NvEncodeAPIGetMaxSupportedVersion pre-flight
gate) — the signature of a userspace/kernel driver version skew that a host
reboot fixes — and the log pointed at a missing GPU instead. A restart did
fix it.
Add encode/nvenc_status.rs: a shared NVENCSTATUS -> cause mapper that folds
the real cause into the anyhow::Error at construction, so every downstream
{e:#} log (the encode-recovery loop, session teardown) improves for free.
INVALID_VERSION now reads "update the NVIDIA driver, or reboot if you just
updated it (a host restart is the usual fix)"; NO_ENCODE_DEVICE /
DEVICE_NOT_EXIST / INCOMPATIBLE_CLIENT_KEY (session-count limit) / OOM /
UNSUPPORTED_PARAM get their own glosses. The required API version comes from
the SDK consts so it stays correct across crate bumps.
Wire it into all NVENC entry-point failures in both backends
(encode/linux/nvenc_cuda.rs, encode/windows/nvenc.rs) — every open, init,
preset/resource/bitstream call.
Also: when the encode-recovery loop exhausts its in-place rebuilds it now
logs a clear terminal line with the underlying cause instead of the session
silently vanishing after the last identical "rebuilt in place" line.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
ab4c9e44cc |
fix(apple): drop premature ITSAppUsesNonExemptEncryption — unblock uploads
ci / docs-site (push) Successful in 50s
ci / web (push) Successful in 54s
ci / bench (push) Successful in 6m1s
ci / rust (push) Successful in 24m53s
apple / swift (push) Successful in 1m13s
apple / screenshots (push) Successful in 4m49s
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 11s
decky / build-publish (push) Successful in 25s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 11s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 11s
android-screenshots / screenshots (push) Successful in 3m17s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 7m39s
windows-host / package (push) Successful in 8m42s
release / apple (push) Successful in 9m30s
linux-client-screenshots / screenshots (push) Successful in 9m1s
docker / deploy-docs (push) Successful in 24s
web-screenshots / screenshots (push) Successful in 2m52s
deb / build-publish (push) Successful in 12m52s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 4m4s
arch / build-publish (push) Successful in 13m47s
android / android (push) Successful in 14m35s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 4m10s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m52s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m0s
flatpak / build-publish (push) Successful in 6m35s
Setting ITSAppUsesNonExemptEncryption=true (
v0.12.0
|
||
|
|
ac60db5410 |
fix(host): make the stream-marker lifecycle test parallel-safe
ci / web (push) Successful in 50s
ci / docs-site (push) Successful in 53s
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 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 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 7s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 7s
ci / bench (push) Successful in 6m20s
docker / deploy-docs (push) Successful in 24s
windows-host / package (push) Successful in 8m17s
arch / build-publish (push) Successful in 12m27s
android / android (push) Successful in 13m42s
deb / build-publish (push) Successful in 14m38s
ci / rust (push) Successful in 19m11s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m7s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m52s
apple / swift (push) Has been cancelled
apple / screenshots (push) Has been cancelled
marker_appears_while_held_and_vanishes_after drove the PROCESS-GLOBAL registry and mutated XDG_RUNTIME_DIR mid-run — the punktfunk1 integration tests announce real sessions concurrently in the same test process, so whichever registered first became the primary and the marker carried its mode instead of the test's 2560x1440 (flaky on CI, green locally by timing). The registry gains insert/remove methods and rewrite() takes the target path, so the test now exercises the same end-to-end lifecycle (atomic write, primary retention, session count, removal) against a LOCAL registry and an explicit temp path — no env mutation, no shared state. Production behavior unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
6b9f261dff |
style(host): rustfmt sweep for the console-sweep push
|
||
|
|
75b3c94f60 |
fix(web): console sweep — pairing, displays, stats, logs, auth, i18n
ci / rust (push) Failing after 45s
ci / web (push) Successful in 52s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
ci / docs-site (push) Successful in 1m6s
decky / build-publish (push) Successful in 33s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 30s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 10s
ci / bench (push) Successful in 6m1s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8m33s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9m40s
docker / deploy-docs (push) Successful in 26s
windows-host / package (push) Successful in 15m34s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m24s
arch / build-publish (push) Successful in 20m32s
android / android (push) Successful in 20m50s
deb / build-publish (push) Successful in 20m33s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 21m41s
apple / swift (push) Has been cancelled
apple / screenshots (push) Has been cancelled
Pairing: - Refresh the paired-devices list after a native PIN pairing (the happy path never invalidated it, so a newly paired device stayed hidden until remount). - Moonlight PIN: a 204 means "PIN delivered to the waiting handshake", NOT paired, so it now reads "PIN sent" instead of a false "Paired successfully". - Hide the Moonlight pairing card on native-only hosts (HostInfo.gamestream) — it could never receive a PIN there. - Per-row pending on unpair/approve/deny; PIN input maxLength 16 (was 8). Displays / Library: - "Arrange displays" save refreshes the settings card (it rewrites the policy), without clobbering unsaved Custom edits (re-seed only when the draft still matches the server). - Live-display list wrapped in QueryState so errors don't read as "no displays". - "Forever" keep-alive option in the custom editor; edit-game form round-trips the logo artwork (was dropped on save); per-card delete pending. Stats: - Distinct colour for the native "queue" latency stage (it collided with "capture"). - "Not measured on this path" note on the GameStream health chart; configured-bitrate target line on throughput; host-authoritative elapsed timer; LiveCard surfaces non-404 errors. Shell / auth / i18n: - SSR-stable locale: first client render matches the base-locale SSR (no hydration mismatch), then adopts the persisted/browser locale post-hydration. - BFF proxy maps an upstream (mgmt-token) 401 to 502 so a logged-in user isn't bounced into a post-login redirect loop. - Logout checks the POST result before navigating; logs dedup by seq (StrictMode); login "next" keeps query/hash; Dashboard shows the active-session count. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b8da32e8b6 |
fix(host): native sessions on the console + GPU-aware codecs + gamestream capability
The web console Dashboard read AppState.{streaming,launch,stream}, which only the
GameStream path writes, so a native punktfunk/1 session (the DEFAULT plane) showed
"Idle / no session" while actively streaming — only the Stats page (shared recorder)
reflected it. Add a plane-neutral per-session registry (session_status.rs) the native
video loop publishes to; /status now merges both planes, reports active_sessions, and
the Stop / Request-IDR buttons reach native sessions too (so surfacing them doesn't
leave dead buttons). LocalSummary (tray) gets the same fix.
Also on the management API:
- /host codecs derive from Codec::host_wire_caps() instead of a hardcoded
[H264,H265,AV1], so codecs the GPU can't encode no longer appear.
- ApiCodec serializes HEVC as "hevc" (matching the wire/SDP/stats label) so the same
codec reads identically across console pages.
- HostInfo.gamestream reports whether the GameStream planes run (--gamestream), so the
console can hide the Moonlight-only pairing UI on the native-only default host.
- StatsStatus.elapsed_ms (host-monotonic) so the capture timer doesn't mix host/browser
clocks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
c4645a8938 |
fix(host/linux): TCP_NODELAY on accepted usbip loopback sockets
audit / bun-audit (push) Successful in 13s
ci / web (push) Successful in 49s
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 8s
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 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
audit / cargo-audit (push) Successful in 2m15s
ci / bench (push) Successful in 6m21s
windows-host / package (push) Successful in 8m44s
docker / deploy-docs (push) Successful in 23s
android / android (push) Successful in 12m37s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m59s
arch / build-publish (push) Successful in 13m46s
deb / build-publish (push) Successful in 13m32s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m35s
ci / rust (push) Failing after 16m28s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m44s
flatpak / build-publish (push) Failing after 8m13s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m42s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m4s
ci / docs-site (push) Successful in 51s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m47s
apple / swift (push) Has been cancelled
apple / screenshots (push) Has been cancelled
release / apple (push) Has been cancelled
The vhci-bound socket already set nodelay, but the server-side accepted socket — the one carrying every URB reply back to the kernel — did not. The wired single-interface device never tripped it, but the Puck's six concurrent endpoint streams turn the request/response URB pattern into classic write-write-read Nagle/delayed-ACK stalls: measured ~22 reports/s on Steam's active Puck hidraw (each ~45 ms apart, sequence jumping by 12) against a clean 266 Hz feed from the client. Trackpad felt accordingly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
252960291e |
feat(host/linux): PUNKTFUNK_GAMESCOPE_STEAM opt-in for bare gamescope spawns
Adds --steam (before the -- terminator, where PUNKTFUNK_GAMESCOPE_APP cannot reach) to the bare headless gamescope spawn when the env var is truthy, enabling gamescope's Steam integration for steam -gamepadui dedicated sessions. Default off; managed gamescope-session-plus/SteamOS sessions own their own flags and are unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
01266ff18d |
feat(gamepad): SC2 Puck-dongle passthrough with the native 28DE:1304 topology
Community-contributed round 5 of the Steam Controller 2 passthrough, reviewed + verified. A Puck-captured pad now presents the dongle's real seven-interface identity (CDC pair, four controller HID slots, management HID) instead of relabelling its reports as a wired 1302 — Steam's Puck feature dances (wireless_transport / esb/bond / 0xB4 slot status) get capture-shaped answers, and the wired identity's canned replies are corrected to the real captures (attribute count, string-attr framing, 0xF2 firmware info, bcdDevice nibble encoding). - new wire pref 10 = SteamController2Puck (Hello/Welcome byte; older peers degrade to Auto), selected by the Android capture link when the transport is a dongle, or by VID/PID in the degraded InputDevice path - TRITON_RDESC is now the captured numbered descriptor (mouse/keyboard lizard collections + per-id vendor reports); unnumbered framing made hidraw mangle feature report 2 and Steam eventually closed the device - interrupt-IN now queues sparse reports (battery/RSSI/wireless edges) instead of keeping latest-only, so a 250 Hz state packet can no longer erase them before the USB/IP poll observes them; EP0 SET_REPORT is split by wValue report type (OUTPUT parsed for rumble vs FEATURE) - vendored usbip-sim: config attributes/max-power, IAD prefix + BOS descriptor support, correct BCD minor.patch encoding (Deck's 0x0300/ 0x0200 values are nibble-zero, so its bytes are unchanged), and full-speed interrupt pacing in ms (was 8 kHz from the HS formula) - Triton feedback is serviced at 1 kHz while an SC2 backend exists so Steam's trackpad haptic writes reach the client unbatched Verified: clippy -D warnings + 319 host tests green on Linux, core wire tests green, Android kit/app compile + unit tests green. On-glass Puck retest owed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b50b698078 |
fix(host/linux): satisfy clippy -D warnings on the cursor-blend path
ci / web (push) Successful in 1m1s
ci / docs-site (push) Successful in 1m12s
android / android (push) Successful in 12m33s
decky / build-publish (push) Successful in 24s
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 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 31s
ci / bench (push) Successful in 6m53s
arch / build-publish (push) Successful in 15m27s
docker / deploy-docs (push) Successful in 11s
ci / rust (push) Failing after 17m35s
deb / build-publish (push) Successful in 11m47s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m18s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m52s
windows-host / package (push) Successful in 14m15s
apple / swift (push) Successful in 4m48s
apple / screenshots (push) Has been cancelled
The Linux clippy leg has been red since
|
||
|
|
762a627df9 |
chore(release): bump workspace version to 0.12.0
audit / cargo-audit (push) Successful in 2m29s
audit / bun-audit (push) Successful in 14s
ci / web (push) Successful in 58s
ci / docs-site (push) Successful in 1m9s
ci / rust (push) Failing after 6m54s
decky / build-publish (push) Successful in 23s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 14s
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 19s
ci / bench (push) Successful in 6m10s
android / android (push) Successful in 14m35s
arch / build-publish (push) Successful in 14m8s
windows-host / package (push) Successful in 14m15s
flatpak / build-publish (push) Successful in 7m48s
deb / build-publish (push) Successful in 14m22s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m34s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m34s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m31s
docker / deploy-docs (push) Successful in 24s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 18m32s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 4m48s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m18s
apple / swift (push) Successful in 5m20s
apple / screenshots (push) Has been cancelled
release / apple (push) Has been cancelled
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a93f5a71ce |
feat(host): script-facing stream-active runtime marker file
ci / web (push) Successful in 1m54s
ci / docs-site (push) Successful in 1m50s
decky / build-publish (push) Successful in 23s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 15s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 14s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 17s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 12s
ci / rust (push) Failing after 7m8s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m12s
ci / bench (push) Successful in 6m10s
docker / deploy-docs (push) Successful in 27s
arch / build-publish (push) Successful in 11m11s
android / android (push) Successful in 14m47s
windows-host / package (push) Successful in 15m22s
deb / build-publish (push) Successful in 14m9s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m6s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 15m43s
apple / swift (push) Successful in 4m58s
apple / screenshots (push) Successful in 21m8s
Maintain $XDG_RUNTIME_DIR/punktfunk/stream while any client is streaming, holding the primary session's negotiated mode. A per-title launch wrapper can branch on it: present → session is already at the stream mode, run the game as-is; absent → run the local (e.g. multi-head gamescope) path. - New stream_marker module: RAII Guard registered per session, refcounted for concurrent clients, atomic (temp+rename) writes, injection-safe single-quoted client name. POSIX-sh-sourceable KEY=value, namespaced PF_STREAM_* keys, schema-versioned, additive-only. - Hooked into serve_session so every exit path (disconnect, error, panic-unwind) retracts the marker. File exists iff a stream is live. Unblocks the downstream triple-head gamescope launch-wrapper use case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a7d4a93ff2 |
fix(gamepad/android): make the exit chord usable again — shorter hold + on-screen hint
ci / web (push) Successful in 57s
ci / docs-site (push) Successful in 1m7s
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 8s
ci / bench (push) Successful in 5m21s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (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
docker / deploy-docs (push) Successful in 24s
ci / rust (push) Failing after 8m21s
arch / build-publish (push) Successful in 11m13s
deb / build-publish (push) Successful in 12m19s
android / android (push) Successful in 15m9s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 12m54s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 17m56s
apple / swift (push) Successful in 4m57s
apple / screenshots (push) Successful in 20m12s
The Select+Start+L1+R1 close-stream chord read as broken after
|
||
|
|
499bf2dae8 |
docs: document the community pf-webos client for LG TVs
ci / web (push) Successful in 45s
ci / docs-site (push) Successful in 51s
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 9s
ci / bench (push) Successful in 6m15s
ci / rust (push) Failing after 9m21s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m15s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 6m29s
android / android (push) Successful in 12m35s
arch / build-publish (push) Successful in 14m4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8m26s
docker / deploy-docs (push) Successful in 27s
deb / build-publish (push) Successful in 15m2s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 15m57s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m6s
apple / swift (push) Successful in 5m18s
apple / screenshots (push) Successful in 20m11s
Adds an LG webOS TV entry to Clients and Install a Client, pointing at the community-maintained pf-webos project (dyptan-io) and its sideload steps (Homebrew Channel + .ipk) — not an official punktfunk client. |