feat: Add WebAssembly browser port + fix Ctrl+Alt+F8 fast mode on all platforms - #159
feat: Add WebAssembly browser port + fix Ctrl+Alt+F8 fast mode on all platforms#159anomixer wants to merge 11 commits into
Conversation
…ent support Squashed commits for Emscripten features and fixes.
|
Seeing Clemens on the browser looks great. NOTE: I understand this was developed with AI based on the included Agent.md plus some other markers. It would be good to know to what extent this is agent-driven vs human encouraged with assistance from Claude/Gemini/etc. I'm not wholly against AI assisted PRs if they are beneficial to the project (Emscripten support is an example.) There are a few issues with the PR though that I'll have to resolve before merging:
|
- Restore original build-linux.yml, build-macos.yml, build-windows.yml
from upstream (samkusin/clemens_iigs) unchanged. The Emscripten
deploy workflow (deploy-emscripten.yml) is a separate addition that
does not conflict with the existing CI pipelines.
- Remove committed rom/rom.v3 (Apple IIgs ROM 3). The ROM is copyright
Apple Computer, Inc. and should not be distributed in the repository.
Users must supply their own ROM file.
- Add CLEMENS_ROM_PATH CMake cache variable (host/CMakeLists.txt) so
users can specify a custom ROM path at configure time:
cmake ... -DCLEMENS_ROM_PATH=/path/to/rom.v3
Default path remains rom/rom.v3 (relative to project root).
CMake now fails with a clear error message if the file is missing.
- Update build_emscripten.bat to check for ROM existence before
building and print actionable instructions if it is not found.
Also supports CLEMENS_ROM_PATH environment variable override.
- Update deploy-emscripten.yml to restore ROM at build time from
a base64-encoded GitHub Secret (IIGS_ROM_V3_BASE64) so CI/CD
can build the web demo without committing the ROM to the repo.
- Update .gitignore to exclude the entire rom/ directory.
GitHub Secrets have a 65,536 char limit. The Apple IIgs ROM (256KB) encodes to ~349KB in base64, so a single secret is too large. - Add split_rom_secret.ps1 to the project root. Run it once to split the ROM into 6 base64 chunks (each ~60KB). The output txt files are gitignored and will not be committed. - Update deploy-emscripten.yml to concatenate IIGS_ROM_V3_BASE64_1 through IIGS_ROM_V3_BASE64_6 and pipe them into base64 -d to reconstruct rom/rom.v3 before the CMake build step. - Update .gitignore to exclude rom_secret_part*.txt output files.
…edDB cache
Instead of embedding the ROM at build time (which requires users to
have the file locally before building, and caused the GitHub Secret
size issue), the ROM is now loaded entirely at runtime in the browser.
How it works:
1. At startup, the emulator enters a WaitForRom state (Emscripten only).
2. clem_host_platform_load_rom() is called once, which runs async JS:
- Checks IndexedDB ('ClemensIIGS' db, 'assets' store) for a cached ROM.
- If found: loads it directly into MEMFS at /rom.v3 and signals ready.
- If not found: shows a styled overlay with a 'Select ROM file' button.
3. When the user picks a file, it is validated (must be 262144 bytes),
written to MEMFS, and saved to IndexedDB for future sessions.
4. Once ROM state is 'ready' (state=2), startup continues normally.
5. On subsequent page loads the ROM is restored from IndexedDB silently.
Changes:
- host/platform/host_emscripten.c: Add ROM state machine, EM_JS loader
with IndexedDB persistence and styled file picker overlay.
Exports clemens_emscripten_rom_ready/error as EMSCRIPTEN_KEEPALIVE
for JS-to-C callbacks via Module.ccall.
- host/clem_host_platform.h: Declare clem_host_platform_load_rom() and
clem_host_platform_rom_state() under CLEMENS_PLATFORM_EMSCRIPTEN.
- host/clem_startup_view.hpp/.cpp: Add WaitForRom mode between Initial
and Preamble (Emscripten only, guarded by #ifdef __EMSCRIPTEN__).
Other platforms are completely unaffected.
- host/CMakeLists.txt: Remove --preload-file ROM line and CLEMENS_ROM_PATH
variable. No ROM needed at build time.
- .github/workflows/deploy-emscripten.yml: Remove ROM secret handling;
build is now self-contained with no external dependencies.
- build_emscripten.bat: Remove ROM pre-check; add note about first-launch
ROM selection.
- agent.md: rewritten to document all three tasks (disk selection, shortcut fix, PR feedback / runtime ROM loading). Includes full technical decision table and modified file list. - README.md: update web demo description to explain ROM selection on first launch and IndexedDB persistence. Add step 4 to Emscripten build instructions noting the first-launch ROM prompt.
…ve split_rom_secret.ps1 The previous replace_file_content edit only replaced the beginning of host_emscripten.c, leaving the entire original content duplicated from line 297 onward. This caused 20+ 'redefinition of ...' errors during the Emscripten build (GitHub Actions Deploy Emscripten samkusin#14 failure). Fixed by removing the duplicated block (dangling EM_JS body + redefined C functions for clem_host_platform_select_disk, clemens_emscripten_mount_disk, get_process_executable_path, get_local_user_directory, get_local_user_data_directory, get_local_user_config_directory, and others). Also removes split_rom_secret.ps1 (no longer needed since the ROM is loaded at runtime; no secrets splitting required).
After the user selects rom.v3 via the browser file picker and writeRomToMemfs() completes, the config_.romFilename was still empty. This caused the Main view to show the Settings/Power-On screen asking the user to locate the ROM again, instead of booting the Apple IIgs. Fix: when romState == 2 (ROM ready in MEMFS at /rom.v3), set config_.romFilename = 'rom.v3' before transitioning to Mode::Preamble. This is the relative path that the emulator uses to read the ROM.
The #ifdef __EMSCRIPTEN__ / #endif block for WaitForRom swallowed the 'case Mode::Preamble:' label that was originally placed immediately after it. This caused the Preamble code block to be dead code - it was never reached regardless of mode_, leaving the startup view stuck with no ImGui content (rendering just the clear-color blue background). Fix: add 'case Mode::Preamble:' label between #endif and the preamble body so the switch dispatches correctly to both WaitForRom (Emscripten) and Preamble (all platforms).
Hi Sam, thanks for the detailed feedback and for taking time to review! 1. AI Usage TransparencyThis PR was developed with substantial AI assistance (Antigravity / Gemini Pro + Claude), 2. Restored Original CI WorkflowsThe existing 3. ROM Copyright — Now Fully Runtime-LoadedThe rom/rom.v3 file has been removed from the repository entirely (
Now the merge is ok to go! Thanks again! |
|
One more note on the CI checks — all workflows on your end are showing To run them, go to your Actions tab Once approved they should all pass. 🙂 |
feat: Add WebAssembly browser port + fix Ctrl+Alt+F8 fast mode on all platforms
Hi @samkusin,
I've been working on a WebAssembly/Emscripten port of Clemens IIGS that lets the emulator run directly in a browser — no installation needed. Along the way, I also found and fixed the
Ctrl+Alt+F8Fast Mode shortcut, which was broken on all platforms (not just web).WASM Demo here: https://anomixer.github.io/clemens_iigs/
What's included
🌐 WebAssembly / Emscripten Browser Port
clem_host_platform_select_diskandclemens_host_import_diskto support disk image loading from the browser.EM_JSto embed a JavaScript file picker that reads a user-selected disk image into Emscripten's MEMFS and passes it back to C++ viaModule.ccall.clemens_emscripten_mount_disk(exported viaEMSCRIPTEN_KEEPALIVE) to receive the file path from JS and mount it in the emulator.get_local_user_directoryreturn a valid VFS path (/home/web_user).host/CMakeLists.txtto exportccallandUTF8ToStringruntime methods, preventing runtime aborts when JS calls into C++.clem_front.cppto conditionally trigger disk selection via the platform API when disk icons are clicked in Emscripten builds.build_emscripten.bat— a one-step build script that automatically downloads the Emscripten SDK and Ninja, then compiles the project.⌨️ Ctrl+Alt+F8 Fast Mode Fix (affects all platforms)
The original code in
clem_front.cpphard-codesImGuiKey_LeftAlt:This means Right Alt + Ctrl + F8 does nothing on Windows, Linux, and macOS — and on the web, even Left Alt detection is unreliable due to how Sokol maps modifier keys.
The fix replaces these with
ImGui::GetIO().KeyAltandImGui::GetIO().KeyCtrl, which correctly detect either Left or Right modifier keys on all platforms. I also removed the alternative number key bindings (8,5,0,-) to avoid conflicts with emulated Apple IIGS software.Final shortcut layout:
Ctrl + Alt + F8F5F10F11Ctrl + LAlt + RAlt + F1Files Changed
host/clem_front.cpphost/clem_front.hpphost/clem_host.hpphost/clem_host_app.cpphost/clem_host_platform.hhost/platform/host_emscripten.chost/CMakeLists.txthost/resources/emscripten_shell.htmlbuild_emscripten.batREADME.md.gitignoreHow to Test
Web version:
.\build_emscripten.bat(Emscripten SDK downloaded automatically).\run_emscripten.bathttp://localhost:6931/clemens_iigs.htmlCtrl+Alt+F8toggles Fast Mode correctlyDesktop version:
Ctrl+Alt+F8now works with both Left and Right Alt/Ctrl keys