Commit Graph
4 Commits
Author SHA1 Message Date
enricobuehler e19f11bb0d feat(abi/apple): carry the trigger motors to non-Rust clients — ABI 18, next_rumble_cmd2
windows-drivers / probe-and-proto (pull_request) Successful in 25s
apple / swift (pull_request) Successful in 1m37s
apple / screenshots (pull_request) Skipped
windows-drivers / driver-build (pull_request) Failing after 1m43s
ci / web (pull_request) Successful in 2m57s
ci / bun-nix (pull_request) Successful in 18s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 1m36s
android / android (pull_request) Successful in 4m0s
ci / rust-arm64 (pull_request) Successful in 4m19s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 2m32s
ci / docs-site (pull_request) Successful in 4m36s
ci / rust (pull_request) Failing after 9m15s
nix / flake (pull_request) Successful in 15m3s
The `0xCA` wire already carries the two Xbox impulse-trigger motors (v3), and the Rust decode path
already parses them; `datagram_task.rs` dropped them on the floor with a comment naming exactly this
work as what remained. The blocker was the C ABI: every non-Rust client pulls rumble through
`punktfunk_connection_next_rumble_cmd`, whose out-params cannot carry two more channels.

    PunktfunkStatus punktfunk_connection_next_rumble_cmd2(
        PunktfunkConnection *c, uint16_t *pad, uint16_t *low, uint16_t *high,
        uint16_t *left_trigger, uint16_t *right_trigger,
        uint32_t *backstop_ms, uint32_t timeout_ms);

⚠️ ADDED, not widened. `_cmd` keeps its signature and its values bit-identical for handle-only
traffic — out-of-tree embedders depend on it and `docs/embedding-the-c-abi.md` documents it, so
silently changing an exported symbol would break every consumer at once. `nm` on the staticlib shows
all four rumble entry points still exported. `ABI_VERSION` 17 → 18; every other site reads it
dynamically, so there are no hardcoded mirrors to drift.

⚠️ ONE HONEST BEHAVIOURAL DELTA, documented in `abi.rs` and pinned by a test: against a
trigger-driving host a `_cmd` caller now receives commands with `low == high == 0` where the demux
previously dropped the update entirely. They are idempotent handle stops, and the redundant-stop
suppression cannot fold them because the command as a whole is not silent. Zero cost today —
nothing sources non-zero triggers.

The dedupe-jitter proof was RE-DERIVED rather than widened, which is the kind of thing that quietly
rots when a tuple grows: the nudge touches only `low` by ±1 LSB and `emit` is only reached with a
non-silent level, so the nudged tuple can collide with the four-field stop sentinel only at
`(1,0,0,0)`. A test pins both directions — refuse at `(1,0,0,0)`, flip freely at `(1,0,lt,0)`.

Apple renders them: `RumbleRenderer` gains `Motor?` slots at `GCHapticsLocality.leftTrigger` /
`.rightTrigger` beside the existing handles. A controller without trigger actuators degrades
silently — a nil engine yields a nil slot and `reconcile` no-ops — and absent localities are never
logged, because on most pads that is the normal case rather than a fault. The macOS DualSense
raw-HID branch stays a deliberate no-op: a DualSense has ADAPTIVE triggers, not trigger rumble
motors, and inventing a mapping there would buzz the wrong thing.

🛑 BUILT AHEAD OF A PRODUCER, DELIBERATELY, AND NOTHING HERE CLAIMS OTHERWISE. Nothing can currently
source trigger rumble on Windows and that is measured, not assumed: `XINPUT_VIBRATION` has two
members, and GameInput — the only four-motor API — does not enumerate an xinputhid-promoted Xbox pad
at all, verified against a REAL Microsoft Elite which is equally invisible to it while classic
XInput reads it live. So this path has never been exercised end to end and the comments say so.

VERIFIED
  * `cargo test -p punktfunk-core --features quic --lib` 378 passed on macOS, 203 on Windows;
    clippy `-D warnings` clean with and without default features; `cargo fmt --all --check` clean.
  * The generated header is regenerated and idempotent on re-run (CI diffs it).
  * SWIFT ACTUALLY COMPILES AND RUNS: `swift build` clean and `swift test` 262 passed / 0 failures
    in `clients/apple`, against a locally built xcframework. (Editor SourceKit errors about
    `PunktfunkCore`/`DualSenseHID` are index noise from that gitignored artifact — a real build
    resolves both, and the `DualSenseHID` references are untouched by this change.)
  * `cargo build -p punktfunk-host` clean on Windows.

NOT VERIFIED
  * End to end — see above; there is no producer.
  * Whether a real Xbox pad on Apple actually reports the two trigger localities. The degrade needs
    no code, but the positive case is untested.
  * `pf-client-core` (the SDL renderer) does not build on macOS at baseline and is unbuilt here. It
    only reads `RumbleCommand` fields and never constructs one, so added fields cannot break it, but
    it still calls `_cmd`; wiring `SDL_RumbleGamepadTriggers` is separate work.

