87114ab1866e503b25c14af0a44392bd5ff0f6fe
10 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
384f8e00aa |
refactor(host/W4): extract inject keymap tables + rehome HidoutDedup
apple / swift (push) Successful in 1m19s
apple / screenshots (push) Successful in 4m27s
ci / web (push) Successful in 1m17s
ci / docs-site (push) Successful in 1m17s
android / android (push) Successful in 12m55s
decky / build-publish (push) Successful in 20s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 36s
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 10s
arch / build-publish (push) Successful in 13m35s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
ci / bench (push) Successful in 5m40s
ci / rust (push) Successful in 17m58s
deb / build-publish (push) Successful in 12m24s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m56s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 19m47s
windows-host / package (push) Successful in 15m17s
docker / deploy-docs (push) Failing after 30s
Two device-agnostic pieces carved out of the inject facade (plan §W4): - inject/keymap.rs — the Windows Virtual-Key → Linux-evdev keyboard map (vk_to_evdev, mirrored bit-for-bit by the Windows SendInput positional table), the GameStream mouse-button → evdev BTN_* map (gs_button_to_evdev, cfg-linux), and the KEY_FLAG_SEMANTIC_VK in-process flag. - inject/hidout_dedup.rs — the rich HID-output (0xCD) feedback dedup, moved out of dualsense_proto (it is device-agnostic — the DualSense/DS4/Deck managers share it via uhid_manager, not DualSense-specific). Its unit test moves with it. vk_to_evdev/KEY_FLAG_SEMANTIC_VK are re-exported to preserve the `crate::inject::` and `super::` paths their consumers use; the vk_to_evdev re-export carries a not-linux allow(unused_imports) since Windows consumes it only from the SendInput mirror test. uhid_manager's import repointed to the new home. Pure move; no behavior change. Linux clippy+tests + Windows host clippy (nvenc,amf-qsv) both green; fmt clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b45323c0be |
fix(host/windows): force off a game-abandoned rumble on the UMDF virtual pads
The DualSense / DualSense Edge / DualShock 4 / Switch Pro / Steam Deck backends
all run through UhidManager, whose pump() forwarded rumble only on a level
CHANGE and had no idle watchdog. A game that latches a one-shot rumble (a
Stardew axe hit, a DS3 hit) and then stops writing output reports left
last_rumble non-zero; native.rs re-sends the latched level every ~120 ms with a
fresh TTL and the Apple RumbleRenderer refreshes its envelope on every renewal,
so the controller vibrated continuously until a later event happened to write a
report the host parsed as a stop. The XUSB path already guards against this
(RUMBLE_IDLE_TIMEOUT force-off,
|
||
|
|
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> |
||
|
|
5c7e0afa99 |
fix(host): Linux virtual-pad feedback access — hidraw udev rules, per-pad DS MAC, SET_REPORT acks
ci / web (push) Successful in 1m9s
ci / docs-site (push) Successful in 1m3s
ci / bench (push) Successful in 5m49s
decky / build-publish (push) Successful in 19s
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 10s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 11s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 11s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
docker / deploy-docs (push) Successful in 23s
arch / build-publish (push) Successful in 11m11s
android / android (push) Successful in 14m24s
deb / build-publish (push) Successful in 11m55s
ci / rust (push) Successful in 17m18s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 15m0s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m54s
windows-host / package (push) Successful in 15m11s
apple / swift (push) Successful in 4m43s
apple / screenshots (push) Successful in 21m4s
Root-cause fixes for "rumble + adaptive triggers never work with Linux hosts" (the capture code itself was proven good on-hardware — see the new tests): * 60-punktfunk.rules now grants the `input` group the VIRTUAL pads' hidraw nodes (DS/Edge/DS4/Switch/Deck/SC). Steam/SDL drive DualSense adaptive triggers, lightbar, and player LEDs exclusively over hidraw — and Steam without hidraw demotes a PlayStation pad to a generic evdev device, losing its rumble handling too. Coverage no longer depends on the distro's steam-devices rules + logind's active-seat uaccess ACL (which a headless/ dedicated streaming session never gets). Verified live: nodes now come up root:input 0660. * Per-pad MAC in the DualSense (0x09) and DS4 (0x12) pairing feature replies: hid-playstation adopts the MAC as the HID uniq and SDL/Steam dedup controllers by that serial — identical MACs made a second virtual pad read as the first one re-connecting over another transport. * DualSense/DS4 UHID backends now ack UHID_SET_REPORT (err=0) instead of ignoring it, so a SET_REPORT writer no longer blocks on the kernel's 5 s timeout. * New #[ignore] on-box tests play the GAME's role against a real kernel and pin the full feedback surface (all green on real hw): DualSense evdev-FF + raw hidraw output report (rumble/lightbar/LEDs/both trigger blocks verbatim, per-pad uniq), uinput X-Box FF upload→pump→stop-on-erase, and usbip Deck 0xEB rumble via the controller interface (idle interfaces ACK silently, like real hardware). Windows note: the UMDF driver keeps its own pairing blob copies — the shared- MAC dedup hazard exists there too and needs a driver-side follow-up. 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> |
||
|
|
59fc820226 |
perf(inject/host): dedup the DualSense HID-output feedback plane (G17)
A game's DualSense output report bundles rumble + lightbar + player-LEDs + adaptive-triggers into one report, so a pad that is merely rumbling re-sends its unchanged lightbar / LED / trigger state on every output report. The managers already dedup rumble, but forwarded every rich `HidOutput` event verbatim — flooding the 0xCD feedback plane to the client during continuous rumble. Add a shared `HidoutDedup` (dualsense_proto, used by both the Linux UHID and Windows UMDF managers) that forwards Led/PlayerLeds/Trigger only on a value change (per side for the two triggers) and always forwards one-shot TrackpadHaptic pulses — mirroring the rumble dedup two lines above and the DS4 backend's lightbar dedup. Reset per pad on create/unplug. Verified on Linux .21 (clippy -D warnings clean, new HidoutDedup unit test + full suite green); Windows .173 with the rest of Phase 3. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
43e52437c0 |
fix(gamepad/host): map BTN_MISC1 to the DualSense mute button (G6)
DsState::from_gamepad mapped GUIDE→PS and TOUCHPAD→TOUCHPAD into buttons[2] but never handled BTN_MISC1, so the mic-mute / capture button clients send was inert on every PlayStation-family virtual pad (DualSense/DualShock4), and btn2::MUTE was dead code. Map BTN_MISC1 → btn2::MUTE (rebuilt from the wire bit each frame like PS/TOUCHPAD, so no persistence gap) and drop the #[allow(dead_code)]. Test extended (from_gamepad_maps_touchpad_click); green on Linux (.21). Co-Authored-By: Claude Opus 4.8 (1M context) <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> |
||
|
|
e6d9454251 |
feat(host): fold Steam Deck to DualSense on Windows; split-pad touch + pad clicks
A Deck client asking for 'steamdeck' on a Windows host resolved to Xbox360, whose apply_rich is a no-op — gyro and both trackpads silently discarded. Windows already ships a full DualSense backend (UMDF driver, touchpad + motion, wire units 1:1), so pick_gamepad now folds SteamDeck -> DualSense there. The three near-identical DualSense-family appliers (Linux UHID, Windows DualSense, Windows DS4) are hoisted into one shared dualsense_proto::DsState::apply_rich, with two mapping upgrades for Steam dual-pad clients everywhere: * the Deck's two pads SPLIT the single DualSense touchpad — left pad -> contact 0 on the left half, right pad -> contact 1 on the right half — mirroring the physical thumb layout and the split-pad zones games and Steam Input already use (the left pad was previously dropped outright) * TouchpadEx pad clicks now press the touchpad-click button (persisted in DsState::touch_click, OR-ed in by both serializers; previously dropped by every DualSense-family backend, Linux included) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
fced221684 |
refactor(windows-host): confine platform code under windows/ + linux/ folders (Goal-1 stage 6)
Move 36 platform-specific files into per-module `windows/` and `linux/` subfolders (and the
shared HID codecs into `inject/proto/`):
capture/{windows,linux}/ encode/{windows,linux}/ inject/{windows,linux,proto}/
audio/{windows,linux}/ vdisplay/{windows,linux}/
src/windows/ (service, wgc_helper, win_adapter, win_display)
src/linux/ (dmabuf_fence, drm_sync, zerocopy/)
Done with `#[path]`, NOT a module rename: every file moves into its folder while the
`crate::*::*` module names stay FLAT, so all caller paths and every internal `super::`/`crate::`
reference are unchanged — only the parent `mod` decls gained `#[path = "..."]`. This is the
codebase's existing pattern (inject's gamepad_windows) and makes the move byte-identical in
behaviour with ZERO reference churn, far lower risk than collapsing to a single
`crate::capture::windows::` namespace (that deeper rename is an optional follow-on; this delivers
the cfg-sprawl folder confinement the stage is about). Done LAST, after the semantic stages, so
the path churn didn't fight them.
Verified: Linux cargo check + clippy (-D warnings) clean; my mod-decl changes fmt-clean (the 3
remaining fmt diffs are pre-existing local-rustfmt-version skew that moved with their files); all
36 `#[path]` targets exist; no internal `#[path]`/`include!`/file-child-mod in any moved file
(the inline `mod X {` blocks are self-contained). Box build to follow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|