2e43fcc27c0c3663ef83ff028876ead6a1e58c93
957 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2e43fcc27c |
fix(web): unify display-field spacing (shared Field) + clearer layout help
- Every option in the custom form now renders through one `Field` wrapper (label → control → help at a consistent `space-y-3`), so the label→input gap is roomier and identical across keep-alive, the button groups, and max-displays — the first field no longer spaces differently from the rest. - Reworded the multi-monitor layout help: it now says Auto is side-by-side and Manual gives a per-display X/Y editor "in the Live displays section below once two or more are streaming" — instead of pointing at an "arrangement table" that isn't visible until clients connect. web tsc + vite build + biome-lint green; deployed on .21. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2aa7ac8c7e |
fix(web): explicit keep-alive Off/Keep toggle + roomier custom display form
Two UX fixes on the Virtual displays Configuration card: - Keep-alive is no longer implicitly "on" by typing in the seconds field. It's an explicit two-button toggle — **Off** (tear down at disconnect) vs. **Keep for** [N] seconds — and the seconds input only appears when "Keep for" is selected. The duration is remembered across toggles, and the help text explains both modes. - Opened up the cramped custom form: the fields container is `space-y-6` with more padding (`p-5`, rounded-lg), each option group is `space-y-2.5`, and the Save button sits below a divider — so it reads as sections with room instead of a pressed stack. web tsc + vite build + biome-lint green; deployed on .21. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6b4f9f86ed |
feat(web): move Virtual displays to its own nav section; roomier preset grid
The Host page was crowded (identity, codecs, ports, GPU, displays, compositors) and the virtual-display config surface is large enough to warrant its own home. - New **Virtual displays** nav section: `/displays` route + `sections/Displays` (moved DisplayCard out of `sections/Host`), a `MonitorPlay` sidebar entry after Host, and `nav_displays` i18n. Removed the displays card from the Host page/view. - On its own page the card splits into two: **Configuration** (presets + custom axes) and **Live displays** (the live list + arrangement table) — room to breathe. - Presets now render in a max-2-column grid (`sm:grid-cols-2`) with larger padding, a bigger section heading + preset titles (text-base semibold), roomier spacing, and bottom-aligned "what it sets" badges so the cards line up. web tsc + vite build + biome-lint green; deployed + verified on the Mutter box (.21). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8986667b78 |
feat(web): full virtual-display config surface — one-click presets + every axis editable
The Virtual displays card previously only exposed keep_alive/topology/max_displays as editable custom fields; conflict/identity/layout (enforced since Stages 3-5) had no controls, and the presets weren't surfaced as one-click options. Rework the card so the whole policy is configurable WITHOUT any client connected: - Presets front-and-center: each of the five (default/shared-desktop/hotdesk/workstation/ gaming-rig) is a one-click row showing its story AND what it sets (keep-alive · topology · conflict · identity badges), highlighting the active one. A click applies it immediately. gaming-rig stays disabled + "coming soon" (keep_alive: forever isn't cross-platform yet). - Custom mode reveals EVERY axis editably — keep-alive, topology, conflict, identity, layout, max-displays — seeded from the current effective behavior, with a Save button. A reusable `Choice` button-group + a tolerant `tr()` label lookup keep it tidy. - The live-display list + multi-monitor arrangement table stay below (they need a live session); the settings above work standalone. - en+de i18n for the new controls; refreshed the effective-preview row to show all axes. web tsc + vite build + biome-lint green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
62e0367f4b |
feat(punktfunk1): configurable data-plane UDP port (--data-port)
The native data plane used a random ephemeral UDP port (hole-punched), which a strict firewall can't pre-open — so remote clients behind one couldn't connect. Add an optional fixed data port: - `Punktfunk1Options`/`NativeServe` gain `data_port`; `bind_data_socket` binds the fixed port (→ direct, no hole-punch) or falls back to a random port + hole-punch when unset or the fixed port is busy (a concurrent session already holds it). - `UdpTransport::from_socket`/`from_socket_punch` adopt an already-bound socket, so the host keeps the SAME data socket from handshake through streaming — no drop-then-rebind window in which a concurrent session could steal a fixed port. - `main.rs` wires the CLI flag through to `NativeServe`. - Firewall docs updated (troubleshooting.md + apt/pacman/bazzite READMEs): control plane is the fixed UDP 9777; the data plane is a separate random port that usually needs no rule, with the fixed-port option for strict firewalls. Unit-tested: default random+hole-punch, and fixed-port-then-fallback-when-busy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
677a4f4cf5 |
perf(gamestream): move FEC packetization off the encode loop (3-stage pipeline)
FEC/Reed-Solomon packetization ran inline on the encode loop (~3 ms/frame at 4K), serializing behind encode and capping the GameStream frame rate below what the encoder alone can sustain. Split it into a 3-stage pipeline, each stage on its own thread joined by a depth-2 bounded queue: encode loop → [raw AUs] → packetizer (FEC/RS) → [wire batch] → paced sender - `spawn_packetizer`: turns each `RawFrame`'s access units into wire datagrams via the stateful VideoPacketizer, off the encode loop. Above-normal priority (on the per-frame critical path). Tallies goodput (bytes to the wire) for the stats window. - Backpressure chains up: a slow sender blocks the packetizer, which fills the encode→packetizer queue, which makes the encode loop drop the NEWEST frame — encode itself never waits. - A dropped frame now consumes no client-visible frameIndex (packetization is downstream), so the host re-anchors the reference chain: a drop arms a keyframe on the next iteration (`recover_after_drop`), routed through the same coalesce gate as client IDR requests so a burst of drops (congestion) can't become an IDR storm. - Perf/stats relabeled: `pkt` = AU drain, `send` = enqueue to the pipeline (both should be near-zero now; nonzero = encode being stalled by pipeline backpressure). Goodput read from the packetizer's atomic at the 1 s stats boundary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fa45608628 |
docs(display-management): Stage 5 host + web build complete — only on-glass validation + residuals left
Mark the web arrangement table done and narrow "remaining Stage 5" to validation (2 clients on a GPU box, not the dev VM) plus the two documented residuals (wlroots exclusive, Mutter APPLY_TEMPORARY revert). No further host/web build work in Stage 5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a7ff1cf312 |
feat(web): display arrangement table — the Stage 5 console x/y editor
Completes Stage 5's web piece (design/display-management.md §6.2): a `DisplayArrangement` editor in the Virtual displays card. For a ≥2-display group, it renders an x/y table over the live displays that carry a stable identity slot (the manual-layout key), seeded from the current computed positions; Save writes `PUT /display/layout` (via the generated `useSetDisplayLayout`), which switches the host to a manual layout applied from the next connect. Shared/anonymous displays (no identity slot) are omitted (they can't be pinned). Also refreshes the now-stale `display_pending_note` copy (conflict/identity/layout ARE enforced as of Stages 3-5) in en + de. web tsc + vite build green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
87435e6547 |
feat(vdisplay): complete Stage 5 §6A group semantics — per-group restore, Mutter group-aware, gamescope groups
Host-side completion of Stage 5 (§6A many-clients-as-monitors), all unit-tested; two-session on-glass validation still pending (no GPU on the dev VM): - Per-group topology restore (§6.1): the KWin `exclusive` restore no longer rides the per-session StopGuard (which re-enabled the physical the moment the FIRST of several exclusive sessions dropped, under a live sibling). KWin hands its restore to the registry as a closure (new trait `take_topology_restore`); the registry keeps it in the display group (`Entry.topology_restore`) and, on teardown, floats it to a surviving same-group sibling (`hand_off_restore`) or runs it when the group empties — outside the lock, before the last output's keepalive drops, so the compositor never sees zero outputs. All three teardown paths (lease drop / linger expiry / mgmt release) honor it. Single-display path byte-for-byte unchanged. Unit-tested: float / run-on-last / non-carrier-first / never-cross-backend. - Mutter group-aware (new trait `set_first_in_group`): the registry tells each backend whether it's the first display of its group; a non-first Mutter session EXTENDS into the already-exclusive desktop instead of re-applying a sole-monitor ApplyMonitorsConfig that would disable the first session's virtual. (Mutter connectors are un-nameable, so it can't build a keep-all-virtuals config; skipping is the safe equivalent.) Single-session unchanged. Residual APPLY_TEMPORARY revert documented. - gamescope groups (§6.1): `registry::group_key` makes each gamescope spawn its own group (independent nested session, no shared desktop) — never auto-rowed against or restore-/topology-grouped with another gamescope. Applied in both the /display/state assembly and the acquire-time position computation. Unit-tested. Remaining Stage 5: the web console arrangement table, on-glass validation, and the documented residuals (wlroots exclusive, Mutter APPLY_TEMPORARY). design doc updated. cargo build/test (214)/clippy --all-targets/fmt green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e0f15822ae |
feat(vdisplay): Stage 5 layout foundation — arrangement engine + /display/layout + group placement
§6A layout, riding the Stages 1-3 registry with no protocol change: - vdisplay/layout.rs: pure arrangement engine — auto-row (left-to-right in acquire order, top-aligned) + manual (per-identity-slot offsets, auto-row fallback for unpinned members). Unit-tested. - Registry group model (Linux): group = backend (one desktop per compositor session). /display/state groups entries, orders by acquire (gen), and computes each member's position via the engine (pure `assemble_displays`, unit-tested). DisplayInfo carries group/display_index/position/identity_slot/topology. The backend reports its resolved slot via the new VirtualDisplay::last_identity_slot (KWin only), so the arrangement + state key on per-client identity. - Registry-driven position apply: new VirtualDisplay::apply_position(x,y) (default no-op; KWin drives kscreen-doctor). Right after create the registry computes the new display's position over its whole group (pure `position_for_new`, unit-tested) and applies it — one seam for BOTH deterministic auto-row AND manual placement. Guarded: the origin (0,0) is skipped, so a single-display / first-of-group session (and every non-KWin backend) issues no positioning — the historical single-display path is unchanged. On-glass-validation-pending. - PUT /api/v1/display/layout: persists the console's manual arrangement via the pure EffectivePolicy::with_manual_layout transform (locks current effective behavior into explicit Custom fields + sets a manual layout, so arranging is orthogonal to the other axes). OpenAPI regenerated. - /display/settings `enforced` now lists all five axes (keep_alive, topology, mode_conflict [Stage 4], identity [Stage 3], layout [Stage 5]) — was stale at keep_alive+topology; the console reads it to know which controls are live. Still Stage-5 TODO (design/display-management.md §11): Mutter/wlroots group-aware analogues, per-group topology restore, the web arrangement table, gamescope decline. cargo build/test/clippy/fmt green; OpenAPI in sync. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a5dc3134de |
docs(display-management): handoff — mark Stages 0-4 done, Stage 5 started
Update the design doc for handoff: top-of-doc status, a Status/handoff block in §11 (per-stage state, validation boxes, key decisions), and per-stage [DONE]/[STARTED] markers. Records the decisions that diverged from the plan as written — the Windows admission default is reject (single-capturer IDD-push), reject is typed (QUIC 0x42), Stage 5's group-aware exclusive fixes a Stage-3 latent bug — and what's left in Stage 5 (Mutter/wlroots analogues, layout, /display/layout, per-group restore). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
eddcd91f48 |
feat(vdisplay/kwin): group-aware exclusive — never disable a sibling output (Stage 5 §6.1)
The critical latent bug Stage 3 introduced: per-slot output names mean a 2nd exclusive session's other_enabled_outputs() (which disabled 'everything not named Virtual-punktfunk') would black out the 1st session's Virtual-punktfunk-<id> output. Fix: recognise the whole managed group by the shared Virtual-punktfunk prefix — exclusive now disables only NON-managed outputs (bootstrap/physical), never a group sibling. Plus first-slot-wins for the group primary (a_managed_output_is_primary): a later session joins as a secondary monitor of the shared desktop instead of stealing the shell off the first. Unit-tested. Start of Stage 5 (§6A many-clients-one-desktop). Remaining: Mutter/wlroots group-aware analogues, layout (auto-row/manual + /display/layout + console), per-group topology restore, gamescope groups. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
23446fa177 |
fix(vdisplay): Windows admission default is reject, not join (single-capturer limit)
Two concurrent Windows sessions both drive the same pf-vdisplay monitor's single-capturer IDD-push channel (newest-delivery-wins), which freezes the live client and can wedge the driver (observed live: a concurrent-session test wedged .173 → Moonlight 'no video'; needed a reboot). True multi-session capture is §6.6/ Stage 7. So on Windows 'separate' (incl. the unconfigured default) now resolves to REJECT — a 2nd client gets a clean 503 and the live session is protected — instead of join (which would freeze it). join/steal stay explicit opt-ins; Linux keeps separate (real multi-view). Centralized as admission::effective_conflict(), shared by the native handshake + GameStream h_launch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
980939ed6b |
refactor(gamestream): extract + unit-test gamestream_admission (Stage 4)
Pull the GameStream mode-conflict decision out of h_launch into a pure gamestream_admission(live, req_fp, policy) -> GsDecision so the 503/join/take-over logic is unit-tested (no live session / same-client → Serve; different client → Reject/Join/Serve per policy; anonymous requester treated as different) — the GameStream path can't be driven without a Moonlight client, so this covers the logic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
cfad0cf7ee |
feat(vdisplay): finish Stage 4 — typed reject, Windows join-default, GameStream 503
Completes the mode-conflict admission surface deferred from the initial Stage 4: - REJECT now delivers the reason to the client: punktfunk/1 closes the QUIC connection with a distinct BUSY code (0x42) + the 'host busy: streaming WxH@Hz to <client>' string, which the client reads from ApplicationClosed (validated on loopback: the probe logs 'closed by peer: host busy … (code 66)'). - Windows default: separate (incl. the unconfigured default) resolves to JOIN — the Windows native host admits a second client at the live mode instead of the old silent last-wins reconfigure of the shared monitor (release-note behavior fix; the reconfigure is now opt-in as steal). separate stays multi-view on Linux. - GameStream 503: h_launch tracks the session owner fp (LaunchSession.owner_fp, kept [u8;32] for Copy) and applies the policy when a DIFFERENT paired client launches — reject → 503 (Moonlight 'host busy'), join → serve the live mode, steal/separate → take over. Same-client re-launch is never a conflict. Native reject-reason loopback-validated; Windows join-default pending .173 rebuild; GameStream 503 pending a Moonlight client (can't drive /launch autonomously). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
42b1158ea7 |
feat(vdisplay): mode-conflict admission — separate/join/steal/reject (Stage 4)
The mode_conflict policy is now enforced at ADMISSION, before the punktfunk/1 Welcome, when a DIFFERENT client connects while another client's session is live: - separate (default, unconfigured → no change): each client its own display. - join: admit at the live display's mode (honest-downgrade — the Welcome carries it). - steal: signal the victim session(s)' stop flags, wait the release grace, serve. - reject: refuse the handshake with a busy reason (live mode + client label). New vdisplay/admission.rs: the pure decide() (unit-tested — same-client never conflicts, anonymous clients each distinct, join targets the oldest session) + a live-session registry (identity + mode + stop flag) sessions register in once up. Wired into punktfunk1 serve_session: admit() before validate_dimensions, register after the data plane binds. A same-client reconnect never conflicts. Validated on loopback (two probes, distinct identities, differing modes) across all four policies: separate→own mode, join→live mode, steal→victim interrupted, reject→handshake refused. Remaining Stage-4 surface (deferred): GameStream 503 path, Windows-specific defaults (separate→join map, silent-reconfigure→steal), reject reason delivered to the client as a typed message (currently host-side log + connection close). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
029d1134a9 |
harden(vdisplay/windows): verify+retry exclusive isolation; pack primary layout
Exclusive (topology=exclusive) was fire-and-forget — a field-reported bug had a physical monitor STAY ACTIVE. isolate_displays_ccd now re-queries after each apply and RETRIES (up to 4x) until count_other_active()==0, never trusting rc alone; logs SOLE-active on success, an error if a display survives all attempts. Secure desktop correctness depends on the lock screen not landing on a stray panel. Primary: drop the temporary per-path diagnostic; pack the kept displays left-to- right from the virtual's right edge instead of blindly shifting each by virt_width (which left a dead gap when extend already placed them right). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e35b6991e2 |
fix(vdisplay/windows): topology=primary force-extends to reactivate the physical
Root cause: on a headless box the IDD auto-activates as the SOLE display, so QueryDisplayConfig sees only the virtual — the physical is already deactivated before set_virtual_primary_ccd runs (no physical to keep). Force EXTEND first to reactivate every connected display alongside the virtual, then reposition to make the virtual primary, keeping the physical active. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
913f6ce659 |
diag(vdisplay/windows): log active paths in set_virtual_primary_ccd
Temporary diagnostic — the physical monitor goes black in topology=primary despite rc=0; the SSH/session-0 view can't see the real interactive-session topology, so log the active paths the host actually operates on. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d23bd9b0cf |
fix(vdisplay/windows): DISPLAYCONFIG_PATH_SOURCE_INFO union field access
modeInfoIdx lives in the Anonymous union (windows-rs), not directly on sourceInfo — set_virtual_primary_ccd now reads .Anonymous.modeInfoIdx. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
eda7cac78e |
feat(vdisplay/windows): topology=primary — keep physicals active, virtual primary
Implements the deferred Windows primary-only CCD (Stage 2). set_virtual_primary_ccd repositions the virtual output's source to (0,0) = primary and shifts the physical display(s) to its right, ALL kept active — one atomic CCD SetDisplayConfig (not GDI CDS_SET_PRIMARY, which storms MODE_CHANGE_IN_PROGRESS with another display live). The manager's should_isolate() becomes topology_action() (3-way): extend (skip), primary (set_virtual_primary_ccd), exclusive (isolate_displays_ccd). Restore-on-teardown covers both. Validates the user's two scenarios on a physical-monitor .173. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d73951414c |
feat(vdisplay): KWin per-slot output naming for persistent scaling (Stage 3)
The KWin backend names its output Virtual-punktfunk-<id> from the client's stable identity slot, so KWin persists per-output config (scale/mode) by name in kwinoutputconfig.json and reapplies that client's scaling on reconnect — the KDE scaling ask. Also fixes the latent clash where two concurrent sessions both used Virtual-punktfunk (topology name-matching now uses the per-slot name). - identity::global() + resolve_slot(fp, mode, default) — the shared persisted map (Windows manager dropped its own field; both use the global — never same-process). Default identity is per-platform: PerClient on Windows, Shared on Linux, so unconfigured hosts keep today's behavior (Linux = single 'punktfunk' name). - KwinDisplay carries the client fp (set_client_identity), computes the per-slot name, threads it through the stream_virtual_output name + the topology helpers (set_custom_refresh / apply_virtual_primary[_only] / other_enabled_outputs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b150d79626 |
feat(vdisplay): platform-neutral identity map + per-client-mode (Stage 3)
Generalize the Windows-only per-client stable-id map into vdisplay/identity.rs: - DisplayIdentityMap keyed on a composable string (identity_key: fingerprint, or fingerprint+resolution under per-client-mode); LRU at 15, persisted to display-identity.json (migrated from the legacy pf-vdisplay-identity.json). - Windows manager wired to it, picking the key from the identity policy. - Foundation for KWin per-slot output naming (persistent KDE scaling) — the KWin wiring is the next Stage-3 step (needs a KWin box). - Unit-tested (stable, per-client-mode split, LRU, key composition). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
cb7ddc0411 |
feat(vdisplay): topology decoupling — distinct primary level (Stage 2)
The three topology levels become distinct behaviors (Stage 0 only did extend-vs-exclusive, faking primary): - vdisplay::effective_topology() -> the concrete level (console policy > legacy *_VIRTUAL_PRIMARY env > Auto default). Backends read it directly at create time; apply_session_env no longer writes the boolean env (one fewer connect- path env mutation). - Mutter: extend (no config), primary (virtual primary + physicals kept as secondaries — build_primary_keeping_physicals), exclusive (sole, physicals disabled). KWin: extend (no-op), primary (kscreen primary only), exclusive (primary + disable others). - Windows should_isolate treats primary as isolate (the primary-only CCD variant is a follow-up); wlroots exclusive + the physical-keep effect need a display-attached box (headless lab boxes can't observe primary vs exclusive). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
60816709c4 |
fix(vdisplay): call life.acquire() outside debug_assert (release no-op)
The pooled entry's lifecycle transition was inside debug_assert_eq!, whose arguments don't evaluate in release builds — so acquire() never ran, the entry stayed Idle, and release saw Noop → immediate teardown (no keep-alive). Caught on-glass on the CachyOS box. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
783c52dfad |
feat(vdisplay): Linux keep-alive pool — registry-owned display lifecycle (Stage 1b)
The ownership split (design/display-management.md §3): the registry owns the per-session virtual-display lifecycle on Linux, so a display can outlive its session (keep-alive) and be reused on reconnect. - registry.rs: a Linux pool driven by the pure lifecycle machine. acquire() reuses a kept (lingering/pinned) display of the same backend+mode, else creates one and keeps the backend's keepalive so the compositor output (and its PipeWire node_id) survives the session. The session's capturer holds a gen-stamped DisplayLease instead of the real keepalive; its drop drives linger/teardown. Enabling fact: KWin/Mutter/gamescope put their node on the DEFAULT PipeWire daemon (remote_fd=None) — reconnect re-attaches by node_id, no fd re-open. wlroots (remote_fd=Some, xdpw portal) passes through unchanged (teardown-on-drop) pending the fresh-portal-capture re-attach. - Default (unconfigured) linger = Immediate → today's teardown-on-disconnect, so no behavior change without a keep-alive policy; concurrent sessions still each create their own output (reuse only matches LINGERING entries). - Wired build_pipeline (punktfunk1) + gamestream through registry::acquire; capture_virtual_output signature unchanged. Windows delegates to vd.create (the manager already leases) — unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2dd17dda80 |
test(mgmt): display state/release endpoint smoke test
Covers the idle path (empty /display/state + released:0 /display/release) on a unit-test host, exercising the wiring + auth without touching any global owner. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
87f0ce7997 |
feat(vdisplay): lifecycle state machine + display state/release API (Stage 1)
Stage 1 of design/display-management.md — the lifecycle core + the display
management surface:
- vdisplay/lifecycle.rs: pure per-slot state machine (Idle/Active{refs}/
Lingering{until}/Pinned) with acquire/release/expiry/force-release
transitions. No I/O, no OS types — the platform-neutral distillation of the
Windows manager's model. Unit + a 200k-iteration seeded property walk
(no leaks / double-frees / refcount underflow across arbitrary interleavings).
- vdisplay/registry.rs: neutral snapshot/release facade over the per-OS
lifecycle owners. Windows reads/controls the VirtualDisplayManager; Linux
keep-alive (a per-session pool) lands in a following increment (needs GPU-box
validation).
- windows/manager.rs: additive snapshot() + force_release() (no behavior change
to the on-glass-validated path).
- mgmt: GET /api/v1/display/state (live/kept displays) + POST /api/v1/display/release
(tear down lingering/pinned now; refuses active). OpenAPI regenerated.
- web console: Virtual displays card gains a live-display list (polled) with
per-row + release-all buttons and a linger countdown.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
bbd98241e4 |
feat(vdisplay): display-management policy surface (Stage 0)
A user-configurable policy layer above the per-compositor VirtualDisplay backends: keep-alive, topology, conflict, identity, layout, max-displays — persisted to display-settings.json, editable from the web console, applied per connect. Design: design/display-management.md. Stage 0 stands up the surface and wires the two behaviors the existing code can already express — the Windows monitor linger duration and the "make the streamed output the sole desktop" topology — through it; every other option is stored + echoed but not yet enforced (later stages). An unconfigured host (no display-settings.json) keeps today's exact behavior. - vdisplay/policy.rs: pure DisplayPolicy + 5 presets + JSON store (gpu-settings pattern) + EffectivePolicy; 9 unit tests. - vdisplay.rs: resolve_topology(Auto); apply_session_env drives *_VIRTUAL_PRIMARY from the policy only when a settings file exists. - windows/manager.rs: linger_ms() + should_isolate() read the policy when configured. - mgmt: GET/PUT /api/v1/display/settings (bearer-only); PUT rejects keep_alive forever until the lifecycle stage. OpenAPI regenerated. - web console: Host → Virtual displays card (preset picker + custom fields); en+de. - docs-site: virtual-displays.md + configuration.md cross-links. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
202f40fd4e |
chore(release): bump workspace version to 0.7.4
apple / swift (push) Successful in 1m7s
audit / cargo-audit (push) Successful in 1m33s
ci / web (push) Successful in 46s
ci / docs-site (push) Successful in 58s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 51s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 55s
ci / rust (push) Successful in 7m8s
android-screenshots / screenshots (push) Successful in 46s
ci / bench (push) Successful in 4m49s
android / android (push) Successful in 3m20s
release / apple (push) Successful in 9m30s
windows-host / package (push) Successful in 7m13s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m24s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m13s
arch / build-publish (push) Successful in 8m8s
apple / screenshots (push) Successful in 5m47s
deb / build-publish (push) Successful in 2m56s
decky / build-publish (push) Successful in 14s
flatpak / build-publish (push) Successful in 4m53s
linux-client-screenshots / screenshots (push) Successful in 1m40s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 9m51s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m36s
docker / deploy-docs (push) Successful in 6s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 5s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 4s
web-screenshots / screenshots (push) Successful in 2m40s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>v0.7.4 |
||
|
|
8f90563ffd |
docs: dedicated Arch Linux host+client guide
android / android (push) Has been cancelled
apple / screenshots (push) Has been cancelled
apple / swift (push) Has been cancelled
arch / build-publish (push) Has been cancelled
ci / web (push) Has been cancelled
ci / docs-site (push) Has been cancelled
ci / bench (push) Has been cancelled
ci / rust (push) Has been cancelled
deb / build-publish (push) Has been cancelled
decky / build-publish (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
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
Every other distro has a full Host Setup page; Arch only had table rows. Add docs/arch.md (signed pacman binary repo: key import + repo + install, GPU prereqs, service/linger, web console, client, PKGBUILD appendix), slot it into the nav after fedora-kde, and point the install/client tables at it. Update the client-install rows from 'from the PKGBUILD' to the binary repo now that it exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2e6b822fd6 |
docs(ci/arch): correct the header's pacman setup (key import, not TrustAll) + note the trust root
android / android (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
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
apple / swift (push) Has been cancelled
apple / screenshots (push) Has been cancelled
arch / build-publish (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
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 (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
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f7c5314b5e |
fix(packaging/arch): correct pacman setup — import the registry key, cache cargo git
apple / swift (push) Successful in 1m10s
android / android (push) Successful in 3m18s
apple / screenshots (push) Has been cancelled
arch / build-publish (push) Has been cancelled
ci / web (push) Has been cancelled
ci / docs-site (push) Has been cancelled
ci / bench (push) Has been cancelled
ci / rust (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
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
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
The Gitea Arch registry signs its DB + packages, so 'SigLevel = Optional TrustAll' fails non-interactively (pacman still needs the key to verify). Document the one-time pacman-key import instead; install is then signature-validated under pacman's default SigLevel (verified end-to-end: clean archlinux container -> repo sync -> install, 'Validated By: Signature'). Also cache /usr/local/cargo/git in arch.yml: the workspace pulls clients/windows' git-pinned windows-reactor/windows deps to resolve, cloning windows-rs (huge) every run otherwise — same registry+git cache deb.yml uses. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d6669fc3fb |
fix(ci/arch): create CARGO_HOME before chown — actions/cache doesn't on a miss
android / android (push) Has been cancelled
apple / screenshots (push) Has been cancelled
apple / swift (push) Has been cancelled
ci / web (push) Has been cancelled
ci / docs-site (push) Has been cancelled
ci / bench (push) Has been cancelled
ci / rust (push) Has been cancelled
deb / build-publish (push) Has been cancelled
decky / build-publish (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
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Has been cancelled
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
arch / build-publish (push) Successful in 7m31s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e292084225 |
fix(ci/arch): install nodejs before actions/checkout — act_runner doesn't inject node
apple / screenshots (push) Has been cancelled
apple / swift (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
android / android (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
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
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
arch / build-publish (push) Failing after 43s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c758b0393a |
docs: sysext + pacman repo are the Bazzite/Arch install paths
apple / swift (push) Successful in 1m8s
ci / rust (push) Successful in 1m37s
ci / web (push) Successful in 53s
android / android (push) Successful in 3m37s
ci / docs-site (push) Successful in 58s
apple / screenshots (push) Successful in 5m23s
ci / bench (push) Successful in 4m52s
decky / build-publish (push) Successful in 14s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 4s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
deb / build-publish (push) Successful in 4m36s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 52s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m20s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 9m57s
docker / deploy-docs (push) Successful in 20s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m40s
arch / build-publish (push) Has been cancelled
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d6a659a1ee |
feat(packaging/arch): distribute binary packages via the Gitea Arch registry
New arch.yml builds the split PKGBUILD (host/client/web, PF_WITH_WEB=1) in an
archlinux:base-devel container on every push and publishes to the pacman repos
'punktfunk' (tags) / 'punktfunk-canary' (main, X.Y.Z-0.<run#> — pkgrel allows
only digits+dots, so the run number carries the ordering). Consumers add one
pacman.conf section; no more build-it-yourself as the only Arch path.
PKGBUILD: pkgver/pkgrel env-driven (PF_PKGVER/PF_PKGREL), source=() when
PF_SRCDIR is set (a canary version has no tag to clone), stale NVENC-only
header fixed, and options=('!lto' '!debug') — makepkg's lto option injects
-flto=auto into CFLAGS, aws-lc-sys compiles its C with it, and rust's lld
cannot read GCC LTO bitcode: 'undefined symbol: aws_lc_*' at link (reproduced
minimally on Arch + rust 1.90). Full build + clean-container install
smoke-tested locally (binaries run, payload + scriptlets intact).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
2190dad2ad |
feat(packaging/bazzite): systemd-sysext replaces rpm-ostree layering as the primary install path
Layering is a last resort per the Bazzite docs (slows every OS update, can block upgrades until removed); a sysext never enters an rpm-ostree transaction, survives OS updates, and installs/updates with no reboot — the mechanism Fedora Atomic ships via fedora-sysexts. - build-sysext.sh wraps the built host+web RPMs into punktfunk-<V-R>-x86-64.raw: /etc payload relocated to /usr/share/punktfunk/etc (a sysext carries only /usr), the punktfunk-sysext helper embedded, ID=fedora + VERSION_ID pinned (merges on Bazzite via ID_LIKE; REFUSED after a major rebase instead of running soname-broken binaries — both behaviors validated live on Bazzite 43). SELinux labels are baked in as squashfs pseudo-xattrs from matchpathcon: unlabeled files run fine for user units but system daemons are DENIED (udev couldn't read the gamepad rule under enforcing) — validated on-glass. Refuses duplicate input package names (a stale noarch punktfunk-web next to the x86_64 one built a chimera image with the dead node launcher once). - punktfunk-sysext.sh: install/update/status/remove against per-Fedora-major feeds (…/generic/punktfunk-sysext/f43[-canary]), SHA-256-verified, applies the udev/sysctl scriptlet work + /etc copies, prints the layering-migration hint. Live-validated on the .41 Bazzite box incl. service restart + web console. - publish-sysext-feed.sh + rpm.yml: build + publish the image per matrix leg (fedver 43/44), canary feeds pruned to 6, stable release assets attached. - update-punktfunk.sh warns when the sysext shadows a layered install. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5b5ec15ead |
fix(client-linux): GL presenter — eglCreateImageKHR takes EGLint attribs, not EGLAttrib
apple / swift (push) Successful in 1m12s
apple / screenshots (push) Successful in 5m47s
android / android (push) Successful in 3m18s
ci / rust (push) Successful in 1m35s
ci / web (push) Successful in 55s
ci / docs-site (push) Successful in 1m35s
ci / bench (push) Successful in 4m57s
decky / build-publish (push) Successful in 15s
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
deb / build-publish (push) Successful in 4m37s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5s
flatpak / build-publish (push) Successful in 4m18s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m57s
docker / deploy-docs (push) Successful in 6s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m14s
The KHR variant reads 32-bit attrib pairs; the pointer-sized array fed it garbage and every plane import came back rejected (observed on-Deck; the new fallback ladder caught it and demoted to software exactly as designed). Also print the real EGL error enum instead of its discriminant. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c9ff144492 |
Merge branch 'main' of git.unom.io:unom/punktfunk
apple / swift (push) Successful in 1m6s
ci / rust (push) Successful in 1m49s
ci / web (push) Successful in 51s
ci / docs-site (push) Successful in 1m6s
android / android (push) Successful in 3m51s
windows-host / package (push) Successful in 6m56s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m22s
deb / build-publish (push) Successful in 4m40s
ci / bench (push) Successful in 4m48s
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
release / apple (push) Successful in 7m51s
decky / build-publish (push) Successful in 24s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
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 50s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m17s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 58s
flatpak / build-publish (push) Successful in 4m12s
apple / screenshots (push) Successful in 5m39s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m50s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m41s
docker / deploy-docs (push) Successful in 20s
|
||
|
|
7930d2f0f4 |
fix(core): split WIRE_VERSION from ABI_VERSION — new clients locked out of every deployed host
ABI_VERSION was doing double duty: the embeddable C surface AND the punktfunk/1
Hello/Welcome version that hosts equality-check. The WoL feature's v3 bump added
a client-local FFI function without changing a single wire byte — and every new
client started refusing against every deployed host ("ABI mismatch: client 3
host 2", observed live Deck → Bazzite). The wire now carries its own
WIRE_VERSION (still 2); ABI_VERSION stays 3 for the C header and the mgmt API's
informational field. Bump WIRE_VERSION only when the handshake/planes actually
change incompatibly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
160b67d043 |
fix(apple/release): embed Developer ID provisioning profile in the DMG
apple / swift (push) Successful in 1m8s
windows-host / package (push) Successful in 7m45s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m18s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m12s
ci / web (push) Successful in 47s
ci / rust (push) Successful in 12m28s
ci / docs-site (push) Successful in 58s
ci / bench (push) Successful in 5m4s
release / apple (push) Successful in 9m21s
apple / screenshots (push) Successful in 5m42s
android-screenshots / screenshots (push) Successful in 2m25s
android / android (push) Successful in 3m34s
decky / build-publish (push) Successful in 20s
deb / build-publish (push) Successful in 4m49s
flatpak / build-publish (push) Successful in 4m21s
linux-client-screenshots / screenshots (push) Successful in 2m15s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m56s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m51s
docker / deploy-docs (push) Successful in 6s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 5s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
web-screenshots / screenshots (push) Successful in 2m33s
The notarized Developer ID .dmg was SIGKILLed at launch ("Launchd job spawn
failed", POSIX errno 163) before main() ran: the sandboxed macOS app declares
the MANAGED keychain-access-groups entitlement, which AMFI only honors when an
embedded provisioning profile authorizes it. The DMG embedded none — App Sandbox
and the network/device keys are self-asserted for Developer ID, but a keychain
access group is not — so every launch was killed at spawn. Validly signed and
notarized (Gatekeeper accepted it), which is why this looked like a mystery. ⌘R
and the App Store build hid it: Xcode embeds a development / App Store profile;
the raw-codesign DMG path did not, so "⌘R == DMG" never held for this entitlement.
Embed a "Punktfunk macOS Developer ID" profile (Keychain Sharing) into
Contents/embedded.provisionprofile before codesign so its entitlements authorize
the access group, exactly like the App Store build's profile does. If the profile
isn't installed on the runner, warn and strip keychain-access-groups instead so
the app still launches via ClientIdentityStore's legacy file-keychain fallback —
a missing/expired profile can never reship the errno-163 brick again.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v0.7.3
|
||
|
|
6c4ba77606 |
fix(wol): clippy + cfg-gate the Windows client module — main compiles again
windows-host / package (push) Successful in 7m18s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m28s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m17s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 50s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 56s
apple / swift (push) Successful in 1m16s
android / android (push) Successful in 3m40s
ci / web (push) Successful in 46s
ci / docs-site (push) Successful in 58s
ci / rust (push) Successful in 8m16s
ci / bench (push) Successful in 4m42s
release / apple (push) Successful in 8m37s
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 5s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 47s
deb / build-publish (push) Successful in 3m45s
apple / screenshots (push) Successful in 5m29s
flatpak / build-publish (push) Successful in 4m29s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m51s
docker / deploy-docs (push) Successful in 20s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m26s
The Wake-on-LAN batch landed with lints that fail `clippy -D warnings` (doc continuation, char-array split, io::Error::other, redundant closure) and an ungated `mod wol;` in the Windows client, which pulls windows-only crates into the non-Windows stub build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
eeee2782f5 |
Merge remote-tracking branch 'origin/main'
apple / screenshots (push) Has been cancelled
audit / cargo-audit (push) Has been cancelled
ci / rust (push) Has been cancelled
windows-host / package (push) Failing after 2m40s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m9s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m10s
windows / build (aarch64-pc-windows-msvc) (push) Failing after 41s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 42s
apple / swift (push) Successful in 1m16s
android / android (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 (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Has been cancelled
deb / build-publish (push) Has been cancelled
decky / build-publish (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 (bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
flatpak / build-publish (push) Has been cancelled
|
||
|
|
b488bd1d99 |
feat(client-linux): in-process GL presenter — hardware decode ships on the Steam Deck
VAAPI decode stays; what changes is who touches the YUV. The direct path hands the NV12 dmabuf (tiled AMD modifier since Mesa 25.1) to GdkDmabufTexture, and GTK's tiled-NV12 import renders corrupt/gray/washed-out on the Deck. Moonlight and mpv are clean on the same box because they import the dmabuf into their own EGL context and convert with their own shader — video_gl.rs is that architecture for the GTK client: per-plane EGLImages (R8 + GR88, modifier passed through) → our YUV→RGB shader (matrix/range from the stream's CICP signaling, unit-tested) → RGBA texture in a GdkGLContext-shared context → fence-synced GdkGLTexture. GTK composites plain RGBA; no YUV negotiation, no compositor CSC. The Deck's decoder default flips back to hardware (the software stopgap is gone); desktops keep the direct dmabuf path (offload/scan-out eligible). PUNKTFUNK_PRESENT=direct|gl overrides either way. New failure ladder: GL converter init failure or a convert-error streak raises a shared flag and the session pump demotes the decoder to software with a keyframe re-request — the same mechanism also closes the old silent-black-screen gap where a rejected dmabuf import had no recovery at all. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
7e6561aaa2 |
style: rustfmt the Wake-on-LAN modules
ci / rust (push) Failing after 51s
ci / web (push) Successful in 53s
windows-host / package (push) Failing after 2m54s
apple / swift (push) Successful in 1m19s
ci / docs-site (push) Successful in 1m10s
android / android (push) Successful in 3m38s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m21s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m11s
windows / build (aarch64-pc-windows-msvc) (push) Failing after 39s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 41s
ci / bench (push) Successful in 4m48s
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 3s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
release / apple (push) Successful in 8m47s
deb / build-publish (push) Successful in 9m26s
flatpak / build-publish (push) Successful in 4m44s
apple / screenshots (push) Successful in 5m56s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
docker / deploy-docs (push) Successful in 17s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e9c5030190 |
feat(clients): Wake-on-LAN in apple/linux/windows/android/decky
apple / swift (push) Successful in 1m7s
audit / cargo-audit (push) Successful in 1m14s
ci / rust (push) Failing after 49s
ci / web (push) Successful in 52s
windows-host / package (push) Failing after 2m58s
ci / docs-site (push) Successful in 1m5s
android / android (push) Successful in 4m7s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m15s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m15s
windows / build (aarch64-pc-windows-msvc) (push) Failing after 48s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 49s
ci / bench (push) Successful in 5m5s
decky / build-publish (push) Successful in 29s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 4s
release / apple (push) Successful in 8m30s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 3s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
deb / build-publish (push) Has been cancelled
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Has been cancelled
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Has been cancelled
flatpak / build-publish (push) Has been cancelled
apple / screenshots (push) Has been cancelled
docker / deploy-docs (push) Successful in 19s
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> |
||
|
|
22c0d92f2e |
feat(core,host): Wake-on-LAN sender + host MAC advertisement
Add a runtime-free Wake-on-LAN sender in punktfunk-core (per-interface subnet-directed broadcast + 255.255.255.255 on ports 9/7, repeated, optional last-known-IP unicast) exposed both as a Rust fn and a punktfunk_wake_on_lan C-ABI (ABI v3), plus a parse_mac helper. The host enumerates its wake-capable NIC MAC(s) and advertises them in a new mDNS `mac` TXT record (routed NIC first), and best-effort detects & warns (never modifies) when the NIC isn't armed for WoL. MAC delivery is via the unauthenticated mDNS TXT rather than the connection handshake by design: a spoofed MAC only makes a wake fail (the packet is inert; the cert fingerprint still gates the connection), and it avoids threading through the hot connect path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
097cc6faf4 |
fix(apple/gamepad): deliver PS/Home + Share buttons on macOS
macOS reserves the controller Home/PS and Share/Create buttons for its own system gestures and never delivers them to the app unless it declares the Game Controllers capability. Add GCSupportsControllerUserInteraction=YES to the macOS target only (iOS/tvOS rely on the focus engine, so it must not be in the shared plist), alongside the existing preferredSystemGestureState=.disabled. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8b37badae4 |
docs(security): record measured WDA_EXCLUDEFROMCAPTURE behavior + capture-vs-viewer framing
Tested on .173: a WDA_EXCLUDEFROMCAPTURE window (affinity readback 0x11, confirmed active) is pixel-identically visible in the punktfunk/1 stream across no-flag / flag-set / flag-cleared phases — the flag makes no difference to a present-tap capture. Replace the "untested, treat as expected" note in the IDD-push residual list with the measured result, and correct the framing: WDA visibility matches what a person at the screen sees (it exceeds an ordinary capture tool, not the physical viewer). Add the matching public-facing paragraph to the security page covering both asymmetries — WDA windows appear (same as a physical viewer), DRM video is blanked (less than a physical viewer) — tied back to the page's "a client sees what someone at the machine sees" model. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |