c3ecc291170c3ef5cc7eb02cb786cd4fa495b219
12
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f06b3d9d04 |
feat(console): the console answers a mouse and a finger, and host cards get a menu
Two gaps, both found on the shared Linux/Windows console UI. **The settings tabs only moved for a gamepad.** They were bound to the shoulder buttons and to PgUp/PgDn, and the legend spells PgUp/PgDn out only when NO pad is attached — so with a controller plugged in a keyboard user had nothing to find, and a mouse or a touchscreen could not change section at all. The root cause was wider than the strip: `SkiaOverlay::handle_event` matched only `KeyDown` and `TextInput`, so every mouse button, wheel and touch contact fell past the console into the run loop, which routes pointer input exclusively at `stream.capture` — `None` while you are browsing. Nothing in the console had ever been clickable. Making just the pills answer would not have helped either: the settings screen is opened with X from home, so a mouse could not reach it. So the console gets a real pointer path: - `Overlay::handle_pointer` carries mouse/touch in SWAPCHAIN PIXELS. The run loop converts (it owns the window, hence the display scale, and mouse coordinates are logical while fingers are normalised); the console then hit-tests the very rects it drew last frame. Only DIRECT touch devices are offered — an indirect trackpad already drives the mouse. - Widgets act on the PRESS, not the release. The list and both carousels scroll the focused item toward the centre, so what you pressed has slid out from under your finger by the time it lifts; press-to-act has no such race and there is no drag gesture to compete with. - The hint bar became the pointer's button bar. It is already the console's only on-screen statement of what the face buttons do, and a pointer has none — so its Confirm/Back/Secondary/Tertiary pills are clickable on every screen, which is what puts Settings and Library within reach of a mouse at all. - Tab / Shift+Tab change section; PgUp/PgDn still do, and the keyboard legend now reads "Tab". - Right-click is Back everywhere, EXCEPT at the root: B there quits the launcher and a right-click is far easier to fire by accident. Quitting stays explicit. **Host cards had no menu.** Every other client hangs Wake / Copy link / Edit / Forget off a host card; the console could add a host and connect to one, and that was all — so a renamed machine or a fat-fingered address stayed wrong forever unless you opened a desktop shell. UP on a saved tile now opens that host's menu, the same gesture the Android console uses, on the one direction a horizontal carousel leaves free. - `ConsoleCmd::UpdateHost` edits the stored host IN PLACE. Removing and re-adding would silently drop the fingerprint, the learned MAC, the pinned cards and the profile binding — that is a rename, not a re-pair. - `ConsoleCmd::ForgetHost` drops it; if it is still advertising it returns as a discovered, unpaired row, which is the honest state. - Forget arms on the first press and fires on the second. The other clients forget outright; a console is driven by a thumbstick from across a room. - A pinned profile card offers only Unpin. It is a shortcut, not a second host, and offering to forget the host from it would blur exactly the distinction a pin draws. - "Edit…" REPLACES the menu on the stack rather than stacking over it, so Back from the editor doesn't land on a menu describing the host as it was before the edit. Verified in the pf-lxcheck2 container (this crate compiles to nothing on macOS — a bare `cargo check` there is vacuous): plain build and `clippy --all-targets` clean under `-D warnings`, 72 tests pass. Seven are new, and cover the reported bug directly — a press on a pill selects that tab, and each tab still keeps its own cursor when a pointer is what switched it. |
||
|
|
bbbcf321e5 |
Merge origin/main into worktree-native-decode-m0
ci / web (pull_request) Successful in 1m19s
apple / swift (pull_request) Successful in 1m32s
ci / docs-site (pull_request) Successful in 1m23s
apple / screenshots (pull_request) Skipped
ci / bun-nix (pull_request) Successful in 25s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 1m8s
android / android (pull_request) Successful in 3m31s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 3m23s
ci / rust-arm64 (pull_request) Successful in 5m36s
nix / flake (pull_request) Failing after 11m59s
ci / rust (pull_request) Successful in 14m27s
main moved 93 commits while this branch ran. Two conflicts, both where main's new
work sat next to M10's excision:
packaging/flatpak/io.unom.Punktfunk.yml — main added the vendored gamescope WSI
layer (the only route to HDR on a Deck) and, before it, a vulkan-headers module.
Took both: this branch predates them and deletes neither. But the headers module's
stated consumer was pf-ffvk's bindgen over FFmpeg's hwcontext_vulkan.h, and M10
deleted pf-ffvk — so it now reads as dead weight to the next person. It is not:
the WSI layer IS a Vulkan layer, compiles against those headers, and builds after
it, so module order is the dependency. Rewrote the rationale to say so, including
why dropping it would be expensive to discover — flatpak.yml has no pull_request:
trigger, so a manifest break reaches main invisibly and a tag then ships no Linux
flatpak. Also recorded that the native decoder needs nothing from there: pf-vkdecode
reaches Vulkan through ash, which is pure Rust bindings, no bindgen, no C headers.
crates/pf-console-ui/src/screens/settings.rs — main restructured the gamepad
settings into TABS, which removed the per-row section headers; this branch had left
Some("Video") untouched from the merge base and added the pre-M10 decoder migration
next to it. Git could not tell those apart. Took main's structure (no header, its
deliberate change) with this branch's migration layered on: a stored `vulkan`,
`vaapi` or `d3d11va` names no preset in the tabbed list and would render as "—",
then silently rewrite the user's preference on the next save.
Gates on the merged tree, Linux container: fmt clean; cargo check --workspace
--all-targets clean; clippy --workspace --all-targets -D warnings clean; tests
green across pf-vkdecode (187), pf-client-core (163), pf-console-ui (58) and
punktfunk-host (447 of 448 — the one failure is the pre-existing
gamestream::stream::tests::sender_delivers_batches, a UDP-loopback EINTR under
qemu that fails identically on a pristine HEAD).
|
||
|
|
d5e23146c0 |
feat(client): M8 — the software rung is openh264 and rav1d, and swscale is gone
The ladder's last rung no longer runs FFmpeg. H.264 decodes through openh264, AV1 through rav1d, and HEVC is refused outright: no permissively licensed software HEVC decoder exists, so an HEVC session that exhausts its hardware rungs now tears down and re-dials advertising HEVC-less caps, and the host picks H.264. The plan calls that a first-class path; it is one. swscale is deleted, and with it the BT.601 default that its correction code existed to undo. Colour on the H.264 lane now comes from the same pf-bitstream planner every hardware rung submits from — openh264 reports no VUI at all — and AV1's comes per-picture from the sequence header. One colour source, one CSC: the old default is unrepresentable rather than merely fixed. Frames reach the presenter as three tightly-packed planes through the planar CSC pass, which had to be un-gated from the pyrowave feature and its device probe, since the last rung must exist on devices that failed that probe. rav1d rather than the dav1d crate, deliberately and against the plan's literal wording: dav1d-sys is system-deps-only, so it would add a system library and a .pc file to every client package — in the milestone family whose excision checklist exists to delete exactly that. rav1d is the same decoder, same licence, statically linked. The cost is honest: no-asm builds on both decoders, and software throughput is still unmeasured. The colour test is the milestone's exit criterion, so it is built to fail. Three fixtures, and a mutation check: hardcoding the swscale default turns the red bar to [255,24,0], and swapping Cb/Cr turns red to blue — a silent error no metadata assertion could catch. Review then disproved the range half of it numerically: with eight saturated bars, decoding the full-range fixture with the wrong range gives max error ZERO, because a mismatch only pushes values outside [0,1] where the shader clamps. A mid-tone was added; the wrong range now costs 11, well past the tolerance. The exit criterion I set was satisfiable by a test that proved nothing. Two blocking defects, both emergent rather than local. Software AV1 on a 10-bit stream never reached its typed refusal: rav1d is built 8-bit-only and returns ENOPROTOOPT, which the send loop turned into a generic error, so the pump's typed downcast missed and every AU failed identically — a permanent freeze on precisely the shipping case, since AV1 is advertised only where hardware AV1 exists and hardware AV1 plus HDR is Main 10. The shape is now read from the sequence header before any byte reaches the decoder, exactly as the H.264 leg reads the active SPS. And the new Reconnecting phase was the first state that is not streaming, not connecting, and still holding a live stream — which opened all three guards that had made a second launch impossible. Pressing A assigned over `stream` where every other site shuts down first, and StreamState has no Drop, so the old pump was detached: a second live session still submitting to a Vulkan device that gets destroyed underneath it. Nothing about the reconnect was wrong in isolation; the defect lived between a new state and three guards nobody re-examined. Start is now defensive and the retry raises the connecting modal, so the UI matches the state and B can cancel. Also closed: retry_caps was computed, tested and never applied, so a shape refusal could end a session reporting no codec available while a working retry existed; the retry inherited force_software sticky-true, landing an HEVC→H.264 fallback on software H.264 with working hardware H.264; it re-dialled with a stale mode; the CPU present arm had no survivable-failure handling where the pyrowave arm — same pass — has it; HEVC is no longer advertised when the decoder is pinned to software; and the software rung now feeds the recovery-point SEI it already had in hand to the re-anchor gate. ⚠ Two host-side gaps found while tracing, neither in scope here: Hello::launch is NOT idempotent (gog:/custom: targets spawn a second copy on a retry; the field is kept verbatim because dropping it orphans the gamescope display whose reuse key includes the command), and a reconnected session can never adopt a game predating its own launch stamp, so it has no game-exit detection. ⚠ OWED: the on-glass software run. ~200 lines of new Vulkan on a path that only runs because the GPU already failed, and no driver has seen it. The review's minimum check is sync validation enabled, a non-multiple-of-16 mode, a mid-session resize and demotion, and both colour matrices. Gates: container clippy -D warnings over four crates, 236 tests, workspace check. pf-vkdecode and pf-bitstream are byte-for-byte untouched, so the hardware rungs' 250/250 stands. |
||
|
|
4a9a1c3ed4 |
feat(clients/gamepad-ui): multi-tone palettes, and a UI that takes its colours from them
ci / docs-site (pull_request) Successful in 1m10s
ci / rust-arm64 (pull_request) Successful in 1m18s
apple / swift (pull_request) Successful in 1m25s
apple / screenshots (pull_request) Skipped
ci / web (pull_request) Successful in 1m59s
android / android (pull_request) Successful in 5m53s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 6m36s
ci / rust (pull_request) Successful in 9m57s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 4m41s
The first pass built each palette by rotating ONE colour field's hue, and it
showed: every option was a single tone at several brightnesses, which reads flat
next to any real gradient. A palette is now an ordered ramp of DISTINCT hues.
The 4×4 mesh samples that ramp along the diagonal with a fixed per-cell offset
table, so neighbouring cells land on different parts of it and the colours pool
and swirl instead of banding; the control points' existing drift then moves the
pools around. Violet keeps its explicit sixteen colours, so the default is
untouched.
Twelve of them now, dark first then pale: Violet, Nebula, Abyss, Ember, Moss,
Graphite, then Holo, Sunset, Bloom, Dawn, Mint, Opal. Holo and Sunset are
straight takes on the two reference gradients — foil and poster.
`every_palette_is_multi_tone` measures the hue spread across all sixteen cells
and fails under 45° (20° for Graphite and Opal, which are meant to be
restrained). It caught Ember at 35°, all reds and oranges — the very flatness
this rework exists to remove — and Graphite at 3° despite a comment claiming it
drifted cool to warm. Both were rebuilt until the numbers matched the prose.
The UI follows the palette now, rather than wearing brand violet over whatever
happens to be behind it. Each palette carries an accent and a light flag, and an
Ink derived from those (foreground, accent, on-accent, glass, scrim and its
strength) is published to the whole tree — a thread-local in the console, an
environment value on Apple, a CompositionLocal on Android. Pale palettes flip
the ink: dark text on white frost, with the materials, tray scrims and every
wash that sits under text following suit.
Three things only the renders could have told us:
- Additive blending blows out over a pale ground. Android's blobs and Apple's
legacy field composite with Plus/plusLighter, which over near-white
saturates every blob to white — Holo rendered as a grey wash. Pale palettes
blend normally.
- A white scrim at the dark field's strength BLEACHES the gradient. Mixing
toward black at 0.4 reads as depth; toward white at 0.4 destroys the chroma
it is drawn over. The scrim now carries a per-palette strength.
- White glass over a bright field has far less separating it from its backdrop
than dark glass over a dark one, and needed more body.
Verified: console build + clippy -D warnings + 173 tests, Apple build + 200
tests + an iOS-triple typecheck, Android compile + 62 tests, and eyeball passes
on real renders of both the vivid and the pale ends (console CPU rasters; a new
Roborazzi light-palette scene, which is what exposed the blend-mode bug).
|
||
|
|
5a7f7f0fc5 |
feat(clients/gamepad-ui): section tabs, background palettes, and a backdrop that moves everywhere
ci / web (pull_request) Successful in 1m17s
ci / docs-site (pull_request) Successful in 1m42s
ci / rust-arm64 (pull_request) Successful in 2m36s
android / android (pull_request) Successful in 3m33s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 8m37s
ci / rust (pull_request) Successful in 8m58s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 3m47s
apple / swift (pull_request) Successful in 1m29s
apple / screenshots (pull_request) Skipped
The console settings were one 30-row scroll, which on a Deck meant thumbing past Video and Audio to reach the pad settings. They are now split across sections — Stream · Video · Audio · Controller · Interface · Profiles, plus Input on the desktop console, which alone carries the touch/mouse rows. L1/R1 walks them, each section remembers where its cursor was, and the names are the same word on every client so a setting is where you looked for it last. Shoulders are not the only route, because a D-pad remote hasn't got any: on Android, Up from the first row moves onto the strip (left/right walks sections there, A drops back in), and on tvOS the pills are focusable, so the focus engine handles it — a Siri Remote has no extended gamepad profile and never reaches the input poll at all. The desktop console needs neither; PageUp and PageDown already map to the same events. New "Background" row, six palettes: Violet (the brand default), Tide, Forest, Ember, Rose, Graphite. A palette is a hue rotation plus a saturation scale over the ONE colour field each client already draws, so every palette inherits its structure and Violet is the identity transform — existing installs see exactly what they see today. The maths is ported three times (Rust/Swift/Kotlin) under one shared `ui_palette` key, with the same assertions pinned in each language. It is presentation only, so it is a device preference and never part of a profile. The form screens no longer have a backdrop of their own. Settings, add-host and pair used to sit on a still gradient; they now wear the same living field at a calm mix — pools dimmed onto the palette's own corner colour, vignette halved so rows that run to the edges don't get crushed. On the desktop console that collapsed the old aurora-over-static crossfade into one shader pass with a chased uniform. Motion speed is identical in both modes on purpose: changing it would make the field jump mid-transition. Nothing in the gamepad UI is backed by a static image now, and Reduce Motion (Apple) / "remove animations" (Android) still freeze it. Also: the settings screen had no raster coverage at all — the eyeball dump is `#[ignore]`d — so a new test draws every tab, and the Android screenshot set gains a console-settings scene. Both earned their keep immediately: the renders showed the extra hint pushing "Done" off a 360 dp phone (the legend scrolls now, and the Section cell only appears where shoulders exist) and the form backdrop crushing its own edges. |
||
|
|
63a4f583b9 |
feat(console): profiles reach the gamepad UI — pinned cards, pin management, settings section
The Skia console now renders a pinned profile card after its host's primary tile (KnownHost::pinned_profiles resolved by the service thread), connects with that profile as a one-off via the existing effective_settings resolver, and shows the bound default profile on the primary tile. The settings screen gains a trailing Profiles section — one row per catalog profile with a live pin count — whose activation opens a pin-to-hosts screen; toggles ride the new ConsoleCmd::SetPin to the binary, which persists pinned_profiles (the same field the CLI resolves for Decky's host list). Profiles themselves stay desktop-authored (design client-settings-profiles.md §5.2a, §5.4). |
||
|
|
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. |
||
|
|
22a61e0b48 |
refactor(console-ui/W8): split shell.rs into shell/ facade + render/overlays/tests
Break the 1212-line pf-console-ui/src/shell.rs into a facade + shell/ subdir
(shell.rs stays the parent; `mod render;` resolves to shell/render.rs):
- shell/render.rs : the per-frame screen compose/transition path (Shell::render
+ the LayerEnv paint helper)
- shell/overlays.rs : the modal overlays (Shell::draw_overlays + draw_takeover)
- shell/tests.rs : the inline #[cfg(test)] module, extracted verbatim
The Shell struct + its public API + draw_aurora() stay in shell.rs (both children
reach draw_aurora + the private fields as descendants — no bumps). Sole visibility
change: draw_overlays -> pub(in crate::shell) (its caller Shell::render is now a
sibling). Zero re-exports needed (Shell/ConsoleOptions never leave the root). Pure
move; no behavior change.
Verified both platforms: Linux (home-worker-5) clippy -p pf-console-ui
(--all-targets -D warnings) + test; Windows (winbox, ASCII CARGO_HOME) clippy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
927a571414 |
feat(console): touch-mode setting + request-access pairing + polish
Extend the gamepad/console shell (pf-console-ui) to parity with the other clients: - Settings gain a Touchscreen → Touch mode row (Trackpad / Direct pointer / Touch passthrough), the one couch-relevant Settings field the screen lacked. - The pair screen adds the no-PIN delegated-approval path: a "Request access" action (only when the host advertises a fingerprint to pin) opens a connect the host PARKS until the operator approves this device, then persists it as paired. A role-based row model keeps the cursor off stale indices; manual hosts stay PIN-only, matching the desktop shells. - Threads request_access through OverlayAction::Launch and ConnectIntent; the shell shows a "Waiting for approval…" takeover, and the session binary parks on a 185 s budget (PendingApproval → persist-as-paired via on_connected). Auto-wake (WoL) was already implemented end-to-end and is left as-is. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
dd02e1f402 |
feat(clients): unified full-screen connect/wake takeover + iOS/tvOS Wake-on-LAN
ci / docs-site (push) Successful in 1m8s
ci / web (push) Successful in 54s
decky / build-publish (push) Successful in 20s
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 11s
android / android (push) Successful in 12m23s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
ci / bench (push) Successful in 6m3s
arch / build-publish (push) Successful in 12m57s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9m40s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 7m4s
docker / deploy-docs (push) Successful in 11s
flatpak / build-publish (push) Successful in 5m57s
deb / build-publish (push) Successful in 13m26s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m50s
apple / swift (push) Successful in 4m31s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m8s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m44s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 4m7s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m9s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m45s
release / apple (push) Successful in 21m27s
ci / rust (push) Failing after 6m47s
apple / screenshots (push) Successful in 20m18s
Give instant feedback the moment a host is picked, and make the wake wait a full-screen takeover instead of a modal card — unified into one ConnectOverlay across every client: - android: new ConnectOverlay (aurora backdrop; Connecting / Waking / timed-out phases) replaces the tiny inline "Connecting…" row and the WakeOverlay card. The dial phase is now cancelable and hands off to the wake wait in one frame. - console (pf-console-ui): the connect/wake overlays become a full-screen aurora takeover (draw_takeover) instead of a centered card over a dim scrim; the Waking → Connecting handoff no longer blinks. - apple: new ConnectOverlay mirrors it (macOS / iOS / tvOS), replacing the per-tile connecting spinner + the WakeOverlay card; instant "Connecting…" from model.phase, and the carousel is gated inactive during the dial. Also enable Wake-on-LAN on iOS/tvOS now that the multicast entitlement is approved: enable com.apple.developer.networking.multicast and flip wakeOnLANAvailable to true on every platform (MACs were already learned from mDNS, so wake works immediately). Verified: Android compileDebugKotlin + screenshot renders; console clippy + 36 tests + rendered phases on Linux; Apple swift build + 121 tests + rendered phases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
f470858110 |
fix(console-ui): gate input the instant A starts a wake — optimistic Waking card
Pressing A on an offline-but-wakeable host only queued ConsoleCmd::Wake; the service thread round-trips its first WakeStatus ~100 ms–1 s later, and until then the shell had no modal state — navigation kept flowing, the cursor drifted (e.g. onto Add Host), the arriving status then froze the UI wherever it sat, and for a fast wake the "Waking…" card was never rendered before the stream abruptly appeared. Every other input-initiated async path already gates synchronously (connect via `connecting`, pair via `busy`, screen pushes via `Motion`); the wake branch was the unique offender. `apply` now raises an optimistic WakeStatus placeholder in the same call that forwards the command — the Waking card shows immediately and the wake modal gate engages on the very next event. `sync` keeps the placeholder alive until the service's first real status supersedes it (tracked via `wake_optimistic`), and B-cancel clears both. Regression test replays the exact A-then-navigate sequence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
180ac3aa61 |
feat(console): full gamepad shell — hosts, PIN pairing, settings, OSK, screen transitions
apple / swift (push) Successful in 1m6s
android / android (push) Has been cancelled
apple / screenshots (push) Has been cancelled
arch / build-publish (push) Has been cancelled
audit / bun-audit (push) Has been cancelled
audit / cargo-audit (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
deb / 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
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
release / apple (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 / build (aarch64-pc-windows-msvc) (push) Has been cancelled
windows / build (x86_64-pc-windows-msvc) (push) Has been cancelled
windows-host / package (push) Has been cancelled
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Has been cancelled
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Has been cancelled
decky / build-publish (push) Successful in 25s
flatpak / build-publish (push) Successful in 4m31s
The Skia console UI grows from the single library coverflow into a complete couch shell (Apple gamepad-UI parity), so a Deck/gamescope session is self-sufficient end to end: - Home: host carousel (saved + discovered + Add Host tile) with presence pips, paired locks, wake & connect; A/Y/X/B per the Apple launcher. - In-console SPAKE2 PIN pairing (previously needed the Decky plugin or a desktop), add-host with a controller keyboard, couch settings over the shared Settings store, wake-on-LAN overlay with retry, cancelable dials. - Shell chrome: screen stack with push/pop entrance/exit choreography (slide + fade + recede; aurora↔form backdrop crossfade), per-pad button glyphs (PS shapes vs ABXY, keycaps with no pad), menu haptics, toasts, embedded Geist typography, in-stream start banner. - Steam Deck: our OSK never draws — fields start SDL text input (the new Overlay::text_input_active hook) and Steam's keyboard types; a hint chip points at STEAM + X. Other Linux gets the full gamepad keyboard tray. - punktfunk-session: bare --browse opens Home; --browse host opens that host's library with Home behind it (Decky launches keep working, B now pops instead of quitting). Service threads run discovery, 10 s probes, pairing, wake loops, fetches, and known-hosts persistence. - Presenter contract: Launch actions carry the host, CancelConnect never engages a won race, pad kind/list ride FrameCtx, menu events flow while dialing so B can cancel. Every screen renders to PNG on CPU raster for review (PF_CONSOLE_DUMP=<dir> cargo test -p pf-console-ui -- --ignored dump). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |