35c61fee64f8a870ace7240e4debc88d9f409cba
53
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
35c61fee64 |
chore(client): the windows-rs pin moves a month forward, onto the SDK-metadata bindings
The July 2026 windows-rs brings a reconciler keyed-child-order fix (#4728), widget validation (#4727), a DPI collision fix (#4751), icon elements (#4736), multi-window support (#4730) and scroll virtualization (#4710) — the re-render fixes the Windows client has been working around at the architecture level. All three pinned deps (windows-reactor, windows, windows-reactor-setup) move together so windows-core stays unified across the swap-chain hand-off, and pf-client-core moves with them. The bulk of the diff is #4689: windows/windows-sys now generate straight from the Windows SDK, so the `Win32_*` namespace features became one feature per SDK header (winuser, dxgi, d3d11, …), the PascalCase namespace modules became header-named modules, struct-returning COM methods take explicit out-params and return HRESULT, Win32 functions return their raw BOOL/HANDLE instead of Result, and flag constants are plain integers. Both crates' Win32 code is rewritten to that shape; behaviour is unchanged on every path. Riding along, all already stale before the bump: the README and the three Windows workflows stop claiming windows-reactor's build.rs needs CARGO_WORKSPACE_DIR (that build.rs no longer exists — staging moved to windows-reactor-setup via OUT_DIR); the README layout section stops describing modules that moved into the session binary long ago and gains the manual smoke checklist; the notices generator learns the SPDX for crates that ship license files without a `license` field, which turns windows-reactor-setup's UNKNOWN into MIT OR Apache-2.0; and the crate records its real rust-version (1.96) instead of inheriting the workspace's 1.82. Verified: cargo check/clippy/fmt clean on punktfunk-client-windows, pf-client-core and punktfunk-client-session; both bins build; --discover finds the LAN hosts; the GUI shell comes up (WinAppSDK bootstrap intact under the new reactor-setup). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
944c03dd32 |
feat(client): the desktop clients wear the host's OS mark
The client half of the host's new `os=` advert, shared once in pf-client-core: `sanitize_os` (mDNS is unauthenticated input — lowercase `[a-z0-9._-]` tokens, capped) and `os_icon_tokens`, the most-specific-first walk with the brand aliases (`macos`→apple, `steamos`→steam) every platform resolves through. `DiscoveredHost` carries the chain, `KnownHost` persists it (`serde(default)`, elided when empty — older stores load unchanged and older clients read back exactly what they wrote), `upsert` moves it only when carried, and `learn_os` mirrors `learn_mac` — no-op, no disk write when unchanged — so the mark survives the host going to sleep. GTK shells: the card's status row leads with a recolorable symbolic glyph. That needed real embedded assets — the shells had none — so `data/` gains the ten `pf-os-*-symbolic` SVGs compiled into a gresource (new build.rs, glib-build-tools) and registered on the icon theme at startup; the Adwaita theme then tints them like every other status glyph. WinUI shell: reactor renders raster-from-URI only, so the embedded mid-gray PNGs (legible on both themes) materialize once into %LOCALAPPDATA%\punktfunk\os-icons\ — the library's poster-art pattern — and the tile's status row leads with a 16px image. The couch UI plumbs `HostRow.os` (live advert preferred, else the store) for a Skia glyph that is a declared follow-up; `--list-hosts` / `hosts --json` emit the stored chain so the Decky plugin can read it. A host that advertises no `os` renders everywhere exactly as it did before the field existed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
dff61ecea2 |
fix(client/windows): the scope switcher leaves the page content, and the polish from the GTK round comes across
ci / web (push) Successful in 58s
ci / docs-site (push) Successful in 1m8s
decky / build-publish (push) Successful in 36s
ci / bench (push) Failing after 6m40s
deb / build-publish-client-arm64 (push) Failing after 5m55s
ci / rust-arm64 (push) Failing after 7m3s
arch / build-publish (push) Failing after 7m26s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 36s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 31s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 35s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 32s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 27s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 7m0s
apple / swift (push) Successful in 5m9s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Failing after 1m53s
deb / build-publish-host (push) Successful in 10m36s
docker / build-push-arm64cross (push) Successful in 13s
android / android (push) Successful in 12m36s
docker / deploy-docs (push) Successful in 25s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m21s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 2m0s
deb / build-publish (push) Failing after 6m54s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Failing after 1m42s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Failing after 7m17s
ci / rust (push) Failing after 8m37s
flatpak / build-publish (push) Failing after 8m52s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Failing after 9m54s
apple / screenshots (push) Successful in 20m39s
Four things the Linux review found, applied here without waiting to re-find them: The profile switcher was rendered as the first group INSIDE each section's content, so on About — a page with barely any rows — it read as an About setting, and on every other page it read as one more row. It is chrome: which layer am I editing. It now sits in its own card above the NavigationView, visible from every section and never mistakable for a setting. Profiles get their colour here too, from the same eight-swatch palette as GTK, and the chip on a host tile is tinted with it — a profile that is red on Linux is red on Windows. An unparsable accent falls back to the neutral chip rather than being handed to the renderer. The tile flyout had become a list of everything, with connect/library/speed buried under list management. Two rules thin it: anything that CONFIGURES the host moves into the editor — the pinned tiles join the default profile and the clipboard toggle there, since a pin is a property of the record — and what remains is grouped by separators, so a glance lands on the right third. Compiled on .221; the last one-line clippy fix went in after the box dropped. |
||
|
|
8a5a5edc37 |
fix(small crates): the proof lint now covers every first-party crate but one
Six crates were still unguarded, and all six are OURS — none vendored: pf-console-ui, pf-gpu, punktfunk-tray, clients/windows, tools/display-disturb, wdk-probe. Two of them ship (the tray and the Windows client), so "small" was about item count, not exposure. Five are closed here. Only 17 of their 75 unsafe items actually lacked a proof — pf-gpu, punktfunk-tray and display-disturb were already fully documented and needed nothing but the deny, which is the good case: the convention was being followed, just not enforced. The 17 that were missing are the usual Win32/COM shapes, and two were worth stating properly. `clients/windows`'s `GetCurrentPackageFullName` is called with `len = 0` and no buffer — that is the documented identity PROBE, which writes nothing, and reading it as a normal query would be a mistake. `pf-console-ui`'s two `destroy_image_view` calls are the load-bearing ones: the comment above one already argued that in-flight sampling of that slot ended two presents ago (the ring alternates and the presenter waits its fence before each record), which is exactly the kind of reasoning a `// SAFETY:` should carry and it was sitting there unlabelled. Also fixes a real Windows-only clippy error this uncovered: `pf-gpu` had a `#[cfg(target_os = "windows")]` fn AFTER its `mod tests`, tripping `items_after_test_module`. It never fired on Linux (the item does not exist there) and no CI job clippies pf-gpu on Windows, so it sat unseen. Moved above the test module. Remaining: `wdk-probe` (26 items) alone, and only because it needs the WDK to build — .47 cannot, so nothing here can verify a deny on it. Verified: Linux .21 fmt + both CI clippy steps rc=0; Windows .47 the four Windows-relevant crates at `-D warnings` rc=0. |
||
|
|
c27065c260 |
feat(client/windows): punktfunk:// opens the Windows client, and cards can write a shortcut
windows / build (aarch64-pc-windows-msvc) (push) Canceled after 0s
windows / build (x86_64-pc-windows-msvc) (push) Canceled after 0s
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
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 0s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 0s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Canceled after 0s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Canceled after 0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Canceled after 0s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Canceled after 0s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Canceled after 0s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 0s
docker / build-push-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
decky / build-publish (push) Canceled after 0s
deb / build-publish (push) Canceled after 0s
deb / build-publish-host (push) Canceled after 0s
deb / build-publish-client-arm64 (push) Canceled after 0s
ci / rust (push) Canceled after 0s
ci / rust-arm64 (push) Canceled after 0s
ci / web (push) Canceled after 0s
ci / docs-site (push) Canceled after 0s
ci / bench (push) Canceled after 0s
arch / build-publish (push) Canceled after 0s
apple / swift (push) Canceled after 0s
apple / screenshots (push) Canceled after 0s
android / android (push) Canceled after 0s
WP4 / D2. Protocol activation of a full-trust packaged app delivers the URI as the command line, so a browser prompt, `start punktfunk://…` and a written `.lnk` all arrive the same way — as a positional argument, the same door the Linux shell uses. What Windows does not give us is single-instancing: unlike GApplication, a second activation is simply a second process. So the first instance claims the named mutex `unom.punktfunk.client` and any later one hands its URL to the winner over `WM_COPYDATA` and exits — one window, and the link opens where the user's hosts already are. The hand-off retries while the primary's window is still coming up (a shortcut double-clicked during startup is the ordinary case), and a hand-off that finds nobody falls through so this process becomes the shell that opens it. A link is never silently dropped, which is also why the inbox is a queue: two shortcuts in quick succession are two links. Routing is the same four lines of translation as on Linux, because the decisions belong to `plan_from_link`: a resolved link becomes the call a tile click makes, with the same wake, trust and error surfaces. Never preempting a live session is checked here, since only this layer knows one is running. The manifest declares the protocol and an app execution alias rather than writing registry keys, so uninstall leaves nothing behind — and Windows' own "allow this app to handle punktfunk links?" prompt is the origin friction the design wants, not duplicated in-app. (Manifest comments are free of double hyphens: XML forbids them and makepri rejects the whole file, which is how the console flag broke the v0.15.0 build. A test parses the substituted manifest and asserts both declarations, so that can't rot silently.) "Create shortcut…" writes a `.lnk` targeting the ALIAS with the URL as an argument, not a `.url` internet shortcut: both work while the scheme is registered, only this one still works if it isn't, and targeting the alias keeps it valid across updates when the package path changes. Verified on the CI VM (192.168.1.133): clippy -D warnings clean and its tests green. Not yet exercised on glass — `start punktfunk://…` cold and with the app running, and a written shortcut, still want a real desktop session. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f5de661c9e |
feat(client/windows): overridden rows are marked and resettable, and the speed test targets the right layer
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (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
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
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 0s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Canceled after 0s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Canceled after 0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Canceled after 0s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Canceled after 0s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Canceled after 0s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 0s
docker / build-push-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
decky / build-publish (push) Canceled after 0s
deb / build-publish (push) Canceled after 0s
deb / build-publish-host (push) Canceled after 0s
deb / build-publish-client-arm64 (push) Canceled after 0s
ci / rust (push) Canceled after 0s
ci / rust-arm64 (push) Canceled after 0s
ci / web (push) Canceled after 0s
ci / docs-site (push) Canceled after 0s
ci / bench (push) Canceled after 0s
arch / build-publish (push) Canceled after 0s
apple / swift (push) Canceled after 0s
apple / screenshots (push) Canceled after 0s
android / android (push) Canceled after 0s
The two remaining pieces of P1 on Windows, both closing the same kind of gap the Linux client closed earlier. Overridden rows in profile scope now say so — an accent-marked caption — and carry the only way back to inheriting. Without it a profile is a one-way door here too, because overrides are recorded on touch and never inferred from comparing values. Reset calls the shared `SettingsOverlay::clear`, so the list of what can be cleared stays in one place across both shells. It also bumps a revision counter: the catalog changes behind the controls and nothing the page reads as state does, and root state compares before re-rendering, so without the bump the row would keep showing the value it just dropped. The speed test writes to the layer the TESTED host reads bitrate from, not the global — measuring one host used to re-tune every other one. Resolved exactly the way a connect resolves it: the one-off the test was started with (a pinned tile carries one), else the host's binding, else the global; a dangling binding falls back. The button names its target, so it says where it will write before you press it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
3d25482469 |
feat(client/windows): pinned host+profile tiles render in the grid
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 2m40s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 4m27s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m19s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 1m56s
apple / swift (push) Successful in 5m12s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 0s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 0s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Canceled after 0s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Canceled after 0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Canceled after 0s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Canceled after 0s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Canceled after 0s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 0s
docker / build-push-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
decky / build-publish (push) Canceled after 0s
deb / build-publish (push) Canceled after 0s
deb / build-publish-host (push) Canceled after 0s
deb / build-publish-client-arm64 (push) Canceled after 0s
ci / rust (push) Canceled after 0s
ci / rust-arm64 (push) Canceled after 0s
ci / web (push) Canceled after 0s
ci / docs-site (push) Canceled after 0s
ci / bench (push) Canceled after 0s
arch / build-publish (push) Canceled after 0s
android / android (push) Canceled after 0s
apple / screenshots (push) Canceled after 0s
The menu could pin since the last commit but nothing appeared, which is worse than not offering it. Each host's pins now render right after it, sharing its live status because they read the same record, with the profile named on an accent chip. A pin whose profile was deleted simply doesn't render. A pinned tile carries no menu of its own: it is a shortcut, not a second host, and pin/unpin already live on the primary tile's menu — the one place you decide it. Its hover key is `<fp>#<profile-id>` so two tiles for one host don't light up together. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2a088e13a4 |
feat(client/windows): bind a host to a profile, connect with one, pin one, copy its link
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 2m8s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m37s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m25s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 3m43s
apple / swift (push) Successful in 5m17s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 0s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 0s
flatpak / build-publish (push) Canceled after 0s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Canceled after 0s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Canceled after 0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Canceled after 0s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 0s
docker / build-push-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
decky / build-publish (push) Canceled after 0s
deb / build-publish (push) Canceled after 0s
deb / build-publish-host (push) Canceled after 0s
deb / build-publish-client-arm64 (push) Canceled after 0s
apple / screenshots (push) Canceled after 0s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Canceled after 0s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Canceled after 0s
android / android (push) Canceled after 2m34s
ci / rust-arm64 (push) Canceled after 0s
ci / web (push) Canceled after 0s
ci / docs-site (push) Canceled after 0s
ci / bench (push) Canceled after 0s
ci / rust (push) Canceled after 2m30s
arch / build-publish (push) Canceled after 2m39s
The WinUI half of §5.2: `Target` carries a one-off profile into the session's `--profile`,
the host editor gains the binding picker, and the tile names its bound profile on a chip so
what a plain click will do is visible without opening anything.
Shape notes, both forced by this toolkit rather than chosen:
- The flyout has no submenus, so "Connect with", "Pin as card" and "Unpin card" are flat
items, one per profile, matched by prefix. The prefixes end in ": " so a profile named
"Copy link" can't collide with a fixed entry.
- The binding picker commits on change rather than at Save. The rest of that sheet is text
boxes with draft refs, which need a Save; a ComboBox doesn't, and making it wait would be
the odd one out.
Semantics stay identical to the Linux client, because they are the design's, not the
platform's: "Connect with" never rebinds, "Default settings" is `Some("")` so it really
does override a bound host for one session, and a binding whose profile was deleted reads
as Default settings and resolves to it.
`pf_client_core::clipboard::set_text` is the small addition the crate needed for "Copy
link" — the OS clipboard plumbing was already there, just private to the streaming bridge.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
b220daf537 |
feat(client/windows): the settings screen edits profiles too
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 2m9s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 2m30s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m19s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 1m53s
apple / swift (push) Successful in 5m13s
ci / docs-site (push) Successful in 1m4s
ci / web (push) Successful in 3m3s
ci / bench (push) Successful in 5m43s
deb / build-publish-host (push) Failing after 6m17s
arch / build-publish (push) Failing after 9m36s
ci / rust-arm64 (push) Failing after 9m41s
ci / rust (push) Failing after 9m44s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 32s
decky / build-publish (push) Successful in 43s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 25s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 24s
deb / build-publish (push) Successful in 8m52s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 17s
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 21s
docker / build-push-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
apple / screenshots (push) Canceled after 0s
android / android (push) Canceled after 13m2s
deb / build-publish-client-arm64 (push) Canceled after 6m50s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 2m47s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 2m42s
P1 on the WinUI shell. The same surface now edits either layer, switched by a combo above the section title — one settings UI, not a second editor that drifts from the first. The interesting difference from GTK is where the override gets recorded. This shell commits PER CONTROL, so it cannot hand over a list of touched rows the way a commit-on-close dialog can. Instead each control hands `SettingsOverlay::absorb` the effective settings before and after it fired, and absorb records the field that moved. The comparison is against what the control was SHOWING, not against the globals, so setting a value to whatever the global happens to be still records an override — the pin the design asks for — and nothing is ever removed by inference. ⚠️ The content key becomes `(scope, section)`. Keyed on section alone, switching scope diffs one scope's controls into the other's, which re-sets each reused ComboBox's items (clearing WinUI's selection) while skipping `selected_index` wherever the two scopes' values compare equal — the combos then render blank until touched. That is the same trap the section key already existed for, one axis wider. Only profileable rows render in profile scope: auto-wake, the library toggle, the decoder and GPU pickers and the forwarded-pad picker are facts about this device or this network, not about "Game vs Work". Two shapes follow from this toolkit rather than from the design. Creating a profile takes an auto-numbered name and is renamed from the row below, because ContentDialog here is text-only and cannot hold a text field — the same constraint that put the host editor in a tile. And Delete arms a root state that renders the confirmation declaratively, since dialogs here are elements with `is_open`, not calls. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
37edb1cc1a |
refactor(clients): both shells wake through the shared state machine
audit / bun-audit (push) Successful in 21s
ci / web (push) Successful in 1m39s
ci / docs-site (push) Successful in 2m7s
audit / cargo-audit (push) Successful in 2m55s
apple / swift (push) Successful in 5m25s
ci / bench (push) Successful in 7m47s
ci / rust-arm64 (push) Successful in 10m28s
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 27s
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
windows-host / package (push) Failing after 11m22s
windows-host / winget-source (push) Skipped
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 8s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m17s
deb / build-publish-client-arm64 (push) Failing after 6m0s
deb / build-publish (push) Successful in 13m3s
arch / build-publish (push) Successful in 16m27s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m36s
flatpak / build-publish (push) Successful in 6m50s
android / android (push) Successful in 19m52s
deb / build-publish-host (push) Successful in 16m54s
ci / rust (push) Successful in 22m54s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 4m48s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 6m1s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Failing after 20m33s
release / apple (push) Successful in 30m41s
docker / build-push-arm64cross (push) Successful in 24s
docker / deploy-docs (push) Successful in 26s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 28m24s
apple / screenshots (push) Successful in 22m37s
The last of C0's duplication: GTK's `wake_and_connect` and the WinUI shell's ran their own copies of the same 90 s / 6 s / 1 s loop, and the Windows one's comment said it "mirrors the Apple HostWaker" — now it literally does, because both drive `WakeWait`. What is left in each shell is the part that genuinely differs: its dialog or screen, its advert drain, its re-key, and its route back into the trust gate. Behavioural review, since a sleeping host to test against wasn't available: - GTK polled every 500 ms and resent on a wall-clock timer; it now ticks once a second like every other implementation. Up to half a second slower to notice a host coming back, and exactly the reference cadence in exchange. - GTK took the FIRST matching advert in a drain and returned mid-loop; it now drains fully and takes the last, i.e. the freshest address. Same connect, fresher re-key. - Windows sent one packet before the loop AND one on its first pass; the machine owns the packets now, so that duplicate is gone. Resends still land at 6 s, 12 s, … and the budget still expires at 90 s. - Both keep their own cancel path, their own re-key, and their own error surface. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a2be3f4de9 |
feat(client): settings profiles — the override catalog, the one resolver, and the session flag
A client setting is global today: pick 4K@120 HDR and the retro box gets it too.
The one escape hatch (per-host `clipboard_sync`) proves the shape works but covers a
single field. This is P0 of design/client-settings-profiles.md — the headless core the
shells, Apple, Android and the deep-link grammar all build against. No UI yet.
A profile is a NAMED BUNDLE OF OVERRIDES, not a snapshot: `SettingsOverlay` is sparse
`Option`s, so a field the user never touched keeps following the global value live, and
fixing a global once fixes it everywhere. `Some(x)` equal to today's global is still
meaningful — it pins x against a later global change — which is why the UI will write
`Some` on touch and `None` on an explicit reset, never by diffing.
The catalog is its own `client-profiles.json`, deliberately not part of the settings
file: that file has five whole-file load-modify-save writers with no merge, so a profile
written by one would be dropped by the next. Which host uses which profile is a field on
the host record instead of a map keyed by host — that is what dissolves the client's
long-standing "what identifies a host record" question for this feature.
Resolution has exactly one implementation, `trust::effective_settings()`:
effective = overlay(profile).apply(global)
profile = one-off pick ?? host binding ?? none
Both session construction sites go through it (`main.rs` AND `console.rs` — touching one
and not the other is a Windows-only build break), so the console, and therefore Decky,
honor host bindings with no work of their own. Nothing here can fail a connect: a
deleted binding resolves as "no profile", and a one-off that can't be honored falls back
to the DEFAULTS rather than to the host's own profile — "connect with Work" must never
quietly stream "Game". `--profile <id|name>` is the one-off door for the shells' coming
"Connect with ▸" and for `punktfunk://…&profile=`; `--profile ""` forces the defaults on
a bound host. The active profile's name closes the stats overlay's first line, so
"which profile am I on?" is answerable without leaving the stream.
Also here, because this effort touches every one of these anyway:
- `KnownHost` gains `profile_id`, `pinned_profiles` and a lazily minted stable `id`. The
id is groundwork (design §4.5) — no lookup is re-keyed, `fp_hex`/`addr:port` stay.
- `upsert` now preserves the state the USER set on a record — the profile binding, its
pins, the clipboard decision, the id — against refreshes that carry none of it.
`clipboard_sync` survived that only because the function happened not to mention it.
- `KnownHost::default()` exists so construction sites read `..Default::default()`; five
hand-written literals were exactly how a new field gets silently dropped on re-pair.
- All three client stores now write temp+rename. A torn settings file loads as `Default`
— i.e. silently resets every setting the user has.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
df04880273 |
feat(client/input): desktop (absolute) mouse mode — remote-desktop sweep M1
New physical-mouse model beside capture: Desktop leaves the pointer uncaptured and sends absolute positions through the letterbox (MouseMoveAbs — every host injector already consumes it). Capture stays the default and the game model. - pf-client-core: MouseMode (capture|desktop) persisted in Settings, default capture so existing stores are unchanged. - pf-presenter: Capture grows a desktop model — latest-wins pending abs position coalesced per loop iteration (same 1000 Hz discipline as relative), flushed before clicks/keys/wheel so they land where the cursor is; Ctrl+Alt+Shift+M flips the model live; local cursor stays hidden over the window (the host's composited cursor is the one you see until the M2 cursor channel); Windows keyboard grab only engages for capture (a desktop stream is something you Alt-Tab away from). - gamescope gating: its EIS is relative-only, so desktop mode is pinned off there (resolved_compositor), with a log note. - Settings surfaces: GTK row (dynamic caption), WinUI combo, console-UI row + step test, capture-hint line. Verified: fmt + clippy -D warnings + tests (33/40/19) on Linux .21; clippy -D warnings for all five crates incl. punktfunk-client-windows native on .173. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
8fe90a8a4b |
fix(client): clipboard poll cadence was never applied (and CI clippy)
ci / web (push) Successful in 1m8s
apple / swift (push) Successful in 1m17s
ci / docs-site (push) Successful in 1m9s
apple / screenshots (push) Successful in 6m17s
ci / bench (push) Successful in 6m51s
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 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 10s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
deb / build-publish (push) Successful in 9m11s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 10s
android / android (push) Successful in 13m32s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 8m1s
arch / build-publish (push) Successful in 17m21s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Failing after 5m6s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 7m43s
deb / build-publish-host (push) Successful in 11m42s
flatpak / build-publish (push) Failing after 8m23s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Failing after 6m14s
ci / rust (push) Successful in 25m38s
windows / build (aarch64-pc-windows-msvc) (push) Failing after 5m45s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 15m54s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m32s
docker / deploy-docs (push) Successful in 27s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 9m8s
POLL was dead: the local clipboard was re-read once per inbound event wait (<=120 ms) instead of on its own 400 ms cadence, so the constant documenting the interval described something the code did not do. Give it a deadline of its own -- the event wait is short because it bounds teardown latency, which is no reason to hammer the Win32 clipboard eight times a second while the user is copying in another app. Build State in one expression while here, and note for next time: CI runs clippy as --workspace --all-targets -- -D warnings, so a scoped run without -D warnings (what I did) does not reproduce it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
cac23b7a05 |
feat(client/windows): console UI in the header, one Edit dialog per host
ci / web (push) Successful in 48s
ci / docs-site (push) Successful in 57s
apple / swift (push) Successful in 1m20s
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 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 11s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 11s
docker / build-push (ci, ci/rust-ci-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 8s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Failing after 4m57s
ci / bench (push) Successful in 6m31s
ci / rust (push) Failing after 6m36s
apple / screenshots (push) Successful in 6m17s
deb / build-publish (push) Successful in 10m38s
flatpak / build-publish (push) Failing after 8m17s
docker / deploy-docs (push) Successful in 26s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Failing after 6m15s
deb / build-publish-host (push) Successful in 10m24s
arch / build-publish (push) Successful in 12m43s
android / android (push) Successful in 14m5s
windows / build (aarch64-pc-windows-msvc) (push) Failing after 6m1s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m15s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 18m40s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 8m56s
Three related bits of the same cleanup: - The console UI moves from a card in the page body to a button in the header row, beside Add host / Shortcuts / Settings. It also opens the console's OWN host view now (bare --browse) rather than diving straight into one host's library, which is what the card did -- the couch counterpart of this page, not a shortcut past it. - spawn_browse / open_console take an Option target to express that. - The per-host overflow menu collapses: rename and the two clipboard labels become one Edit dialog carrying every per-host property, mirroring the Apple client's add/edit sheet (name, address, port, Wake-on-LAN MAC, share clipboard). A menu item per field read as clutter and buried the entries that matter. Cleared fields mean leave-as-is rather than erase, except the MAC, which is legitimately clearable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2508b72062 |
feat(client/windows): make the console UI findable, and launchable on its own
Two problems, one feature. The gamepad/couch UI existed but you had to
already know it was there:
- On the hosts page its card only rendered when a controller was CONNECTED,
so with no pad plugged in there was no visible entry point at all -- the
only other door being a per-host overflow menu behind a "..." nobody
opens. The card now always shows, with copy that adapts to whether a pad
is present, so the feature is findable before you own the hardware.
- There was no way to start it directly. An HTPC or TV box wants the couch
interface as its first screen, not the desktop shell. "punktfunk-client
--console" now hands straight off to it (fullscreen unless --windowed),
which covers shortcuts, Steam entries, autostart and Task Scheduler.
The Start-menu tile needs its own executable: an MSIX <Application> cannot
pass arguments to a full-trust exe, so "punktfunk-client.exe --console" is
not expressible there. punktfunk-console.exe is a ~20-line hand-off to the
session binary's browse mode, staged into the package and given its own
tile ("Punktfunk Console").
Nothing new is implemented behind either door: a bare "--browse" was
already a complete standalone client (host list, discovery, PIN pairing,
settings, Wake-on-LAN, library). It just had no front door.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
aa45757a72 |
feat(client/windows): per-host clipboard toggle
The bridge landed always-on whenever the host permitted it; sharing a clipboard is a trust decision about a specific host, so it needs to be opted into. Mirrors the Apple client's per-host model (StoredHost.clipboardSync, "Share clipboard with this host") rather than a global switch: KnownHost::clipboard_sync, toggled from the host card's overflow menu, default off. The session binary resolves the stored flag itself in session_params, so a direct connect and the console's own launches honor the same decision without every caller having to remember to pass it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
a042dc2850 |
fix(client/windows): settings combos render blank until touched
The section-switch key was moved onto the scroll_view's direct child during the parity restructure, which silently disabled it: ScrollView::children() is Children::PositionalSingle, reconciled positionally with keys ignored. The column was therefore reused across section switches, diffing one section's controls into another's -- which re-sets a reused ComboBox's items (clearing WinUI's selection) but skips selected_index whenever the two sections' values compare equal, so every combo rendered empty until changed. Put the keyed column back inside a panel's child list, where the keyed diff path actually runs and the switch remounts. This is the same bug the original code carried a comment about; the comment now names the PositionalSingle trap that makes the key placement load-bearing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
67d2964db9 |
fix(client/windows): render the settings category title in the content column
NavigationView.Header carries WinUI's own left inset -- the reactor exposes it as a string prop, so the template's padding is not ours to move, and the title sat visibly right of the cards beneath it. Render the title as the first element of the content column instead, where it shares the cards' left edge by construction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
344618e1f0 |
fix(client/windows): flush the settings headings with the cards below
The shared section() helper carries a 2px left inset -- right for the hosts/licenses lists it was written for, but in Settings it left every sub-section heading hanging one nudge right of its card's edge. Use a settings-local heading pinned flush left, and drop the same inset from the footer notes, so heading, card and note all share one left edge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
51f7ff1b3f |
feat(client/windows): invert scroll direction
The Apple client's toggle, wired through the shared session presenter: Settings::invert_scroll -> SessionOpts -> Capture, applied at the single seam where wheel deltas enter (before accumulation, so the fractional remainders stay consistent with what was actually sent). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
7a1809547b |
fix(client/windows): left-align setting captions, drop the width cap, add auto-wake
Three follow-ups to the parity sweep: - Captions read centred: a TextBlock defaults to Stretch alignment, so the MaxWidth cap left it floating in the middle of the leftover width instead of sitting under its control. Pin them (and the footers) Left. - The settings column kept the shared page() 640px cap, which -- next to the NavigationView pane spending the left third -- squeezed the cards into a narrow ribbon. Drop the cap here. - Auto-wake on connect (the Apple client's toggle) is now a setting rather than unconditional behavior. Default on, which is what it did before. Gating the flow's ENTRY, not its packet sends: with it off a failed dial just fails, instead of showing a "Waking..." wait that would never send a packet to end itself. The explicit "Wake" host-card menu action is deliberately NOT gated -- that is the user asking directly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
72583b272e |
feat(client/windows): settings parity with the Apple client's revamp
Adopt the Apple client's 2026-07 settings structure so the two desktop
clients read the same way. Category map is now theirs verbatim -- General
(session/app behavior), Display (everything about the picture), Input,
Audio, Controllers, About -- which moves four things: statistics and the
game-library toggle out of Video/About into General, controllers out of
Input into their own category, and the codec/bitrate/HDR/decoder controls
out of the old Windows-only "Video" tab into Display, where Apple keeps
them. Sub-section headers (Resolution / Quality / Decoding / Host output)
mirror their Sections, as do the two "applies from the next session"
footers.
Every field now carries its explanation directly under it instead of only
in a hover tooltip -- the same move Apple made, for the same reason. The
old comment here argued against prose in the card because a paragraph
ABOVE a control reads as its label; that holds, so the caption goes below,
which is what Apple does and what Windows Settings itself does. Wording is
shared verbatim wherever a setting means the same thing on both platforms.
Where behavior differs the text stays Windows-specific -- notably the
forwarded-controller picker, where Apple forwards one pad as player 1 and
this client forwards every controller as its own player.
Two fixes fell out of the sweep: the decoder picker never offered D3D11VA
even though the chain accepts it (and
|
||
|
|
1ef0229bd9 |
feat(client/windows): PyroWave decode + surface it in the GUI codec picker
The decoder was gated to Linux because, when it landed, the Windows client still had its own in-process WinUI/D3D11 presenter and the PyroWave present path there was an open question. That client has since been retired: Windows now spawns the SAME Vulkan session presenter as Linux, and the decoder is plain Vulkan compute on the presenter's device (no fds, no dmabuf, no D3D11 interop), so the question that gated it answered itself. pyrowave-sys already builds on Windows too -- the Windows HOST encoder ships on it. So this is a port by un-gating: every cfg(all(target_os = "linux", feature = "pyrowave")) becomes any(linux, windows) -- decoder module, backend variant, Decoder::new_pyrowave, the CODEC_PYROWAVE advertisement, the session pump's opt-in/build/label arms, and the presenter's planar CSC pass. No new code. Then offer it in the Windows GUI, which is what prompted this. It stays preference-only (resolve_codec never auto-picks it) and a host or device that can't do PyroWave just falls back down the ladder to HEVC. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
871ebb31ce |
feat(clients): render-scale setting on every client — shared punktfunk_core::render_scale
Client-side supersampling/downscaling: the client asks the host to render and encode at chosen-resolution × scale (the host does no scaling) and the presenter rescales the decoded frame to the display. >1 supersamples for sharpness; <1 lightens the host GPU and the link. Default 1.0 = Native, the prior behavior. The geometry lives once in punktfunk_core::render_scale (multiply, preserve aspect ratio, floor to even, clamp to the codec's per-axis ceiling — 4096 for H.264, 8192 otherwise), the Rust twin of the Apple client's RenderScale.swift, consumed by the native session client, the presenter's match-window path, the Windows/Linux settings UIs, Decky, and Android (settings + host connect + unit test). Implemented and platform-verified by the Apple-client-features session (Linux+Android+Apple green there); the punktfunk-core wiring (pub mod render_scale) is restored here after being lost in a working-tree reconciliation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1fc9ef0050 |
feat(core,host,clients): typed pairing rejections — every client says WHY, not "not accepted"
ci / web (push) Successful in 49s
ci / docs-site (push) Successful in 52s
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
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 44s
ci / bench (push) Successful in 5m56s
flatpak / build-publish (push) Successful in 6m41s
docker / deploy-docs (push) Successful in 27s
apple / swift (push) Successful in 4m40s
deb / build-publish (push) Successful in 12m7s
arch / build-publish (push) Successful in 14m10s
android / android (push) Successful in 17m16s
ci / rust (push) Successful in 17m57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m50s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m15s
windows-host / package (push) Successful in 14m33s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m55s
release / apple (push) Successful in 25m31s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m49s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m11s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 6m9s
apple / screenshots (push) Successful in 19m41s
A host's pairing-gate rejections (not armed / bound to another device /
rate-limited / identity required / denied / approval timeout / superseded /
wire-version mismatch) used to drop the connection with a bare code-0 close,
and every client collapsed that — plus plain unreachability — into one
"wrong PIN / not accepted" message. A dead network path, a disarmed host,
and an operator denial were indistinguishable, which is exactly the
misdiagnosis behind the recent Android pairing support thread.
- core: new ungated `reject` module — shared close-code block 0x60–0x67
(+ 0x42 busy promoted from the host), `RejectReason`, and
`PunktfunkError::Rejected`; `pair()`/`connect()` decode the host's
ApplicationClosed code into `Rejected` instead of a generic Io error.
C ABI v7: status block −20…−28 and `punktfunk_connect_ex8` (`status_out`
reports the failure cause; NULL-return alone can't). Wire unchanged —
old peers see exactly the old bare close.
- host: every gate rejection `conn.close()`s with its typed code (and the
human reason as close bytes) before erroring out of the session task.
- pf-client-core: shared `pair_error_message`/`connect_reject_message`
wording consumed by the Windows + Linux + console-UI + CLI surfaces; a
connect failure now renders the host's stated reason.
- android: `nativeTakeLastError()` JNI token + `ConnectErrors.kt` — a
network timeout is no longer reported as "wrong PIN, or the host isn't
armed", and a typed rejection skips the wake-and-wait fallback (the host
is demonstrably awake).
- apple: `HostRejection` + `.rejected`; the pair sheet and session alerts
show the stated reason; connect moves to `ex8`.
Completes the cross-client half of the hunks that rode along in
|
||
|
|
f88d0ae4dc |
feat(touch): cross-client touch-input modes on Linux + Windows
ci / web (push) Successful in 43s
ci / rust (push) Failing after 53s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 8s
decky / build-publish (push) Successful in 16s
ci / docs-site (push) Successful in 1m5s
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 6s
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
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m41s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m53s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 52s
apple / swift (push) Successful in 4m41s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 54s
ci / bench (push) Successful in 6m30s
docker / deploy-docs (push) Successful in 23s
flatpak / build-publish (push) Failing after 8m9s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
android / android (push) Has been cancelled
apple / screenshots (push) Has been cancelled
arch / build-publish (push) Has been cancelled
deb / build-publish (push) Has been cancelled
Bring the SDL presenter (Linux/Deck + Windows) to parity with the Android and
Apple clients: a persisted TouchMode selects how a touchscreen drives the host —
* Trackpad (default): relative cursor with pointer ballistics + the shared
gesture vocabulary (tap = left click, two-finger tap = right click,
two-finger drag = scroll, tap-then-drag = held left drag, three-finger tap =
cycle the stats overlay).
* Direct pointer: the cursor jumps to and follows the finger (absolute).
* Touch passthrough: every finger is a real host touchscreen contact.
Previously the presenter had no finger handling, so SDL synthesized mouse events
from touch and — under the stream's relative-mouse lock — walked the host cursor
into the corner (the reported Deck bug). SDL touch->mouse synthesis is now off;
DIRECT touchscreens route through a new incremental gesture engine (a port of
Android TouchInput.kt / Apple TouchMouse.swift), while INDIRECT trackpads keep
driving the mouse. Fingers map through the aspect-fit letterbox onto the content
rect.
TouchMode lives in the shared trust::Settings (default trackpad, so passthrough
is opt-in like the other clients); the GTK and WinUI settings screens both gained
a "Touch input" picker. Gesture engine, letterbox mapping, and settings
back-compat are unit-tested (28 tests green); clippy -D warnings clean; full
Linux client + session build verified on-host.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
236c59754b |
refactor(gamepad/windows): drop the dead shell fork, use pf-client-core's service
clients/windows/src/gamepad.rs was a 629-line near-verbatim fork of
pf-client-core's SDL gamepad service, frozen at an old single-pad design.
Commit
|
||
|
|
ef5808254a |
refactor(windows): remove the legacy in-process builtin stream path
The real Windows client is the spawned punktfunk-session Vulkan binary (pf-client-core); the in-process builtin GUI stream — reachable only via PUNKTFUNK_BUILTIN_STREAM=1 — was dead weight kept alive by nothing and a recurring source of wasted effort. Remove it: delete present/render/input/ audio.rs and the builtin remainder of session/video.rs, rip all the builtin wiring (app/mod, connect, stream), and make connect always spawn. Preserve the two shipped keepers that happened to live in those files by relocating them to a new probe.rs: run_speed_probe (the per-host network speed test used by the Settings speed page and --headless --speed-test) and decodable_codecs (the codec-capability advert on the probe connect). Trim gpu.rs to just the Settings adapter picker (adapter_names + helpers). --headless now supports only --speed-test — the in-process decode/frame-counter went with the pump. Drops the now-orphaned deps opus, wasapi, crossbeam-channel, anyhow; keeps ffmpeg-next (probe::decodable_codecs still needs it). Net 4432 deletions. Statically verified (module wiring, imports, orphaned symbols/deps all clean); the type-level compile runs on the windows-amd64 CI runner, which has the toolchain this non-Windows host lacks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9514a8c0e2 |
fix(client): correct Linux/Windows "Forwarded controller" copy for multi-controller
ci / web (push) Successful in 51s
ci / docs-site (push) Successful in 56s
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 8s
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 10s
decky / build-publish (push) Successful in 52s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
apple / swift (push) Successful in 4m39s
ci / bench (push) Successful in 6m27s
windows-host / package (push) Successful in 8m34s
flatpak / build-publish (push) Successful in 7m15s
docker / deploy-docs (push) Successful in 24s
android / android (push) Successful in 12m49s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 4m16s
deb / build-publish (push) Successful in 12m50s
arch / build-publish (push) Successful in 16m32s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m53s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m44s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m14s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 17m53s
ci / rust (push) Successful in 25m18s
release / apple (push) Successful in 22m3s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m57s
apple / screenshots (push) Successful in 18m56s
The relm4 (Linux) and native (Windows) shells spawn punktfunk-session, which
since the native-plane rework forwards ALL controllers by default — but the
"Forwarded controller" settings dropdowns still described the pre-rework world
("Automatic (most recent)", "Exactly one controller is forwarded to the host").
The dropdown already lists every detected pad and wires set_pinned(None)=all /
set_pinned(key)=single-player; this fixes only the misleading labels, subtitle,
tooltip, and stale leading comments to match: Automatic forwards every real
controller (each its own player); pick one to force single-player.
cargo check -p punktfunk-client-linux green; Windows is windows-gated (pure
string edits, CI windows.yml).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
a95b518ef3 |
fix(windows): show app version on About screen + capitalize Punktfunk on licenses
ci / docs-site (push) Successful in 51s
ci / web (push) Successful in 57s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
decky / build-publish (push) Successful in 19s
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 7s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 7s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 38s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 2m1s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 2m3s
apple / swift (push) Successful in 5m1s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m4s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 1m25s
ci / bench (push) Successful in 8m10s
docker / deploy-docs (push) Successful in 20s
arch / build-publish (push) Successful in 11m11s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 12m47s
android / android (push) Successful in 16m10s
deb / build-publish (push) Successful in 16m57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 17m47s
ci / rust (push) Successful in 23m26s
apple / screenshots (push) Successful in 19m57s
The About settings card had no version at all — add an identity block (app name + "Version <CARGO_PKG_VERSION>", the workspace version) at the top, the WinUI Settings convention and matching the Apple client's "Version X" wording. Also capitalize the brand name on the licenses screen (was lowercase "punktfunk"). Verified against the pinned windows-reactor source + cargo fmt --check; full Windows link left to CI (Windows-only crate). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
890c7531d8 |
Merge branch 'midstream-resize': mid-stream resolution resize
apple / swift (push) Successful in 4m19s
ci / rust (push) Failing after 46s
ci / web (push) Successful in 51s
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 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
ci / bench (push) Successful in 5m17s
arch / build-publish (push) Successful in 10m44s
docker / deploy-docs (push) Successful in 11s
android / android (push) Successful in 16m43s
windows-host / package (push) Failing after 8m29s
flatpak / build-publish (push) Failing after 8m13s
deb / build-publish (push) Successful in 11m24s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m51s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 4m1s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 12m24s
release / apple (push) Successful in 24m35s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 12m24s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m2s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 5m25s
apple / screenshots (push) Successful in 19m55s
Lands the mid-stream resolution resize feature (client-driven Reconfigure so the host's virtual display + encoder follow a resized client window without a reconnect), all paths default OFF: - host hardening H1-H5 + session-binary Match window (C1) - Apple macOS/iPadOS Match-window trigger + settings (C3) and the resize overlay (blur + spinner) client UX - Windows on-glass fixes: corrective-ack actual resolution + pf-vdisplay monitor re-arrival for out-of-list mid-stream modes - Linux backend matrix + the live-reconfigure gate unit tests Validated on-glass: Windows IDD-push (.173), Linux Mutter + KWin. Android (C4) deferred; Apple full build pending on a Mac. |
||
|
|
e6fbcecdb9 |
fix(clients/windows): GUI text inputs read the live value, not a stale render snapshot
ci / web (push) Successful in 50s
ci / docs-site (push) Successful in 1m5s
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 16s
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 14s
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 1m0s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 2m33s
apple / swift (push) Successful in 4m24s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 2m18s
ci / bench (push) Successful in 7m10s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m17s
arch / build-publish (push) Successful in 11m38s
deb / build-publish (push) Successful in 12m6s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m9s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m46s
android / android (push) Successful in 17m7s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m48s
docker / deploy-docs (push) Successful in 22s
ci / rust (push) Successful in 26m33s
apple / screenshots (push) Successful in 19m29s
A component() page re-renders reliably only when its props change: root wraps every screen in a stable animated border, so once the entrance tween settles the reconciler skips that unchanged-props subtree and a page's own use_state writes never force a re-render. Three text fields read their value at click time from that stranded local state: - PIN pairing sent an empty PIN, so pairing always failed with "wrong PIN, or not armed?" — the reported bug. The CLI --pair path bypasses the reactor and worked. - "Add host" Connect captured the empty mount-time address and silently did nothing (you open the modal precisely when the host isn't being discovered, so no discovery tick re-renders the page while you type). - Rename round-tripped the draft through an always-deferred AsyncSetState into a controlled text box, fighting the caret on fast typing and dropping the last character when Save was clicked before the write landed. Fix: hold each field's live value in a use_ref cell written on every keystroke and read at commit time (uncontrolled input), instead of a render-time snapshot. Rename is seeded when its target changes and no longer re-renders the whole page per keystroke. Reviewed the rest of the app (settings, speed test, library, stream, connect/request-access/waking, forget) — all driven by root-state props and wired correctly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d0d9bd5bfb |
feat(resize): mid-stream resolution resize — host hardening (H1–H5) + session-binary Match window (C1)
design/midstream-resolution-resize.md Phase 0 + Phase 1. Host (Phase 0): - H1/H5: per-backend Reconfigure acceptance gate — reject for gamescope (all sub-modes; a resize must never relaunch the title) and under the per-client-mode identity policy (a resize would resolve a different display slot). Synthetic stays reconfigurable on purpose (the protocol test source; the C-ABI roundtrip test rides it). Plus a 500 ms host-side min-interval backstop against Reconfigure spam. - H2: rollback/corrective ack — the data plane reports the mode actually live after a failed rebuild (or a refresh the backend capped) through a reconfig_result channel; the control task forwards it as a second accepted Reconfigured so the client's mode slot self-corrects. - H3: live stats mode — SendStats reads a packed AtomicU64 (w|h|hz) updated on every switch instead of latching the session-start mode. - H4: registry::retire(gen) — a mode-switch rebuild force-releases the superseded Linux display, so linger/forever keep-alive policies don't accumulate kept monitors at stale modes. VirtualOutput carries pool_gen (fresh AND reused) and the Pipeline tuple threads it to the switch arm. Client (Phase 1, default off): - Settings: match_window policy + persisted last window size; exposed as the Resolution tri-state (Native / Match window / explicit) in the Skia console, GTK and WinUI settings pages. - pf-presenter: window opens at the persisted size; Hello mode follows the window's pixel size; D2 trigger discipline (400 ms debounce to resize-end, ≥1 s spacing, even-floor + ≥320×200 clamp, each distinct size requested at most once — covers rejects and host rollbacks) as a pure, unit-tested decision; HUD line + title refresh on a switch. - Session binary wires both --connect and --browse paths; the WinUI shell is session-always, so this covers Windows too. Verified: workspace tests + clippy green; synthetic --remode end-to-end; live session-binary run (window at persisted 1000×600 → Hello 1000×600@60). On-glass per-backend matrix (Mutter/KWin/gamescope-reject, keep-alive accumulation) still pending before any default flip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d4467a44e2 |
feat(core): mid-stream clock re-sync — live offset survives wall-clock steps and drift
Networking-audit deferred plan §2. The host↔client offset was measured once at connect; an NTP step or slow drift silently corrupted the clock-based jump-to-live signal, the ABR one-way-delay signal, and every latency stat — 4a3b1ae2's disarm backstop stopped the IDR storm but lost the detector for the session. Now the client re-estimates mid-stream and recovers it. - quic: ClockResync — the connect-time 8-round probe/echo estimate as a select!-driven state machine (rounds matched by echoed t1, stale batches ignored), plus accept_resync (batch min-RTT ≤ max(2 ms, 1.5× connect RTT) so a congested window can never bias the offset). No wire change: the host has always answered ClockProbe at any time on the control stream. - client: the offset lives in an Arc<AtomicI64> seeded at connect; the control task re-probes every 60 s and immediately after the pump's FIRST no-op clock flush (the "clock stepped under me" signal, sent on the next report tick). On apply: store, reset stale_frames/noop_clock_flushes, re-arm the clock detector if a step had disarmed it. The disarm heuristic stays as the final backstop. Public NativeClient::clock_offset_ns keeps the connect-time value (ABI untouched); new clock_offset_now_ns() / clock_offset_shared() expose the live value. - consumers migrated to the live offset: pf-client-core session stats, the pf-presenter e2e stamp, Windows session/render, Android feeder/drain/ DisplayTracker (the tracker holds the shared handle, not the client, so the leaked render-callback refcount can't pin the session). - probe: --clock-resync runs a second full handshake mid-connection and asserts a sane, consistent estimate. Live against the local canary host: offsets 8646/2139 ns, disagreement 6 µs, 8/8 rounds — OK. Unit tests cover the round collection, stale-echo rejection, batch restart, min-RTT selection, and the acceptance guard. cargo ndk check green. Remaining manual validation: `sudo date -s "+2 sec"` on a live streaming client → expect one no-op flush, a re-sync, re-armed detector, no IDR pulse. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
38b9f310e2 |
feat(clients): tiered stats overlay everywhere — Compact/Normal/Detailed on every platform
ci / docs-site (push) Successful in 1m5s
apple / swift (push) Successful in 1m10s
ci / web (push) Successful in 1m12s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m46s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m57s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 53s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 1m10s
ci / bench (push) Successful in 5m43s
decky / build-publish (push) Successful in 17s
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 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 6s
release / apple (push) Successful in 8m10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
arch / build-publish (push) Successful in 11m59s
android / android (push) Successful in 13m3s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 6m39s
docker / deploy-docs (push) Successful in 18s
apple / screenshots (push) Successful in 5m32s
deb / build-publish (push) Successful in 14m54s
ci / rust (push) Successful in 23m19s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 15m53s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m44s
flatpak / build-publish (push) Failing after 35s
Ship the Android client's 3-tier stats-overlay semantics in every other client (design/stats-unification.md vocabulary): Off → Compact (one line: fps · e2e ms · Mb/s + loss flag) → Normal (mode + e2e p50/p95 + loss counters) → Detailed (decoder path, HDR tag, per-stage latency equation). Apple: new StatsVerbosity in PunktfunkKit persisted under punktfunk.statsVerbosity (migrates the legacy hudEnabled bool: explicit off → Off, else Normal). The existing three-finger tap (TouchMouse, trackpad/pointer modes only — touch passthrough untouched) now cycles the tiers instead of toggling, matching Android; ⌃⌥⇧S (menu + captured-state monitor) cycles the same ladder. Tiered StreamHUDView (compact glass pill / headline HUD / full equation HUD); the iOS corner disconnect also shows in Compact (the pill carries no button). Tier pickers on iOS, macOS, tvOS and the gamepad settings UI. Session stack (Linux + Windows + Deck share punktfunk-session): shared pf_client_core::trust::StatsVerbosity; Settings grows stats_verbosity with a show_stats fallback, and writes keep the legacy bool in sync so pre-tier binaries reading the same JSON agree on off vs on. Ctrl+Alt+Shift+S cycles the tier and re-renders the OSD immediately from the last stats window; the stdout stats: line always carries the full Detailed text so the shell status card and scripts keep a stable shape; --stats bumps Off → Normal without demoting a richer tier. Tier pickers in the GTK dialog, the WinUI settings page and the console-UI settings row; shortcut copy updated (GTK shortcuts window, Windows help, session README). The Windows legacy builtin path keeps its bool HUD. Tests: tier migration/round-trip in trust.rs, tiered stats_text output in pf-presenter. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
bf9be59f0b |
feat(clients/windows): rename the Help button to Shortcuts
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m39s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m37s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m15s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 1m30s
apple / swift (push) Successful in 1m12s
apple / screenshots (push) Successful in 5m48s
android / android (push) Successful in 4m30s
arch / build-publish (push) Successful in 5m47s
ci / web (push) Successful in 1m0s
ci / docs-site (push) Successful in 1m19s
ci / rust (push) Successful in 5m10s
deb / build-publish (push) Successful in 3m30s
ci / bench (push) Successful in 5m5s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
decky / build-publish (push) Successful in 13s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 6s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 5s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 6s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 5s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 11m25s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 11m33s
docker / deploy-docs (push) Successful in 12s
"Shortcuts" says what the page actually is — the in-stream keyboard/controller reference — and the keyboard glyph reads better than the generic help icon (user feedback from the live-test round). Page title follows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f4850625bd |
feat(clients/windows): game library page + session window placement + help polish
apple / swift (push) Successful in 1m10s
android / android (push) Successful in 4m56s
arch / build-publish (push) Successful in 5m56s
audit / bun-audit (push) Successful in 13s
audit / cargo-audit (push) Successful in 1m34s
ci / web (push) Successful in 1m11s
windows-host / package (push) Successful in 7m57s
ci / docs-site (push) Successful in 1m27s
release / apple (push) Successful in 8m39s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m26s
ci / bench (push) Successful in 5m0s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m16s
apple / screenshots (push) Successful in 5m35s
ci / rust (push) Successful in 10m32s
decky / build-publish (push) Successful in 13s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 7s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 4m47s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m36s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 5s
deb / build-publish (push) Successful in 6m38s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m39s
flatpak / build-publish (push) Successful in 5m32s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 11m0s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 10m33s
docker / deploy-docs (push) Successful in 18s
UX polish batch 2 (plan workstreams D/E5/E6): - D: a mouse/keyboard game library page over pf-client-core::library (the GTK ui_library.rs counterpart): responsive 2-6 column poster grid (2:3 portraits, store badge, monogram placeholder while art streams in), loading / error+retry / empty / grid states, tap-to-play via a normal spawn carrying --launch id. Poster bytes land in a disk cache (%LOCALAPPDATA%\punktfunk\art-cache) so the Image widget loads file:/// URIs (reactor has no from-bytes source) and revisits render instantly. Reached from a paired host's "Browse library..." menu item behind the new "Show game library (experimental)" Settings toggle (the shared library_enabled field, Apple/GTK parity). A Shared::library_gen generation guard keeps a superseded fetch from publishing (speed-test pattern). - E5: the session window opens at the shell's own top-left (--window-pos, new SessionOpts::window_pos) instead of centered on the primary display - streams land on the monitor the shell is on, and the hide/restore handoff reads as one window changing content. Fullscreen follows that display. - E6: the Help shortcuts add Alt+Enter and the controller escape chord (LB+RB+Start+Back, hold to disconnect). Also: rustfmt normalization of the merged probe helper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f5f186b691 |
Merge remote-tracking branch 'origin/main'
# Conflicts: # clients/windows/src/app/mod.rs |
||
|
|
573b2af334 |
feat(clients/windows): single-window handoff, shared settings store, console-UI surfacing
UX polish batch 1 (clients/windows/ui-polish-plan.md, workstreams W0/A1/B/C/E):
- W0: the shell's duplicated trust/settings structs are gone — src/trust.rs
re-exports pf-client-core's, so the shell and the spawned session binary share
ONE Settings shape over client-windows-settings.json. Fixes real bugs: the
shell's saves no longer drop session-side fields; the stats-overlay toggle
(show_hud -> show_stats, serde alias migrates old files) and the forwarded-
controller pick now actually reach the spawned session. The "Streaming engine"
Settings combo is gone (PUNKTFUNK_BUILTIN_STREAM=1 stays as the dev A/B knob).
- Forwarded-controller pinning by stable key (vid:pid:name, pf-client-core's
format): persisted as forward_pad, applied by the shell's own service at
startup AND by the session binary in session_params (both OSes — the session
never applied the pin before).
- A1 single window: the shell hides itself when the spawned session window
presents its first frame ({"ready":true}) and restores + foregrounds on the
child's exit — exactly one visible Punktfunk window at any time. Restore runs
before the request-access cancel gate so a Ready/Cancel race can't strand the
shell hidden.
- C console UI: punktfunk-session --browse gains --json-status (ready when the
library window presents; error line on a failed start), and the shell
surfaces it — "Open console UI" on paired hosts' menus, plus a controller-
detected hint card targeting the most recent paired host (x64 only; aarch64
ships no skia ui feature). Browse spawns hide/restore the shell like streams.
- B responsive: minimum window size (420x360); the hosts header collapses to
icon-only buttons below 700 px; the session status card shrinks instead of
clipping (max_width); busy pages, help actions, licenses and long labels wrap.
- E polish: session exe gets the app icon (winresource + WM_SETICON via the new
pf-presenter win32.rs); both exes share an explicit AppUserModelID on
unpackaged runs (packaged identity wins) so the windows group as one taskbar
app across the visibility handoff; "Start streams fullscreen" toggle passes
--fullscreen (GTK parity); connects stamp KnownHost::last_used; the connect
target is stashed for every route so "Connecting to X" always names the host.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
01428ced58 |
feat(clients): reachability-probed presence + shareable Decky host management
apple / swift (push) Failing after 27s
release / apple (push) Failing after 26s
apple / screenshots (push) Has been skipped
windows-host / package (push) Has been cancelled
arch / build-publish (push) Has been cancelled
android / android (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / web (push) Has been cancelled
ci / docs-site (push) Has been cancelled
ci / bench (push) Has been cancelled
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Has been cancelled
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Has been cancelled
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Has been cancelled
docker / deploy-docs (push) Has been cancelled
flatpak / build-publish (push) Has been cancelled
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Has been cancelled
windows / build (aarch64-pc-windows-msvc) (push) Has been cancelled
windows / build (x86_64-pc-windows-msvc) (push) Has been cancelled
deb / build-publish (push) Has been cancelled
decky / build-publish (push) Has been cancelled
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Has been cancelled
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Has been cancelled
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Has been cancelled
Add a bounded, trust-agnostic, mDNS-INDEPENDENT QUIC reachability probe and surface it everywhere saved-host presence is shown, so a host reached over a routed network (Tailscale/VPN/multicast-filtering LAN) no longer reads Offline just because it isn't advertising — the display-side companion to the 0.8.4 dial-first connect fix. Core: - punktfunk-core: NativeClient::probe (bounded handshake; a real host answers even on trust mismatch, a wrong/closed/TCP-only port fails) + punktfunk_probe C ABI (ABI_VERSION 3->4, header regenerated). - pf-client-core: trust::probe_reachable_many (parallel per-host sweep). Presence pips now read `advertising OR probed-reachable`, refreshed by a ~10-12s background sweep off the UI thread: - Linux (relm4): ui_hosts probed map + HostsMsg::Probed sweep. - Windows (windows-reactor): pf-probe worker -> HostsProps.probed. - Apple (SwiftUI): HostStore.refreshReachability, driven by HomeView + GamepadHomeView .task. - Android (Compose): nativeProbe JNI seam + periodic LaunchedEffect (LNP-gated), online dot added to the touch HostCard. - Decky already probes via --list-hosts --probe. Decky client: make the flatpak client's known-hosts store the single source of truth via new headless CLI modes (--list-hosts / --add-host / --set-host / --forget-host / --reset / --reachable). The plugin can now add a host by address, edit/forget hosts, reset all state (keeping the client identity), and shows probe-backed online pips — state is shared with the desktop client, not duplicated. Also lands in-progress Android 17 LNP groundwork (targetSdk 37 + ACCESS_LOCAL_NETWORK runtime flow, permission dialogs) that was already present in the working tree. Verified: cargo check + clippy clean (punktfunk-core, pf-client-core, linux, android native); android assembleDebug BUILD SUCCESSFUL; decky typecheck + rollup build clean; probe true/false-positive behaviour exercised against a live host. Windows and Apple were not compiled locally (no MSVC/Xcode on this Linux box). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d6647b9183 |
feat(clients/windows): port the Vulkan session client to Windows — session-always
The punktfunk-session Vulkan client (clients/linux-session, now clients/session) builds and runs on Windows; the WinUI shell spawns it for every stream. Verified live: 10-bit HEVC via Vulkan Video on both AMD (iGPU) and NVIDIA, 5120x1440 at 130 fps / 8 ms end-to-end on the RTX 4090. - pf-ffvk: Windows bindgen branch (FFMPEG_DIR + PF_FFVK_VULKAN_INCLUDE, no pkg-config); provisioning fetches Vulkan-Headers (pinned v1.4.309). - pf-client-core: builds on Windows — WASAPI audio (audio_wasapi.rs, cfg-swapped via #[path], same surface as the PipeWire twin), VAAPI/dmabuf gated inline (chain = vulkan -> software), trust reads the WinUI shell's %APPDATA% stores (parity tests pin both serialized shapes), Settings gains adapter/hdr_enabled (serde-defaulted; Linux stores unaffected). - pf-presenter: builds on Windows — dmabuf module Linux-gated; SDL keyboard grab while captured (Alt+Tab/Win reach the host); pick_device ranks discrete over integrated (device 0 was the iGPU on hybrid boxes — the silent footgun) and honors PUNKTFUNK_VK_ADAPTER (the Settings GPU pick, exported by the session). - run loop: block in one SDL wait woken by input AND decoded frames (a per- session forwarder pushes a FrameWake user event) instead of a 1 ms poll — measured 111%% -> 5%% of a core (NVIDIA), 86%% -> 3.5%% (AMD), stats unchanged. The pump's decode-fence wait became once-per-window sampling (no per-frame pipeline stall; the stat now shows true backlog). - pf-console-ui: builds on Windows (skia-safe msvc prebuilts); font lookup falls through fontconfig aliases to concrete DirectWrite families (Consolas/Segoe UI) — browse/coverflow works, verified against a live host. - WinUI shell: session-always via new src/spawn.rs (GTK spawn.rs port — CREATE_NO_WINDOW, stdout contract, kill handle); the Stream screen is a status card (chips + stage lines from the child's stats). The legacy in-process D3D11VA path stays behind Settings "Streaming engine" / PUNKTFUNK_BUILTIN_ STREAM=1 as the A/B baseline until Phase 8 deletes it. SessionParams.video_caps makes the HDR toggle real. - clients/linux-session renamed to clients/session (builds for both OSes). - CI/MSIX: both workflows build/test both bins with widened path filters; the MSIX ships punktfunk-session.exe. ARM64 session builds --no-default-features (rust-skia has no aarch64-pc-windows-msvc prebuilts; flip when it does). A/B on this box (5120x1440 HEVC vs home-worker-5): NVIDIA Vulkan 130 fps / 8 ms e2e / 1.6 ms decode — clearly better than the built-in path. The AMD iGPU VCN saturates at ~52 fps where its own D3D11VA does ~70 — Adrenalin Vulkan decode is slower on APU silicon; discrete RDNA validation gates Phase 8. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ab6790ef6c |
fix(clients): dial-first reconnect — don't gate the connect on mDNS presence
A saved host that stopped advertising on mDNS but has a learned Wake-on-LAN MAC was routed into a wake-and-WAIT-for-mDNS flow instead of being dialed. For a host reached over a routed network (Tailscale/VPN/another subnet) mDNS never sees it, so the wait could never succeed and reconnects were impossible — the host log showed no connection attempt at all. The tile's Offline pip and this connect gate shared the same LAN-only `advertises`/`liveAdvert` predicate, so a perfectly reachable host read as unreachable. The first connect worked only because the MAC hadn't been learned yet (a direct dial); once learned, every reconnect wedged in the waker. All four full clients carried this pattern (deliberate mirrors of the Apple `HostWaker`). Each now DIALS FIRST: fire the magic packet up front (fire-and-forget — harmless if the host is awake, and a genuinely-asleep box is booting while the dial times out) and attempt the connection unconditionally. Only a FAILED dial falls into the visible "Waking…" wait, whose redial carries no fallback so it can't loop. A fingerprint-mismatch/trust-rejection failure means the host ANSWERED, so it never takes the wake path. - apple: SessionModel.connect gains `onUnreachable`; startSession dials first. - linux (relm4): AppMsg::WakeConnect wakes+dials; a per-request wake_fallback is armed and consumed in SessionExited, matched by fingerprint/address. - windows: initiate_waking + ConnectOpts.wake_on_fail; the worker's Failed arm escalates to wake_and_connect only on a non-trust failure. - android: doConnectDirect gains onFailure; doConnect wakes+dials first. Decky was already correct (always launches `--connect`; WoL fire-and-forget). Explicit "Wake host" menu actions are unchanged — waiting on mDNS is right when the user explicitly asked to wake a box. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
8a4b0c7bae |
feat(windows): surface shortcuts on a Help screen; exit fullscreen when the stream stops
apple / swift (push) Successful in 1m10s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m13s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m13s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 44s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 41s
android / android (push) Successful in 4m11s
ci / rust (push) Failing after 1m21s
ci / web (push) Successful in 55s
apple / screenshots (push) Successful in 5m52s
ci / docs-site (push) Successful in 1m2s
arch / build-publish (push) Has been cancelled
ci / bench (push) Has been cancelled
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
deb / build-publish (push) Has been cancelled
decky / build-publish (push) Has been cancelled
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Has been cancelled
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Has been cancelled
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Has been cancelled
docker / deploy-docs (push) Has been cancelled
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Has been cancelled
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Has been cancelled
The in-stream keyboard shortcuts were only reachable deep in Settings > Input. Move them to a dedicated Help screen reached from a Help button on the host list (mirrors the Licenses screen — a hook-free page routed from root), and drop the reference card out of Settings so there's one home for it. Fullscreen (F11) was a window-style toggle that stream stop never undid, so disconnecting while fullscreen left the GUI borderless-fullscreen. Split toggle_fullscreen into is_fullscreen/enter_fullscreen/exit_fullscreen sharing a module-level SAVED_PLACEMENT, and have input::uninstall() call exit_fullscreen() on teardown. exit_fullscreen is a no-op when nothing was saved, so it's safe on every stream stop and idempotent with F11 — fullscreen is now streaming-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
bf07700c74 |
feat(clients): windows shortcut parity (Ctrl+Alt+Shift+S, F11) + surface stream shortcuts
apple / swift (push) Successful in 1m7s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m39s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m19s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 54s
android / android (push) Successful in 4m45s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 57s
arch / build-publish (push) Successful in 5m31s
ci / rust (push) Failing after 1m11s
ci / web (push) Successful in 56s
ci / docs-site (push) Successful in 1m0s
apple / screenshots (push) Successful in 5m56s
ci / bench (push) Successful in 5m17s
deb / build-publish (push) Successful in 4m53s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
decky / build-publish (push) Successful in 26s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m35s
flatpak / build-publish (push) Successful in 4m42s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Failing after 8m48s
docker / deploy-docs (push) Successful in 21s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Failing after 7m54s
Windows was missing two of the four stream shortcuts the GTK client has: Ctrl+Alt+Shift+S (toggle the stats overlay live) and F11 (toggle fullscreen). Add both to the low-level keyboard hook — S flips a HUD_VISIBLE atomic seeded from Settings::show_hud at install (Settings is the default, the key overrides it for the session, matching GTK), F11 drives a borderless-fullscreen toggle on the window HWND and re-locks the pointer geometry for the new client rect. Both are consumed locally, never sent on the wire. Surface the full key set in two places, on both clients: - in the UI: a read-only "In-stream keyboard shortcuts" reference card in the Windows Settings > Input section (the counterpart of the GTK Shortcuts window), plus the expanded HUD hint; the Linux keyboard hint gains F11. - at stream start: a bottom-centre banner listing the shortcuts for the first few seconds of every session, independent of the HUD setting. Linux gets the matching start-flash of its capture hint (capture engages on map and hid it, so the keys were never shown until the first release). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
19388f3412 |
feat(windows-client): WOL wait-until-up + IP re-key (Apple/Android parity)
Like the Linux client, the Windows client had WOL send + MAC storage + a Wake action + fire-and-forget auto-wake, but no poll-until-up + IP re-key. Add the polished flow (mirrors Apple HostWaker + the request_access screen pattern): - connect::wake_and_connect — send the magic packet, show a cancelable Screen::Waking busy page, poll discovery::browse() until the host reappears (re-sending every 6 s, 90 s budget), then dial; re-key the saved host (KnownHosts::upsert) if it woke on a new IP. - Screen::Waking + waking_page, routed in app/mod.rs (mirrors RequestAccess). - the saved-host tile routes an offline-with-MAC tap to wake_and_connect; MENU_WAKE stays a pure send-only button. Reviewed against the request_access reference — DiscoveredHost/KnownHost/Target types, the widgets, .call()/.lock(), and the initiate signature all match; compile-verified by Windows CI (no local Windows toolchain). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
3545febbeb |
style: rustfmt the Wake-on-LAN modules
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fb709d68ce |
feat(clients): Wake-on-LAN in apple/linux/windows/android/decky
Each client learns a host's MAC from the mDNS `mac` TXT while it's awake, persists it on the saved-host record, and — when reconnecting to an offline host — sends a magic packet before connecting, plus an explicit "Wake host" action. Apple wraps the C-ABI; linux/windows call the core fn directly (linux also gains a --wake CLI mode); android via a new nativeWakeOnLan JNI export (the mDNS browse record gains a 7th mac field); decky shells out to the linux client's --wake before launching the stream. iOS/tvOS need the managed com.apple.developer.networking.multicast entitlement (pending Apple approval), so the wake path + UI are gated off via PunktfunkConnection.wakeOnLANAvailable and the entitlement is commented out — keeping iOS/tvOS releasable. MAC-learning stays active on every platform so it lights up the moment it's ungated. macOS works today. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d6dbce1dcc |
feat(clients): host/network split in every stats HUD (stats phase 2, client side)
Consumes the 0xCF host-timing plane (
|
||
|
|
83460cd2c0 |
feat(clients): unified stats vocabulary across every client + Moonlight comparison docs
One stat model everywhere (design/stats-unification.md): four measurement points (capture/received/decoded/displayed), three stages that tile the interval exactly, and a HUD that shows the addition explicitly — end-to-end 14.2 ms p50 · 19.8 p95 · capture→on-glass = host+network 9.8 + decode 2.1 + display 2.3 replacing each client's ad-hoc mix of overlapping absolutes (the Apple HUD's three arrow lines that looked sequential but weren't), mean-vs-median decode times (Windows/Linux), missing same-host-clock flags (Windows/Linux), and three different names for the same capture→received measurement (probe's "reassembled", Apple/Android's "client", Windows/Linux's post-decode "lat"). Per client: Apple threads receivedNs through the VT decode via the frame refcon bit pattern so the decode stage exists at all (stage-1 fallback honestly degrades to a capture→received headline); Windows carries FrameTimes through the existing frame channel to the render thread and adds e2e p50/p95 post-Present; Linux stamps received at AU pop and rides decoded_ns on DecodedFrame to the paintable-set site; Android pairs receipt stamps with MediaCodec output buffers via the codec's pts round-trip (JNI stats array 14→16 doubles, indexes 0-13 unchanged). fps now uniformly counts received AUs; lost/(received+lost) per window, hidden at zero. docs-site gains "Understanding the Stats Overlay": what each line means, why the equation only approximately sums (percentiles), and a line-by-line Moonlight/Sunshine matrix — including that Moonlight has no end-to-end number and its "network latency" is an ENet control RTT, so punktfunk's headline must not be compared against any single Moonlight line. Verified here: linux client + probe + core check/clippy/fmt green, android native cargo-ndk arm64 check green. Pending: Windows CI + on-glass, swift test on the mac, on-device Android. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
84fcbd3345 |
feat(clients/windows): WinUI UX batch - tile hover, Settings NavigationView, modal slide-up
Bump windows-reactor + windows to a4f7b2cb (from b4129fcc) for the new PointerEntered/PointerExited events; migration is mechanical renames only (SymbolGlyph->Symbol, placeholder->placeholder_text, on_changed-> on_text_changed/on_toggled, on_menu_item_clicked->on_item_clicked, on_ready->on_mounted). New runtime model: reactor lost its build.rs, so the client build.rs stages the WinAppSDK bootstrap via windows-reactor-setup::as_framework_dependent() and main calls windows_reactor::bootstrap() (missing either = 0x80040154 at launch); staged filenames unchanged, so pack-msix and the MSIX manifest are untouched. - Host tiles: WinUI pointer-over fill (ControlFillSecondary) via the new pointer enter/exit events, hover id in root state (backend-wired handlers bypass the reconciler flush, like the flyout clicks). - Settings: stock NavigationView sidebar (Windows-Settings pattern) with Display/Video/Input/Audio/About panes, built-in back arrow, wide content column, and a per-section content slide-up tween. The section card is KEYED by section: an in-place diff across sections re-sets a reused ComboBox's items (clearing WinUI's selection) but skips selected_index when the values compare equal, rendering a blank selection - the key forces a remount. Card titles/descriptions dropped; per-control guidance moved to hover tooltips (ToolTipService). - New "Show the stats overlay (HUD)" setting (show_hud, default on), honored mid-stream via the 400 ms HUD re-render. - Add-host modal: entrance fade + slide-up tween (scrim fades with it). - Self-initiated disconnect (Ctrl+Alt+Shift+D -> Ended(None)) returns to the host list silently instead of raising the error banner. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |