v2.8.0 "Touchpoint"
Rusty2600 v2.8.0 "Touchpoint"
Fifth release of the RustyNES gap-closure arc — the first wave of
wasm-winit web parity, building real interactivity on top of v2.5.0's
bare wasm render.
Shipped through PR #18, reviewed by GitHub Copilot and Gemini Code
Assist — 6 findings, 5 fixed, 1 dismissed with documented evidence.
On-screen touch controls (the headline)
A D-pad + fire button + console-switch row for the wasm-winit build
(ShellState::render_touch_overlay, wasm32-only), since a browser user
on mobile Safari/Chrome has no physical keyboard. All plain egui
widgets, feeding the exact same InputState the keyboard path
populates, via a new input::TouchButton/TouchOverlayState pair:
pure, target-agnostic press/hold/release + latch-edge tracking, unit-
tested under the ordinary native cargo test --workspace run (not
gated to wasm32-only, so this logic is exercised on every CI platform).
Latching switches (Color/Difficulty) toggle exactly once per press
rather than re-toggling 60 times a second while held; momentary buttons
(joystick directions/fire, Select/Reset) emit a press/release edge
matching how a real key's keydown/keyup already drives them. Defaults
visible (a touch-only device has no other way to drive the emulator);
View -> Touch overlay toggles it off for a desktop-browser user who
does have a keyboard.
Honestly documented limitation: egui-winit's default touch handling
tracks one synthesized pointer, so true simultaneous multi-finger
combos (holding a direction while also tapping Fire) aren't guaranteed
to both register — a real egui/egui-winit constraint, unverifiable
either way in this project's sandbox (no real touchscreen), not
something this release introduces or works around.
Settings panel + console-switch buttons — verified, not built
Reviewed the Settings window tab-by-tab for wasm32-safety: the Video/
Audio/Input/System tabs are plain egui widgets with no native-only API
in the path (no rfd dialogs, no blocking file I/O), so the panel
already worked correctly on wasm32 structurally — no code needed there.
Console-switch buttons were likewise already reachable "for free" via
the shared Emulation -> Console switches menu on both native and
wasm32. The real gap this release closes is persistence (below) and the
touch-friendly on-screen row, for a user who can't easily reach a
dropdown menu with a finger.
Real localStorage config persistence
Config::save()/Config::load() now genuinely persist on wasm32 via
web_sys::window().local_storage(), replacing a previous no-op stub
(and a previously-nonexistent load()). Shares TOML (de)serialization
helpers with the native config.toml file path, so both backends are
covered by one set of native unit tests. Falls back to defaults on any
missing/corrupt/foreign stored value, never blocking launch.
Save-state SLOT persistence (v2.4.0's manual save slots) is
deliberately deferred — it needs per-slot localStorage keys, an
mtime substitute (localStorage has none), and real size budgeting
across 8 slots within the ~5-10MB origin quota, a distinctly bigger
lift than the single-key Settings value above. Documented in
docs/frontend.md rather than rushed alongside this release's headline
items.
PR review, for real
PR #18 was reviewed by GitHub Copilot and Gemini Code Assist — 6
findings:
- Draggable touch-overlay Areas (Gemini Code Assist):
egui::Area
is movable by default, so the D-pad/fire/console-switch overlays
could be accidentally dragged by a touch. Fixed with.movable(false)
on all 3. - A wasted per-frame audio-sample allocation on wasm32 (Gemini Code
Assist): theVeccollecting DC-blocked samples was allocated and
populated every frame (60 times/sec) even on wasm32, where nothing
reads it (audio_txis native-only, wasm-winit audio being an
explicitly deferred stretch goal). Fixed — now gated to native only;
the DC-blocker filter state still advances every sample on both
targets, since that state must stay correct for whenever wasm32 audio
eventually gets wired up. - A misleading doc comment (Copilot):
TouchOverlayState's doc
comment claimed momentary buttons "emit their live level every
frame" — the code was already correct (edge-based, matching
keydown/keyup), only the comment was wrong. Corrected. - A false-positive claim (Gemini Code Assist, dismissed with
evidence): that this project's&& letchain syntax "will cause
compilation to fail on stable Rust toolchains." This project's
edition is 2024 (let-chains stabilized since Rust 1.88), the exact
same pattern is already used inemu_thread.rs(merged inv2.7.0),
and CI for this very PR had already passed on Linux/macOS/Windows
with this exact syntax on stable rustc 1.96. Dismissed rather than
"fixed" into a stylistic regression.
Test count
342 tests passing on default features (346 with --features test-roms) — up from 333/337, the 9 new tests covering
TouchButton/TouchOverlayState and the shared TOML (de)serialization
helpers. Full CI green — Linux/macOS/Windows, the perf regression gate,
and the no_std gate. Independently re-verified beyond the standard
gate: cargo check/cargo clippy --target wasm32-unknown-unknown --features wasm-winit and cargo check --target wasm32-unknown-unknown --features wasm-canvas (the other wasm build, confirming no
regression) both clean.
Honest verification boundary
Live in-browser interactive verification (touch input actually working
on a real touchscreen) remains blocked by the same sandboxed-headless-
Chromium GPU limitation v2.5.0 already documented — this sandbox
can't render wasm-winit at all, let alone drive it with touch.
Everything verifiable without live browser GPU access (unit tests,
wasm32 compile + clippy checks) has been confirmed.
What's next
v2.9.0 "Full Circle" — Web Parity Wave 2: in-browser Lua scripting, a
share-link feature, PWA install, and a wasm debugger overlay, closing
the remaining wasm-demo capability gap against RustyNES. See
to-dos/ROADMAP.md for the complete v2.4.0 -> v3.0.0 roadmap.