Skip to content

On-screen config UI + auto-reconnect + reference-frame invalidation - #1

Open
Delta062 wants to merge 1 commit into
masterfrom
feature/on-screen-config-and-reconnect
Open

On-screen config UI + auto-reconnect + reference-frame invalidation#1
Delta062 wants to merge 1 commit into
masterfrom
feature/on-screen-config-and-reconnect

Conversation

@Delta062

@Delta062 Delta062 commented Aug 24, 2026

Copy link
Copy Markdown
Owner

⚠️ Heads-up: this is vibecoded

This patch set was written with AI assistance and has not been exercised on real hardware yet. It compiles and links cleanly with the repo's own Docker toolchain (devkitppc 20260503 + wut 47ae413), and the nn::swkbd calls were checked against the actual wut header at the pinned commit — but the on-screen UI, the keyboard rendering, and the reconnect behaviour have only been verified at the build/symbol level, not by actually streaming on a Wii U. Expect to find bugs. Please treat this as a draft for review and testing, not a finished feature. The test checklist at the bottom is the minimum you should run before trusting it.

📦 Try the build

A pre-built moonlight.wuhb is attached as a release asset so you can test without building:

Download moonlight-wiiu-feature.wuhb

Copy it to wiiu/apps/moonlight/moonlight.wuhb on your SD card (replacing the existing one) and launch. Built from base 760e7761 + this patch set using the repo's Docker toolchain.

✨ What's new since last review

This is a full refresh of the branch (previous head superseded — fresh, complete patch from base commit 760e7761, not incremental). On top of the settings UI / app picker / help screen / branding / auto-reconnect work, this round adds:

  • Button controls, fully standardizedB now means "back/cancel" on every screen with zero exceptions (previously it also meant Help on one screen and Pair on another). Pairing moved from B to Plus. Minus now opens Help from every screen (previously only from Connected); Settings → Minus opens a new USB Ethernet adapter info screen.
  • UI renders at the GamePad's native 854×480 instead of an arbitrary 1920×1080 canvas. This eliminates the downscale blur that made text hard to read on the GamePad screen specifically — the TV output now upscales instead, the better tradeoff given viewing distance. Every font size and layout constant was rescaled to match, and every screen checked against the smaller canvas to confirm nothing overflows.
  • New USB Ethernet adapter info screen (Settings → Minus) with real compatibility info: official RVL-015 adapter, ASIX AX88772-family chipset compatibility, Realtek chipsets frequently not detected, 10/100 only, avoid USB hubs.
  • New Settings tips — a resolution tip explaining the 854×480 native-match benefit, and a note that Steam's own UI/display scale on the PC is usually the bigger lever for text legibility than stream resolution alone.
  • Several real bugs fixed (found in a self-review pass):
    • A color leak where the X/Y button badges would silently turn nearby text white.
    • A vertical misalignment where those same badges were positioned relative to the wrong text reference point.
    • A couple of guessed spacing constants (settings list, app picker) replaced with values derived from the font's actual measured line height — the resolution change made the margins on those guesses too thin to trust.

Under the hood: the 854×480 change means every Font_GetLineHeight() / Font_GetTextWidth() call now returns GamePad-native pixels rather than 1080p-scaled values. The X/Y badge fix corrects both the color state (badges were leaving the text color set to white for subsequent draws) and the Y-offset anchor (badges were measured from the label's top rather than the row's baseline). The spacing constants now use the font's measured line height instead of hard-coded pixel guesses.

The problem, in plain terms

Streaming from this Wii U client to a Sunshine/Apollo PC was dropping constantly with:

Connection terminated with error: -1
Connection terminated with error: -5

Those two codes aren't Moonlight bugs — they're moonlight-common-c's raw socket/ENet failure codes, and in practice they're almost always caused by the Wii U's weak 2.4 GHz-only built-in Wi-Fi under sustained streaming load [6]. The old client's answer to a drop was to dump you straight back to the menu, and the only way to change anything (including which PC to connect to) was to pull the SD card, edit a text file on a computer, and put it back. That's a poor experience for a console.

Rather than just saying "lower your bitrate," this patch makes the client (a) actually recover from those drops on its own, (b) make packet loss cheaper to recover from, and (c) let you configure everything — including the server address — from the console itself [6].

What changed, and why it helps you

1. On-screen config, not a text file

New STATE_ENTER_IP and STATE_SETTINGS screens use the Wii U's real on-screen keyboard (nn::swkbd, wrapped in the new src/wiiu/keyboard.h / keyboard.cpp) so you can type your PC's address on the console [6]. A full settings menu (press X from the main/connected screen) covers resolution, fps, bitrate, packet size, app name, local audio, quit-app-after, view-only, disable-GamePad, swap-buttons, mouse mode, and rotation — all with D-Pad navigation [6].

Everything you change is saved to settings.conf / address.conf on the SD card and takes priority over moonlight.conf on future launches. moonlight.conf is now just the one-time seed for first boot and for the advanced options that aren't in the menu [6].

Under the hood: keyboard.cpp is a thin C wrapper around the C++-only nn::swkbd API. It allocates work memory via nn::swkbd::GetWorkMemorySize, creates an FSClient for resource loading, and exposes plain Keyboard_Open / Keyboard_Update / Keyboard_DrawTV / Keyboard_DrawDRC / Keyboard_IsDone / Keyboard_WasCancelled / Keyboard_GetText / Keyboard_Close functions that main.c (compiled as C) can call directly [2][5][7]. The UTF-16 ↔ UTF-8 conversions are intentionally simple because we only care about ASCII (IPs/hostnames) [7].

Benefit: you never touch a computer or an SD card again to point the Wii U at a new PC or tweak a setting.

2. Auto-reconnect

_connection.c now classifies why the stream died. DRM terminations and host-side capture errors are left alone (retrying won't help and can be actively wrong). Everything else — including the -1/-5 codes — gets up to 3 automatic reconnect attempts with a 2-second backoff (new STATE_RECONNECTING screen) before giving up and returning to the menu [6].

Under the hood: a new is_retryable_error() helper in _connection.c returns 0 for ML_ERROR_GRACEFUL_TERMINATION, ML_ERROR_UNEXPECTED_EARLY_TERMINATION, and ML_ERROR_PROTECTED_CONTENT (where retrying is pointless or harmful), and 1 for everything else — including the raw negative socket/ENet codes that show up on flaky Wi-Fi [8]. When a retryable error fires and attempts remain, it sets pending_auto_reconnect = 1 and a deadline 2 seconds out; main.c's new STATE_RECONNECTING case counts down and then re-enters STATE_CONNECTING with reconnect_stream_after_connect = 1 so the stream auto-starts once the handshake succeeds [8].

Benefit: a Wi-Fi hiccup no longer kills your session. The stream usually just comes back on its own.

3. Reference-frame invalidation

decode.c's H.264 decoder now advertises CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC, telling the host it can patch around a missing reference frame instead of forcing a full keyframe (IDR) re-send on every dropped packet [6].

Under the hood: the change is a single line in decoder_callbacks_wiiu.capabilities = 0 becomes .capabilities = CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC [8]. moonlight-common-c disables this again on its own for resolutions where it's known to misbehave (see Connection.c), and the existing DR_NEED_IDR fallback in wiiu_decoder_submit_decode_unit still covers the case where the hardware decoder can't cope anyway [8].

Benefit: a real bandwidth/latency win on lossy links — fewer, cheaper recovery frames instead of one huge frame right when the link is already struggling.

4. Bitrate guidance tip

The settings screen's Bitrate row shows a live tip: the same baseline bitrate config_parse() would auto-pick for your current resolution/fps, plus practical ceilings (~10 Mbps built-in Wi-Fi, ~35 Mbps USB LAN adapter), with a warning if your value looks too high for Wi-Fi [6].

Under the hood: recommended_bitrate_kbps() in main.c mirrors the auto-bitrate table in config_parse() (config.c) so the tip always matches what Moonlight would pick automatically [8]. The ceilings are defined as WIFI_SAFE_BITRATE_KBPS_MAX 10000 and WIRED_SAFE_BITRATE_KBPS_MAX 35000 — conservative, Wii-U-specific numbers, not theoretical link speeds [8].

Benefit: you get a concrete, console-visible answer to "why is it dropping?" instead of guessing at numbers.

Files

src/_connection.c, src/config.c, src/config.h, src/main.c, src/wiiu/decode.c, src/wiiu/wiiu.h (modified) + src/wiiu/keyboard.h, src/wiiu/keyboard.cpp (new). 8 files, 849 insertions, 9 deletions. third_party/moonlight-common-c is untouched [6].

Credits

  • Claude — designed and wrote the original patch set (the four changes above, the keyboard.h/keyboard.cpp wrapper, and the handover notes) [6].
  • Qwen3.8-27b — picked up the handover, applied the patch, built it with the repo's Docker flow, validated the nn::swkbd calls against the pinned wut header, confirmed the Makefile picks up keyboard.cpp and that no extra -l flag is needed (it links via the existing -lwut), committed to the branch, and opened this PR.
  • Qwen3.8-27b (this round) — applied the full patch refresh, built it on the Docker toolchain, and fixed two build blockers that surfaced on the new code (the font_texture.h multiple-definition link error and the stb_image.h thread-local relocation error in elf2rpl). The build now produces a clean moonlight.wuhb with the logo bundled into /content/.

Test checklist (please run on the console)

  • Fresh SD card / no address.conf → launching goes straight to the on-screen keyboard instead of an error
  • Enter your PC's IP → connects, and address.conf appears on the SD card
  • From the Connected screen, press X → settings menu opens, D-Pad navigates, Left/Right changes values
  • Change bitrate above 10000 → orange warning line appears under the Bitrate row
  • Press B in settings → returns to the previous screen, settings.conf is written
  • Relaunch the app → saved settings persist (not just the address)
  • Force a drop mid-stream (e.g. briefly disable Wi-Fi on the host) → you see "Reconnecting... Attempt X of 3" instead of dumping to the menu

@Delta062
Delta062 force-pushed the feature/on-screen-config-and-reconnect branch 2 times, most recently from e2e106b to 0ad4518 Compare August 26, 2026 07:16
…hernet screen, bug fixes

- Button controls fully standardized: B = back/cancel on every screen
  (zero exceptions), Pairing moved to Plus, Minus opens Help from
  every screen, Settings Minus opens new USB Ethernet info screen
- UI now renders at GamePad native 854x480 (was 1920x1080 canvas)
  - eliminates downscale blur on GamePad; TV upscales instead
  - all font sizes and layout constants rescaled to match
- New USB Ethernet adapter info screen (Settings > Minus):
  RVL-015, ASIX AX88772 compatibility, Realtek caveats, 10/100 only
- New settings tips: resolution native-match benefit, Steam UI scale
  as the bigger lever for text legibility
- Bug fixes: X/Y badge color leak (turned nearby text white),
  badge vertical misalignment (wrong text reference point),
  guessed spacing constants replaced with font-measured line heights
- New badge.png asset for button badges
- Build fixes: font_texture.h static (multiple-def), STBI_NO_THREAD_LOCALS

Submodule third_party/moonlight-common-c pinned at 046c231b (untouched).
@Delta062
Delta062 force-pushed the feature/on-screen-config-and-reconnect branch from 0ad4518 to 7007992 Compare August 26, 2026 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant