78d018ae2f8c89b7bc2eb9aed19d6b1e3888a7f6
15 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
13b1f36d4a |
feat(core,clients): one rumble policy engine for every platform (rumble root fix D)
punktfunk-core client/rumble.rs: a per-connection policy engine consumes seq-gated wire
updates and emits EFFECTIVE actuator commands — re-emits on renewals (duration APIs stay
re-armed), self-silences at the v2 lease, a UNIFORM 1 s legacy-host staleness replacing the
per-platform zoo (Apple 1.6 s / Android 60 s / SDL 1.5 s / Deck 1 s), quirk-declared
actuator keepalives (Deck 40 ms + LSB dedupe-defeat jitter), and one stop per buzzing pad
on connection close. Per-pad mailbox semantics: a stalled embedder wakes to ONE current
command, and a stop can structurally never be the update an overflowing queue drops.
New API/ABI: NativeClient::{next_rumble_command,set_rumble_quirks} +
punktfunk_connection_next_rumble_cmd/_set_rumble_quirks (next_rumble/next_rumble2 stay for
un-migrated embedders; both consumers are fed). Migrations DELETE the platform forks:
pf-client-core loses RumbleState + the Deck keepalive loop + LEGACY_RUMBLE_CEILING_MS and
physically silences a slot at close; Android loses the 60 s legacy one-shot (backstop
repack, cancel-on-zero); Apple loses envelopeDeadline + sessionStaleSeconds + both tick
watchdogs (CoreHaptics realization untouched; mac xcframework rebuilt locally).
design/rumble-root-fix.md par. D. Engine 10/10 unit tests; core tests 176 Linux / 175
Windows + clippy -D warnings; swift build + RumbleTuningTests; Kotlin + android-native
compile green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
11045a0f70 |
chore: consolidate parallel-session WIP (HOLD — do not push)
Local snapshot of intermingled in-flight work, committed to unblock the encode
refactor (a clean ffmpeg_win.rs for the vbv-dedup follow-on). These hunks span
the same files and can't be cleanly split here; the commit bundles three
distinct workstreams that each belong in their own PR:
- logging rework (~43 files: level re-tiering, structured fields, `?e`,
hot-path flood latches)
- conflicting-host detection (detect.rs + detect/{linux,windows}.rs + wiring
in main.rs/mgmt.rs/Cargo.toml/docs/packaging)
- standby-sink DWM-stall attribution (windows/display_events.rs + capture/
vdisplay wiring)
NOT verified as a combination. NOT to be pushed until the refactor is done and
these are re-verified and reorganized into their proper per-workstream PRs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
01266ff18d |
feat(gamepad): SC2 Puck-dongle passthrough with the native 28DE:1304 topology
Community-contributed round 5 of the Steam Controller 2 passthrough, reviewed + verified. A Puck-captured pad now presents the dongle's real seven-interface identity (CDC pair, four controller HID slots, management HID) instead of relabelling its reports as a wired 1302 — Steam's Puck feature dances (wireless_transport / esb/bond / 0xB4 slot status) get capture-shaped answers, and the wired identity's canned replies are corrected to the real captures (attribute count, string-attr framing, 0xF2 firmware info, bcdDevice nibble encoding). - new wire pref 10 = SteamController2Puck (Hello/Welcome byte; older peers degrade to Auto), selected by the Android capture link when the transport is a dongle, or by VID/PID in the degraded InputDevice path - TRITON_RDESC is now the captured numbered descriptor (mouse/keyboard lizard collections + per-id vendor reports); unnumbered framing made hidraw mangle feature report 2 and Steam eventually closed the device - interrupt-IN now queues sparse reports (battery/RSSI/wireless edges) instead of keeping latest-only, so a 250 Hz state packet can no longer erase them before the USB/IP poll observes them; EP0 SET_REPORT is split by wValue report type (OUTPUT parsed for rumble vs FEATURE) - vendored usbip-sim: config attributes/max-power, IAD prefix + BOS descriptor support, correct BCD minor.patch encoding (Deck's 0x0300/ 0x0200 values are nibble-zero, so its bytes are unchanged), and full-speed interrupt pacing in ms (was 8 kHz from the HS formula) - Triton feedback is serviced at 1 kHz while an SC2 backend exists so Steam's trackpad haptic writes reach the client unbatched Verified: clippy -D warnings + 319 host tests green on Linux, core wire tests green, Android kit/app compile + unit tests green. On-glass Puck retest owed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2621b6e6b1 |
feat(core,host,android): Steam Controller 2 as-is passthrough to Linux hosts
ci / web (push) Successful in 55s
ci / docs-site (push) Successful in 1m2s
ci / rust (push) Failing after 5m34s
decky / build-publish (push) Successful in 17s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 52s
ci / bench (push) Successful in 6m3s
docker / deploy-docs (push) Successful in 25s
android / android (push) Successful in 14m2s
arch / build-publish (push) Successful in 11m57s
deb / build-publish (push) Successful in 11m49s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m14s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m5s
flatpak / build-publish (push) Failing after 8m3s
windows-host / package (push) Failing after 8m32s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 4m6s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 4m18s
apple / swift (push) Successful in 5m4s
windows / build (aarch64-pc-windows-msvc) (push) Failing after 5m14s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 5m43s
release / apple (push) Successful in 26m11s
apple / screenshots (push) Has been cancelled
The 2026 Steam Controller (Valve "Ibex" / SDL "Triton") captured on an Android client is passed through AS-IS: the host presents a virtual pad with the real wired identity (28DE:1302) and mirrors the physical pad's raw HID reports, so Steam on the host drives it over hidraw exactly like the real thing — trackpads, gyro, paddles, and its rumble/settings writes flow back onto the physical controller. Protocol ground truth: SDL's Valve-maintained SDL_hidapi_steam_triton.c + steam/controller_structs.h. Core: - GamepadPref::SteamController2 (wire byte 9; names steamcontroller2/ sc2/ibex) + PUNKTFUNK_GAMEPAD_STEAMCONTROLLER2 in the C ABI. - Raw HID planes: RichInput::HidReport (0xCC/0x04, client→host input reports verbatim, Copy fixed-64 body) and HidOutput::HidRaw (0xCD/0x05, host→client feature/output writes for replay). Best-effort is sound by the device protocol's own design (rumble re-sent every ~40 ms, settings every ~3 s — losses self-heal); HidRaw bypasses hidout dedup for exactly that reason. Host (Linux): - triton_proto.rs + steam_controller2.rs: Triton2Manager UHID backend — no kernel driver binds the PID (hidraw only; Steam Input is the consumer), raw mirroring with a typed-fallback 0x42 synthesizer until the first raw report, SET_REPORT ack + raw forward, canned GET_REPORT serial reply, rumble also parsed onto the universal 0xCA plane (phone mirror). Rides the uhid + 28DE-conflict degrades; UHID promotion by Steam is flagged in the creation log (usbip transport is the known follow-up if Steam ignores Interface:-1 devices for Triton too). Android: - Sc2UsbLink (wired/Puck: vendor-interface claim detaches the OS driver, interrupt read loop, lizard-off on the watchdog cadence, raw replay via interrupt-OUT / SET_REPORT with hidapi report-id framing) and Sc2BleLink (Valve vendor GATT service, notify subscribe machine, 0x45 re-framing, HIGH connection priority). - Sc2Capture orchestrator: raw plane + typed mirror (exit chord + host degrade paths keep working) on a GamepadRouter external slot; raw return path via GamepadFeedback.onHidRaw. - nativeSendPadHidReport JNI (direct ByteBuffer, no per-report copy), hidout raw decode, usb-host/BLUETOOTH_CONNECT manifest bits, opt-out settings toggle, StreamScreen engagement incl. the USB permission flow. Verified: core 149 + host 312 tests green on Linux (.21), on-box uhid smoke creates/mirrors/tears down the virtual 28DE:1302, C ABI harness round-trips, Android compileDebugKotlin green. On-glass with the real controller owed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
8c854e0a19 |
feat(gamepad): classic Steam Controller backend — Linux UHID via hid-steam (N3)
The reserved GamepadPref::SteamController = 5 slot goes live: the same hid-steam driver under the wired-SC identity (28DE:1102, ID_CONTROLLER_STATE), UHID-only in v1 (no captured SC USB interface layout, so no Steam-Input promotion — the pre-usbip Deck state; acceptable for discontinued hardware). Layout pinned against the kernel's ID_CONTROLLER_STATE table: 24-bit buttons at 8..11 (low bits shared with the Deck; grips at 9.7/10.0 = the Deck's L5/R5 positions; right-pad click 10.2; joystick click 10.6), u8 triggers at 11/12, the joystick/left-pad MULTIPLEX at 16..20 (a left-pad contact shadows the stick, like real hardware's lpad_touched flag), right pad at 20..24. Mapping: wire left stick -> SC stick; wire right stick -> right-pad coords + touched bit (the SC's camera surface — the second-stick loss is inherent); PADDLE1/2 -> the two grips (natively, masked out of the fold input); PADDLE3/4 + MISC1 -> the remap policy. The SC parser has NO gamepad_mode gate, so no mode-entry pulse. SteamDeckPad grew a SteamModel (open_model); ScProto/SteamCtrlManager; pick_gamepad flips SteamController -> itself on Linux (replacing the Xbox360 fold); SDL picker splits Valve PIDs (Deck 1205 stays SteamDeck, SC 1102/1142 now declare SteamController). Verified: .21 clippy -D warnings + 304/0 tests + on-box UHID smoke (hid-steam binds 1102, BTN_A + right-pad ABS_RX land on evdev, no mode pulse); .133 clippy -D warnings green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
70a74b0d7c |
feat(gamepad): Switch Pro backend — Linux UHID via hid-nintendo (N2)
A virtual Pro Controller (057E:2009, BUS_USB, verbatim 203-byte USB
descriptor triple-cross-checked from real-device captures) bound by
hid-nintendo (>= 5.16): Nintendo-family client pads get correct glyphs +
POSITIONAL layout (wire south/east/west/north -> Switch B/A/Y/X, so the
physical-position <-> glyph relationship survives), live gyro/accel, and
HD-rumble feedback — instead of folding to Xbox360 (mirrored A/B + X/Y,
no motion).
- switch_proto: report-0x30/0x21/0x81 codec + the entire canned probe
conversation, pinned line-by-line against hid-nintendo.c: 0x80-family
USB acks, device info (type 0x03 + per-pad MAC), SPI-flash calibration
blobs (user magics ABSENT -> factory path; sticks 2048 +/- 1400 with
the left/right byte-order difference; IMU offsets 0 + the driver's own
default scales so raw units pass 1:1), rumble amplitude decode through
the driver's inverted joycon_rumble_amplitudes table, player lights ->
0xCD PlayerLeds. 11 new pin tests.
- switch_pro: UHID backend answering the probe from the manager's
service pass; SwitchProManager = UhidManager<SwitchProProto> (the 8 ms
heartbeat doubles as the steady 0x30 stream the driver's post-probe
rate limiter wants). switchpro-test CLI smoke.
- Router/fold: SwitchPro arms; pick_gamepad SwitchPro -> itself on Linux;
degrade_if_no_uhid covers it. SDL picker: NintendoSwitchPro + JoyconPair
declare SwitchPro.
Headless-validated on .21 (hid-nintendo 7.1): probe completes ('using
factory cal' for sticks + IMU, player-1 LED round-trips to the 0xCD
plane), gamepad + IMU input devices created, and an evdev capture pins
the positional swap (wire A/B -> BTN_SOUTH/BTN_EAST) + full-range stick
scaling. .21 clippy -D warnings + 303/0 tests; .133 clippy -D warnings.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
1830e095f8 |
feat(gamepad): DualSense Edge backend — Linux UHID + Windows UMDF (N1)
The plain-DualSense transport + report codec under the Edge USB identity (054C:0DF2, verbatim 389-byte real-device descriptor cross-checked against the raw usbmon capture + hhd's production virtual Edge), so the wire back grips (BTN_PADDLE1..4: Deck L4/L5/R4/R5, Elite P1-P4) land on the Edge's NATIVE buttons[2] bits instead of the fold/drop policy: PADDLE1/2 -> the right/left back buttons, PADDLE3/4 -> the right/left Fn buttons (kernel BTN_TRIGGER_HAPPY1..4 on >= 7.2; SDL/Steam read hidraw on any kernel). - proto: Edge descriptor + btn2 bits + edge_paddle_bits(), pinned against hid-playstation DS_EDGE_BUTTONS_* and SDL_hidapi_ps5 (tests). - Linux: DsUhidIdentity parameterizes the UHID create; DsEdgeLinuxProto / DualSenseEdgeManager. Headless-validated on .21 (7.1): driver=playstation binds 0DF2, all 4 input devices created, probe lightbar/player-LED feedback round-trips; dualsense-test grew --edge (cycles all 4 paddles). - Windows: UMDF driver serves device_type=2 (Edge descriptor/attrs/strings, DS feature blobs); WinDsIdentity parameterizes the SwDevice profile + devtype stamp; DsEdgeWinProto / DualSenseEdgeWindowsManager; INF gains pf_dualsenseedge. Driver change => resign + reinstall before on-glass. - Router: DualSenseEdge arms in route_handle/apply_rich/pump/heartbeat; pick_gamepad folds Edge -> itself on linux||windows; degrade_if_no_uhid covers it. - Client (SDL): 054C:0DF2 declares DualSenseEdge (no distinct SDL type); Edge physical pads take the raw DS5 effects path; console-UI glyphs = Shapes. Apple/Android pickers follow separately. Verified: .21 clippy -D warnings + 292/0 host tests + on-box UHID bind smoke; .133 clippy pending in this push. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
aedffc69dd |
fix(gamepad/client): bound legacy Steam Deck rumble on a lost stop-frame (G16)
Against a legacy (no-TTL) host, a held Deck rumble droned forever if the stop datagram was lost: the 40 ms keep-alive re-kicked the actuator indefinitely and only the v2 lease `deadline` ever bounded it. Add a per-slot `updated_at` clock bumped ONLY by real host datagrams (never by the keep-alive re-kick, unlike `last_at`), and in the legacy branch (`ttl_ms == 0`) issue a single (0, 0) once it is stale past LEGACY_RUMBLE_CEILING_MS (1000 ms = 2x the host's flat 500 ms legacy refresh). A genuinely-held legacy rumble refreshes every 500 ms and never trips; the v2 `deadline` path is untouched and stays authoritative. Verified: Windows .173 `cargo clippy -p pf-client-core -- -D warnings` (green). On-glass owed: real Deck with an induced legacy stop-frame drop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
26cac9ce20 |
fix(gamepad): truncate stick/trigger axes uniformly across clients (G25)
Apple's GamepadCapture rounded axis values (`(v * scale).rounded()`) while SDL-core and Android truncate, so a half-pressed control emitted 128 on Apple vs 127 elsewhere. Drop `.rounded()` so `Int32(Float)` truncates toward zero on Apple too; rails are unchanged (full deflection stays 255 / ±32767). Also clamp SDL-core's LeftX/RightX to a symmetric -32767 like the Y axes and the other clients already do, instead of letting the raw i16 reach -32768. Verified: Apple `swift build` + full PunktfunkKit suite (124 pass); SDL half on Windows .173 `cargo clippy -p pf-client-core -- -D warnings` (green). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
76be4c3e12 |
feat(gamepad): multi-controller support on the native plane
Host was already built for 16 pads; the blocker was every client hard-coding pad 0. This lands the host-side + reference-client contract: - input.rs: new wire kinds GamepadArrival=14 (declares a pad's type: code=GamepadPref byte, flags=pad) and GamepadRemove=13 (flags=seq<<24|pad, shares the snapshot seq space via encode/decode_gamepad_remove). - pf-client-core/gamepad.rs: reworked from a single `open` pad to a slots: Vec<Slot> model — every forwarded controller gets a stable lowest-free wire index held for its lifetime, per-slot held/axis/touch/ rumble state, GamepadArrival on open + GamepadRemove on close, and feedback routed back per wire index. Automatic forwards all real pads; a pin forces single-player. - punktfunk1.rs: replaced the single-session PadBackend enum with a Pads router — per-pad kinds[]/owner[] arrays, lazily-created per-kind managers, pure route_decision keeping a live device in its manager across a kind change (no ghost/dup). Input thread seq-gates GamepadRemove (clears the pad_mask bit, resets rumble) and applies GamepadArrival kinds. - inject linux/windows backends: add the two new no-op InputKind arms. Native/session + default-Windows clients (both spawn punktfunk-session) inherit this. 57 core + 33 client-core + 272 host tests green; clippy clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
73c911cae4 |
feat(rumble): host-authoritative self-terminating envelopes (0xCA v2)
Rumble was level-triggered, unbounded state on a lossy channel: a non-zero level meant "buzz until further notice", healed only by the host re-sending state every 500 ms, and every client guessed when the host had died with its own magic timeout (SDL 1.5 s, Apple 1.6 s, Android up to 60 s). A lost stop, a reordered start, or a dead host could drone the motor for seconds. Make "stuck rumble" inexpressible on the wire. The 0xCA datagram grows a length-tolerant tail — [u8 seq][u16 ttl_ms] — so it self-terminates: the host authorizes a level for at most ttl_ms and renews it (~120 ms) while it holds, letting an abandoned one lapse client-side. seq is a per-pad wrapping reorder gate (reusing GamepadSnapshot::seq_newer) so a reordered stale start can't re-light a stopped motor. Decoders read the first 7 bytes as a plain level and ignore the tail, so no wire-version bump: an old client renders a new host's levels, and a new client falls back to its prior staleness heuristic against an old host (ttl = None). All four generation pairings render correctly. - core: encode_rumble_datagram_v2 / decode_rumble_envelope (datagram.rs); the client demux applies the seq gate then forwards (pad, low, high, Option<ttl>); next_rumble is unchanged (drops ttl), next_rumble_ttl keeps it; ABI adds punktfunk_connection_next_rumble2 + PUNKTFUNK_RUMBLE_NO_TTL, ABI_VERSION 4->5 (WIRE_VERSION unchanged — the tail is backward-compatible). - host (punktfunk1.rs): the flat 500 ms refresh becomes a renewal loop that bumps seq + stamps a fresh TTL on active pads and drains a short post-stop zero burst, then goes quiet. Hatches: PUNKTFUNK_RUMBLE_ENVELOPE=0 (legacy v1 + flat refresh, a bisect switch), PUNKTFUNK_RUMBLE_TTL_MS (clamped [150, 5000]). - renderers honor the TTL as their playback duration/deadline and keep their old heuristic only for a legacy (ttl=None) update: pf-client-core (the Deck haptic keep-alive is now deadline-bounded so it can't sustain a host-stopped rumble), clients/windows (SDL duration), android (JNI packs the lease out-of-band in bit 48 so any u16 ttl is unambiguous; Kotlin createOneShot(ttl)), apple (RumbleRenderer.envelopeDeadline + nextRumble2; sessionStaleSeconds demoted to the legacy fallback). - tests: codec round-trip + tail tolerance + seq-gate reorder (Rust); the probe asserts the v2 tail arrived under PUNKTFUNK_TEST_FEEDBACK; the Apple loopback asserts ttlMs round-trips end to end; RumbleTuning lease-decision cases. The host-side idle-timeout from the previous commit is defense in depth on the game side; this is the guarantee on the client side. Design: punktfunk-planning/design/rumble-envelope-plan.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
35d97ae6ac |
feat(windows): parallel virtual displays — proto v3 ring binding, manager slot map, group topology (W0–W3)
windows-drivers / probe-and-proto (push) Successful in 41s
ci / web (push) Successful in 1m2s
ci / docs-site (push) Successful in 1m7s
apple / swift (push) Successful in 1m11s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 8s
decky / build-publish (push) Successful in 15s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 7s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 6s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 38s
windows-drivers / driver-build (push) Successful in 1m46s
ci / bench (push) Successful in 6m35s
docker / deploy-docs (push) Successful in 20s
windows-host / package (push) Successful in 9m37s
deb / build-publish (push) Successful in 13m48s
arch / build-publish (push) Successful in 14m21s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 2m24s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m34s
android / android (push) Successful in 15m45s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 2m20s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m18s
ci / rust (push) Successful in 18m48s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 1m41s
release / apple (push) Successful in 19m30s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m49s
flatpak / build-publish (push) Failing after 2m9s
apple / screenshots (push) Successful in 19m3s
design/windows-parallel-virtual-displays.md (display-management Stage 7 / §6.6): N simultaneously-live pf-vdisplay monitors, one sealed ring each, every idd-push-security invariant preserved per-ring. - proto v3: SharedHeader._pad → target_id — the ring NAMES its monitor, host-stamped before the magic; the driver publisher refuses a cross-bound ring via the shared, unit-tested frame::check_attach (new DRV_STATUS_BIND_FAIL — the gamepad pad_index validation applied to frames, invariant #10); the host's wait_for_attach surfaces the refusal loudly and self-checks its own stamp. - manager: the one-monitor MgrState becomes a slot map keyed by the client's identity slot (0 = anonymous/GameStream); per-slot reconnect + dead-WUDFHost preempts, slot-scoped begin_idd_setup (a different identity is an admission question, never a preempt), ONE device-level watchdog pinger, per-slot /display/state + /display/release. - group topology: isolate_displays_ccd takes the managed target SET (a sibling slot is never deactivated); SavedConfig + the DDC/PnP axes move to the group record (first-in captures, last-out restores); desktop layout via CCD source origins from the pure layout::arrange (auto-row default, manual pins win), re-applied on create + reconfigure. - admission: the Windows separate→reject override now sits behind the PUNKTFUNK_WIN_SEPARATE=1 validation hatch (the wedge it guarded is structurally gone — a second identity gets its own monitor + ring; default flips in W5 after soak); max_displays and NVENC session-unit budgets decline an unaffordable display AT admission; kick_dwm_compose is process-globally throttled and per-display — cursor jump + 35 ms dwell (a sub-tick jump composes nothing; DWM reads dirties from current state at the next vsync tick). On-glass on the RTX box: V1/V2/V4/V5/V6/V9 green — two paired clients on two monitors streaming ~60 fps each with zero mismatches and zero bind failures, churn-hammer clean (no 0x80070490), per-ring mode-change recreate leaves the sibling untouched, typed budget rejection, fault-injected cross-bind refused loudly with the sibling undisturbed. V7: WUDFHost-kill shared fate is clean; in-process device recovery is a known follow-up (the retired-never-closed control handles block the adapter cycle — reset-pf-vdisplay.ps1 recovers). DWM composes two IDD monitors concurrently at 60 fps — the plan's load-bearing unknown, answered yes. Also carries the client-HDR EDID forwarding that shared this working tree (Hello::display_hdr → AddRequest luminance tail → the monitor's CTA-861.3 HDR block, PUNKTFUNK_CLIENT_PEAK_NITS hatch) and the Deck client fixes (40 ms rumble keep-alive with 1-LSB jitter, HDR self-diagnosing presenter warn, flatpak HDR env). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
dc834ea478 |
fix(rumble): accept the v2 vibration valid-flag; bound stale buzz to 1.5 s
Two hardenings against runaway vibration (one Deck session buzzed on a ~2 s cadence): the DualSense output-report parse now also treats valid_flag2 COMPATIBLE_VIBRATION2 (data[39] BIT2) as rumble-carrying — a writer hardcoding the ≥2.24-firmware convention previously had its rumble INCLUDING stops silently ignored, and a missed stop re-sends stale nonzero state forever via the host's 500 ms refresh. And the client's SDL rumble duration drops 5 s → 1.5 s: long enough that a couple of lost 500 ms refreshes don't gap genuine rumble, short enough that stale state dies on its own. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
89d45f2a55 |
feat(client): Vulkan session binary — SDL3 + ash presenter MVP (phase 1)
punktfunk-session streams one --connect session in an SDL3 window: ash
swapchain with a transfer-only letterboxed blit of the software-decode
path (no graphics pipeline until the phase-2 dmabuf/CSC pass), the
ui_stream input-capture state machine on SDL events (scancode→VK table
cross-checked against the evdev one), gamepads via a new caller-pumped
GamepadService mode (SDL video owns the main thread here), and the
shell↔session stdout contract: {"ready":true}, per-window stats:
lines, JSON error + exit codes 0/2/3/4. Strict trust — no pin, no
connect. Design: punktfunk-planning linux-client-rearchitecture.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
0ab97b597c |
refactor(client): extract UI-agnostic plumbing into pf-client-core (phase 0)
Session pump, FFmpeg decode, PipeWire audio, SDL3 gamepads, keymap, trust store, mDNS discovery, library client and Wake-on-LAN move verbatim from clients/linux into crates/pf-client-core, shared with the upcoming Vulkan session binary (punktfunk-planning: linux-client-rearchitecture.md). The GTK client re-exports them at the crate root so every existing crate::-path keeps resolving; its manifest drops the moved-only deps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |