Files
punktfunk/clients/windows/Cargo.toml
T
enricobuehlerandClaude Opus 5 c27065c260
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
feat(client/windows): punktfunk:// opens the Windows client, and cards can write a shortcut
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>
2026-07-29 00:49:56 +02:00

105 lines
5.6 KiB
TOML

[package]
name = "punktfunk-client-windows"
description = "Native Windows punktfunk/1 client — WinUI 3 (windows-reactor) shell, SDL3 gamepads; streaming runs in the spawned punktfunk-session binary"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
authors.workspace = true
repository.workspace = true
[[bin]]
name = "punktfunk-client"
path = "src/main.rs"
# The couch/HTPC Start-menu entry. Its own executable because an MSIX <Application> cannot
# pass arguments to a full-trust exe — see the binary's own docs.
[[bin]]
name = "punktfunk-console"
path = "src/bin/punktfunk-console.rs"
# Everything is Windows-gated so `cargo build --workspace` stays green on Linux/macOS (the
# other native clients live in clients/linux and clients/apple); on other
# platforms this builds as a stub binary. Mirrors the Linux client's cfg(target_os="linux")
# gating exactly.
[target.'cfg(windows)'.dependencies]
# The protocol core, linked directly (no C ABI) — same as the GTK Linux client. NativeClient
# is Sync (mutexed plane receivers), so it drops into a UI app cleanly.
punktfunk-core = { path = "../../crates/punktfunk-core", features = ["quic"] }
# The shared client service layer: the trust/settings stores (ONE `Settings` struct for the
# shell and the spawned session binary — src/trust.rs re-exports it) and the game-library
# data model (fetch + art pipeline) behind the library page.
#
# `default-features = false` drops pf-client-core's default `pyrowave`, which would otherwise
# build the vendored PyroWave C++ INTO THE SHELL — dead weight here (the shell never decodes;
# it only offers "pyrowave" as a codec preference string the session binary acts on) and fatal
# on ARM64, where Granite's math falls back to x86 SSE intrinsics and stops at
# `simd.hpp: #error "Implement me."`. This does NOT drop PyroWave from the Windows client:
# decode lives in the spawned punktfunk-session binary, whose own default enables the feature,
# and cargo's feature unification turns it back on for the shared pf-client-core whenever that
# binary is in the same build (x64). On the ARM64 leg both are built --no-default-features, so
# nothing enables it and the C++ is never compiled.
pf-client-core = { path = "../../crates/pf-client-core", default-features = false }
# WinUI 3 UI via windows-reactor (a declarative React-like framework backed by WinUI). Its
# `build.rs` downloads the Windows App SDK NuGets and stages the bootstrap DLL + resources.pri
# next to the exe; it requires `CARGO_WORKSPACE_DIR` to be set in the build env. Unpublished
# (version 0.0.0) and fast-moving, so pinned to a verified commit.
windows-reactor = { git = "https://github.com/microsoft/windows-rs", rev = "a4f7b2cb7c63c6bb7fc77a2affe57145be1d8c4f" }
# Win32 / Direct3D11 / DXGI for the SwapChainPanel composition swapchain. Pulled from the SAME
# windows-rs commit as windows-reactor so their `windows-core` unifies — the `IDXGISwapChain1`
# we hand to `SwapChainPanelHandle::set_swap_chain` must satisfy reactor's `windows_core::Interface`.
windows = { git = "https://github.com/microsoft/windows-rs", rev = "a4f7b2cb7c63c6bb7fc77a2affe57145be1d8c4f", features = [
"Win32_Foundation",
"Win32_Graphics_Dxgi",
"Win32_Graphics_Dxgi_Common",
# GetCurrentPackageFullName — the packaged-run probe guarding the explicit
# AppUserModelID (main.rs; MSIX identity must win over the dev-run tag).
"Win32_Storage_Packaging_Appx",
"Win32_Graphics_Direct3D",
"Win32_Graphics_Direct3D11",
"Win32_Graphics_Direct3D_Fxc",
"Win32_Graphics_Gdi",
# IShellLinkW + IPersistFile for the "Create shortcut" writer, and COPYDATASTRUCT for the
# link hand-off between instances (deeplink.rs).
"Win32_System_Com",
"Win32_System_DataExchange",
"Win32_System_Console",
"Win32_System_LibraryLoader",
"Win32_System_Threading",
"Win32_UI_HiDpi",
"Win32_UI_Input_KeyboardAndMouse",
# Win32 window subclassing (SetWindowSubclass/DefSubclassProc) — the stream input hooks
# subclass the WinUI window + its content-island children to swallow WM_SETCURSOR so the
# local cursor stays hidden while the pointer is locked (WinUI otherwise re-asserts the arrow
# on every pointer move, defeating a one-shot ShowCursor(false)).
"Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging",
] }
# FFmpeg — used only to enumerate which codecs this client can decode (probe::decodable_codecs),
# advertised to the host on the speed-test connect. Same pin as the host/Linux client. (Real
# decode + present live in the spawned punktfunk-session binary.)
ffmpeg-next = "8"
# Gamepad enumeration + pin persistence for Settings runs on pf-client-core's shared SDL service
# (see the `gamepad` field in app/); the spawned punktfunk-session does the actual forwarding. SDL3
# itself (built from source via the bundled CMake on Windows) is pulled transitively by
# pf-client-core with the same `build-from-source,hidapi` features, so it is not a direct dep here.
mdns-sd = "0.20"
async-channel = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Embeds the app icon as an exe resource (build.rs) — Windows hosts only (rc.exe from the SDK).
# windows-reactor-setup stages the Windows App SDK runtime bootstrap (framework-dependent) next
# to the exe — the new-model replacement for the old windows-reactor build.rs; same pinned rev.
[target.'cfg(windows)'.build-dependencies]
winresource = "0.1"
windows-reactor-setup = { git = "https://github.com/microsoft/windows-rs", rev = "a4f7b2cb7c63c6bb7fc77a2affe57145be1d8c4f" }
[lints]
workspace = true