mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-29 06:02:25 +03:00
feat: controller/gamepad support (#350)
Adds `nesgamepad`, basically "diet-coke vimputti" for Nestri's microVM needs here. Comes with direct mapping for: - Sony - DualShock 4 (v2), DualSense - Microsoft - Xbox 360 pad, Xbox One S pad - Nintendo - Pro Controller - Generic pad for unknown controllers --------- Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
DatCaptainHorse
Claude Opus 5.5
parent
291fabd145
commit
451b495de6
+18
-5
@@ -2,8 +2,8 @@
|
||||
# nestri guest rootfs — the open half
|
||||
#
|
||||
# Builds a bootable Arch image containing Mesa (virtio-gpu native context)
|
||||
# and the five open guest components: nesinit, nescope, neshub, neswire,
|
||||
# nescapture. Two leaf targets, selected with `--target`:
|
||||
# and the six open guest components: nesinit, nescope, neshub, neswire,
|
||||
# nescapture, nesgamepad. Two leaf targets, selected with `--target`:
|
||||
#
|
||||
# runtime_prod stripped, root locked (default: `make build`)
|
||||
# runtime_debug debug tools, autologin root (`make build-debug`)
|
||||
@@ -141,18 +141,20 @@ COPY apps/nescope apps/nescope
|
||||
COPY apps/neshub apps/neshub
|
||||
COPY apps/neswire apps/neswire
|
||||
COPY apps/nescapture apps/nescapture
|
||||
COPY apps/nesgamepad apps/nesgamepad
|
||||
|
||||
FROM nestri-src AS nestri-build
|
||||
RUN --mount=type=cache,target=/root/.cargo/registry \
|
||||
--mount=type=cache,target=/build/nestri/target \
|
||||
cargo build --release \
|
||||
-p nesinit -p nescope -p neshub -p neswire -p nescapture && \
|
||||
-p nesinit -p nescope -p neshub -p neswire -p nescapture -p nesgamepad && \
|
||||
mkdir -p /artifacts/nestri/usr/bin /artifacts/nestri/usr/lib \
|
||||
/artifacts/nestri/usr/share/vulkan/implicit_layer.d && \
|
||||
install -Dm755 target/release/nesinit /artifacts/nestri/usr/bin/nesinit && \
|
||||
install -Dm755 target/release/nescope /artifacts/nestri/usr/bin/nescope && \
|
||||
install -Dm755 target/release/neshub /artifacts/nestri/usr/bin/neshub && \
|
||||
install -Dm755 target/release/neswire /artifacts/nestri/usr/bin/neswire && \
|
||||
install -Dm755 target/release/nesgamepad /artifacts/nestri/usr/bin/nesgamepad && \
|
||||
install -Dm755 target/release/libnescapture_layer.so \
|
||||
/artifacts/nestri/usr/lib/libnescapture_layer.so && \
|
||||
install -Dm644 apps/nescapture/manifest/VK_LAYER_nescapture.json \
|
||||
@@ -264,7 +266,7 @@ RUN pacman -Syu --noconfirm --needed \
|
||||
expat zlib llvm-libs lm_sensors elfutils libva shaderc vulkan-icd-loader \
|
||||
pixman libxkbcommon xcb-util-keysyms xorg-xwayland \
|
||||
pipewire pipewire-audio pipewire-pulse libpulse wireplumber opus \
|
||||
python libunwind \
|
||||
python libunwind sdl2-compat \
|
||||
&& rm -f /usr/share/libalpm/hooks/dbus-reload.hook \
|
||||
&& pacman -Rdd --noconfirm systemd systemd-sysvcompat \
|
||||
&& pacman -Scc --noconfirm
|
||||
@@ -291,6 +293,16 @@ RUN pacman -Syu --noconfirm --needed \
|
||||
# reads like an ordinary finish. Found 2026-09-12, on the first session that
|
||||
# got as far as launching one.
|
||||
|
||||
# `sdl2-compat` is Wine's controller support, and the only one there is for a
|
||||
# controller that is not a Steam Input one. Proton's device bus reads evdev
|
||||
# controllers through SDL2 and nothing else -- its udev backend defers every
|
||||
# evdev device to the SDL one on purpose -- and it loads the library with
|
||||
# `dlopen`, so its absence fails nothing at build time or at launch. A game
|
||||
# simply sees no controller, while `nesgamepad` reports it plugged in. Found
|
||||
# 2026-09-27, on the first session with one. Real Steam brings SDL in its
|
||||
# runtime, which is why this is never missed anywhere else. On Arch the
|
||||
# library is SDL2's API over SDL3, which arrives as its dependency.
|
||||
|
||||
# `dbus-reload.hook` is deleted above, before the removal rather than after,
|
||||
# and it is the whole reason that line is there: the hook runs
|
||||
# `/usr/share/libalpm/scripts/systemd-hook`, which systemd owns, so the
|
||||
@@ -494,7 +506,7 @@ RUN for intruder in /usr/lib/systemd/systemd /sbin/openrc-init /usr/bin/openrc-i
|
||||
RUN failed=0; \
|
||||
: > /tmp/missing-libs; \
|
||||
for f in /usr/bin/nesinit /usr/bin/nescope /usr/bin/neshub /usr/bin/neswire \
|
||||
/usr/lib/libnescapture_layer.so \
|
||||
/usr/bin/nesgamepad /usr/lib/libnescapture_layer.so /usr/lib/libSDL2-2.0.so.0 \
|
||||
/usr/bin/dbus-daemon /usr/bin/pipewire /usr/bin/pipewire-pulse \
|
||||
/usr/bin/wireplumber /usr/bin/ip \
|
||||
/usr/lib/libgallium-*.so /usr/lib/libEGL_mesa.so.0 \
|
||||
@@ -517,6 +529,7 @@ RUN failed=0; \
|
||||
fi
|
||||
|
||||
RUN for required in /usr/bin/nesinit /usr/bin/nescope /usr/bin/neshub /usr/bin/neswire \
|
||||
/usr/bin/nesgamepad /usr/lib/libSDL2-2.0.so.0 \
|
||||
/usr/bin/dbus-daemon /usr/bin/pipewire /usr/bin/pipewire-pulse \
|
||||
/usr/bin/wireplumber /usr/bin/ip /usr/bin/python3 \
|
||||
/usr/share/steam/compatibilitytools.d/proton-cachyos/proton; do \
|
||||
|
||||
+5
-5
@@ -1,10 +1,10 @@
|
||||
# build/ — the guest rootfs
|
||||
|
||||
Builds a bootable Arch image for the box's virtio-blk root: Mesa (virtio-gpu
|
||||
native context) plus the five open guest components —
|
||||
native context) plus the six open guest components —
|
||||
[`nesinit`](../apps/nesinit), [`nescope`](../apps/nescope),
|
||||
[`neshub`](../apps/neshub), [`neswire`](../apps/neswire),
|
||||
[`nescapture`](../apps/nescapture) — laid out
|
||||
[`nescapture`](../apps/nescapture), [`nesgamepad`](../apps/nesgamepad) — laid out
|
||||
the way [borealis](https://chromium.googlesource.com/chromiumos/overlays/board-overlays/+/main/project-borealis)
|
||||
lays out its `build/`: one big multi-stage `Containerfile`, `--target` picks the
|
||||
flavor, `etc/` holds the files that get overlaid onto the image verbatim.
|
||||
@@ -49,7 +49,7 @@ Three things worth knowing about how this is put together:
|
||||
explicit sanity check for doesn't exist here to check for.
|
||||
|
||||
3. **One `cargo build --release --workspace`, not one stage per binary.**
|
||||
`nescope`, `neshub`, `neswire` and `nescapture` share one Cargo workspace
|
||||
`nescope`, `neshub`, `neswire`, `nescapture` and `nesgamepad` share one Cargo workspace
|
||||
and one `Cargo.lock` — a BuildKit cache mount on `target/` gives cargo's
|
||||
own incremental compiler per-crate isolation without needing a separate
|
||||
Docker stage (and a separate full rebuild of `nesprotocol`) per binary.
|
||||
@@ -254,9 +254,9 @@ needs — was paid for an init system that is no longer here.
|
||||
|
||||
| was | now |
|
||||
|---|---|
|
||||
| `devfs`, `dmesg`, `udev`, `udev-trigger` | `devtmpfs` makes the nodes; init sets the two modes that matter. The compositor takes input through Wayland and opens nothing `udev` provides |
|
||||
| `devfs`, `dmesg`, `udev`, `udev-trigger` | `devtmpfs` makes the nodes; init sets the two modes that matter. The compositor takes input through Wayland and opens nothing `udev` provides. Controllers are the one thing a game finds through `udev`, and `nesgamepad` announces the ones it creates itself |
|
||||
| `guest-net`, `hostname`, `xdg-runtime`, `cgroups` | init, before it dials out |
|
||||
| `dbus`, `dbus-session`, `pipewire`, `wireplumber`, `neshub`, `neswire` | a table compiled into `nesinit` |
|
||||
| `dbus`, `dbus-session`, `pipewire`, `wireplumber`, `neshub`, `neswire`, `nesgamepad` | a table compiled into `nesinit` |
|
||||
| `nescope` in the `default` runlevel | **not a service.** It wraps the workload and is started by a launch, with that launch's geometry, and dies with it |
|
||||
| `agetty` on `hvc0` | nothing. See below |
|
||||
| `/etc/fstab` | init's own mounts, and shares named in the boot descriptor |
|
||||
|
||||
@@ -14,6 +14,20 @@
|
||||
# NTSYNC is a great improvement over fsync and esync approaches.
|
||||
CONFIG_NTSYNC=y
|
||||
|
||||
# ── Controllers ──────────────────────────────────────────
|
||||
# A controller of a family the box can rebuild is made here as the device
|
||||
# itself, from the real one's descriptor, through uhid; hid-generic binds to
|
||||
# it and gives it a hidraw node. That node is what Proton reads a controller through
|
||||
# when it wants the device rather than a gamepad abstraction of it, and it is
|
||||
# the only way a game that parses a controller's own reports -- to tell a
|
||||
# DualShock from an Xbox pad, say, and show the right buttons -- can work.
|
||||
#
|
||||
# Two generic switches and no vendor drivers, on purpose: games read the
|
||||
# hidraw node, not whatever a vendor driver would have made of the device, so
|
||||
# no controller family needs a switch of its own. HID_GENERIC is already on.
|
||||
CONFIG_UHID=y
|
||||
CONFIG_HIDRAW=y
|
||||
|
||||
# ── Timers ───────────────────────────────────────────────
|
||||
# The one that cost a day of silent audio. The guest has no sound hardware, so
|
||||
# PipeWire drives its whole graph off a timerfd at a 2.67ms cycle, which a
|
||||
|
||||
Reference in New Issue
Block a user