Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- CLOUDSYNC: Enable WebDAV support for Android
- CLOUDSYNC: Speed up cloudsync on Apple
- DATABASE: Improve multidisk game scanning
- EMSCRIPTEN: Support core switching
- EMSCRIPTEN: Support suspend screensaver
- EMSCRIPTEN/RWEBCAM: Fix camera driver
- EMSCRIPTEN/RWEBINPUT: Add accelerometer/gyroscope support
Expand Down
2 changes: 1 addition & 1 deletion Makefile.emscripten
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ ifeq ($(HAVE_SDL2), 1)
endif

LDFLAGS := -L. --no-heap-copy -s STACK_SIZE=$(STACK_SIZE) -s INITIAL_MEMORY=$(INITIAL_HEAP) \
-s EXPORTED_RUNTIME_METHODS=$(EXPORTS) \
-s EXPORTED_RUNTIME_METHODS=$(EXPORTS) -s EXIT_RUNTIME=1 \
-s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS="$(EXPORTED_FUNCTIONS)" \
-s MODULARIZE=1 -s EXPORT_ES6=1 -s EXPORT_NAME="libretro_$(subst -,_,$(LIBRETRO))" \
-s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 \
Expand Down
13 changes: 13 additions & 0 deletions audio/drivers/audioworklet.c
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,19 @@ bool audioworklet_external_block(void)
}
#endif

/* called on program exit */
void audioworklet_close(void)
{
audioworklet_data_t *audioworklet = audioworklet_static_data;

if (!audioworklet)
return;

MAIN_THREAD_EM_ASM({
emscriptenGetAudioObject($0).close();
}, audioworklet->context);
}

