diff --git a/Makefile.emscripten b/Makefile.emscripten index 06e52ce24779..22118d8baf8b 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -102,9 +102,9 @@ OBJDIR := obj-emscripten EXPORTED_FUNCTIONS = _main,_malloc,_free,_cmd_savefiles,_cmd_save_state,_cmd_load_state,_cmd_undo_save_state,_cmd_undo_load_state,_cmd_take_screenshot,\ _cmd_toggle_menu,_cmd_reload_config,_cmd_toggle_grab_mouse,_cmd_toggle_game_focus,_cmd_reset,_cmd_toggle_pause,_cmd_pause,_cmd_unpause,\ _cmd_set_volume,_cmd_set_shader,_cmd_cheat_set_code,_cmd_cheat_get_code,_cmd_cheat_toggle_index,_cmd_cheat_get_code_state,_cmd_cheat_realloc,\ -_cmd_cheat_get_size,_cmd_cheat_apply_cheats +_cmd_cheat_get_size,_cmd_cheat_apply_cheats,EmscriptenSendCommand,EmscriptenReceiveCommandReply -EXPORTS := callMain,FS,PATH,ERRNO_CODES,ENV,stringToNewUTF8,UTF8ToString,Browser,GL +EXPORTS := callMain,FS,PATH,ERRNO_CODES,ENV,stringToNewUTF8,UTF8ToString,Browser,GL,EmscriptenSendCommand,EmscriptenReceiveCommandReply LIBS := -s USE_ZLIB=1 -lbrowser.js diff --git a/command.c b/command.c index 0b1a86d1032e..b4594aa64f1d 100644 --- a/command.c +++ b/command.c @@ -337,7 +337,7 @@ command_t* command_stdin_new(void) command_t *cmd; command_stdin_t *stdincmd; -#ifndef _WIN32 +#if !(defined(_WIN32) || defined(EMSCRIPTEN)) #ifdef HAVE_NETWORKING if (!socket_nonblock(STDIN_FILENO)) return NULL; @@ -363,6 +363,61 @@ command_t* command_stdin_new(void) } #endif +#if defined(EMSCRIPTEN) +void PlatformEmscriptenCommandReply(const char *, size_t); +int PlatformEmscriptenCommandRead(char **, size_t); +typedef struct +{ + char command_buf[CMD_BUF_SIZE]; +} command_emscripten_t; + +static void emscripten_command_reply(command_t *_cmd, + const char *s, size_t len) +{ + PlatformEmscriptenCommandReply(s, len); +} + +static void emscripten_command_free(command_t *handle) +{ + free(handle->userptr); + free(handle); +} + +static void command_emscripten_poll(command_t *handle) +{ + command_emscripten_t *emscriptencmd = (command_emscripten_t*)handle->userptr; + ptrdiff_t msg_len = PlatformEmscriptenCommandRead((char **)(&emscriptencmd->command_buf), CMD_BUF_SIZE); + if (msg_len == 0) + return; + command_parse_msg(handle, emscriptencmd->command_buf); +} + +command_t* command_emscripten_new(void) +{ + command_t *cmd; + command_emscripten_t *emscriptencmd; + + cmd = (command_t*)calloc(1, sizeof(command_t)); + emscriptencmd = (command_emscripten_t*)calloc(1, sizeof(command_emscripten_t)); + + if (!cmd) + return NULL; + if (!emscriptencmd) + { + free(cmd); + return NULL; + } + cmd->userptr = emscriptencmd; + cmd->poll = command_emscripten_poll; + cmd->replier = emscripten_command_reply; + cmd->destroy = emscripten_command_free; + + return cmd; +} +#endif + + + bool command_get_config_param(command_t *cmd, const char* arg) { size_t _len; diff --git a/command.h b/command.h index 8f869adea79c..d1aa67651ae8 100644 --- a/command.h +++ b/command.h @@ -329,11 +329,20 @@ struct rarch_state; bool command_event(enum event_command action, void *data); /* Constructors for the supported drivers */ +#ifdef HAVE_NETWORK_CMD command_t* command_network_new(uint16_t port); +bool command_network_send(const char *cmd_); +#endif +#ifdef HAVE_STDIN_CMD command_t* command_stdin_new(void); +#endif +#ifdef LAKKA command_t* command_uds_new(void); +#endif +#ifdef EMSCRIPTEN +command_t* command_emscripten_new(void); +#endif -bool command_network_send(const char *cmd_); void command_event_set_mixer_volume( settings_t *settings, diff --git a/emscripten/library_platform_emscripten.js b/emscripten/library_platform_emscripten.js index f97c75c4f3fe..a2d71057d3c6 100644 --- a/emscripten/library_platform_emscripten.js +++ b/emscripten/library_platform_emscripten.js @@ -12,7 +12,9 @@ var LibraryPlatformEmscripten = { RPE.powerState.dischargeTime = Number.isFinite(e.target.dischargingTime) ? e.target.dischargingTime : 0x7FFFFFFF; RPE.powerState.level = e.target.level; RPE.powerState.charging = e.target.charging; - } + }, + command_queue:[], + command_reply_queue:[], }, PlatformEmscriptenPowerStateInit: function() { @@ -49,6 +51,15 @@ var LibraryPlatformEmscripten = { PlatformEmscriptenGetFreeMem: function() { if (!performance.memory) return 0; return (performance.memory.jsHeapSizeLimit || 0) - (performance.memory.usedJSHeapSize || 0); + }, + + $EmscriptenSendCommand__deps:["PlatformEmscriptenCommandRaiseFlag"], + $EmscriptenSendCommand: function(str) { + RPE.command_queue.push(str); + _PlatformEmscriptenCommandRaiseFlag(); + }, + $EmscriptenReceiveCommandReply: function() { + return RPE.command_reply_queue.shift(); } }; diff --git a/frontend/drivers/platform_emscripten.c b/frontend/drivers/platform_emscripten.c index 7a5710ca2fdc..8463ff891be3 100644 --- a/frontend/drivers/platform_emscripten.c +++ b/frontend/drivers/platform_emscripten.c @@ -91,6 +91,33 @@ bool PlatformEmscriptenPowerStateGetCharging(void); uint64_t PlatformEmscriptenGetTotalMem(void); uint64_t PlatformEmscriptenGetFreeMem(void); +void PlatformEmscriptenCommandReply(const char *msg, size_t len) { + MAIN_THREAD_EM_ASM({ + var message = UTF8ToString($0,$1); + RPE.command_reply_queue.push(message); + }, msg, len); +} +static bool command_flag = false; +size_t PlatformEmscriptenCommandRead(char **into, size_t max_len) { + if(!command_flag) { return 0; } + return MAIN_THREAD_EM_ASM_INT({ + var next_command = RPE.command_queue.shift(); + var length = lengthBytesUTF8(next_command); + if(length > $2) { + console.error("[CMD] Command too long, skipping",next_command); + return 0; + } + stringToUTF8(next_command, $1, $2); + if(RPE.command_queue.length == 0) { + setValue($0, 0, 'i8'); + } + return length; + }, &command_flag, into, max_len); +} +void PlatformEmscriptenCommandRaiseFlag() { + command_flag = true; +} + /* begin exported functions */ /* saves and states */ @@ -350,32 +377,46 @@ void PlatformEmscriptenMountFilesystems(void *info) { */ int max_line_len = 1024; printf("[FetchFS] read fetch manifest from %s\n",fetch_manifest); - FILE *file = fopen(fetch_manifest, O_RDONLY); + FILE *file = fopen(fetch_manifest, "r"); + if(!file) { + printf("[FetchFS] missing manifest file\n"); + abort(); + } char *line = calloc(sizeof(char), max_line_len); - size_t len = 0; + size_t len = max_line_len; while (getline(&line, &len, file) != -1) { char *path = strstr(line, " "); backend_t fetch; int fd; - if (!path) { - printf("Manifest file has invalid line %s\n",line); - return; + if(len <= 2 || !path) { + printf("[FetchFS] Manifest file has invalid line %s\n",line); + continue; } *path = '\0'; path += 1; - printf("Fetch %s from %s\n", path, line); + path[strcspn(path, "\r\n")] = '\0'; + printf("[FetchFS] Fetch %s from %s\n", path, line); { char *parent = strdup(path); path_parent_dir(parent, strlen(parent)); if(!path_mkdir(parent)) { - printf("mkdir error %d\n",errno); + printf("[FetchFS] mkdir error %d\n",errno); abort(); } free(parent); } - fetch = wasmfs_create_fetch_backend(line, 8*1024*1024); + fetch = wasmfs_create_fetch_backend(line, 16*1024*1024); + if(!fetch) { + printf("[FetchFS] couldn't create fetch backend\n"); + abort(); + } fd = wasmfs_create_file(path, 0777, fetch); + if(!fd) { + printf("[FetchFS] couldn't create fetch file\n"); + abort(); + } close(fd); + len = max_line_len; } fclose(file); free(line); @@ -433,7 +474,9 @@ void emscripten_bootup_mainloop(void *argptr) { int main(int argc, char *argv[]) { args_t *args = calloc(sizeof(args_t), 1); - + args->argc = argc; + args->argv = argv; + PlatformEmscriptenWatchCanvasSize(); PlatformEmscriptenPowerStateInit(); diff --git a/input/input_driver.c b/input/input_driver.c index e69f894a567c..974feb715fe5 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -5104,10 +5104,14 @@ void input_driver_init_command(input_driver_state_t *input_st, } #endif -#ifdef HAVE_LAKKA +#if defined(HAVE_LAKKA) if (!(input_st->command[2] = command_uds_new())) RARCH_ERR("Failed to initialize the UDS command interface.\n"); +#elif defined(EMSCRIPTEN) + if (!(input_st->command[2] = command_emscripten_new())) + RARCH_ERR("Failed to initialize the emscripten command interface.\n"); #endif + } void input_driver_deinit_command(input_driver_state_t *input_st) diff --git a/pkg/emscripten/libretro-thread/libretro.js b/pkg/emscripten/libretro-thread/libretro.js index 8d0a3e874079..e5cb6d0e1782 100644 --- a/pkg/emscripten/libretro-thread/libretro.js +++ b/pkg/emscripten/libretro-thread/libretro.js @@ -14,64 +14,15 @@ var Module = { noImageDecoding: true, noAudioDecoding: true, - encoder: new TextEncoder(), - message_queue: [], - message_out: [], - message_accum: "", - - retroArchSend: function(msg) { - let bytes = this.encoder.encode(msg + "\n"); - this.message_queue.push([bytes, 0]); - }, - retroArchRecv: function() { - let out = this.message_out.shift(); - if (out == null && this.message_accum != "") { - out = this.message_accum; - this.message_accum = ""; - } - return out; - }, + retroArchSend: function(msg) { + this.EmscriptenSendCommand(msg); + }, + retroArchRecv: function() { + return this.EmscriptenReceiveCommandReply(); + }, preRun: [ function(module) { Module.ENV['OPFS'] = "/home/web_user/retroarch"; - }, - function(module) { - function stdin() { - // Return ASCII code of character, or null if no input - while (module.message_queue.length > 0) { - var msg = module.message_queue[0][0]; - var index = module.message_queue[0][1]; - if (index >= msg.length) { - module.message_queue.shift(); - } else { - module.message_queue[0][1] = index + 1; - // assumption: msg is a uint8array - return msg[index]; - } - } - return null; - } - - function stdout(c) { - if (c == null) { - // flush - if (module.message_accum != "") { - module.message_out.push(module.message_accum); - module.message_accum = ""; - } - } else { - let s = String.fromCharCode(c); - if (s == "\n") { - if (module.message_accum != "") { - module.message_out.push(module.message_accum); - module.message_accum = ""; - } - } else { - module.message_accum = module.message_accum + s; - } - } - } - module.FS.init(stdin, stdout); } ], postRun: [], diff --git a/pkg/emscripten/libretro-thread/libretro.worker.js b/pkg/emscripten/libretro-thread/libretro.worker.js index cbb24c6862ec..f7d5954a4333 100644 --- a/pkg/emscripten/libretro-thread/libretro.worker.js +++ b/pkg/emscripten/libretro-thread/libretro.worker.js @@ -60,7 +60,7 @@ onmessage = async (msg) => { } postMessage({command:"loaded_bundle", time:resp.headers.get("last-modified")}); } else if(msg.data.command == "upload_file") { - await writeFile("/home/web_user/retroarch/userdata/content/"+msg.data.name, new Uint8Array(msg.data.data)); + await writeFile("userdata/content/"+msg.data.name, new Uint8Array(msg.data.data)); postMessage({command:"uploaded_file",name:msg.data.name}); } }