ANDROID: NOT DONE, and it should stay that way for now. `pack_rumble` packs pad/backstop/low/high
into bits 0..52 of a `jlong` with `-1` reserved as a sentinel — two more `u16` do not fit. The right
fix if ever wanted is the direct-`ByteBuffer` shape `nativeNextHidout` already uses in the same file
(zero-allocation, caller-owned, the established idiom), not a second `jlong` (racy across two calls)
nor `long[]` (an allocation per pull). But no Android device exposes trigger actuators at all, so
there is nothing to render. Separately stale and also not fixed: `NativeBridge.kt`'s KDoc still
documents the v2 `ttl_ms` layout rather than `backstop_ms`.
2026-08-09 23:30:02 +02:00
enricobuehlerandClaude Fable 5 aca5aa9993 feat(host+inject): pen P1 — per-session uinput virtual tablet, HOST_CAP_PEN live
The Linux injection leg of design/pen-tablet-input.md: 0xCC/0x05 pen batches now
route through the per-session PenTracker into a lazily-created 'Punktfunk Pen'
uinput tablet (BTN_TOOL_PEN/RUBBER, pressure, tilt-from-polar, ABS_Z barrel
roll, hover distance, INPUT_PROP_DIRECT) — compositors pick it up via libinput
and hand apps zwp_tablet_v2 with full fidelity. The host now advertises
HOST_CAP_PEN when /dev/uinput is accessible (PUNKTFUNK_PEN=0 kill-switch);
transitions group into SYN frames so proximity-enter carries its position.
Stroke failsafe: clients heartbeat ≤100ms while in range (documented wire
contract — capture APIs are silent for a stationary pen); 200ms of silence
force-releases. 'punktfunk-host pen-test' draws a pressure-ramped sine stroke
through the real tracker→uinput chain, no client needed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:18:31 +02:00
enricobuehlerandClaude Fable 5 4714235fe6 feat(core): stylus wire P0 — state-full RICH_PEN batches, PenTracker, HOST_CAP_PEN
apple / swift (push) Successful in 1m24s
release / apple (push) Successful in 10m3s
android / android (push) Successful in 13m31s
arch / build-publish (push) Successful in 13m39s
ci / web (push) Successful in 1m4s
ci / docs-site (push) Successful in 1m12s
windows-host / package (push) Successful in 16m40s
apple / screenshots (push) Successful in 6m38s
ci / bench (push) Successful in 5m26s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m9s
ci / rust (push) Successful in 20m46s
decky / build-publish (push) Successful in 30s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m15s
deb / build-publish (push) Successful in 8m50s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 45s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 12s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 12s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 12s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 4m31s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 7m6s
deb / build-publish-host (push) Successful in 9m52s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 6m44s
docker / deploy-docs (push) Successful in 28s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 7m4s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m35s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m45s
flatpak / build-publish (push) Failing after 8m6s
The pen plane from design/pen-tablet-input.md, protocol side only (the P1 uinput
tablet injector will consume it): 0xCC kind 0x05 carries batches of state-full
PenSamples (pressure, polar tilt + azimuth, barrel roll, hover distance, eraser
tool, barrel buttons); PenTracker diffs samples into injector transitions so a
lost datagram self-heals; HOST_CAP_PEN (0x10) gates sending and stays
unadvertised until a real backend exists. C ABI: PunktfunkPenSample +
punktfunk_connection_send_pen, documented in docs/embedding-the-c-abi.md §8.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 12:58:31 +02:00
enricobuehlerandClaude Opus 4.8 0dc414f197 docs: guide for embedding the C ABI (webOS, Xbox, Tizen examples)
ci / web (push) Successful in 1m4s
ci / docs-site (push) Successful in 1m19s
decky / build-publish (push) Successful in 33s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 40s
apple / swift (push) Successful in 3m38s
ci / bench (push) Successful in 7m5s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9m21s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8m25s
docker / deploy-docs (push) Successful in 35s
android / android (push) Successful in 12m34s
arch / build-publish (push) Successful in 15m3s
deb / build-publish (push) Successful in 16m18s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 15m7s
apple / screenshots (push) Successful in 19m20s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 12m17s
ci / rust (push) Successful in 23m12s
Developer integration guide for building a punktfunk client on any platform
by linking punktfunk-core through its stable C ABI: what the core does vs.
what the embedder supplies, build/link/cross-compile, the full client
lifecycle (identity/pairing, connect ladder, video+recovery loop, audio,
input, feedback planes, teardown), plus worked blueprints for webOS, Xbox
(GDK), and Tizen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 10:49:13 +02:00