static bool audioworklet_stop(void *data)
{
audioworklet_data_t *audioworklet = (audioworklet_data_t*)data;
Expand Down
122 changes: 94 additions & 28 deletions emscripten/library_platform_emscripten.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,21 @@ var LibraryPlatformEmscripten = {
$RPE: {
canvasWidth: 0,
canvasHeight: 0,
battery: null,
observer: null,
memoryUsageTimeout: null,
sentinelPromise: null,
command_queue: [],
command_reply_queue: []
},

PlatformEmscriptenWatchCanvasSizeAndDpr__deps: ["platform_emscripten_update_canvas_dimensions_cb"],
$PlatformEmscriptenOnWindowResize: function() {
RPE.observer.unobserve(Module.canvas);
RPE.observer.observe(Module.canvas);
},

PlatformEmscriptenWatchCanvasSizeAndDpr__deps: ["platform_emscripten_update_canvas_dimensions_cb", "$PlatformEmscriptenOnWindowResize"],
PlatformEmscriptenWatchCanvasSizeAndDpr__proxy: "sync",
PlatformEmscriptenWatchCanvasSizeAndDpr: function(dpr) {
if (RPE.observer) {
RPE.observer.unobserve(Module.canvas);
Expand All @@ -34,39 +43,57 @@ var LibraryPlatformEmscripten = {
_platform_emscripten_update_canvas_dimensions_cb(width, height, dpr);
});
RPE.observer.observe(Module.canvas);
window.addEventListener("resize", function() {
RPE.observer.unobserve(Module.canvas);
RPE.observer.observe(Module.canvas);
}, false);
window.addEventListener("resize", PlatformEmscriptenOnWindowResize, false);
},

$PlatformEmscriptenOnCanvasPointerDown: function() {
Module.canvas.focus();
},

PlatformEmscriptenWatchWindowVisibility__deps: ["platform_emscripten_update_window_hidden_cb"],
$PlatformEmscriptenOnCanvasContextMenu: function(e) {
e.preventDefault();
},

PlatformEmscriptenCanvasListenersInit__deps: ["$PlatformEmscriptenOnCanvasPointerDown", "$PlatformEmscriptenOnCanvasContextMenu"],
PlatformEmscriptenCanvasListenersInit__proxy: "sync",
PlatformEmscriptenCanvasListenersInit: function() {
Module.canvas.addEventListener("pointerdown", PlatformEmscriptenOnCanvasPointerDown, false);
Module.canvas.addEventListener("contextmenu", PlatformEmscriptenOnCanvasContextMenu, false);
},

$PlatformEmscriptenOnVisibilityChange__deps: ["platform_emscripten_update_window_hidden_cb"],
$PlatformEmscriptenOnVisibilityChange: function() {
_platform_emscripten_update_window_hidden_cb(document.visibilityState == "hidden");
},

PlatformEmscriptenWatchWindowVisibility__deps: ["$PlatformEmscriptenOnVisibilityChange"],
PlatformEmscriptenWatchWindowVisibility__proxy: "sync",
PlatformEmscriptenWatchWindowVisibility: function() {
document.addEventListener("visibilitychange", function() {
_platform_emscripten_update_window_hidden_cb(document.visibilityState == "hidden");
}, false);
document.addEventListener("visibilitychange", PlatformEmscriptenOnVisibilityChange, false);
},

$PlatformEmscriptenPowerStateChange__deps: ["platform_emscripten_update_power_state_cb"],
$PlatformEmscriptenPowerStateChange: function(e) {
$PlatformEmscriptenOnPowerStateChange__deps: ["platform_emscripten_update_power_state_cb"],
$PlatformEmscriptenOnPowerStateChange: function(e) {
_platform_emscripten_update_power_state_cb(true, Number.isFinite(e.target.dischargingTime) ? e.target.dischargingTime : 0x7FFFFFFF, e.target.level, e.target.charging);
},

PlatformEmscriptenPowerStateInit__deps: ["$PlatformEmscriptenPowerStateChange"],
PlatformEmscriptenPowerStateInit__deps: ["$PlatformEmscriptenOnPowerStateChange"],
PlatformEmscriptenPowerStateInit__proxy: "sync",
PlatformEmscriptenPowerStateInit: function() {
if (!navigator.getBattery) return;
navigator.getBattery().then(function(battery) {
battery.addEventListener("chargingchange", PlatformEmscriptenPowerStateChange);
battery.addEventListener("levelchange", PlatformEmscriptenPowerStateChange);
PlatformEmscriptenPowerStateChange({target: battery});
RPE.battery = battery;
battery.addEventListener("chargingchange", PlatformEmscriptenOnPowerStateChange);
battery.addEventListener("levelchange", PlatformEmscriptenOnPowerStateChange);
PlatformEmscriptenOnPowerStateChange({target: battery});
});
},

$PlatformEmscriptenUpdateMemoryUsage__deps: ["platform_emscripten_update_memory_usage_cb"],
$PlatformEmscriptenUpdateMemoryUsage: function() {
// unfortunately this will be inaccurate in threaded (worker) builds
_platform_emscripten_update_memory_usage_cb(BigInt(performance.memory.usedJSHeapSize || 0), BigInt(performance.memory.jsHeapSizeLimit || 0));
setTimeout(PlatformEmscriptenUpdateMemoryUsage, 5000);
RPE.memoryUsageTimeout = setTimeout(PlatformEmscriptenUpdateMemoryUsage, 5000);
},

PlatformEmscriptenMemoryUsageInit__deps: ["$PlatformEmscriptenUpdateMemoryUsage"],
Expand All @@ -75,22 +102,33 @@ var LibraryPlatformEmscripten = {
PlatformEmscriptenUpdateMemoryUsage();
},

PlatformEmscriptenWatchFullscreen__deps: ["platform_emscripten_update_fullscreen_state_cb"],
$PlatformEmscriptenOnFullscreenChange__deps: ["platform_emscripten_update_fullscreen_state_cb"],
$PlatformEmscriptenOnFullscreenChange: function() {
_platform_emscripten_update_fullscreen_state_cb(!!document.fullscreenElement);
},

PlatformEmscriptenWatchFullscreen__deps: ["$PlatformEmscriptenOnFullscreenChange"],
PlatformEmscriptenWatchFullscreen__proxy: "sync",
PlatformEmscriptenWatchFullscreen: function() {
document.addEventListener("fullscreenchange", function() {
_platform_emscripten_update_fullscreen_state_cb(!!document.fullscreenElement);
}, false);
document.addEventListener("fullscreenchange", PlatformEmscriptenOnFullscreenChange, false);
},

PlatformEmscriptenGLContextEventInit__deps: ["platform_emscripten_gl_context_lost_cb", "platform_emscripten_gl_context_restored_cb"],
$PlatformEmscriptenOnGLContextLost__deps: ["platform_emscripten_gl_context_lost_cb"],
$PlatformEmscriptenOnGLContextLost: function(e) {
e.preventDefault();
_platform_emscripten_gl_context_lost_cb();
},

$PlatformEmscriptenOnGLContextRestored__deps: ["platform_emscripten_gl_context_restored_cb"],
$PlatformEmscriptenOnGLContextRestored: function() {
_platform_emscripten_gl_context_restored_cb();
},

PlatformEmscriptenGLContextEventInit__deps: ["$PlatformEmscriptenOnGLContextLost", "$PlatformEmscriptenOnGLContextRestored"],
PlatformEmscriptenGLContextEventInit__proxy: "sync",
PlatformEmscriptenGLContextEventInit: function() {
Module.canvas.addEventListener("webglcontextlost", function(e) {
e.preventDefault();
_platform_emscripten_gl_context_lost_cb();
});
Module.canvas.addEventListener("webglcontextrestored", function() {
_platform_emscripten_gl_context_restored_cb();
});
Module.canvas.addEventListener("webglcontextlost", PlatformEmscriptenOnGLContextLost);
Module.canvas.addEventListener("webglcontextrestored", PlatformEmscriptenOnGLContextRestored);
},

$PlatformEmscriptenDoSetCanvasSize: async function(width, height) {
Expand Down Expand Up @@ -156,6 +194,34 @@ var LibraryPlatformEmscripten = {

$EmscriptenReceiveCommandReply: function() {
return RPE.command_reply_queue.shift();
},

$PlatformEmscriptenFreeBrowser__proxy: "sync",
$PlatformEmscriptenFreeBrowser__deps: ["$PlatformEmscriptenDoSetWakeLock", "$PlatformEmscriptenOnCanvasPointerDown", "$PlatformEmscriptenOnCanvasContextMenu", "$PlatformEmscriptenOnWindowResize", "$PlatformEmscriptenOnVisibilityChange", "$PlatformEmscriptenOnFullscreenChange", "$PlatformEmscriptenOnPowerStateChange"],
$PlatformEmscriptenFreeBrowser: function() {
if (RPE.memoryUsageTimeout) clearTimeout(RPE.memoryUsageTimeout);
PlatformEmscriptenDoSetWakeLock(false);
if (RPE.observer) {
RPE.observer.unobserve(Module.canvas);
RPE.observer = null;
}
Module.canvas.removeEventListener("pointerdown", PlatformEmscriptenOnCanvasPointerDown);
Module.canvas.removeEventListener("contextmenu", PlatformEmscriptenOnCanvasContextMenu);
window.removeEventListener("resize", PlatformEmscriptenOnWindowResize);
document.removeEventListener("visibilitychange", PlatformEmscriptenOnVisibilityChange);
document.removeEventListener("fullscreenchange", PlatformEmscriptenOnFullscreenChange);
if (RPE.battery) {
RPE.battery.removeEventListener("chargingchange", PlatformEmscriptenOnPowerStateChange);
RPE.battery.removeEventListener("levelchange", PlatformEmscriptenOnPowerStateChange);
RPE.battery = null;
}
},

PlatformEmscriptenFree__deps: ["$PlatformEmscriptenFreeBrowser"],
PlatformEmscriptenFree: function() {
Module.canvas.removeEventListener("webglcontextlost", PlatformEmscriptenOnGLContextLost);
Module.canvas.removeEventListener("webglcontextrestored", PlatformEmscriptenOnGLContextRestored);
PlatformEmscriptenFreeBrowser();
}
};

Expand Down
74 changes: 59 additions & 15 deletions frontend/drivers/platform_emscripten.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ void emscripten_mainloop(void);

/* javascript library functions */
void PlatformEmscriptenWatchCanvasSizeAndDpr(double *dpr);
void PlatformEmscriptenCanvasListenersInit(void);
void PlatformEmscriptenWatchWindowVisibility(void);
void PlatformEmscriptenPowerStateInit(void);
void PlatformEmscriptenMemoryUsageInit(void);
Expand All @@ -84,6 +85,7 @@ void PlatformEmscriptenGLContextEventInit(void);
void PlatformEmscriptenSetCanvasSize(int width, int height);
void PlatformEmscriptenSetWakeLock(bool state);
uint32_t PlatformEmscriptenGetSystemInfo(void);
void PlatformEmscriptenFree(void);

typedef struct
{
Expand All @@ -95,8 +97,10 @@ typedef struct
uint64_t memory_used;
uint64_t memory_limit;
double device_pixel_ratio;
double device_pixel_ratio_temp;
enum platform_emscripten_browser browser;
enum platform_emscripten_os os;
enum frontend_fork fork_mode;
int raf_interval;
int canvas_width;
int canvas_height;
Expand Down Expand Up @@ -383,7 +387,7 @@ size_t platform_emscripten_command_read(char **into, size_t max_len)
var next_command = RPE.command_queue.shift();
var length = lengthBytesUTF8(next_command);
if (length > $2) {
console.error("[CMD] Command too long, skipping", next_command);
err("[CMD] Command too long, skipping", next_command);
return 0;
}
stringToUTF8(next_command, $1, $2);
Expand Down Expand Up @@ -552,6 +556,8 @@ static void frontend_emscripten_get_env(int *argc, char *argv[],
char bundle_path[PATH_MAX];
const char *home = getenv("HOME");

path_set(RARCH_PATH_CORE, getenv("LIBRARY_PATH"));
Comment thread
BinBashBanana marked this conversation as resolved.

if (home)
{
size_t _len = strlcpy(base_path, home, sizeof(base_path));
Expand Down Expand Up @@ -686,6 +692,51 @@ static uint64_t frontend_emscripten_get_free_mem(void)
return (PLATFORM_GETVAL(u64, &emscripten_platform_data->memory_limit) - used);
}

#ifdef HAVE_AUDIOWORKLET
void audioworklet_close(void);
#endif

static void frontend_emscripten_exec_browser(void *path)
{
const char *core = emscripten_platform_data->fork_mode == FRONTEND_FORK_NONE ? 0 : path;
const char *content = emscripten_platform_data->fork_mode == FRONTEND_FORK_CORE_WITH_ARGS ? path_get(RARCH_PATH_CONTENT) : 0;

#ifdef HAVE_AUDIOWORKLET
audioworklet_close();
#endif

EM_ASM({
#ifdef PROXY_TO_PTHREAD
/* undo OffscreenCanvas */
let newCanvas = Module.canvas.cloneNode();
Module.canvas.replaceWith(newCanvas);
Module.canvas = newCanvas;
#endif
if (typeof Module.retroArchExit == "function")
setTimeout(Module.retroArchExit, 0, $0 && UTF8ToString($0), $1 && UTF8ToString($1));
else
out("[INFO] Exiting, but Module.retroArchExit was not provided");
}, core, content);
emscripten_force_exit(0);
}

static void frontend_emscripten_exec(const char *path, bool should_load_content)
{
PlatformEmscriptenFree();
platform_emscripten_run_on_browser_thread_sync(frontend_emscripten_exec_browser, (void *)path);
}

static void frontend_emscripten_exitspawn(char *s, size_t len, char *args)
{
frontend_emscripten_exec(s, false);
}

static bool frontend_emscripten_set_fork(enum frontend_fork fork_mode)
{
emscripten_platform_data->fork_mode = fork_mode;
return true;
}

/* program entry and startup */

#ifdef HAVE_EXTRA_WASMFS
Expand Down Expand Up @@ -890,14 +941,6 @@ int main(int argc, char *argv[])
if (!Module.canvas.getAttribute("tabindex"))
Module.canvas.setAttribute("tabindex", "-1");
Module.canvas.focus();
Module.canvas.addEventListener("pointerdown", function() {
Module.canvas.focus();
}, false);

/* disable browser right click menu */
Module.canvas.addEventListener("contextmenu", function(e) {
e.preventDefault();
}, false);

/* background should be black */
Module.canvas.style.backgroundColor = "#000000";
Expand All @@ -908,21 +951,22 @@ int main(int argc, char *argv[])

/* ensure canvas size is constrained by CSS, otherwise infinite resizing may occur */
if (window.getComputedStyle(Module.canvas).display == "inline") {
console.warn("[WARN] Canvas should not use display: inline!");
err("[WARN] Canvas should not use display: inline!");
Module.canvas.style.display = "inline-block";
}
var oldWidth = Module.canvas.clientWidth;
var oldHeight = Module.canvas.clientHeight;
Module.canvas.width = 64;
Module.canvas.height = 64;
if (oldWidth != Module.canvas.clientWidth || oldHeight != Module.canvas.clientHeight) {
console.warn("[WARN] Canvas size should be set using CSS properties!");
err("[WARN] Canvas size should be set using CSS properties!");
Module.canvas.style.width = oldWidth + "px";
Module.canvas.style.height = oldHeight + "px";
}
});

PlatformEmscriptenWatchCanvasSizeAndDpr(malloc(sizeof(double)));
PlatformEmscriptenWatchCanvasSizeAndDpr(&emscripten_platform_data->device_pixel_ratio_temp);
PlatformEmscriptenCanvasListenersInit();
PlatformEmscriptenWatchWindowVisibility();
PlatformEmscriptenPowerStateInit();
PlatformEmscriptenMemoryUsageInit();
Expand Down Expand Up @@ -956,10 +1000,10 @@ frontend_ctx_driver_t frontend_ctx_emscripten = {
frontend_emscripten_get_env, /* environment_get */
NULL, /* init */
NULL, /* deinit */
NULL, /* exitspawn */
frontend_emscripten_exitspawn, /* exitspawn */
NULL, /* process_args */
NULL, /* exec */
NULL, /* set_fork */
frontend_emscripten_exec, /* exec */
frontend_emscripten_set_fork, /* set_fork */
NULL, /* shutdown */
NULL, /* get_name */
NULL, /* get_os */
Expand Down
3 changes: 3 additions & 0 deletions frontend/frontend_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ size_t frontend_driver_get_core_extension(char *s, size_t len)
if (envIsHomebrew())
return strlcpy(s, "3dsx", len);
return strlcpy(s, "cia", len);
#elif defined(EMSCRIPTEN)
/* may not contain the core */
return strlcpy(s, "core", len);
#else
return 0;
#endif
Expand Down
Loading
Loading