The in-app shutdown visual didn't work because SDL cleanup wiped the
framebuffer. Instead of hacking around that, move the shutdown display
to the device wrapper where it belongs. The wrapper now decodes the
stock firmware's goodbye.png with Python3+PIL and writes raw BGRA
pixels directly to /dev/fb0 before calling poweroff.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sdlamp2 now catches SIGTERM and SIGINT via a sig_atomic_t flag checked
in the main loop, ensuring position and volume are saved before exit.
Previously, a kill signal would terminate instantly without saving.
New tools/rg35xx-wrapper.sh replaces sdlamp2 as the dmenu_ln CMD on
the RG35XX Plus. Skeleton includes placeholders for WiFi hotspot and
power button monitoring (TBD after on-device investigation).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move device-specific content (hardware, software, libraries) from
TOOLCHAIN.md into new docs/rg35xx-plus.md. Add partition layout,
full boot chain trace (systemd → launcher.sh → loadapp.sh → dmenu_ln),
and sdlamp2 deployment instructions via dmenu_ln config toggle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Docker build target now runs `make clean all` instead of bare
`make`, so it rebuilds regardless of an existing build/sdlamp2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge the separate Stop and Play buttons into one slot that toggles
between play/stop and shows the corresponding icon. Use the freed
slot for a Previous Cassette button, mirroring Next Cassette.
Layout: [Volume] [Prev] [Rewind] [Play/Stop] [FF] [Next]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Player was autoplaying on startup and on file switch. Now switch_file()
preserves the current paused state so the app launches paused and
"next tape" only continues playing if already playing. Joystick name
printf moved behind --debug flag.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Buildroot toolchain references with the new docker-arm64 setup
in CLAUDE.md, TOOLCHAIN.md, and the changelog.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The docker/ directory contained a 32-bit ARM Buildroot toolchain that
didn't match the target device (aarch64 Ubuntu 22.04). Replaced by the
arm64 Docker container in docker-arm64/.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uses ubuntu:22.04 arm64 image running under QEMU emulation to produce
aarch64 binaries that match the target device exactly (same glibc, same
distro). Replaces the broken Buildroot cross-compilation approach.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a Target Device section to TOOLCHAIN.md with specs gathered over
SSH from the Anbernic RG35XX Plus. Confirms the device runs Ubuntu 22.04
with all required shared libraries (SDL2, FFmpeg) pre-installed, making
the Buildroot toolchain unnecessary. Updates Possible Approaches to
reflect that native arm64 container builds are confirmed working.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Docker/Buildroot toolchain produces 32-bit ARM binaries but the
RG35XX target runs a 64-bit aarch64 userland. Add docs/TOOLCHAIN.md
covering the full pipeline, config details, patches, and three possible
approaches to fix the architecture mismatch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Single Makefile supports native builds (pkg-config) and cross-compilation
(CROSS_COMPILE/PREFIX env vars). Fixes -Wformat-truncation and
-Wstringop-truncation warnings at -O2 by sizing current_file to match
audio_files (256) and replacing strncpy with snprintf.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Open the joystick via SDL_JoystickOpen() when no GameController mapping
exists, fixing d-pad and face button input on devices like the Anbernic
retro handheld (GUID not in SDL's database). Handles JOYHATMOTION for
d-pad navigation and JOYBUTTONDOWN button 0 for activation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The spritesheet is compiled in as a C byte array. An external
controls.png in cwd still takes precedence for skinning. Includes
tools/embed_png.py to regenerate the header if the asset changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Logs joystick enumeration at startup (name, GameController status, GUID)
and all SDL input events in the main loop to help diagnose why the retro
handheld's d-pad/buttons aren't recognized by the GameController API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The m4a muxer doesn't support mjpeg as a regular video stream, causing
concatenation to fail when inputs contain album art. Extract art separately
and re-attach it with attached_pic disposition. Also strip leading track
numbers (e.g. "01 ") from chapter titles.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shell script to recombine individual story m4a files back into
per-cassette files using ffmpeg concat demuxer (stream copy, no
re-encoding). Generates chapter markers from input filenames and
preserves album art.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace mouse input with cursor-based navigation (arrow keys / d-pad +
Enter / A button) and add app-level volume control with a persistent
vertical slider, enabling use on a handheld gaming device without mouse
or system mixer access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace fire-and-forget decode_audio() with a streaming Decoder that uses
libswresample to convert planar float to interleaved stereo, fixing the
sped-up audio bug and eliminating multi-GB memory usage for long files.
Add 10-second rewind/fast-forward, stop (pause in place), position
persistence per file via positions.txt, directory scanning with file
switching, embedded album art display, and a progress bar. Handles both
old and new FFmpeg channel layout APIs via version preprocessor check.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>