Files
punktfunk/Cargo.toml
enricobuehler 1d755ebeeb release: 0.31.4 — version bump, notes, CHANGELOG, Play notes
21 commits since v0.31.3 (14 non-merge). Cut from origin/main c407f6a6 (#393
merged).

THE NUMBER: a patch, and the cleanest one in a while — NOTHING versioned moves.
WIRE_VERSION 2, C ABI 25 with include/punktfunk_core.h showing NO diff against
the v0.31.3 tag (nor against v0.31.2 or v0.31.1 — third release running), driver
protocol 6 / min 3 with pf-driver-proto unchanged, gamepad channel 3, plugin
index schema 1, host event schema 1 (events.rs no diff), gamescope +pfhdr8 with
no new patch files, SDK 0.1.6 and plugin-kit 0.4.4 both untouched. api/openapi.json
is CONTENT-identical — info.version is the entire diff, the first release since
v0.31.0 where the management API grows nothing. No lockfile moves, in any of the
five. Four feat commits, all additive and all client- or Linux-host-side; v0.31.3
and v0.31.1 each carried two feats as a patch on the same reasoning.

THE SHAPE: Linux-host, and the faults share a premise — a policy written against
ONE desktop that silently did nothing on the others. Topology::Exclusive on a
gamescope session was implemented as org_kde_kwin_dpms and reached only a live
KDE desktop, so a Game Mode box (the deployment whose TV the operator most wants
dark) got nothing: #389 is five commits closing that — a DRM floor for a box with
no desktop at all, sway and Hyprland arms, the managed route's own hold, and the
box-session free that was gated on is_steam_launch. The sleep:idle BLOCK
inhibitor refused every Suspend() for a stream's length including the operator's
own from Steam's power menu, and on a DM-stopping takeover polkit's allow_active
fallback found no session to elect and dropped all three power actions to
auth_admin_keep (#391). The web console served the LEGACY identity because every
launcher names that pair and none of them CAN choose, so browsers rejected it and
the tray's mgmt-pinned probe refused the handshake and labelled a healthy console
dead (#393). Plus the client's encode down-driver (#392), an installTap format
snapshot reaching the ObjC terminate handler (#387), the console's bitrate row
(#388), and a GTK front door for the console UI (#390).

TWO ENTRIES WORTH THE READER'S ATTENTION, both recorded as such:
  * The sway arm of #389 is NOT verified on glass — the lab has Bazzite, Nobara
    and SteamOS, no sway box. Hyprland, KDE and the DRM floor were all measured
    on real machines. The notes say so in "Thanks" rather than burying it.
  * #389's Hyprland arm shipped WRONG in its first cut and was corrected on glass
    (540e282e): the dpms dispatcher is a TOGGLE that ignores the state word, so
    the blind "send off, later send on" would have LIT an already-dark head at
    stream start and DARKENED a lit one at teardown — the exact failure the
    feature exists to prevent. Read -> act only if different -> verify.

DOCS FRESHNESS, per docs/releases/README.md step 1: #390 and #393 carried their
own docs-site updates (clients.md, and #389's two gamescope.md edits). Two facts
were left owed, both in gamescope.md and both the "stale sentence" class that
docs-drift cannot catch:
  * the darkening mechanism list still read "KDE, else DRM" and never learned
    about the sway and Hyprland arms from e0a82201;
  * the "could not deliver a dark screen" example was a box in Game Mode, which
    is now precisely the case that IS served by the DRM floor. The real remaining
    gap is GNOME/Mutter, so it says that instead.
Also: the punktfunk group grew a third grant this cycle (the three logind power
actions, via 49-punktfunk-power.rules), and the group's own callout block
enumerated only the usbip nodes — one sentence added there.
No new PUNKTFUNK_* variable this cycle (PUNKTFUNK_UI_TLS_CERT/KEY are
pre-existing and already in the baseline), no new host subcommand, and no install
command, repo URL or port change, so data/platforms.json and the website's
vendored copy need nothing.

VERIFIED HERE: scripts/ci/check-docs-drift.sh clean, and PROVEN LIVE rather than
assumed — a deliberate one-byte break of the openapi copy makes it fail, and it
passes again restored; scripts/ci/check-docs-links.sh clean; the android.yml Play
notes gate run verbatim, 481/500 characters and unique against every other
release's file; both openapi copies cmp identical and stamped 0.31.4; cargo fmt
--all --check clean; git diff v0.31.3..HEAD empty on include/punktfunk_core.h, on
crates/pf-driver-proto, on punktfunk-core/src/lib.rs, on punktfunk-host/src/events.rs
and on api/openapi.json, which is the direct evidence for those five version rows;
git diff v0.31.1..HEAD on the C header also empty, which is the "third release
running" claim; Cargo.lock's 36 workspace version strings moved with Cargo.toml
and zero 0.31.3 strings remain; 27 crate dirs / 39 workspace members, both
unchanged; notes voice scan clean (zero backticked terms above ## For developers,
and zero internal names on a scan for gamescope/polkit/logind/DPMS/CRTC/DRM/
systemd/kwin/mutter/wlroots/rustls/ioctl); CHANGELOG link pinned to
src/tag/v0.31.4.

NOT RUN HERE, and why: any punktfunk-host build, clippy or cargo test — the host
does not compile on macOS at all, and CI covers it; the web/ and docs-site/ bun
builds — nothing under web/ is touched by this commit and the docs-site edits are
prose in an existing .md; the Android unit tests — nothing here touches Kotlin.

LEFT AS A DECISION, not made here: the tag itself. Notes, CHANGELOG and Play text
are authored ahead of it exactly as docs/releases/README.md step 1 requires, so
whichever workflow wins the create race seeds a complete release body — but
pushing v0.31.4 fans out every build workflow, and that is step 2.
2026-08-24 23:34:38 +02:00

128 lines
6.2 KiB
TOML

[workspace]
resolver = "2"
members = [
"crates/punktfunk-core",
"crates/punktfunk-host",
"crates/punktfunk-host/vendor/usbip-sim",
# The capability-carrying PyroWave encode worker. A SEPARATE binary by design — never a
# hardlink of, or a subcommand of, punktfunk-host (design/gpu-priority-capability-worker.md).
"crates/punktfunk-encode-worker",
"crates/punktfunk-tray",
"crates/pf-bitstream",
"crates/pf-bitstream/vendor/cros-codecs",
"crates/pf-client-core",
"crates/pf-clipboard",
"crates/pf-presenter",
"crates/pf-console-ui",
"crates/pf-driver-proto",
"crates/pf-paths",
"crates/pf-update",
"crates/pf-update-check",
"crates/pf-host-config",
"crates/pf-gpu",
"crates/pf-zerocopy",
"crates/pf-frame",
"crates/pf-win-display",
"crates/pf-encode",
"crates/pf-capture",
"crates/pf-inject",
"crates/pf-vdisplay",
"crates/pf-vkdecode",
"crates/pf-dxvadec",
"crates/pf-vaadec",
"crates/pyrowave-sys",
"crates/libvpl-sys",
"clients/probe",
"clients/cli",
"clients/linux",
"clients/session",
"clients/windows",
"clients/android/native",
"tools/cursor-probe",
"tools/display-disturb",
"tools/latency-probe",
"tools/loss-harness",
]
# Standalone PoC (built on its own; pulls usbip/tokio/libusb we don't want in the workspace).
# The vendored `ndk` is a [patch.crates-io] source, not a member: it only compiles for the
# `*-linux-android` targets, so workspace membership would break host `cargo build --workspace`.
exclude = [
"packaging/linux/steam-deck-gadget/usbip-poc",
"clients/android/native/vendor/ndk",
# Bring-your-own-hardware measurement tools. `hid-descriptor-dump` pulls `hidapi`, a C library
# wanting libudev on Linux; `win-input-matrix` is Windows-only and asks the live input stacks
# what they can see. Neither belongs in `cargo build --workspace` or on a CI leg with no pad.
"tools/hid-descriptor-dump",
"tools/win-input-matrix",
]
# ndk 0.9.0 verbatim from crates.io plus ONE visibility change (and two warning fixes — an
# unnecessary `std::` qualification and a feature-gated `Result` import): `MediaCodec::as_ptr` made public
# (upstream keeps it private and exposes no frame-rendered binding), so the Android client can
# call `AMediaCodec_setOnFrameRenderedCallback` via ndk-sys for the HUD's `display` stage
# (design/stats-unification.md). Drop the patch when upstream exposes the pointer or the callback.
[patch.crates-io]
ndk = { path = "clients/android/native/vendor/ndk" }
[workspace.package]
version = "0.31.4"
edition = "2024"
rust-version = "1.85"
license = "MIT OR Apache-2.0"
authors = ["unom"]
repository = "https://git.unom.io/unom/punktfunk"
# The `unsafe` discipline the `packaging/windows/drivers/*` crates already run, extended to the
# workspace. `unsafe fn` marks a CONTRACT the caller must uphold; it is not a licence for the whole
# body to skip checking. Without this lint an `unsafe fn` body is unchecked end to end, so a 600-line
# function hides which handful of lines are actually the unsafe ones — exactly the reviewer-hostile
# shape we are working down. (This is the Rust 2024 default; adopting it early also pays off the
# edition migration.)
#
# `deny`, not `warn`. `warn` was never actually a softer setting: CI runs `cargo clippy … -D
# warnings`, which promotes it to a hard error anyway — that is how adopting this lint turned main
# red on every platform for a day without the level in this file ever saying `deny`. A level that
# lies about its own severity is worse than a strict one, so this now states what CI already does,
# and the exemptions are written down per file instead of hiding in a 689-warning wall nobody reads.
#
# THE EXEMPTIONS. Fourteen GPU/FFI backend files carry `#![allow(unsafe_op_in_unsafe_fn)]` with a
# one-line reason each. They are not "not done yet" — they are where this lint stops paying:
# their bodies are ash/CUDA/AMF/libav calls almost line for line (measured: 64% of the sites are a
# single third-party FFI call, and of the 44 `unsafe fn`s in them only 4 have a body containing no
# unsafe operation at all). Narrowing them means one `unsafe {}` per line plus, since pf-encode also
# denies `clippy::undocumented_unsafe_blocks`, one hand-written SAFETY comment per line that could
# only ever restate "an ash call on a live device" — the precise noise that made `unsafe` stop
# meaning anything here before (see the header of `pf-win-display/src/win_display.rs`).
#
# Everything else in the workspace is at zero and enforced. Removing one of those allows, file by
# file, is real work with a real payoff; blanket-narrowing all fourteen is not. Prefer DELETING an
# `unsafe fn` marker over wrapping its body: keep the marker only where a caller can actually break
# something (a raw pointer, a borrowed HANDLE, a GPU object that must not be in flight).
[workspace.lints.rust]
unsafe_op_in_unsafe_fn = "deny"
# The companion lint: every `unsafe {}` / `unsafe impl` carries a `// SAFETY:` proof. Hoisted here
# from ~85 per-file `#![deny(...)]` attributes so a NEW crate (or a new module in an old one) is
# covered on creation rather than on remembering — the per-file form left pf-vkhdr-layer,
# wdk-probe, and half of pf-clipboard uncovered for months. NOTE: this table reaches only crates
# with `[lints] workspace = true`; `packaging/windows/drivers` and `packaging/windows/pf-vkhdr-layer`
# are SEPARATE workspaces and restate it (any "workspace-wide" claim must be made three times or it
# is false). Of the members, only the two vendored snapshots (pf-bitstream/vendor/cros-codecs,
# punktfunk-host/vendor/usbip-sim) stay out, deliberately — upstream code stays pristine.
[workspace.lints.clippy]
undocumented_unsafe_blocks = "deny"
[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
# NOTE: deliberately NOT `panic = "abort"`. punktfunk-core ships as a cdylib/staticlib into
# third-party apps (Swift/Kotlin/C) and its C ABI catches panics at the boundary
# (`catch_unwind` → `PunktfunkStatus::Panic`). `panic = "abort"` would make that guard a
# no-op and let a stray panic abort the embedding application. Unwinding keeps the
# documented isolation guarantee real.
# The per-frame hot path must stay fast even in dev builds.
[profile.dev.package."*"]
opt-level = 2