e27718b406c6083d1769543887448510f2175ce1
9 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e27718b406 |
packaging: ship firewalld services on rpm + deb too, share from packaging/linux
apple / swift (push) Successful in 1m10s
apple / screenshots (push) Successful in 5m45s
android / android (push) Successful in 4m2s
arch / build-publish (push) Successful in 5m37s
ci / web (push) Successful in 1m4s
ci / docs-site (push) Successful in 1m9s
ci / rust (push) Successful in 4m39s
deb / build-publish (push) Successful in 2m56s
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
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
ci / bench (push) Successful in 4m41s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 10m8s
docker / deploy-docs (push) Successful in 6s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m54s
Mirror the Arch firewalld service definitions into the RPM spec and the Debian
host package so every Linux packager installs them, and move the two XML files
to the shared packaging/linux/ home (alongside the .desktop files both the
PKGBUILD and deb scripts already source there) so there's one source of truth
instead of three drifting copies.
- rpm: install punktfunk-{gamestream,native}.xml to /usr/lib/firewalld/services/,
list them in %files host, and print the firewalld enable command in %post
(gated on firewall-cmd). Fedora/RHEL run firewalld by default, so this is where
it matters most; Bazzite inherits it via the sysext built from the package /usr.
- deb: install both XMLs in build-deb.sh and add the same firewalld-gated hint to
the postinst. Debian/Ubuntu ship no active firewall, so it's a no-op unless the
admin runs firewalld.
- PKGBUILD + arch README updated to the packaging/linux/ path.
- Firewall docs (bazzite README now leads with --add-service; debian README gains
a firewalld block) point at the shipped services; XML comments made
distro-neutral. Never auto-enabled — packages don't touch the admin's firewall.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
8005b11faf |
feat(tray): system-tray status icon for the host (Windows + Linux)
New crates/punktfunk-tray — a small per-user companion showing the host service state at a glance (running / stopped / starting / degraded / failed + the live session in the tooltip) with one-click actions: open web console, approve a pending pairing request, start/stop/restart, open logs. No more digging through logs to learn whether the service came back after a reboot or an update. Status is service-manager-FIRST (SCM / systemd user unit — a port squatter can never fake Running), then the new loopback-only unauthenticated GET /api/v1/local/summary (counts/booleans only; the mgmt token and cert.pem are SYSTEM/Admins-DACL'd on Windows, so a non-elevated tray cannot bearer-auth). Windows: windows_subsystem binary (a console exe in the Run key would flash a terminal at sign-in), Shell_NotifyIcon + hidden window, per-session single instance, TaskbarCreated re-add, --quit for the uninstaller; service actions elevate per click via ShellExecuteW "runas" onto the new `punktfunk-host service restart` (stop → wait Stopped → start). Linux: ksni/StatusNotifierItem over zbus, systemctl --user actions (no polkit), /etc/xdg/autostart entry whose --autostart self-gates to actual host users. Icons: scripts/gen-tray-icons.py (pure stdlib) renders the brand lens + status dot into committed .ico/hicolor assets; deb/rpm/arch ship binary+autostart+icons. Live-validated: Linux on the headless KDE session (SNI registration, state transitions, menu-driven start, dbusmenu layout); Windows on the RTX box (session-1 launch with no NIM_ADD failure, single instance, --quit, restart round-trip, summary loopback-200/LAN-401). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4f0b4aa68f |
docs(steam): production plan for Deck client pass-through + shippable usbip host
Write design/steam-deck-passthrough-plan.md — the build plan to ship exact Steam Deck pass-through from the Linux client (incl. the Steam + QAM buttons) plus a virtual Deck on any Linux host. Key validated facts captured so the next session doesn't re-investigate: - Client capture is ALREADY correct: SDL3 maps Steam->Guide, QAM->Misc1; the client forwards BTN_GUIDE/BTN_MISC1; the host maps them to btn::STEAM/btn::QAM. Only precondition: Steam Input disabled on the client (the Decky UX). - Shippable host transport = usbip + vhci_hcd (in-tree + signed everywhere, no module build, no MOK) — PROVEN on Bazzite: Steam promotes the usbip interface-2 Deck (XInput slot + X-Box pad), identical to raw_gadget on SteamOS. - Build steps: refactor steam_gadget.rs into shared Deck-logic + a transport trait; add the usbip transport (vendor-trim the usbip crate to drop rusb/libusb, in-process vhci attach); transport-select raw_gadget->usbip->UHID/DualSense; client leave-shortcut (controller chord + Ctrl+Alt+Shift+D); serial polish. Also checks in the working usbip Deck PoC (packaging/linux/steam-deck-gadget/ usbip-poc/) for the next session to build on. Not pushed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
963c406f33 |
feat(host/steam): default the gadget Deck on for SteamOS (glass-confirmed)
The virtual Steam Deck is validated glass-to-glass on a Deck: it appears as a
distinct second Steam controller, a held A drives Steam's overlay ("Resume
Game"), and a button press registers in a real game (confirmed in-game).
gadget_preferred() now defaults ON for SteamOS hosts (/etc/os-release ID=steamos
or ID_LIKE), OFF elsewhere where the universal UHID path stays the default;
PUNKTFUNK_STEAM_GADGET=1/0 forces it. A Deck-as-host with a physical Deck never
reaches this path — resolve_gamepad's conflict gate degrades SteamDeck → DualSense
first, so the two-Deck case never happens in production (it was only a test-rig
confound on the dev Deck).
The feature is complete: a virtual Steam Deck that Steam Input recognizes +
promotes, churn-free, with input flowing to games. Workspace clippy/fmt/test
green. Not pushed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
7ab8acaf55 |
feat(host/steam): harden the gadget feature contract — fixes the evdev churn
The virtual Deck's gamepad evdev was churning (destroyed + recreated) because Steam kept re-probing: GetControllerInfo reads HID feature reports, and the gadget served zeros for them. Captured the real contract off a physical Deck (packaging/linux/steam-deck-gadget/get_deck_attrs.c, hidraw HIDIOCGFEATURE — usbmon truncates to 32B) and implemented it in steam_gadget.rs::feature_reply: - 0x83 GET_ATTRIBUTES_VALUES: [83, 2d, 9×(attr-id, u32-LE)] — product id 0x1205, a per-instance unit serial (0x0a/0x04, so a gadget never collides with a real Deck or another gadget), and the capability attrs (0x09=0x2e, 0x0b=0x0fa0, rest 0). - 0xAE GET_STRING_ATTRIBUTE: [ae, len, attr, ascii] — serial (attr 1) / board serial (attr 0). - other commands (0x87 settings): echo the last write. Validated on the Deck: 1 connect / 0 disconnect / 1 gamepad evdev (was constant churn), Steam activates the gadget cleanly (no GetControllerInfo failed, no zombie) and emits its X-Box 360 pad. usbmon on the gadget's bus confirms our state reports (pressed button at byte 8) are delivered on the interrupt-IN and consumed by hid-steam — so with M1/M2's byte-8→BTN_SOUTH decode the input chain is proven end-to-end. Remaining: a foreground-game confirmation of Steam Input's XInput mapping, then default the gadget on for SteamOS. Workspace clippy/fmt/test green. Not pushed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c8e19396e4 |
feat(host/steam): raw_gadget Deck host backend (Steam-Input path, opt-in)
Port the proven raw_gadget virtual Deck to a Rust host gamepad backend, the SteamOS-only transport that gets Steam Input to actually promote the Deck. - inject/linux/steam_gadget.rs (new): SteamDeckGadget — a userspace raw_gadget emulator of the real 3-interface USB Deck (mouse=0/keyboard=1/controller=2, 28DE:1205) on a dummy_hcd loopback UDC, descriptors captured from a physical Deck, answering every control transfer incl. the HID feature reports. Driven by the same steam_proto::serialize_deck_state as the UHID pad; rumble feedback via parse_steam_output. The raw_gadget UAPI is funneled through 4 documented ioctl wrappers (the crate denies undocumented unsafe). - inject/linux/steam_controller.rs: the manager pad is now a DeckTransport enum (Uhid | Gadget); ensure() prefers the gadget when PUNKTFUNK_STEAM_GADGET=1 (best-effort modprobe dummy_hcd+raw_gadget), gracefully falling back to the universal UHID SteamDeckPad. write/pump/heartbeat dispatch through the enum. Validated on a real Deck via a static musl harness that #[path]-includes the module: enumerates, hid-steam binds + reads our serial + creates the Steam Deck + Motion Sensors evdevs — identical to the C PoC. Caught a real portability bug: raw_gadget's no-arg ioctls (RUN/CONFIGURE/EP0_STALL) reject a non-zero `value` with EINVAL, and on musl an omitted ioctl vararg is a garbage register — so they must pass an explicit 0. Opt-in (default off) while the Steam GetControllerInfo feature contract is hardened (to stop the gamepad-evdev churn). Workspace clippy/fmt/test green. Not pushed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8870e85233 |
feat(steam): raw_gadget virtual Deck — full Steam Input recognition (proven on Deck)
The interface-2 wall is climbed. packaging/linux/steam-deck-gadget/deck_raw_gadget.c is a raw_gadget userspace emulator of a real 3-interface USB Steam Deck (28DE:1205, mouse=0/keyboard=1/controller=2) on a dummy_hcd loopback UDC, with descriptors captured verbatim from a physical Deck and full HID feature-report handling. Live on a real Deck (SteamOS 3.8.11): hid-steam reads our serial (PFDECK000), creates the Steam Deck + Motion Sensors evdevs, and Steam Input PROMOTES it — controller.txt "Interface: 2 ... device opened ... reserving XInput slot 1" + "input: Microsoft X-Box 360 pad 1". Stable (1 connect, 0 disconnects, no zombie); the kernel Steam Deck evdev is then grabbed by Steam Input which exposes its own X-Box pad, exactly like a real Deck. First time a virtual Deck is fully Steam-Input promoted (UHID can't — it has no USB interface number, so Steam filters it). Also includes the configfs f_hid variant (configfs_gadget_up/down.sh) — the minimal reproducer that proved interface 2 makes Steam open+XInput-reserve the device, but f_hid can't serve feature reports so Steam dropped it as a zombie. Gotchas documented in the README: 7-byte vs 9-byte endpoint descriptor, no-data OUT controls acked via zero-length EP0_READ (not WRITE, else error -110), streamer must not start before SET_CONFIGURATION is acked. SteamOS-host only (needs dummy_hcd + raw_gadget). Recognition proven; feeding real client reports + a host backend is next. Not pushed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8e18d01af5 |
fix(host/kwin): authorize Desktop-mode streaming via a shipped .desktop
Streaming the KDE *Desktop* (KWin) session failed on a real interactive Plasma session with "KWin does not expose zkde_screencast_unstable_v1": KWin treats the screencast/virtual-output and fake_input globals as restricted and advertises them only to a client whose installed .desktop lists them under X-KDE-Wayland-Interfaces (matched by /proc/<pid>/exe -> Exec, and cached per-executable on first connect). The host shipped no .desktop, so it was permanently denied; it only ever worked on the headless dev box via KWIN_WAYLAND_NO_PERMISSION_CHECKS=1. Ship packaging/linux/io.unom.Punktfunk.Host.desktop (least-privilege: only the host, only zkde_screencast_unstable_v1 + org_kde_kwin_fake_input) and install it from the RPM/.deb/Arch host packaging so it is present before the host first connects. Drop the blunt session-wide NO_PERMISSION_CHECKS hack from kde-desktop-setup.sh (it now only seeds the RemoteDesktop input grant) and fix the now-misleading kwin.rs docs/errors. Validated live on a Bazzite Kinoite box (KWin 6.6.4): probe-compositor + spike --source kwin-virtual succeed against a KWin running WITHOUT the permission bypass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4ff6f447a8 |
ci(packaging): punktfunk-client .deb + RPM subpackage
Hook the Linux client into the existing packaging CI:
- deb.yml builds both binaries and publishes punktfunk-host AND
punktfunk-client to the Gitea apt registry; new
packaging/debian/build-client-deb.sh mirrors the host script
(shlibdeps auto-Depends — GTK4/libadwaita/SDL3/FFmpeg/PipeWire
sonames; no NVIDIA filter, the client links no CUDA). Built and
inspected locally on Ubuntu 26.04.
- punktfunk.spec gains a "client" subpackage (binary + desktop entry +
udev rule); rpm.yml's publish loop picks it up unchanged.
- New shared assets: packaging/linux/io.unom.Punktfunk.desktop and
scripts/70-punktfunk-client.rules — DualSense hidraw uaccess (USB +
Bluetooth, steam-devices style) so SDL's HIDAPI driver gets
touchpad/motion/lightbar/triggers instead of degrading to evdev.
- Builder images learn the client link deps (rust-ci already had
them; fedora-rpm adds gtk4/libadwaita/SDL3-devel) with idempotent
install steps in deb.yml/rpm.yml since jobs run against the
previous push's image.
Workspace check CI (build/clippy/test) already covers the crate since
|