Files
rspektrum/src/platform_web.c
T
tyler cef7619833 feat(web): proper viewport scaling, instant load, and file downloads
Make the Emscripten build behave like the desktop app for loading, window
scaling, and exporting. All changes are #ifdef __EMSCRIPTEN__-gated or no-op on
desktop, so native behavior is unchanged.

Scaling:
- Drop FLAG_WINDOW_HIGHDPI on web. The emscripten-GLFW shim forces a fixed
  pixel canvas style (!important) when HiDPI-aware, overriding the shell's
  100vw/100vh CSS so the canvas can't fill the page; raylib's own resize/window
  callbacks also disagree about dividing by devicePixelRatio, desyncing the
  framebuffer from the reported screen size.
- Sync raylib's window size to window.innerWidth/innerHeight each frame via
  SetWindowSize (guarded against no-op churn). This keeps screen size, GL
  viewport, and projection consistent, so the UI fills the viewport and reflows
  on resize like the desktop window.

Loading:
- Compute the full-resolution STFT synchronously when a file loads instead of
  the desktop overview-then-deferred-high-res path, which relied on many
  main-loop iterations yielding to the browser and appeared to stall partway.
- Allow the wasm heap to grow (INITIAL_MEMORY + ALLOW_MEMORY_GROWTH) so longer
  recordings fit now that everything is computed up front.

Exports:
- Add Platform_OfferFileToUser(): no-op on desktop (file is already on disk);
  on web it reads the just-written file from MEMFS and triggers a browser
  download, then unlinks the temp copy. Wired into PNG and WAV export, which
  now show just the filename in the status message.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 11:48:47 -07:00

96 lines
3.1 KiB
C

/*
* platform_web.c — Emscripten/WebAssembly stub implementation
*
* The web version cannot spawn child processes, so these functions
* return "not supported". A real implementation would use Emscripten's
* pthreads or asyncify if ffmpeg integration is needed.
*/
#include "platform.h"
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <errno.h> /* ENOSYS */
#include <emscripten.h> /* EM_ASM */
/* ── Helpers ─────────────────────────────────────────────────────────── */
static char last_error_msg[256] = { 0 };
static void set_last_error(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vsnprintf(last_error_msg, sizeof(last_error_msg), fmt, args);
va_end(args);
}
/* ── Platform API ────────────────────────────────────────────────────── */
PlatformError Platform_SpawnChild(const char *program, const char **argv,
PlatformSpawnHandle *out_handle) {
(void)program;
(void)argv;
set_last_error("Spawning child processes is not supported on the web");
out_handle->last_error = ENOSYS;
return PLATFORM_NOT_SUPPORTED;
}
PlatformError Platform_WaitForChild(PlatformSpawnHandle *handle) {
(void)handle;
return PLATFORM_NOT_SUPPORTED;
}
SpawnStatus Platform_GetExitStatus(const PlatformSpawnHandle *handle, int *code) {
(void)handle;
(void)code;
return SPAWN_WAITING;
}
void Platform_CloseSpawnHandle(PlatformSpawnHandle *handle) {
if (!handle) return;
handle->handle = NULL;
handle->status = SPAWN_WAITING;
handle->exit_code = 0;
handle->last_error = 0;
}
const char *Platform_GetLastErrorMessage(void) {
return last_error_msg;
}
const char *Platform_GetTempDir(void) {
/*
* Emscripten's virtual filesystem: "/" works as root.
* For persistent downloads/uploads, consider IDBFS mount.
*/
return "/";
}
void Platform_OfferFileToUser(const char *path) {
if (!path) return;
/*
* The file was just written into the in-memory (MEMFS) filesystem, which
* the user can't see. Read it back and hand it to the browser as a
* download, then unlink the temporary copy so we don't leak heap memory.
*/
EM_ASM({
var p = UTF8ToString($0);
try {
var data = FS.readFile(p); // Uint8Array
var name = p.split('/').pop() || 'download';
var blob = new Blob([data], { type: 'application/octet-stream' });
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setTimeout(function() { URL.revokeObjectURL(url); }, 1000);
try { FS.unlink(p); } catch (e) {}
} catch (e) {
console.error('rspektrum: download failed for ' + p + ': ' + e);
}
}, path);
}