0dce1cd1160098be34758bcfbd55579590ecd407
1692
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0dce1cd116 |
chore(release): bump workspace version to 0.19.0
audit / bun-audit (push) Successful in 11s
ci / web (push) Successful in 50s
ci / docs-site (push) Successful in 54s
apple / swift (push) Successful in 1m20s
audit / cargo-audit (push) Successful in 2m13s
ci / bench (push) Successful in 6m52s
android-screenshots / screenshots (push) Successful in 3m58s
release / apple (push) Successful in 11m11s
deb / build-publish (push) Failing after 8m56s
decky / build-publish (push) Failing after 24s
deb / build-publish-host (push) Failing after 9m15s
android / android (push) Successful in 13m6s
ci / rust (push) Successful in 25m44s
arch / build-publish (push) Successful in 13m37s
docker / deploy-docs (push) Successful in 11s
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 9s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (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 9s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 4m31s
apple / screenshots (push) Successful in 6m32s
web-screenshots / screenshots (push) Successful in 3m2s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 2m43s
flatpak / build-publish (push) Failing after 8m20s
linux-client-screenshots / screenshots (push) Successful in 7m46s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 15m45s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 18m4s
windows-host / package (push) Successful in 15m54s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m26s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m49s
Headline: the whole pen/tablet input stack (P0–P5) — a pressure-sensitive stylus plane across core wire, Linux, GameStream/Moonlight, Windows, iPad Pencil and Android. Plus touch-injection fixes, cursor-DPI scaling, three Windows-host display/cursor fixes, a gamepad same-PID conflict fix, and the Android mouse/keyboard (TV) regressions. Release notes authored in-repo at docs/releases/v0.19.0.md (first release on the new flow; CI seeds the release body from it). cargo update --workspace syncs the lock (versions only). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c3c0115a82 |
fix(core/abi): bump ABI_VERSION to 13 for punktfunk_connection_send_pen
The pen P0 work added a new embeddable C entry point (punktfunk_connection_send_pen) but left ABI_VERSION at 12 — the value 0.18.0 shipped. Per the project's convention (0.18.0 bumped to 12 for the additive cursor channel), additive C-surface growth bumps the ABI so embedders can gate on the new symbol. Regenerated include/punktfunk_core.h via cbindgen (cargo build -p punktfunk-core); WIRE_VERSION is unchanged (pen is capability-gated on HOST_CAP_PEN). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
00c8c12a0f |
feat(ci/apple): export the iOS .ipa to the release + run artifacts
The iOS leg archives with development signing and re-signs for App Store distribution at
export time, but the existing export uses destination=upload (straight to TestFlight) and
leaves no .ipa on disk. Add an export step that re-exports the SAME archive with
destination=export to produce an App Store distribution-signed .ipa, then:
* attach it as the `punktfunk-ios-ipa` run artifact on every build (upload-artifact@v3,
since Gitea's backend rejects @v4), and
* attach it to the unified Gitea release on vX.Y.Z tags (alongside the DMG), via the
shared ensure_release/upsert_asset helpers.
Same gate as the archive; warn+skip (never fails the best-effort iOS leg) if the archive is
absent. Note: an App Store-signed .ipa installs only via TestFlight/App Store, not by direct
sideload — it is a release/archival artifact.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
988b5742ec |
fix(gamestream): tolerate NaN pressureOrDistance — VoidLink's finger touches were dropped whole
apple / swift (push) Successful in 1m18s
ci / web (push) Successful in 1m2s
ci / docs-site (push) Successful in 1m6s
apple / screenshots (push) Successful in 6m11s
ci / bench (push) Successful in 5m34s
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 27s
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-noble.Dockerfile, punktfunk-rust-ci-noble) (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 11s
windows-host / package (push) Successful in 16m57s
android / android (push) Successful in 15m58s
docker / deploy-docs (push) Successful in 24s
arch / build-publish (push) Successful in 17m3s
deb / build-publish (push) Successful in 9m26s
deb / build-publish-host (push) Successful in 10m5s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m39s
ci / rust (push) Successful in 25m42s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 17m31s
The hex dumps from the on-glass session show textbook SS_TOUCH packets with pressureOrDistance = 0x7fc00000 (NaN): VoidLink encodes 'unknown finger pressure' as NaN (iPad fingers have no force sensor), and the anti-forgery finiteness gate rejected the entire packet — silently disabling the touch plane while pen (real Pencil force, always finite) worked. Coordinates keep the strict gate (they feed injector scaling); pressureOrDistance now sanitizes non-finite to 0.0, the spec's own 'unknown', which the pen path already maps to full-scale ink on contact. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f938174d86 |
debug(gamestream): hex-dump the first few undecodable pointer packets
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
40611cd54d |
debug(gamestream): surface the pointer plane's silent failure modes
An on-glass 'touch does nothing' currently leaves NO trace: log the first SS_TOUCH per session at info (proves the client sends + we decode), and warn when a packet carrying a pointer magic fails the body parse (layout mismatch) instead of vanishing into the unknown-magic drop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
98b97d7d76 |
fix(android/input): stop mouse back/forward leaking to nav + IME popping on hardware typing
apple / swift (push) Successful in 1m25s
apple / screenshots (push) Successful in 7m4s
ci / web (push) Successful in 1m3s
ci / docs-site (push) Successful in 1m14s
android / android (push) Successful in 11m54s
arch / build-publish (push) Successful in 11m58s
ci / bench (push) Successful in 5m36s
decky / build-publish (push) Successful in 24s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 14s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 15s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 11s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 12s
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
deb / build-publish (push) Successful in 11m52s
docker / deploy-docs (push) Successful in 11s
deb / build-publish-host (push) Successful in 10m14s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m56s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m15s
ci / rust (push) Successful in 26m1s
Two Android TV M&K regressions from the
|
||
|
|
51792536d1 |
fix(pf-win-display): escalate CCD isolate to a keep-only supplied config on 0x57
apple / swift (push) Successful in 1m30s
ci / web (push) Successful in 1m8s
apple / screenshots (push) Successful in 6m59s
ci / docs-site (push) Successful in 1m6s
android / android (push) Successful in 14m35s
arch / build-publish (push) Successful in 14m20s
decky / build-publish (push) Successful in 21s
ci / bench (push) Successful in 6m4s
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 18s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 16s
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 15s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 15s
deb / build-publish (push) Successful in 9m41s
ci / rust (push) Successful in 20m14s
deb / build-publish-host (push) Successful in 11m30s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 21m42s
docker / deploy-docs (push) Successful in 12s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 22m2s
Field bug (Steam Deck client, exclusive topology): every isolate retry failed
SetDisplayConfig 0x57 with the physical panel left lit, while iPhone sessions
worked — their virtual monitor's connected-set already had a sole-display
topology persisted, so Windows deactivated the physical on hotplug and the
isolate was a no-op re-commit (deactivated 0). The Deck's monitor identity had
no such entry, forcing the live-deactivate path: supplying the doomed path
(inactive, modes unpinned) plus its orphaned mode entries is rejected 0x57 by
some driver/OS validation combos, so the retry loop could never converge — the
prior mode-index unpin (
|
||
|
|
bf2c4456bb |
fix(inject/windows): compact wire touch ids into slots — Moonlight native touch injected nothing
apple / swift (push) Successful in 1m28s
ci / web (push) Successful in 1m20s
ci / docs-site (push) Successful in 1m12s
release / apple (push) Successful in 9m24s
ci / bench (push) Successful in 6m28s
android / android (push) Successful in 11m59s
decky / build-publish (push) Successful in 22s
arch / build-publish (push) Successful in 12m30s
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 16s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 17s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 15s
apple / screenshots (push) Successful in 6m53s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 19s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 17s
deb / build-publish (push) Successful in 9m36s
deb / build-publish-host (push) Successful in 11m24s
ci / rust (push) Successful in 25m11s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 17m53s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 17m59s
docker / deploy-docs (push) Successful in 25s
On-glass report (iPad Moonlight vs the Windows host): pen inked, native touch did nothing. Cause: POINTER_INFO.pointerId was the client's raw wire id — Moonlight sends arbitrary large pointerIds, and synthetic-pointer injection rejects them (Apollo compacts ids into dense slots for exactly this reason); the failure was logged at trace, i.e. invisibly. Wire ids now map to the lowest free slot for the contact's lifetime, and the FIRST injection failure of a device logs at WARN (pen too) so an inert input plane is never silent again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c0f792ee8d |
feat(android): pen P5 — stylus capture onto the pen plane
The Android leg of design/pen-tablet-input.md §7: against a HOST_CAP_PEN host, stylus/eraser pointers split out of BOTH touch models (passthrough + gesture) into StylusStream — state-full samples with pressure, AXIS_TILT, azimuth from AXIS_ORIENTATION (Android's 0 = away-from-user IS wire north), AXIS_DISTANCE hover, both stylus barrel buttons, the eraser tool, and historical (coalesced) samples batched oldest-first. Kotlin heartbeats ≤100ms per the wire contract. JNI: nativeHostSupportsPen + nativeSendPen (flat 10-float stride, sentinels <0). No barrel-roll axis exists on Android — roll stays unknown here. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c8aa3ed48b |
feat(apple/ios): pen P4 — Apple Pencil (Pro) capture onto the stylus plane
The flagship leg of design/pen-tablet-input.md §7: against a HOST_CAP_PEN host the Pencil splits out of the finger path (independent of touch-input mode) into PencilStream — state-full samples with force→pressure, altitude→polar tilt, azimuth (+90° to the wire's north-clockwise convention), Pencil Pro rollAngle→ barrel roll (17.5+), coalesced touches batched ≤8/send for full 240Hz fidelity, and Pencil hover (zOffset>0 distinguishes it from trackpad hover) as in-range samples with distance. Squeeze holds barrel 1, double-tap clicks barrel 2 (the Pencil has no hardware buttons/eraser — these are how host apps get those affordances). Implements the ≤100ms heartbeat wire contract (80ms timer) so a stationary held stroke survives the host's 200ms dead-client failsafe. Toward a pen-less host nothing changes (Pencil stays a finger). Also fixes hostSupportsCursor testing the STALE 0x04 bit — HOST_CAP_CURSOR moved to 0x08 when TEXT_INPUT claimed 0x04; the old test would mistake a text-input host (e.g. Windows) for a cursor grant and double-render the pointer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
15d09a8c10 |
feat(ci/release): author release notes in-repo + announce stable releases to Discord
ci / web (push) Successful in 51s
ci / docs-site (push) Successful in 1m5s
apple / swift (push) Successful in 1m25s
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 9s
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 9s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (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 10s
ci / bench (push) Successful in 6m55s
apple / screenshots (push) Successful in 6m47s
deb / build-publish (push) Successful in 11m23s
android / android (push) Successful in 12m36s
arch / build-publish (push) Successful in 12m46s
docker / deploy-docs (push) Successful in 23s
deb / build-publish-host (push) Successful in 12m32s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m9s
ci / rust (push) Successful in 26m29s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 20m30s
Release notes now live in the repo at docs/releases/vX.Y.Z.md, authored during the version bump (before the tag). ensure_release (gitea-release.sh + .ps1 twin) seeds the Gitea release body from that file in the create POST, so the release is born WITH its notes instead of being created empty and PATCHed afterward. canary/rc have no file -> empty body, unchanged. New manual `announce` workflow (workflow_dispatch, tag input) re-syncs the notes file over the live release and posts a violet embed (title, notes lead-in, release-page link) to the Discord #releases channel via the DISCORD_RELEASE_WEBHOOK secret. Stable-only: a -rc tag is refused unless allow_prerelease=true. Pressing "go" is the quality gate, so a half-built or failed release is never announced. docs/releases/README.md documents the ritual; TEMPLATE.md is the skeleton. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6cbc3eec8b |
fix(sdl/cursor): scale the forwarded pointer to the video fit
apple / swift (push) Successful in 1m30s
ci / web (push) Successful in 54s
ci / docs-site (push) Successful in 1m3s
android / android (push) Canceled after 1m27s
apple / screenshots (push) Canceled after 1m39s
arch / build-publish (push) Canceled after 1m29s
ci / rust (push) Canceled after 1m29s
ci / bench (push) Canceled after 1m17s
deb / build-publish (push) Canceled after 1m15s
deb / build-publish-host (push) Canceled after 27s
decky / build-publish (push) Canceled after 21s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Canceled after 0s
windows / build (aarch64-pc-windows-msvc) (push) Canceled after 0s
windows / build (x86_64-pc-windows-msvc) (push) Canceled after 0s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 15s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Canceled after 1m18s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 15s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 8s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 18s
flatpak / build-publish (push) Canceled after 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 16s
docker / deploy-docs (push) Canceled after 0s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 17s
Parity with the Apple client (
|
||
|
|
45c9799aa6 |
feat(inject/windows): pen P3 — PT_PEN + PT_TOUCH synthetic pointer injection
apple / swift (push) Successful in 1m26s
ci / web (push) Successful in 59s
ci / docs-site (push) Successful in 54s
release / apple (push) Successful in 9m40s
arch / build-publish (push) Successful in 12m4s
ci / bench (push) Successful in 7m5s
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 12s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 11s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 11s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 22s
apple / screenshots (push) Successful in 6m50s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 19s
android / android (push) Successful in 14m47s
ci / rust (push) Canceled after 13m52s
deb / build-publish (push) Canceled after 7m26s
deb / build-publish-host (push) Canceled after 5m38s
flatpak / build-publish (push) Canceled after 1m55s
docker / deploy-docs (push) Canceled after 0s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 1m59s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 1m59s
windows-host / package (push) Canceled after 8m32s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Canceled after 0s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Canceled after 0s
windows / build (aarch64-pc-windows-msvc) (push) Canceled after 0s
windows / build (x86_64-pc-windows-msvc) (push) Canceled after 0s
The Windows leg of design/pen-tablet-input.md §6, following Apollo's recipe: a per-session PT_PEN device (pressure rescaled 0..1024, polar tilt→tiltX/Y, barrel roll on rotation — Windows Ink renders Pencil Pro roll natively, barrel button, eraser via INVERTED/ERASER flags, hover) with a 40ms refresh thread against the ~100ms synthetic-pointer staleness auto-lift, plus a PT_TOUCH device closing the historical SendInput wire-touch no-op (full active-contact frames, per-id DOWN/UP edges, self-healing lost-DOWN synthesis). pen_supported now probes PT_PEN creation on Windows (1809+), which lights up HOST_CAP_PEN and the GameStream featureFlags there — Moonlight iPad + Pencil can ink on a Windows host. Frame grouping mirrors the Linux uinput backend. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b192825869 |
feat(gamestream): pen P2 — SS_PEN/SS_TOUCH ingest, featureFlags advertised on capable hosts
The Moonlight leg of design/pen-tablet-input.md §4: DESCRIBE now advertises SS_FF_PEN_TOUCH_EVENTS wherever pen injection exists (Linux+uinput, same gate as HOST_CAP_PEN — elsewhere the flag stays 0 and Moonlight keeps client-side mouse emulation exactly as today). SS_PEN packets merge over last-known state into state-full PenSamples (BUTTON_ONLY per spec carries no position; unknown contact pressure 0.0 inks at full scale; UP keeps proximity only for clients that demonstrated hover) and drive the same PenTracker→VirtualPen chain as the native plane — Moonlight iOS + Apple Pencil now exercises the full injection path end to end. SS_TOUCH forwards as wire touch on a synthetic 65535² surface with CANCEL_ALL replayed per tracked contact. No stroke timeout on this plane: ENet is ordered/reliable and Moonlight doesn't heartbeat a stationary pen. Packet layouts pinned against moonlight-common-c Input.h/Limelight.h upstream. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
aca5aa9993 |
feat(host+inject): pen P1 — per-session uinput virtual tablet, HOST_CAP_PEN live
The Linux injection leg of design/pen-tablet-input.md: 0xCC/0x05 pen batches now route through the per-session PenTracker into a lazily-created 'Punktfunk Pen' uinput tablet (BTN_TOOL_PEN/RUBBER, pressure, tilt-from-polar, ABS_Z barrel roll, hover distance, INPUT_PROP_DIRECT) — compositors pick it up via libinput and hand apps zwp_tablet_v2 with full fidelity. The host now advertises HOST_CAP_PEN when /dev/uinput is accessible (PUNKTFUNK_PEN=0 kill-switch); transitions group into SYN frames so proximity-enter carries its position. Stroke failsafe: clients heartbeat ≤100ms while in range (documented wire contract — capture APIs are silent for a stationary pen); 200ms of silence force-releases. 'punktfunk-host pen-test' draws a pressure-ramped sine stroke through the real tracker→uinput chain, no client needed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
248e1cbf08 |
fix(pf-vdisplay/driver): report cursor_excluded adapter-wide — the declare's exclusion is not per-target
ci / web (push) Successful in 51s
ci / docs-site (push) Successful in 1m5s
apple / swift (push) Successful in 1m20s
windows-drivers / probe-and-proto (push) Successful in 1m2s
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 21s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 16s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 11s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
windows-drivers / driver-build (push) Successful in 2m23s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m5s
ci / bench (push) Successful in 6m3s
apple / screenshots (push) Successful in 6m41s
deb / build-publish (push) Successful in 12m20s
arch / build-publish (push) Successful in 12m47s
deb / build-publish-host (push) Successful in 13m7s
android / android (push) Successful in 16m14s
windows-host / package (push) Successful in 15m53s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m23s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m8s
ci / rust (push) Successful in 26m6s
docker / deploy-docs (push) Canceled after 25s
Field report (iPad via GameStream, 2026-07-23): cursor missing from the stream. The session's fresh target 257 reported cursor_excluded=false (DECLARED_TARGETS is keyed per target and only 259 had declared), so the host skipped forced-composite — but DWM's pointer exclusion after an IddCx hardware-cursor declare reaches EVERY later monitor of the adapter, not just the declaring target (proven on-glass: declare on 259 under WUDFHost 2192, then 257's frames streamed pointer-free with CURSOR_SHOWING set and SM_MOUSEPRESENT true). GameStream/Moonlight clients cannot draw a forwarded cursor, so those sessions were silently cursor-less. ADD replies now report any-declare-this-WUDFHost-life (the state's real scope — it dies with the adapter reset, same as before); per-target ids stay only as the dbglog audit trail. Channel-less sessions on a declared adapter then hit the existing forced-composite gate (GDI poller + blend) and carry the pointer in-video. Host code unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
35c2c1450e |
fix(apple/cursor): scale the forwarded pointer to the video fit
apple / swift (push) Successful in 1m24s
release / apple (push) Successful in 10m11s
apple / screenshots (push) Successful in 6m42s
ci / web (push) Successful in 1m3s
ci / docs-site (push) Successful in 1m9s
ci / bench (push) Successful in 5m48s
decky / build-publish (push) Successful in 25s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 20s
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 12s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 18s
android / android (push) Successful in 16m24s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 14s
deb / build-publish (push) Successful in 10m11s
arch / build-publish (push) Successful in 12m18s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 7m5s
docker / deploy-docs (push) Successful in 11s
deb / build-publish-host (push) Successful in 11m42s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m59s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m4s
ci / rust (push) Successful in 18m39s
The Mac client drew the host-forwarded cursor bitmap at its native pixel size (pixel-as-points), so the on-screen pointer tracked the HOST's display scaling: a 4K/high-DPI virtual display renders a 96 px pointer, which then appeared huge on the client — and on retina every pointer was already 2x-inflated and upscaled-blurry, since the backing scale was ignored. Cache the shape raw and rebuild the NSCursor at the live video-fit factor (fit.width / mode.width — the same AVMakeRect fit the input mapping uses) on each apply, re-fitting on resize / retina moves. The pointer now renders at its true size relative to the streamed desktop at any host scaling: crisp and 1:1 with the video at 100%, and proportional (not ballooning) when the host sits at 200-300% — including a deliberately raised scale. iOS/tvOS (UIPointerInteraction) and Android (no forwarded-bitmap path) render the pointer differently and need no change; the SDL/Linux client has the same native-size issue and will follow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
326d6e17c8 |
fix(windows/capture): stand the IddCx hardware cursor down while the secure desktop is up
apple / swift (push) Successful in 1m22s
apple / screenshots (push) Successful in 6m41s
ci / web (push) Successful in 1m7s
ci / docs-site (push) Successful in 1m18s
android / android (push) Successful in 12m44s
arch / build-publish (push) Successful in 12m36s
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 21s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 19s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 20s
ci / bench (push) Successful in 6m14s
windows-host / package (push) Successful in 9m46s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 21s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 19s
deb / build-publish (push) Successful in 9m38s
deb / build-publish-host (push) Successful in 9m58s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 7m0s
ci / rust (push) Successful in 24m1s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 22m7s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 21m55s
docker / deploy-docs (push) Successful in 12s
0.18.0 regression: UAC consent and Winlogon (lock/logon) stopped appearing in streams. The cursor channel's IddCx hardware-cursor declare — re-issued by the driver on every swap-chain assign — keeps the path out of the OS's software-cursor mode, which is the only mode the secure desktop renders through; DWM then never presents UAC/Winlogon into our swap-chain and the stream repeats the last normal-desktop frame for the whole interaction. The GDI cursor poller now classifies the input desktop on its reattach cadence (UOI_NAME != Default = secure, 250 ms cadence instead of 2 s), and the capturer edge-triggers the existing-but-unused proto-v6 IOCTL_SET_CURSOR_FORWARD flip: OFF at secure entry (the driver stops its per-assign re-declare; the host facade forces the same-mode re-commit that actualises the software-cursor default, under the vdisplay manager lock via the new force_recommit()), back ON at dismissal for channel sessions. Channel-session open resets the driver's persisted desired state to ON so a session that died mid-secure-desktop can't leave the next one adopting UNdeclared (the §8.6 cross-session composite trap); orderly teardown does the same. The flip closure is built for every Windows session — a channel-less session can reuse a driver monitor whose earlier-session cursor worker still re-declares, and NOT_FOUND from never-declared targets is ignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
588a7077e8 |
fix(host/gamepad): only degrade a virtual Steam pad on a same-PID conflict
ci / docs-site (push) Successful in 56s
ci / web (push) Successful in 1m0s
apple / swift (push) Successful in 1m26s
decky / build-publish (push) Successful in 32s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 23s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 20s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 26s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (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 19s
ci / bench (push) Successful in 6m10s
apple / screenshots (push) Successful in 6m38s
deb / build-publish (push) Successful in 9m26s
android / android (push) Successful in 12m27s
deb / build-publish-host (push) Successful in 13m55s
windows-host / package (push) Successful in 16m20s
arch / build-publish (push) Successful in 17m51s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 17m16s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 23m2s
ci / rust (push) Successful in 27m20s
docker / deploy-docs (push) Successful in 26s
The Steam-conflict gate matched any physical Valve `28DE` HID device, so a
Steam Machine with a physical Steam Controller 2 (`28DE:1302`) plugged in
degraded a client's requested virtual Steam Deck (`28DE:1205`) to a DualSense
— the reporter's controller passthrough came up as the wrong pad.
Steam Input drives *distinct* Steam controllers side by side; only two
*identical* `28DE` devices confuse it (the Deck-as-host case: built-in
`28DE:1205` + a second virtual Deck, observed live on Bazzite). Narrow the
gate to key on the exact VID+PID instead of the `28DE` vendor alone:
- `steam_backend_product()` maps each virtual Steam backend to the PID it
enumerates as (Deck 1205, classic SC 1102, SC2 1302, SC2 Puck 1304).
- `physical_steam_controller_present()` -> `physical_steam_product_present(pid)`
matches `:28DE:{PID}`, keeping the FVPF-serial / `vhci_hcd` / `/virtual/`
exclusions so our own virtual pads never count.
- `degrade_steam_on_conflict` degrades only on a same-PID duplicate and logs
the conflicting identity. `PUNKTFUNK_STEAM_FORCE=1` override unchanged.
A physical SC2 no longer blocks a virtual Deck (and vice versa); a genuine
same-identity duplicate (physical Deck + virtual Deck, or physical SC2 +
SC2 passthrough) still degrades. Adds a unit test for the PID mapping.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
4714235fe6 |
feat(core): stylus wire P0 — state-full RICH_PEN batches, PenTracker, HOST_CAP_PEN
apple / swift (push) Successful in 1m24s
release / apple (push) Successful in 10m3s
android / android (push) Successful in 13m31s
arch / build-publish (push) Successful in 13m39s
ci / web (push) Successful in 1m4s
ci / docs-site (push) Successful in 1m12s
windows-host / package (push) Successful in 16m40s
apple / screenshots (push) Successful in 6m38s
ci / bench (push) Successful in 5m26s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m9s
ci / rust (push) Successful in 20m46s
decky / build-publish (push) Successful in 30s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m15s
deb / build-publish (push) Successful in 8m50s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 45s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 12s
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 12s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 4m31s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 7m6s
deb / build-publish-host (push) Successful in 9m52s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 6m44s
docker / deploy-docs (push) Successful in 28s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 7m4s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m35s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m45s
flatpak / build-publish (push) Failing after 8m6s
The pen plane from design/pen-tablet-input.md, protocol side only (the P1 uinput tablet injector will consume it): 0xCC kind 0x05 carries batches of state-full PenSamples (pressure, polar tilt + azimuth, barrel roll, hover distance, eraser tool, barrel buttons); PenTracker diffs samples into injector transitions so a lost datagram self-heals; HOST_CAP_PEN (0x10) gates sending and stays unadvertised until a real backend exists. C ABI: PunktfunkPenSample + punktfunk_connection_send_pen, documented in docs/embedding-the-c-abi.md §8. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1a6deeb781 |
feat(host/library): scan the user's non-Steam shortcuts into the game library
apple / swift (push) Successful in 1m21s
ci / web (push) Successful in 51s
ci / docs-site (push) Successful in 57s
apple / screenshots (push) Successful in 6m41s
ci / bench (push) Successful in 6m22s
ci / rust (push) Successful in 24m42s
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 11s
decky / build-publish (push) Successful in 28s
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 12s
android-screenshots / screenshots (push) Successful in 3m10s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 4m41s
windows-host / package (push) Successful in 9m53s
linux-client-screenshots / screenshots (push) Successful in 7m11s
deb / build-publish (push) Successful in 11m1s
docker / deploy-docs (push) Successful in 24s
deb / build-publish-host (push) Successful in 11m55s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 2m31s
arch / build-publish (push) Successful in 12m57s
web-screenshots / screenshots (push) Successful in 3m12s
android / android (push) Successful in 13m46s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 2m34s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 15m0s
release / apple (push) Successful in 10m32s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m45s
flatpak / build-publish (push) Successful in 5m51s
Steam's "Add a Non-Steam Game to My Library" entries carry no `appmanifest`, so `scan_manifests` can't see them and they never appear in the streamed library. Read each Steam account's binary `userdata/<id>/config/shortcuts.vdf` directly (a binary KeyValues parse) to surface the user's own custom entries, plus their grid artwork from `userdata/<id>/config/grid`. Best-effort: an unreadable or absent shortcuts.vdf contributes nothing, and hidden shortcuts / duplicate appids are dropped. Verified: clippy -D warnings + compile green on Linux. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>v0.18.0 |
||
|
|
8c9baf3214 |
fix(decky): recreate the library shortcut when its cached appId is stale
The visible "Punktfunk" shortcut's Steam appId is cached in SteamUI localStorage, which lives in Steam's CEF profile — so it survives a plugin uninstall/reinstall. Deleting the shortcut left the key dangling; on the next mount ensure*Shortcut recalled the dead appId, took the reuse branch, and ran SetShortcut* against a non-existent id (silent no-ops). AddShortcut was never reached, so the entry never came back. The same stale-reuse also made the hidden stream shortcut RunGame a dead gameid. Verify the remembered appId still maps to a live shortcut via appStore.GetAppOverviewByAppID before reusing it; a confident miss falls through to AddShortcut. When appStore is unavailable, assume it exists so a false negative can't spawn a duplicate library entry. Also add a "Recreate library shortcut" recovery button to the QAM About section (recreateShortcuts): drops any dead cached keys and re-ensures. Covers deleting the shortcut without reinstalling, where no mount fires the self-heal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d83eeedde4 |
ci(flatpak): drop the use-vc TCP-DNS hack — the runner-side cache made it the failure
audit / bun-audit (push) Successful in 11s
ci / web (push) Successful in 46s
ci / docs-site (push) Successful in 1m5s
apple / swift (push) Successful in 1m21s
audit / cargo-audit (push) Successful in 2m20s
decky / build-publish (push) Successful in 19s
ci / bench (push) Successful in 6m53s
apple / screenshots (push) Successful in 6m28s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 1m0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 12s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (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 11s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 6m27s
windows-drivers / probe-and-proto (push) Successful in 16s
windows-drivers / driver-build (push) Successful in 1m27s
deb / build-publish-host (push) Successful in 9m37s
android / android (push) Successful in 11m56s
docker / deploy-docs (push) Successful in 24s
deb / build-publish (push) Successful in 11m31s
arch / build-publish (push) Successful in 15m34s
windows-host / package (push) Successful in 11m29s
ci / rust (push) Successful in 24m30s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 15m37s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m28s
flatpak / build-publish (push) Failing after 8m18s
Every flatpak Tooling failure on 2026-07-22 (4 runs, incl. two AFTER the runner fleet got its local dnsmasq cache) died in flatpak remote-add with an instant 'Could not resolve hostname' ×10 while dnf in the SAME container resolved fine. The one thing only this leg does is force glibc onto TCP DNS (use-vc) — a 2026-07-11 mitigation for the embedded resolver dropping UDP under fleet load. With job containers now resolving against the on-box dnsmasq cache (daemon.json dns → docker0), UDP is reliable again and the TCP path through the same chain is the flaky one under concurrency. Remove the hack; keep the nss-resolve drop and retry.sh as the backstop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
786f6ad28f |
fix(pf-driver-proto): catch the layout tests up with the cursor-channel proto bump
The cursor-channel sweep grew AddReply by the cursor_excluded u32 tail (24 bytes, legacy prefix 20) and bumped PROTOCOL_VERSION to 6, but left two lib tests behind: the AddReply byte-layout roundtrip still built the legacy initializer (a compile error that turned the rust CI leg red on main), and the version-coherence assertion still pinned v5 (hidden behind that compile error). Cover the new tail bytes + the legacy-size constant, and widen the compat-window note to v4–v6 (floor stays 3). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1e9957d97b |
chore(web): clear the 2026-07-22 JS advisory wave — overrides + lock re-resolve
bun-audit went red mid-day on a fresh advisory wave (13 findings across two snapshots of a moving DB): node-tar DoS trio+1 (critical), sharp/libvips CVEs, fast-uri host confusion, undici ×7, brace-expansion/linkify-it/js-yaml/postcss DoS-class, immutable, dompurify. All transitive pins in the management-console tree; none ship in the streaming stack. Every fix version is in-range for its consumers, so: pin overrides for the single-major packages (tar/dompurify/linkify-it/sharp/fast-uri/immutable/ undici/postcss/js-yaml — bun ignores range-scoped override keys, so plain keys) and a full lockfile re-resolve, which also lifts brace-expansion 2.1.1→2.1.2 in place while its 5.x line (minimatch@10) stays untouched. : No vulnerabilities found. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
92f38ec3dd |
chore(cleanup): drop TEMP cursor probes + clear KWin-leg clippy debt
audit / bun-audit (push) Failing after 14s
ci / web (push) Successful in 52s
windows-drivers / probe-and-proto (push) Successful in 54s
ci / docs-site (push) Successful in 1m6s
apple / swift (push) Successful in 1m21s
audit / cargo-audit (push) Successful in 2m49s
windows-drivers / driver-build (push) Successful in 2m0s
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 11s
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-noble.Dockerfile, punktfunk-rust-ci-noble) (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 9s
ci / bench (push) Successful in 6m47s
ci / rust (push) Failing after 10m23s
release / apple (push) Successful in 9m24s
deb / build-publish-host (push) Successful in 9m51s
apple / swift (pull_request) Successful in 1m21s
apple / screenshots (pull_request) Skipped
android / android (push) Successful in 12m25s
flatpak / build-publish (push) Failing after 8m8s
deb / build-publish (push) Successful in 12m16s
ci / web (pull_request) Successful in 50s
ci / docs-site (pull_request) Successful in 51s
docker / deploy-docs (push) Successful in 26s
arch / build-publish (push) Successful in 14m34s
windows-host / package (push) Successful in 15m5s
apple / screenshots (push) Successful in 6m33s
ci / rust (pull_request) Failing after 6m56s
ci / bench (pull_request) Successful in 6m5s
android / android (pull_request) Successful in 9m56s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m14s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 15m42s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m13s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m42s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m34s
windows / build (x86_64-pc-windows-msvc) (push) Canceled after 4m47s
windows-drivers / probe-and-proto (pull_request) Successful in 15s
windows-drivers / driver-build (pull_request) Successful in 1m30s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 1m3s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 1m54s
The KWin/Phase-A/B commits were built but never clippy-checked (that distrobox had no clippy component), so they left TEMP on-glass probes and lint debt in the tree. With clippy now runnable (fedora rust 1.96.1 = CI parity): - drop the `fec424ee`/`8cff30d5` TEMP probes: the `update_cursor_meta` SPA_META diagnostic logs (also un-detaches the `// SAFETY:` comment from its `unsafe` block → fixes `undocumented_unsafe_blocks`) and the KWin composite-arm probe in the encode loop. - `#[allow(clippy::too_many_arguments)]` on `spawn_pipewire` (8 params since the KWin leg added `expect_exact_dims`; mirrors `from_virtual_output`). clippy `-p pf-capture -p pf-vdisplay -p punktfunk-host --locked --features nvenc,vulkan-encode -- -D warnings` is now green. (--all-targets additionally trips a pre-existing env mismatch: the fedora libspa binding lacks `SPA_VIDEO_TRANSFER_SMPTE2084`, referenced only by a `#[cfg(test)]` guard-test — not a code issue; CI's pinned pipewire has it.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7058647264 |
fix(vdisplay/capture): channel-less sessions composite the pointer on a sticky-declared target
A declared IddCx hardware cursor is IRREVOCABLE for its OS target's life (§8.6), and the sticky exclusion survives monitor REMOVE→ADD because each client gets a STABLE target id — so once any desktop-mode session declared, every later pure-capture session on that target streamed a cursor-less desktop: DWM excluded the pointer, no channel forwarded it, no blend drew it (the exact no-regression gap §8.6's per-session cap gate cannot see). Driver: track every successful SetupHardwareCursor per target (DECLARED_TARGETS — scoped to the WUDFHost's life, exactly the sticky state's scope) and report it in a new AddReply::cursor_excluded tail field (dual-size discipline, both skews degrade cleanly; no proto bump). Host: the flag rides AddedMonitor → Monitor → WinCaptureTarget; a session WITHOUT the cursor channel on a flagged target forces composite mode in the IDD-push capturer — GDI poller + blend for the session's life, pinned on (set_cursor_forward cannot clear it: with no client drawing, un-compositing would erase the pointer entirely). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d5ae8dcc3e |
feat(capture): gamescope cursor via XFixes shape + QueryPointer position (Phase C)
gamescope excludes its pointer from the PipeWire node it feeds us and can't embed one either (`set_hw_cursor` is inert), so every gamescope stream was cursorless. Read the pointer from gamescope's nested Xwayland instead — XFixesGetCursorImage for shape/hotspot/visibility, core QueryPointer for position — and publish a CursorOverlay into the capturer's existing `cursor_live` slot, so the encoder blend composites it into the video exactly like the SPA_META_Cursor path. - pf-capture/src/linux/xfixes_cursor.rs (new): the XFixes reader. Connects to EVERY nested Xwayland (Gaming Mode runs one per --xwayland-count) and follows the focused one each tick (the display whose pointer moves), reading that display's own cursor shape. Un-premultiplies ARGB -> straight RGBA. Drop stops the thread. - Capturer::attach_gamescope_cursor + the PortalCapturer override spawn it into the same `cursor_live` slot; pf_vdisplay::gamescope_xwayland_cursor_targets discovers the (DISPLAY, XAUTHORITY) pairs via the GAMESCOPE_WAYLAND_DISPLAY scan. - host: SessionPlan.gamescope_cursor (set from the compositor at both resolve sites AND on a mid-stream Desktop->Gaming switch); the blend gate now builds the encoder blend for gamescope; a sibling composite arm attaches capturer.cursor() per tick for capture-mode clients (no channel needed). native NV12 is disabled for these sessions — that encode path can't blend the cursor (it assumes gamescope embeds the pointer), so we capture RGB and route to the proven CUDA VkSlotBlend / compute-CSC blend. - the pipewire thread no longer clobbers `cursor_live` with None on a buffer that carries no SPA_META_Cursor (gamescope) — that raced the XFixes writer and strobed the composited pointer on/off. On-glass (home-bazzite-2, RTX 5070 Ti, Gaming Mode): cursor visible + steady in Big Picture and CS2 menus, follows focus between the two Xwaylands, hidden in-game. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
925130d1f9 |
feat(vdisplay): compositor-embedded pointer for sessions without the cursor channel (Phase B)
Since the cursor-channel work, every Linux virtual output was created in metadata pointer mode — making ALL sessions depend on host-side cursor compositing, including the ones that can never use the channel (Moonlight/ GameStream, legacy clients, capture-mode starts). Those sessions paid the blend bring-up per session and, whenever a visible cursor was composited, the loss of NVENC's stream-ordered submit — for a strictly worse cursor than the compositor's own. Mirror the Windows no-regression gate: the already-wired per-session set_hw_cursor(cursor_forward) now drives the Linux backends too. A cursor-channel session gets metadata (shapes forwarded, composite flip blends host-side — today's validated path, unchanged); every other session gets the pointer compositor-EMBEDDED at creation (KWin zkde pointer=2, Mutter cursor-mode=1, wlroots/hyprland portal CursorMode::Embedded — their pre-channel default; the portal pair also gains the Metadata arm for channel sessions, wired but untested on-glass). SessionPlan.cursor_blend narrows to cursor-forward sessions and rides into the direct-SDK NVENC open, which skips the Vulkan slot-blend bring-up entirely when off — embedded sessions ring on plain CUDA surfaces and pay zero cursor cost, per-session or per-frame. The keep-alive registry's reuse key grows the created pointer mode (new VirtualDisplay::hw_cursor getter): a kept embedded display has no cursor metadata for a channel session to forward, and a kept metadata display would leave a channel-less session with no pointer in its frames. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d2c46eaf3c |
feat(encode/nvenc): SPIR-V cursor blend over Vulkan-allocated input slots — retire the PTX kernels
A vendored PTX blob is JIT'd against the driver's ISA ceiling, so the cursor-blend module silently dies on drivers older than the generating toolkit (CUDA_ERROR_UNSUPPORTED_PTX_VERSION/INVALID_PTX, 222/218 — the KWin leg's invisible composite cursor on driver 595/CUDA 13.2 vs a CUDA 13.3 blob). SPIR-V has no such coupling, and the in-tree precedent already exists twice (vulkan_video's CSC blend, VkBridge's exportable OPAQUE_FD → cuImportExternalMemory bridge). New pf_zerocopy::vkslot::VkSlotBlend: the direct-SDK NVENC encoder now allocates its input ring as exportable Vulkan buffers CUDA-imports (same contiguous InputSurface layouts, pitch = row bytes rounded to 256), and the cursor composite is a compute dispatch over the cursor's rectangle (cursor_blend.comp, vendored .spv; spec-constant selects ARGB/NV12/YUV444; BT.709 limited, matching the retired .cu). The surface SSBO is uint[] with every invocation owning whole words — no 8-bit-storage device dependency. Cursor-bearing frames force the existing CPU-synced submit path so the CUDA copy → Vulkan dispatch (fence-waited) → NVENC encode ordering is CPU-established; cursorless frames keep the stream-ordered fast path untouched. Any bring-up/alloc/registration failure falls back wholesale to plain pitched CUDA surfaces (never a mixed or short ring): sessions always encode, composite mode just loses the cursor, warned once. cursor_blend.cu / cursor_blend.ptx and the CursorBlend PTX loader are deleted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
33121ece4d |
chore(kwin-composite): TEMP blend-path probes — DROP BEFORE MERGE
Rate-limited journal logging bracketing the silent segment of the Linux composite path: what overlay (if any) the encode loop's composite arm hands the encoder, and whether the NVENC cursor-blend kernel launches and with what geometry. On-glass (KWin leg) the blend module loads yet the composite cursor stays invisible — these two probes attribute it to no-overlay / stripped-en-route / kernel-draws-nothing. The module-loaded INFO line is permanent (success must be as attributable as failure); everything else in this commit is temporary. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
af87549052 |
fix(linux/vdisplay): KWin virtual outputs stream above 60 Hz via sacrificial-birth renegotiation
KWin's ScreenCastStream builds its PipeWire format offer — including the maxFramerate cap it actively throttles delivery to — ONCE at stream creation, when the virtual output sits at its hardcoded birth 60 Hz. A kscreen custom-mode change afterwards updates the OUTPUT (readback says 240) but never the offer, so every consumer negotiates max=60 and the stream delivers 60 (pw-dump-verified on KWin 6.6.4). The only path that rebuilds the offer is the stream's own resize handling: a source SIZE change while recording re-runs buildFormats — picking up the output's current refresh — and renegotiates the live stream. So above 60 Hz, birth the output at a sacrificial height (+16), then install + select the real WxH@hz custom mode: the first frame recorded after the consumer connects triggers KWin's resize path, which renegotiates the live stream to WxH@hz. The capturer holds (requeues) buffers until the negotiated size matches — new expect_exact_dims plumbing VirtualOutput → registry (fresh creates only) → open_virtual_output → a self-disarming gate in the process callback — bounded by a 3 s deadline that accepts the producer's dims rather than wedging the session into the first-frame retry loop. set_custom_refresh reads back the full active mode; a size reject (pre-6.6 KWin) recreates plain at the real size @60. Every kscreen operation now addresses the output by its NUMERIC id, resolved by matching the managed name-prefix AND the just-created birth size (newest id wins): a mode-switch supersede reuses the per-slot output NAME (deliberately, for KWin's per-name config persistence) while the superseded sibling is still alive, and name addressing hit the FIRST match = the OLD output — on-glass that resized the live session's display out from under it (wrong-res/black), read the old output back as 'mode applied', and set the old output primary while the replacement starved at its birth mode. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
09113c9899 |
fix(encode/nvenc): cursor-blend PTX must carry a driver-portable ISA version
The vendored cursor_blend.ptx was regenerated with the CUDA 13.3 toolkit, which stamps '.version 9.3' — and a driver whose JIT predates that ISA refuses the whole module with CUDA_ERROR_UNSUPPORTED_PTX_VERSION (222), silently killing host-side cursor compositing (on-glass: composite-mode cursor invisible on driver 595.58, KWin leg). The kernels use only baseline arithmetic, so hand-lower the version directive to 8.0 (CUDA 12.0), which every supported Turing+ driver JITs. Both files now warn that an nvcc regeneration re-stamps the version and must be re-lowered. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4436c7fb61 |
fix(host/stream): composite mode re-blends the LIVE cursor on repeat ticks
The frame-attached overlay is the pointer position at the last damage frame; repeats re-encoding a static desktop froze the blended cursor between redraws — on-glass the composite-mode cursor stuttered while window drags (constant damage) were smooth. Refresh the repeat's overlay from Capturer::cursor each tick so pointer-only motion re-blends at tick rate — the bandwidth the pre-channel embedded mode already paid. Linux only: the Windows capturer composites internally and its frames must never carry an overlay a blend path would double-draw. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
088004cac4 |
feat(linux/capture): serve the LIVE cursor overlay through Capturer::cursor
The forwarder read only frame.cursor — the overlay attached to the LAST EMITTED frame. Mutter's pointer-only buffers update the capture-side cursor state but are skipped as frames, so between damage frames the forwarder re-sent a stale snapshot: on-glass the client cursor appeared exactly when clicking or crossing hover targets (damage) and froze/vanished otherwise — the same pointer-only-motion gap the Windows IddCx channel fills, which is why the encode tick already prefers the capturer's LIVE cursor. Publish the overlay from every dequeued buffer (frames or not) into a shared slot and implement the Capturer::cursor hook on PortalCapturer. Host-composite mode on Linux still updates the blended pointer only on damage frames (no re-encode of a static desktop on cursor motion) — the regen-on-cursor-change the Windows path has is a follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
63d83217ef |
fix(linux/capture): a stale id-0 cursor meta is 'no information', not 'hidden'
Mutter only rewrites a buffer's SPA_META_Cursor region when the cursor changed; recycled buffers between damage frames carry a stale id-0 meta. Treating id 0 as a hidden pointer flickered the client cursor off between hovers on-glass — per the SPA contract id 0 is 'invalid/no cursor info', so keep the last-known state (OBS's consumer does the same). A genuinely hidden pointer stops producing updates; the M3 relative-mode hint keeps its Windows CURSOR_SUPPRESSED source. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9db4841ce4 |
fix(linux/capture): cursor meta size range must cover Mutter's fixed 384x384 offer
Mutter offers SPA_META_Cursor with a FIXED size pod — SPA_POD_Int(CURSOR_META_SIZE(384, 384)) in meta-screen-cast-stream-src.c — while our request capped the range at meta_size(256, 256). The intersection is empty, so the Meta param silently failed to negotiate and no buffer ever carried the meta region: the entire Linux cursor pipeline (forward AND blend) was blind on GNOME, proven by an on-glass probe counting 32k+ buffers with zero metas. KWin's offer fits inside 256², which is why the same consumer code passed there. Raise the max to 1024² headroom (the negotiated allocation follows the producer's value, not our max) and align the bitmap parse guard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
151adc4bcc |
chore(m2b): TEMP cursor-meta probe — DROP BEFORE MERGE
Rate-limited journal logging of SPA_META_Cursor arrival/absence, reported id/position/bitmap offset, and bitmap acceptance — the Linux cursor pipeline is otherwise blind end-to-end during Mutter RecordVirtual metadata bring-up on .21. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0619af391e |
fix(linux/inject): map absolute pointer/touch into the streamed output's region
The libei absolute device advertises one region per logical monitor, and both MouseMoveAbs and touch mapped into regions().first() — whichever output the compositor announced first. Next to a physical monitor (on-glass: GNOME with a dummy HDMI beside the virtual primary) that put the pointer and every click on the WRONG output: the seat cursor never entered the streamed monitor, so neither embedded nor metadata cursor capture could ever see it, and the cursor channel had nothing to forward. Pick the region whose logical size matches the streamed mode (the wire flags already carry it); fall back to first() for the single-monitor case. Region geometry now rides the device-RESUMED log line for diagnosability; matching the screencast mapping_id instead of the size is the follow-up for same-sized-monitor ambiguity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
afe120bf34 |
fix(linux/vdisplay): kwin virtual output ships the cursor as metadata too
Same trap as the Mutter backend: zkde_screencast pointer mode 2 (embedded) never delivers SPA_META_Cursor, starving both the cursor channel and the encoder blend. Mode 4 (metadata) feeds both. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c29186bd1a |
fix(host+vdisplay): a mode-switch replacement display inherits the group topology
The resize rebuild creates the new virtual display BEFORE retiring the old
one (create-before-drop), so the registry saw a live same-backend sibling —
its own dying predecessor — and told the backend it was not first in group.
Mutter then skipped the Primary/Exclusive apply ("joining an existing
display group — extending") and the retiring owner took the topology with
it: every resize silently demoted the virtual output to an extended,
shell-less desktop. Thread the superseded pool gen through acquire so the
replacement establishes topology, and stop counting kept (Lingering/Pinned)
entries as demoting siblings — no session owns them, so there is no live
desktop to clobber.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
315ffaf144 |
fix(linux/vdisplay): mutter virtual monitor ships the cursor as SPA_META_Cursor
RecordVirtual was created with cursor-mode EMBEDDED, which never delivers cursor metadata — the cursor channel had nothing to forward (client-drawn cursor invisible) and the always-built encoder blend had nothing to composite (host-drawn cursor invisible too). Metadata mode feeds both: non-forwarding sessions get the blended pointer (same pixels Mutter would have embedded), forwarding sessions strip the overlay and send shape/state. The capturer already negotiates the meta on every stream (meta_param). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c63ac48c9c |
fix(windows/capture): actually QUERY the HDR SDR-white scale — the refresh block was silently dropped
faad4b57 shipped the field, shader scale, and plumbing but the scripted edit inserting the sdr_white_level_scale() refresh in the scratch-rebuild arm matched nothing and silently no-op'd (no assert) — sdr_white_scale stayed at its 1.0 init, i.e. 80 nits, i.e. exactly the old dark cursor. Now queried on every scratch rebuild, with an info log of queried/applied so an HDR session shows what it uses. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
cecc059830 |
fix(windows/capture): 250 Hz cursor poll + HDR white-scale observability
- The polled position is also the composite-blend position: at 16 ms a 240 fps session reused a stale position for ~4 consecutive frames and the composited pointer visibly stuttered against the video. 4 ms out-paces every session rate; a tick is one GetCursorInfo syscall. - Log the queried DISPLAYCONFIG_SDR_WHITE_LEVEL scale on each blend- scratch rebuild so an HDR session shows what is actually applied (the virtual display's own SDR-brightness setting is the suspect when the cursor still reads dark). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d31d41b6b8 |
fix(windows/capture): match the HDR-composited cursor to the target's SDR white level
sRGB→linear alone lands cursor-white at 80 nits (scRGB 1.0) while DWM composes the surrounding SDR desktop at the user's SDR-brightness setting (~200+ nits by default) — the cursor read visibly dark on HDR. Scale by DISPLAYCONFIG_SDR_WHITE_LEVEL (new win_display::sdr_white_level_scale, queried on blend-scratch rebuilds — the CCD query stays off the per-frame path). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6e37da8b4e |
fix(windows/capture): clippy unnecessary_lazy_evaluations in scratch build
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0af280a793 |
feat(windows/capture): host-side cursor compositing for the capture model — the deterministic fix
Root cause, proven on-glass across five driver builds: a declared IddCx hardware cursor is IRREVOCABLE. There is no un-declare DDI, the empty-caps re-setup is rejected INVALID_PARAMETER, and after a successful same-mode re-commit with the driver's re-declare provably suppressed (sticky per-target flag, zero re-setups logged) DWM still never composites the software cursor back into the monitor's frames. Every DWM-based composite flip is therefore unfixable. The composite (capture) model is now implemented in OUR pipeline: - the driver keeps its hardware cursor declared for the session's whole life (the state that works) — frames stay pointer-free always; - in composite mode try_consume routes the conversion through a blend scratch: slot copy + one alpha-blended quad of the GDI poller's shape at its polled position (CursorBlendPass — fullscreen-triangle VS with viewport placement, straight-alpha PS, sRGB→linear for FP16/HDR rings), covering all four convert paths (NV12, 4:4:4 copy, P010, PyroWave) GPU-side under the slot's keyed mutex; - pointer-only motion produces no driver publish (declared hw cursor), so try_consume REGENERATES from the last slot whenever the polled cursor state changes — the cursor moves on a static desktop at tick rate, without faking freshness for the stall/death watchdogs; - set_cursor_forward becomes purely host-internal state; the SET_CURSOR_FORWARD IOCTL machinery is no longer used by the host (driver keeps it, dormant) and its sender plumbing is removed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
89cfef429e |
fix(windows/vdisplay): cursor-render state sticky per TARGET across monitor generations
On-glass: 're-setup on swap-chain assign -> 0x0' fired AFTER 'enable=0 stored' — duplicate/successor monitor entries (re-arrival churn) kept their default-true flag and re-declared the hardware cursor right after the composite flip's re-commit, undoing it. The desired state now lives in a per-target static (CURSOR_FORWARD_DESIRED): every arrival inherits it (no generation can resurrect a declare the session turned off), and the flip stamps EVERY live entry matching the target, declaring against whichever has the live worker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b50e4767de |
fix(windows/cursor-flip): flag + forced same-mode re-commit — the working un-declare
The empty-caps un-declare is REJECTED by IddCx (STATUS_INVALID_PARAMETER, on-glass driver 9.9.0722.1407) — there is no un-declare DDI. The composite flip now works with the only lever the OS gives us: every mode COMMIT reverts the path to the software cursor, and the driver skips its per-commit re-declare while cursor_forward_on is off. So: - driver: disable stores the flag only (no DDI call); enable still declares immediately against the live worker's event. - host capturer: after a successful disable flip, force a same-mode re-commit (win_display::force_mode_reenumeration) — DWM composites the pointer from the very next commit; the swap-chain flap is the class the driver's preserved-publisher machinery already rides out. - state now survives re-arrivals end-to-end: the capturer caches the APPLIED state (cleared on every channel (re)delivery, which re-created driver entries need — their flag defaults to declared), and the stream loop re-applies every tick instead of edge-gating (steady-state cost: one Option compare). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |