From 25f5a73b014571c0c97199005b38e12f01903e59 Mon Sep 17 00:00:00 2001 From: BinBashBanana <51469593+BinBashBanana@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:17:45 -0800 Subject: [PATCH] emscripten2 --- CHANGES.md | 4 + Makefile.common | 3 +- Makefile.emscripten | 87 +++-- cheat_manager.c | 2 +- command.h | 1 + config.def.h | 3 + configuration.c | 1 + configuration.h | 1 + dist-scripts/dist-cores.sh | 51 ++- emscripten/library_errno_codes.js | 15 - emscripten/library_platform_emscripten.js | 78 +++++ emscripten/library_rwebcam.js | 17 +- frontend/drivers/platform_emscripten.c | 222 ++++++++++--- gfx/drivers/gl2.c | 47 ++- gfx/drivers_context/emscriptenegl_ctx.c | 96 ++---- input/drivers/rwebinput_input.c | 222 +++++++++++-- libretro-common/gfx/gl_capabilities.c | 5 +- menu/drivers/ozone.c | 18 +- pkg/emscripten/README.md | 4 +- pkg/emscripten/libretro/embed.html | 140 ++++---- pkg/emscripten/libretro/index.html | 192 ++++++----- pkg/emscripten/libretro/libretro.css | 16 +- pkg/emscripten/libretro/libretro.js | 374 ++++++++++------------ retroarch.c | 3 + 24 files changed, 995 insertions(+), 607 deletions(-) delete mode 100644 emscripten/library_errno_codes.js create mode 100644 emscripten/library_platform_emscripten.js diff --git a/CHANGES.md b/CHANGES.md index 5e9189bc7aae..5defdc04de72 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,10 @@ - MENU: Add SSL support to the information list - IOS: Ensure webserver notice can be dismissed - VIDEO/SHADERS: Allow exact refresh rate sync with shader subframes +- EMSCRIPTEN: Scale window to correct size +- EMSCRIPTEN: Additional platform functions +- EMSCRIPTEN/RWEBINPUT: Add touch input support +- VIDEO/OpenGLES: Fix FP/sRGB FBO support # 1.20.0 - AUDIO: Fix audio handling in case of RARCH_NETPLAY_CTL_USE_CORE_PACKET_INTERFACE diff --git a/Makefile.common b/Makefile.common index 01bae782ee5a..8b3923b9604d 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1570,10 +1570,11 @@ ifeq ($(HAVE_GL_CONTEXT), 1) DEF_FLAGS += $(OPENGLES_CFLAGS) ifeq ($(HAVE_OPENGLES3), 1) DEFINES += -DHAVE_OPENGLES3 + OBJ += $(LIBRETRO_COMM_DIR)/glsym/glsym_es3.o else DEFINES += -DHAVE_OPENGLES2 + OBJ += $(LIBRETRO_COMM_DIR)/glsym/glsym_es2.o endif - OBJ += $(LIBRETRO_COMM_DIR)/glsym/glsym_es2.o else DEFINES += -DHAVE_GL_SYNC OBJ += $(LIBRETRO_COMM_DIR)/glsym/glsym_gl.o diff --git a/Makefile.emscripten b/Makefile.emscripten index 55a435b563b8..ddd8fa56a17e 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -25,7 +25,6 @@ HAVE_REWIND = 1 HAVE_AUDIOMIXER = 1 HAVE_CC_RESAMPLER = 1 HAVE_EGL = 1 -HAVE_OPENGLES = 1 HAVE_RJPEG = 0 HAVE_RPNG = 1 HAVE_EMSCRIPTEN = 1 @@ -58,34 +57,49 @@ HAVE_CHD ?= 0 # You have been warned. HAVE_RWEBAUDIO = 0 -ASYNC ?= 0 -ifeq ($(LIBRETRO), mupen64plus) - ASYNC = 1 -endif +# help diagnose GL problems (can cause issues in normal operation) +GL_DEBUG ?= 0 -LTO ?= 0 -ifeq ($(LIBRETRO), tyrquake) - LTO = 0 -endif +# enable javascript filesystem tracking +FS_DEBUG = 1 -PTHREAD ?= 0 +HAVE_OPENGLES ?= 1 +HAVE_OPENGLES3 ?= 0 -MEMORY ?= 134217728 +ASYNC ?= 0 +LTO ?= 0 +PTHREAD ?= 0 -PRECISE_F32 = 1 +STACK_SIZE ?= 4194304 +INITIAL_HEAP ?= 134217728 + +# 4194304 ----- 4 MiB (Stack: recommended) +# 8388608 ----- 8 MiB +# 16777216 ---- 16 MiB +# 33554432 ---- 32 MiB +# 67108864 ---- 64 MiB +# 134217728 --- 128 MiB (Heap: recommended) (Stack: recommended for some cores [mupen64plus_next]) +# 268435456 --- 256 MiB (Heap: recommended for some cores [mupen64plus_next]) +# 536870912 --- 512 MiB (Heap: needed for some cores [mednafen_psx(_hw)]) +# 1073741824 -- 1 GiB +# 1610612736 -- 1.5 GiB +# 2147483648 -- 2 GiB OBJDIR := obj-emscripten -#if you compile with SDL2 flag add this Emscripten flag "-s USE_SDL=2" to LDFLAGS: +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 LIBS := -s USE_ZLIB=1 -LDFLAGS := -L. --no-heap-copy -s $(LIBS) -s TOTAL_MEMORY=$(MEMORY) -s NO_EXIT_RUNTIME=0 -s FULL_ES2=1 \ - -s "EXPORTED_RUNTIME_METHODS=['callMain', 'FS', 'PATH', 'ERRNO_CODES']" \ - -s ALLOW_MEMORY_GROWTH=1 -s "EXPORTED_FUNCTIONS=['_main', '_malloc', '_cmd_savefiles', '_cmd_save_state', '_cmd_load_state', '_cmd_take_screenshot']" \ +LDFLAGS := -L. --no-heap-copy -s $(LIBS) -s STACK_SIZE=$(STACK_SIZE) -s INITIAL_MEMORY=$(INITIAL_HEAP) \ + -s EXPORTED_RUNTIME_METHODS=callMain,FS,PATH,ERRNO_CODES,stringToNewUTF8,UTF8ToString \ + -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=1 \ - --js-library emscripten/library_errno_codes.js \ - --js-library emscripten/library_rwebcam.js + --extern-pre-js emscripten/pre.js \ + --js-library emscripten/library_rwebcam.js \ + --js-library emscripten/library_platform_emscripten.js ifeq ($(HAVE_RWEBAUDIO), 1) LDFLAGS += --js-library emscripten/library_rwebaudio.js @@ -98,7 +112,7 @@ ifeq ($(HAVE_AL), 1) endif ifneq ($(PTHREAD), 0) - LDFLAGS += -s WASM_MEM_MAX=1073741824 -pthread -s PTHREAD_POOL_SIZE=$(PTHREAD) + LDFLAGS += -s MAXIMUM_MEMORY=1073741824 -pthread -s PTHREAD_POOL_SIZE=$(PTHREAD) CFLAGS += -pthread HAVE_THREADS=1 else @@ -112,6 +126,22 @@ ifeq ($(ASYNC), 1) endif endif +ifeq ($(HAVE_OPENGLES), 1) + ifeq ($(HAVE_OPENGLES3), 1) + LDFLAGS += -s FULL_ES3=1 -s MIN_WEBGL_VERSION=2 -s MAX_WEBGL_VERSION=2 + else + LDFLAGS += -s FULL_ES2=1 + endif +endif + +ifeq ($(GL_DEBUG), 1) + LDFLAGS += -s GL_ASSERTIONS=1 -s GL_DEBUG=1 +endif + +ifeq ($(FS_DEBUG), 1) + LDFLAGS += -s FS_DEBUG=1 +endif + ifeq ($(HAVE_SDL2), 1) LIBS += -s USE_SDL=2 DEFINES += -DHAVE_SDL2 @@ -126,7 +156,7 @@ libretro := ifeq ($(HAVE_STATIC_DUMMY),1) DEFINES += -DHAVE_STATIC_DUMMY else - libretro += libretro_emscripten.bc + libretro += libretro_emscripten.a endif ifneq ($(V), 1) @@ -135,22 +165,16 @@ endif ifeq ($(DEBUG), 1) LDFLAGS += -O0 -g -gsource-map -s SAFE_HEAP=1 -s STACK_OVERFLOW_CHECK=2 -s ASSERTIONS=1 - CFLAGS += -O0 -g -gsource-map -s SAFE_HEAP=1 -s SAFE_HEAP_LOG=1 -s STACK_OVERFLOW_CHECK=2 -s ASSERTIONS=1 + CFLAGS += -O0 -g -gsource-map else - LDFLAGS += -O3 -s WASM=1 + LDFLAGS += -O3 # WARNING: some optimizations can break some cores (ex: LTO breaks tyrquake) - LDFLAGS += -s PRECISE_F32=$(PRECISE_F32) ifeq ($(LTO), 1) - LDFLAGS += --llvm-lto 3 + LDFLAGS += -flto endif CFLAGS += -O3 endif -# 128 * 1024, double the usual emscripten stack size -LDFLAGS += -s STACK_SIZE=131072 - -LDFLAGS += --extern-pre-js emscripten/pre.js - CFLAGS += -Wall -I. -Ilibretro-common/include -Ideps/7zip -std=gnu99 RARCH_OBJ := $(addprefix $(OBJDIR)/,$(OBJ)) @@ -158,7 +182,8 @@ RARCH_OBJ := $(addprefix $(OBJDIR)/,$(OBJ)) all: $(TARGET) $(TARGET): $(RARCH_OBJ) $(libretro) - @$(if $(Q), $(shell echo echo LD $@),) + @mv -f libretro_emscripten.bc libretro_emscripten.a + @$(if $(Q), $(shell echo echo "LD $@ \ $(libretro) $(LIBS) $(LDFLAGS)"),) $(Q)$(LD) -o $@ $(RARCH_OBJ) $(libretro) $(LIBS) $(LDFLAGS) $(OBJDIR)/%.o: %.c diff --git a/cheat_manager.c b/cheat_manager.c index 240b0f9632e6..f96d40c8daf6 100644 --- a/cheat_manager.c +++ b/cheat_manager.c @@ -126,7 +126,7 @@ void cheat_manager_set_code(unsigned i, const char *str) return; if (!string_is_empty(str)) - strcpy(cheat_st->cheats[i].code, str); + cheat_st->cheats[i].code = strdup(str); cheat_st->cheats[i].state = true; } diff --git a/command.h b/command.h index d032d5588225..8f869adea79c 100644 --- a/command.h +++ b/command.h @@ -260,6 +260,7 @@ enum event_command CMD_EVENT_PRESENCE_UPDATE, CMD_EVENT_OVERLAY_NEXT, CMD_EVENT_OSK_TOGGLE, + CMD_EVENT_RELOAD_CONFIG, #ifdef HAVE_MICROPHONE /* Stops all enabled microphones. */ CMD_EVENT_MICROPHONE_STOP, diff --git a/config.def.h b/config.def.h index e6c94ca71ef5..560d25d453e1 100644 --- a/config.def.h +++ b/config.def.h @@ -764,6 +764,9 @@ #define DEFAULT_MENU_TICKER_SPEED 2.0f #define DEFAULT_MENU_TICKER_SMOOTH true +/* Don't skip rendering assets based on the absence of other assets */ +#define DEFAULT_MENU_IGNORE_MISSING_ASSETS false + #if defined(HAVE_THREADS) #define DEFAULT_MENU_SAVESTATE_RESUME true #else diff --git a/configuration.c b/configuration.c index 301290e2f5b2..be58f9e55da1 100644 --- a/configuration.c +++ b/configuration.c @@ -1933,6 +1933,7 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("menu_dynamic_wallpaper_enable", &settings->bools.menu_dynamic_wallpaper_enable, true, DEFAULT_MENU_DYNAMIC_WALLPAPER_ENABLE, false); SETTING_BOOL("menu_ticker_smooth", &settings->bools.menu_ticker_smooth, true, DEFAULT_MENU_TICKER_SMOOTH, false); SETTING_BOOL("menu_scroll_fast", &settings->bools.menu_scroll_fast, true, DEFAULT_MENU_SCROLL_FAST, false); + SETTING_BOOL("menu_ignore_missing_assets", &settings->bools.menu_ignore_missing_assets, true, DEFAULT_MENU_IGNORE_MISSING_ASSETS, false); SETTING_BOOL("settings_show_drivers", &settings->bools.settings_show_drivers, true, DEFAULT_SETTINGS_SHOW_DRIVERS, false); SETTING_BOOL("settings_show_video", &settings->bools.settings_show_video, true, DEFAULT_SETTINGS_SHOW_VIDEO, false); diff --git a/configuration.h b/configuration.h index b61d3a22c959..2b5c0e11ce2a 100644 --- a/configuration.h +++ b/configuration.h @@ -832,6 +832,7 @@ typedef struct settings bool menu_disable_left_analog; bool menu_disable_right_analog; bool menu_ticker_smooth; + bool menu_ignore_missing_assets; bool settings_show_drivers; bool settings_show_video; bool settings_show_audio; diff --git a/dist-scripts/dist-cores.sh b/dist-scripts/dist-cores.sh index 5c4c675e4c91..42b2ae24abfe 100755 --- a/dist-scripts/dist-cores.sh +++ b/dist-scripts/dist-cores.sh @@ -61,6 +61,7 @@ mkdir -p ../pkg/${platform}/build/rom # Emscripten elif [ $PLATFORM = "emscripten" ] ; then platform=emscripten +# todo: change this to a EXT=bc if [ -z "$EMSCRIPTEN" ] ; then @@ -199,15 +200,10 @@ for f in `ls -v *_${platform}.${EXT}`; do echo Buildbot: building ${name} for ${platform} name=`echo "$f" | sed "s/\(_libretro_${platform}\|\).${EXT}$//"` - async=0 - pthread=${pthread:-0} lto=0 whole_archive= big_stack= - if [ $PLATFORM = "emscripten" ]; then - async=1 #emscripten needs async to sleep - fi if [ $name = "nxengine" ] ; then echo "Applying whole archive linking..." whole_archive="WHOLE_ARCHIVE_LINK=1" @@ -215,10 +211,32 @@ for f in `ls -v *_${platform}.${EXT}`; do echo "Applying big stack..." lto=0 big_stack="BIG_STACK=1" - elif [ $name = "mupen64plus" ] ; then - async=1 - elif [ $name = "dosbox" ] ; then + fi + if [ $PLATFORM = "emscripten" ]; then + async=0 + pthread=${pthread:-0} + gles3=0 + stack_mem=4194304 + heap_mem=134217728 async=0 + if [ $name = "mupen64plus_next" ] ; then + gles3=1 + async=1 + stack_mem=134217728 + heap_mem=268435456 + elif [ $name = "parallel_n64" ] ; then + gles3=1 + async=1 + elif [ $name = "mednafen_psx" ] ; then + heap_mem=536870912 + elif [ $name = "mednafen_psx_hw" ] ; then + gles3=1 + heap_mem=536870912 + elif [ $name = "dosbox" ] ; then + async=1 + elif [ $name = "scummvm" ] ; then + async=1 + fi fi echo "-- Building core: $name --" if [ $PLATFORM = "unix" ]; then @@ -227,15 +245,21 @@ for f in `ls -v *_${platform}.${EXT}`; do cp -f "$f" ../libretro_${platform}.${EXT} fi echo NAME: $name - echo ASYNC: $async echo LTO: $lto + if [ $PLATFORM = "emscripten" ]; then + echo ASYNC: $async + echo PTHREAD: $pthread + echo GLES3: $gles3 + echo STACK_MEMORY: $stack_mem + echo HEAP_MEMORY: $heap_mem + fi # Do cleanup if this is a big stack core if [ "$big_stack" = "BIG_STACK=1" ] ; then if [ $MAKEFILE_GRIFFIN = "yes" ]; then make -C ../ -f Makefile.griffin platform=${platform} clean || exit 1 elif [ $PLATFORM = "emscripten" ]; then - make -C ../ -f Makefile.emscripten PTHREAD=$pthread ASYNC=$async LTO=$lto -j7 clean || exit 1 + make -C ../ -f Makefile.emscripten PTHREAD=$pthread ASYNC=$async LTO=$lto HAVE_OPENGLES3=$gles3 -j7 clean || exit 1 elif [ $PLATFORM = "unix" ]; then make -C ../ -f Makefile LINK=g++ LTO=$lto -j7 clean || exit 1 else @@ -247,8 +271,8 @@ for f in `ls -v *_${platform}.${EXT}`; do if [ $MAKEFILE_GRIFFIN = "yes" ]; then make -C ../ -f Makefile.griffin $OPTS platform=${platform} $whole_archive $big_stack -j3 || exit 1 elif [ $PLATFORM = "emscripten" ]; then - echo "BUILD COMMAND: make -C ../ -f Makefile.emscripten PTHREAD=$pthread ASYNC=$async LTO=$lto -j7 LIBRETRO=${name} TARGET=${name}_libretro.js" - make -C ../ -f Makefile.emscripten $OPTS PTHREAD=$pthread ASYNC=$async LTO=$lto -j7 LIBRETRO=${name} TARGET=${name}_libretro.js || exit 1 + echo "BUILD COMMAND: make -C ../ -f Makefile.emscripten PTHREAD=$pthread ASYNC=$async LTO=$lto HAVE_OPENGLES3=$gles3 STACK_SIZE=$stack_mem INITIAL_HEAP=$heap_mem -j7 LIBRETRO=${name} TARGET=${name}_libretro.js" + make -C ../ -f Makefile.emscripten $OPTS PTHREAD=$pthread ASYNC=$async LTO=$lto HAVE_OPENGLES3=$gles3 STACK_SIZE=$stack_mem INITIAL_HEAP=$heap_mem -j7 LIBRETRO=${name} TARGET=${name}_libretro.js || exit 1 elif [ $PLATFORM = "unix" ]; then make -C ../ -f Makefile LINK=g++ $whole_archive $big_stack -j3 || exit 1 elif [ $PLATFORM = "ctr" ]; then @@ -318,6 +342,9 @@ for f in `ls -v *_${platform}.${EXT}`; do if [ $pthread != 0 ] ; then mv -f ../${name}_libretro.worker.js ../pkg/emscripten/${name}_libretro.worker.js fi + if [ -f ../${name}_libretro.wasm.map ] ; then + mv -f ../${name}_libretro.wasm.map ../pkg/emscripten/${name}_libretro.wasm.map + fi fi # Do manual executable step diff --git a/emscripten/library_errno_codes.js b/emscripten/library_errno_codes.js deleted file mode 100644 index 7ec23ac3b40b..000000000000 --- a/emscripten/library_errno_codes.js +++ /dev/null @@ -1,15 +0,0 @@ -//"use strict"; - -// HACK: This is a dummy library that forces ERRNO_CODES to be used, so it's not optimized away. -// Needed for BrowserFS. - -var LibraryErrnoCodes = { - dummyErrnoCodes__deps: ['$ERRNO_CODES'], - dummyErrnoCodes: function() { - if (!ERRNO_CODES) { - console.error("ERRNO_CODES not imported!"); - } - } -}; - -mergeInto(LibraryManager.library, LibraryErrnoCodes); diff --git a/emscripten/library_platform_emscripten.js b/emscripten/library_platform_emscripten.js new file mode 100644 index 000000000000..99dce4595c13 --- /dev/null +++ b/emscripten/library_platform_emscripten.js @@ -0,0 +1,78 @@ +//"use strict"; + +var LibraryPlatformEmscripten = { + $RPE: { + powerState: { + supported: false, + dischargeTime: 0, + level: 0, + charging: false + }, + powerStateChange: function(e) { + RPE.powerState.dischargeTime = Number.isFinite(e.target.dischargingTime) ? e.target.dischargingTime : 0x7FFFFFFF; + RPE.powerState.level = e.target.level; + RPE.powerState.charging = e.target.charging; + } + }, + + PlatformEmscriptenWatchCanvasSize: function() { + RPE.observer = new ResizeObserver(function(e) { + var width, height; + var entry = e.find(i => i.target == Module.canvas); + if (!entry) return; + if (entry.devicePixelContentBoxSize) { + width = entry.devicePixelContentBoxSize[0].inlineSize; + height = entry.devicePixelContentBoxSize[0].blockSize; + } else { + width = Math.round(entry.contentRect.width * window.devicePixelRatio); + height = Math.round(entry.contentRect.height * window.devicePixelRatio); + } + Module.setCanvasSize(width, height); + Module.print("Setting real canvas size: " + width + " x " + height); + }); + RPE.observer.observe(Module.canvas); + window.addEventListener("resize", function(e) { + RPE.observer.unobserve(Module.canvas); + RPE.observer.observe(Module.canvas); + }, false); + }, + + PlatformEmscriptenPowerStateInit: function() { + if (!navigator.getBattery) return; + navigator.getBattery().then(function(battery) { + battery.addEventListener("chargingchange", RPE.powerStateChange); + battery.addEventListener("levelchange", RPE.powerStateChange); + RPE.powerStateChange({target: battery}); + RPE.powerState.supported = true; + }); + }, + + PlatformEmscriptenPowerStateGetSupported: function() { + return RPE.powerState.supported; + }, + + PlatformEmscriptenPowerStateGetDischargeTime: function() { + return RPE.powerState.dischargeTime; + }, + + PlatformEmscriptenPowerStateGetLevel: function() { + return RPE.powerState.level; + }, + + PlatformEmscriptenPowerStateGetCharging: function() { + return RPE.powerState.charging; + }, + + PlatformEmscriptenGetTotalMem: function() { + if (!performance.memory) return 0; + return performance.memory.jsHeapSizeLimit || 0; + }, + + PlatformEmscriptenGetFreeMem: function() { + if (!performance.memory) return 0; + return (performance.memory.jsHeapSizeLimit || 0) - (performance.memory.usedJSHeapSize || 0); + } +}; + +autoAddDeps(LibraryPlatformEmscripten, '$RPE'); +mergeInto(LibraryManager.library, LibraryPlatformEmscripten); diff --git a/emscripten/library_rwebcam.js b/emscripten/library_rwebcam.js index 5c0bec5dc352..2aaef6e44d47 100644 --- a/emscripten/library_rwebcam.js +++ b/emscripten/library_rwebcam.js @@ -17,18 +17,13 @@ var LibraryRWebCam = { RWebCamInit__deps: ['malloc'], RWebCamInit: function(caps1, caps2, width, height) { if (!navigator) return 0; - - navigator.getMedia = navigator.getUserMedia || - navigator.webkitGetUserMedia || - navigator.mozGetUserMedia || - navigator.msGetUserMedia; - - if (!navigator.getMedia) return 0; + if (!navigator.mediaDevices.getUserMedia) return 0; var c = ++RWC.counter; RWC.contexts[c] = []; RWC.contexts[c].videoElement = document.createElement("video"); + RWC.contexts[c].videoElement.classList.add("retroarchWebcamVideo"); if (width !== 0 && height !== 0) { RWC.contexts[c].videoElement.width = width; RWC.contexts[c].videoElement.height = height; @@ -37,11 +32,11 @@ var LibraryRWebCam = { RWC.contexts[c].glTex = caps1 & (1 << RWC.RETRO_CAMERA_BUFFER_OPENGL_TEXTURE); RWC.contexts[c].rawFb = caps1 & (1 << RWC.RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER); - navigator.getMedia({video: true, audio: false}, function(stream) { + navigator.mediaDevices.getUserMedia({video: true, audio: false}).then(function(stream) { RWC.contexts[c].videoElement.autoplay = true; - RWC.contexts[c].videoElement.src = URL.createObjectURL(stream); + RWC.contexts[c].videoElement.srcObject = stream; RWC.contexts[c].runMode = 2; - }, function (err) { + }).catch(function (err) { console.log("webcam request failed", err); RWC.runMode = 0; }); @@ -53,7 +48,6 @@ var LibraryRWebCam = { RWebCamFree: function(data) { RWC.contexts[data].videoElement.pause(); - URL.revokeObjectURL(RWC.contexts[data].videoElement.src); RWC.contexts[data].videoElement = null; RWC.contexts[data] = null; }, @@ -81,6 +75,7 @@ var LibraryRWebCam = { } if (RWC.contexts[data].rawFb) { RWC.contexts[data].rawFbCanvas = document.createElement("canvas"); + RWC.contexts[data].rawFbCanvas.classList.add("retroarchWebcamCanvas"); ret = 1; } diff --git a/frontend/drivers/platform_emscripten.c b/frontend/drivers/platform_emscripten.c index 5c0511e35d7f..0657d139ecab 100644 --- a/frontend/drivers/platform_emscripten.c +++ b/frontend/drivers/platform_emscripten.c @@ -46,9 +46,22 @@ #include "../../retroarch.h" #include "../../verbosity.h" #include "../../tasks/tasks_internal.h" +#include "../../cheat_manager.h" +#include "../../audio/audio_driver.h" -void dummyErrnoCodes(void); void emscripten_mainloop(void); +void PlatformEmscriptenWatchCanvasSize(void); +void PlatformEmscriptenPowerStateInit(void); +bool PlatformEmscriptenPowerStateGetSupported(void); +int PlatformEmscriptenPowerStateGetDischargeTime(void); +float PlatformEmscriptenPowerStateGetLevel(void); +bool PlatformEmscriptenPowerStateGetCharging(void); +uint64_t PlatformEmscriptenGetTotalMem(void); +uint64_t PlatformEmscriptenGetFreeMem(void); + +//// begin exported functions + +// saves and states void cmd_savefiles(void) { @@ -65,11 +78,113 @@ void cmd_load_state(void) command_event(CMD_EVENT_LOAD_STATE, NULL); } +void cmd_undo_save_state(void) +{ + command_event(CMD_EVENT_UNDO_SAVE_STATE, NULL); +} + +void cmd_undo_load_state(void) +{ + command_event(CMD_EVENT_UNDO_LOAD_STATE, NULL); +} + +// misc + void cmd_take_screenshot(void) { command_event(CMD_EVENT_TAKE_SCREENSHOT, NULL); } +void cmd_toggle_menu(void) +{ + command_event(CMD_EVENT_MENU_TOGGLE, NULL); +} + +void cmd_reload_config(void) +{ + command_event(CMD_EVENT_RELOAD_CONFIG, NULL); +} + +void cmd_toggle_grab_mouse(void) +{ + command_event(CMD_EVENT_GRAB_MOUSE_TOGGLE, NULL); +} + +void cmd_toggle_game_focus(void) +{ + command_event(CMD_EVENT_GAME_FOCUS_TOGGLE, NULL); +} + +void cmd_reset(void) +{ + command_event(CMD_EVENT_RESET, NULL); +} + +void cmd_toggle_pause(void) +{ + command_event(CMD_EVENT_PAUSE_TOGGLE, NULL); +} + +void cmd_pause(void) +{ + command_event(CMD_EVENT_PAUSE, NULL); +} + +void cmd_unpause(void) +{ + command_event(CMD_EVENT_UNPAUSE, NULL); +} + +void cmd_set_volume(float volume) { + audio_set_float(AUDIO_ACTION_VOLUME_GAIN, volume); +} + +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) +bool cmd_set_shader(const char *path) +{ + return command_set_shader(NULL, path); +} +#endif + +// cheats + +void cmd_cheat_set_code(unsigned index, const char *str) +{ + cheat_manager_set_code(index, str); +} + +const char *cmd_cheat_get_code(unsigned index) +{ + return cheat_manager_get_code(index); +} + +void cmd_cheat_toggle_index(bool apply_cheats_after_toggle, unsigned index) +{ + cheat_manager_toggle_index(apply_cheats_after_toggle, index); +} + +bool cmd_cheat_get_code_state(unsigned index) +{ + return cheat_manager_get_code_state(index); +} + +bool cmd_cheat_realloc(unsigned new_size) +{ + return cheat_manager_realloc(new_size, CHEAT_HANDLER_TYPE_EMU); +} + +unsigned cmd_cheat_get_size(void) +{ + return cheat_manager_get_size(); +} + +void cmd_cheat_apply_cheats(void) +{ + cheat_manager_apply_cheats(); +} + +//// end exported functions + static void frontend_emscripten_get_env(int *argc, char *argv[], void *args, void *params_data) { @@ -154,16 +269,45 @@ static void frontend_emscripten_get_env(int *argc, char *argv[], #endif } +static enum frontend_powerstate frontend_emscripten_get_powerstate(int *seconds, int *percent) +{ + enum frontend_powerstate ret = FRONTEND_POWERSTATE_NONE; + + if (!PlatformEmscriptenPowerStateGetSupported()) + return ret; + + if (!PlatformEmscriptenPowerStateGetCharging()) + ret = FRONTEND_POWERSTATE_ON_POWER_SOURCE; + else if (PlatformEmscriptenPowerStateGetLevel() == 1) + ret = FRONTEND_POWERSTATE_CHARGED; + else + ret = FRONTEND_POWERSTATE_CHARGING; + + *seconds = PlatformEmscriptenPowerStateGetDischargeTime(); + *percent = (int)(PlatformEmscriptenPowerStateGetLevel() * 100); + + return ret; +} + +static uint64_t frontend_emscripten_get_total_mem(void) +{ + return PlatformEmscriptenGetTotalMem(); +} + +static uint64_t frontend_emscripten_get_free_mem(void) +{ + return PlatformEmscriptenGetFreeMem(); +} + int main(int argc, char *argv[]) { - dummyErrnoCodes(); + PlatformEmscriptenWatchCanvasSize(); + PlatformEmscriptenPowerStateInit(); EM_ASM({ specialHTMLTargets["!canvas"] = Module.canvas; }); - emscripten_set_canvas_element_size("!canvas", 800, 600); - emscripten_set_element_css_size("!canvas", 800.0, 600.0); emscripten_set_main_loop(emscripten_mainloop, 0, 0); rarch_main(argc, argv, NULL); @@ -171,39 +315,39 @@ int main(int argc, char *argv[]) } frontend_ctx_driver_t frontend_ctx_emscripten = { - frontend_emscripten_get_env, /* environment_get */ - NULL, /* init */ - NULL, /* deinit */ - NULL, /* exitspawn */ - NULL, /* process_args */ - NULL, /* exec */ - NULL, /* set_fork */ - NULL, /* shutdown */ - NULL, /* get_name */ - NULL, /* get_os */ - NULL, /* get_rating */ - NULL, /* load_content */ - NULL, /* get_architecture */ - NULL, /* get_powerstate */ - NULL, /* parse_drive_list */ - NULL, /* get_total_mem */ - NULL, /* get_free_mem */ - NULL, /* install_sighandlers */ - NULL, /* get_signal_handler_state */ - NULL, /* set_signal_handler_state */ - NULL, /* destroy_signal_handler_state */ - NULL, /* attach_console */ - NULL, /* detach_console */ - NULL, /* get_lakka_version */ - NULL, /* set_screen_brightness */ - NULL, /* watch_path_for_changes */ - NULL, /* check_for_path_changes */ - NULL, /* set_sustained_performance_mode */ - NULL, /* get_cpu_model_name */ - NULL, /* get_user_language */ - NULL, /* is_narrator_running */ - NULL, /* accessibility_speak */ - NULL, /* set_gamemode */ - "emscripten", /* ident */ - NULL /* get_video_driver */ + frontend_emscripten_get_env, /* environment_get */ + NULL, /* init */ + NULL, /* deinit */ + NULL, /* exitspawn */ + NULL, /* process_args */ + NULL, /* exec */ + NULL, /* set_fork */ + NULL, /* shutdown */ + NULL, /* get_name */ + NULL, /* get_os */ + NULL, /* get_rating */ + NULL, /* load_content */ + NULL, /* get_architecture */ + frontend_emscripten_get_powerstate, /* get_powerstate */ + NULL, /* parse_drive_list */ + frontend_emscripten_get_total_mem, /* get_total_mem */ + frontend_emscripten_get_free_mem, /* get_free_mem */ + NULL, /* install_sighandlers */ + NULL, /* get_signal_handler_state */ + NULL, /* set_signal_handler_state */ + NULL, /* destroy_signal_handler_state */ + NULL, /* attach_console */ + NULL, /* detach_console */ + NULL, /* get_lakka_version */ + NULL, /* set_screen_brightness */ + NULL, /* watch_path_for_changes */ + NULL, /* check_for_path_changes */ + NULL, /* set_sustained_performance_mode */ + NULL, /* get_cpu_model_name */ + NULL, /* get_user_language */ + NULL, /* is_narrator_running */ + NULL, /* accessibility_speak */ + NULL, /* set_gamemode */ + "emscripten", /* ident */ + NULL /* get_video_driver */ }; diff --git a/gfx/drivers/gl2.c b/gfx/drivers/gl2.c index 16dd269331c9..563651bbaff1 100644 --- a/gfx/drivers/gl2.c +++ b/gfx/drivers/gl2.c @@ -185,9 +185,8 @@ enum gl2_renderchain_flags { GL2_CHAIN_FLAG_EGL_IMAGES = (1 << 0), GL2_CHAIN_FLAG_HAS_FP_FBO = (1 << 1), - GL2_CHAIN_FLAG_HAS_SRGB_FBO_GLES3 = (1 << 2), - GL2_CHAIN_FLAG_HAS_SRGB_FBO = (1 << 3), - GL2_CHAIN_FLAG_HW_RENDER_DEPTH_INIT = (1 << 4) + GL2_CHAIN_FLAG_HAS_SRGB_FBO = (1 << 2), + GL2_CHAIN_FLAG_HW_RENDER_DEPTH_INIT = (1 << 3) }; typedef struct video_shader_ctx_scale @@ -1616,9 +1615,7 @@ static void gl2_create_fbo_texture(gl2_t *gl, if (video_ctx_scaling) video_smooth = false; #endif -#ifndef HAVE_OPENGLES bool force_srgb_disable = settings->bools.video_force_srgb_disable; -#endif GLuint base_filt = video_smooth ? GL_LINEAR : GL_NEAREST; GLuint base_mip_filt = video_smooth ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST; @@ -1650,19 +1647,21 @@ static void gl2_create_fbo_texture(gl2_t *gl, RARCH_ERR("[GL]: Floating-point FBO was requested, but is not supported. Falling back to UNORM. Result may band/clip/etc.!\n"); } -#if !defined(HAVE_OPENGLES2) if ( fp_fbo && (chain->flags & GL2_CHAIN_FLAG_HAS_FP_FBO)) { RARCH_LOG("[GL]: FBO pass #%d is floating-point.\n", i); - gl2_load_texture_image(GL_TEXTURE_2D, 0, GL_RGBA32F, + gl2_load_texture_image(GL_TEXTURE_2D, 0, +#ifdef HAVE_OPENGLES2 + GL_RGBA, +#else + GL_RGBA32F, +#endif gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, GL_RGBA, GL_FLOAT, NULL); } else -#endif { -#ifndef HAVE_OPENGLES bool srgb_fbo = (chain->fbo_scale[i].flags & FBO_SCALE_FLAG_SRGB_FBO) ? true : false; if (!fp_fbo && srgb_fbo) @@ -1678,27 +1677,23 @@ static void gl2_create_fbo_texture(gl2_t *gl, && (chain->flags & GL2_CHAIN_FLAG_HAS_SRGB_FBO)) { RARCH_LOG("[GL]: FBO pass #%d is sRGB.\n", i); + gl2_load_texture_image(GL_TEXTURE_2D, 0, #ifdef HAVE_OPENGLES2 - /* EXT defines are same as core GLES3 defines, - * but GLES3 variant requires different arguments. */ - glTexImage2D(GL_TEXTURE_2D, - 0, GL_SRGB_ALPHA_EXT, - gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, - (chain->flags & GL2_CHAIN_FLAG_HAS_SRGB_FBO_GLES3) - ? GL_RGBA - : GL_SRGB_ALPHA_EXT, - GL_UNSIGNED_BYTE, NULL); + GL_SRGB_ALPHA_EXT, #else - gl2_load_texture_image(GL_TEXTURE_2D, - 0, GL_SRGB8_ALPHA8, + GL_SRGB8_ALPHA8, +#endif gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); +#ifdef HAVE_OPENGLES2 + GL_SRGB_ALPHA_EXT, +#else + GL_RGBA, #endif + GL_UNSIGNED_BYTE, NULL); } else -#endif { -#if defined(HAVE_OPENGLES2) +#if defined(HAVE_OPENGLES) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, @@ -2479,11 +2474,6 @@ static void gl2_renderchain_resolve_extensions(gl2_t *gl, chain->flags |= GL2_CHAIN_FLAG_HAS_FP_FBO; else chain->flags &= ~GL2_CHAIN_FLAG_HAS_FP_FBO; - /* GLES3 has unpack_subimage and sRGB in core. */ - if (gl_check_capability(GL_CAPS_SRGB_FBO_ES3)) - chain->flags |= GL2_CHAIN_FLAG_HAS_SRGB_FBO_GLES3; - else - chain->flags &= ~GL2_CHAIN_FLAG_HAS_SRGB_FBO_GLES3; if (!force_srgb_disable) { @@ -3892,7 +3882,6 @@ static bool gl2_resolve_extensions(gl2_t *gl, const char *context_ident, const v RARCH_WARN("[GL]: GLES implementation does not have BGRA8888 extension.\n" "[GL]: 32-bit path will require conversion.\n"); } - /* TODO/FIXME - No extensions for float FBO currently. */ #endif #ifdef GL_DEBUG diff --git a/gfx/drivers_context/emscriptenegl_ctx.c b/gfx/drivers_context/emscriptenegl_ctx.c index 2082aef98e78..797999227cd1 100644 --- a/gfx/drivers_context/emscriptenegl_ctx.c +++ b/gfx/drivers_context/emscriptenegl_ctx.c @@ -38,8 +38,6 @@ typedef struct #ifdef HAVE_EGL egl_ctx_data_t egl; #endif - int initial_width; - int initial_height; unsigned fb_width; unsigned fb_height; } emscripten_ctx_data_t; @@ -54,76 +52,29 @@ static void gfx_ctx_emscripten_swap_interval(void *data, int interval) static void gfx_ctx_emscripten_get_canvas_size(int *width, int *height) { - EmscriptenFullscreenChangeEvent fullscreen_status; - bool is_fullscreen = false; - EMSCRIPTEN_RESULT r = emscripten_get_fullscreen_status(&fullscreen_status); + EMSCRIPTEN_RESULT r = emscripten_get_canvas_element_size("!canvas", width, height); - if (r == EMSCRIPTEN_RESULT_SUCCESS) + if (r != EMSCRIPTEN_RESULT_SUCCESS) { - if (fullscreen_status.isFullscreen) - { - is_fullscreen = true; - *width = fullscreen_status.screenWidth; - *height = fullscreen_status.screenHeight; - } - } - - if (!is_fullscreen) - { - r = emscripten_get_canvas_element_size("!canvas", width, height); - - if (r != EMSCRIPTEN_RESULT_SUCCESS) - { - *width = 800; - *height = 600; - RARCH_ERR("[EMSCRIPTEN/EGL]: Could not get screen dimensions: %d\n",r); - } + *width = 800; + *height = 600; + RARCH_ERR("[EMSCRIPTEN/EGL]: Could not get screen dimensions: %d\n",r); } } static void gfx_ctx_emscripten_check_window(void *data, bool *quit, bool *resize, unsigned *width, unsigned *height) { - EMSCRIPTEN_RESULT r; int input_width; int input_height; emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data; gfx_ctx_emscripten_get_canvas_size(&input_width, &input_height); - if (input_width == 0 || input_height == 0) - { - input_width = emscripten->initial_width; - input_height = emscripten->initial_height; - emscripten->fb_width = emscripten->fb_height = 0; - } - - *width = (unsigned)input_width; - *height = (unsigned)input_height; - *resize = false; - - if ( (input_width != emscripten->fb_width) - || (input_height != emscripten->fb_height)) - { - r = emscripten_set_canvas_element_size("!canvas", - input_width, input_height); - - if (r != EMSCRIPTEN_RESULT_SUCCESS) - RARCH_ERR("[EMSCRIPTEN/EGL]: error resizing canvas: %d\n", r); - - /* fix Module.requestFullscreen messing with the canvas size */ - r = emscripten_set_element_css_size("!canvas", - (double)input_width, (double)input_height); - - if (r != EMSCRIPTEN_RESULT_SUCCESS) - RARCH_ERR("[EMSCRIPTEN/EGL]: error resizing canvas css: %d\n", r); - - *resize = true; - } - - emscripten->fb_width = (unsigned)input_width; - emscripten->fb_height = (unsigned)input_height; - *quit = false; + *width = emscripten->fb_width = (unsigned)input_width; + *height = emscripten->fb_height = (unsigned)input_height; + *quit = false; + *resize = false; } static void gfx_ctx_emscripten_swap_buffers(void *data) @@ -148,6 +99,25 @@ static void gfx_ctx_emscripten_get_video_size(void *data, *height = emscripten->fb_height; } +static bool gfx_ctx_emscripten_get_metrics(void *data, + enum display_metric_types type, float *value) +{ + switch (type) + { + // there is no way to get the actual DPI in emscripten, so return a standard value instead. + // this is needed for menu touch/pointer swipe scrolling to work. + case DISPLAY_METRIC_DPI: + *value = 150.0f; + break; + + default: + *value = 0.0f; + return false; + } + + return true; +} + static void gfx_ctx_emscripten_destroy(void *data) { emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data; @@ -190,14 +160,6 @@ static void *gfx_ctx_emscripten_init(void *video_driver) if (!emscripten) return NULL; - /* TODO/FIXME - why is this conditional here - shouldn't these always - * be grabbed? */ - if ( (emscripten->initial_width == 0) - || (emscripten->initial_height == 0)) - emscripten_get_canvas_element_size("!canvas", - &emscripten->initial_width, - &emscripten->initial_height); - #ifdef HAVE_EGL if (g_egl_inited) { @@ -310,7 +272,7 @@ const gfx_ctx_driver_t gfx_ctx_emscripten = { NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ - NULL, /* get_metrics */ + gfx_ctx_emscripten_get_metrics, gfx_ctx_emscripten_translate_aspect, NULL, /* update_title */ gfx_ctx_emscripten_check_window, diff --git a/input/drivers/rwebinput_input.c b/input/drivers/rwebinput_input.c index 34b0cfbbec5a..67823f16e227 100644 --- a/input/drivers/rwebinput_input.c +++ b/input/drivers/rwebinput_input.c @@ -23,14 +23,18 @@ #include #include +#include #include +#include "../input_driver.h" +#include "../input_types.h" #include "../input_keymaps.h" #include "../../tasks/tasks_internal.h" #include "../../configuration.h" #include "../../retroarch.h" #include "../../verbosity.h" +#include "../../command.h" /* https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button */ #define RWEBINPUT_MOUSE_BTNL 0 @@ -39,6 +43,8 @@ #define RWEBINPUT_MOUSE_BTN4 3 #define RWEBINPUT_MOUSE_BTN5 4 +#define MAX_TOUCH 32 + typedef struct rwebinput_key_to_code_map_entry { const char *key; @@ -58,26 +64,36 @@ typedef struct rwebinput_keyboard_event_queue size_t max_size; } rwebinput_keyboard_event_queue_t; +typedef struct rwebinput_pointer_states +{ + int x; + int y; + int id; +} rwebinput_pointer_state_t; + typedef struct rwebinput_mouse_states { double pending_scroll_x; double pending_scroll_y; double scroll_x; double scroll_y; - signed x; - signed y; - signed pending_delta_x; - signed pending_delta_y; - signed delta_x; - signed delta_y; + int x; + int y; + int pending_delta_x; + int pending_delta_y; + int delta_x; + int delta_y; uint8_t buttons; } rwebinput_mouse_state_t; typedef struct rwebinput_input { - rwebinput_mouse_state_t mouse; /* double alignment */ - rwebinput_keyboard_event_queue_t keyboard; /* ptr alignment */ + rwebinput_mouse_state_t mouse; /* double alignment */ + rwebinput_keyboard_event_queue_t keyboard; /* ptr alignment */ + rwebinput_pointer_state_t pointer[MAX_TOUCH]; /* int alignment */ + unsigned pointer_count; bool keys[RETROK_LAST]; + bool pointerlock_active; } rwebinput_input_t; /* KeyboardEvent.keyCode has been deprecated for a while and doesn't have @@ -255,11 +271,39 @@ static EM_BOOL rwebinput_mouse_cb(int event_type, uint8_t mask = 1 << mouse_event->button; - rwebinput->mouse.x = mouse_event->targetX; - rwebinput->mouse.y = mouse_event->targetY; + // note: movementX/movementY are pre-scaled in chromium (but not firefox) + // see https://github.com/w3c/pointerlock/issues/42 + rwebinput->mouse.pending_delta_x += mouse_event->movementX; rwebinput->mouse.pending_delta_y += mouse_event->movementY; + if (rwebinput->pointerlock_active) + { + unsigned video_width, video_height; + video_driver_get_size(&video_width, &video_height); + + rwebinput->mouse.x += mouse_event->movementX; + rwebinput->mouse.y += mouse_event->movementY; + + /* Clamp X */ + if (rwebinput->mouse.x < 0) + rwebinput->mouse.x = 0; + if (rwebinput->mouse.x >= video_width) + rwebinput->mouse.x = (int)(video_width - 1); + + /* Clamp Y */ + if (rwebinput->mouse.y < 0) + rwebinput->mouse.y = 0; + if (rwebinput->mouse.y >= video_height) + rwebinput->mouse.y = (int)(video_height - 1); + } + else + { + double dpr = emscripten_get_device_pixel_ratio(); + rwebinput->mouse.x = (int)(mouse_event->targetX * dpr); + rwebinput->mouse.y = (int)(mouse_event->targetY * dpr); + } + if (event_type == EMSCRIPTEN_EVENT_MOUSEDOWN) rwebinput->mouse.buttons |= mask; else if (event_type == EMSCRIPTEN_EVENT_MOUSEUP) @@ -273,8 +317,90 @@ static EM_BOOL rwebinput_wheel_cb(int event_type, { rwebinput_input_t *rwebinput = (rwebinput_input_t*)user_data; - rwebinput->mouse.pending_scroll_x += wheel_event->deltaX; - rwebinput->mouse.pending_scroll_y += wheel_event->deltaY; + double dpr = emscripten_get_device_pixel_ratio(); + rwebinput->mouse.pending_scroll_x += wheel_event->deltaX * dpr; + rwebinput->mouse.pending_scroll_y += wheel_event->deltaY * dpr; + + return EM_TRUE; +} + +static EM_BOOL rwebinput_touch_cb(int event_type, + const EmscriptenTouchEvent *touch_event, void *user_data) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)user_data; + + unsigned touches_max = MIN(touch_event->numTouches, MAX_TOUCH); + unsigned touches_released = 0; + + switch (event_type) + { + case EMSCRIPTEN_EVENT_TOUCHSTART: + case EMSCRIPTEN_EVENT_TOUCHMOVE: + for (unsigned touch = 0; touch < touches_max; touch++) + { + if (!(touch_event->touches[touch].isChanged) && rwebinput->pointer[touch].id == touch_event->touches[touch].identifier) + continue; + + double dpr = emscripten_get_device_pixel_ratio(); + rwebinput->pointer[touch].x = (int)(touch_event->touches[touch].targetX * dpr); + rwebinput->pointer[touch].y = (int)(touch_event->touches[touch].targetY * dpr); + rwebinput->pointer[touch].id = touch_event->touches[touch].identifier; + } + break; + case EMSCRIPTEN_EVENT_TOUCHEND: + case EMSCRIPTEN_EVENT_TOUCHCANCEL: + // note: touches_max/numTouches is out of date here - it uses the old value from before the release + // note 2: I'm unsure if multiple touches can trigger the same touchend anyway... + if (touches_max > 1) + { + for (unsigned touch_up = 0; touch_up < touches_max; touch_up++) + { + if (touch_event->touches[touch_up].isChanged) + { + memmove(rwebinput->pointer + touch_up - touches_released, + rwebinput->pointer + touch_up - touches_released + 1, + (touches_max - touch_up - 1) * sizeof(rwebinput_pointer_state_t)); + touches_released++; + } + } + } + else + touches_released = 1; + + if (touches_max > touches_released) + touches_max -= touches_released; + else + touches_max = 0; + break; + } + + rwebinput->pointer_count = touches_max; + + return EM_TRUE; +} + +static EM_BOOL rwebinput_pointerlockchange_cb(int event_type, + const EmscriptenPointerlockChangeEvent *pointerlock_change_event, void *user_data) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)user_data; + + rwebinput->pointerlock_active = pointerlock_change_event->isActive; + + if (!pointerlock_change_event->isActive) + { + input_driver_state_t *input_st = input_state_get_ptr(); + + if (input_st->game_focus_state.enabled) + { + enum input_game_focus_cmd_type game_focus_cmd = GAME_FOCUS_CMD_OFF; + command_event(CMD_EVENT_GAME_FOCUS_TOGGLE, &game_focus_cmd); + } + + if (input_st->flags & INP_FLAG_GRAB_MOUSE_STATE) + { + command_event(CMD_EVENT_GRAB_MOUSE_TOGGLE, NULL); + } + } return EM_TRUE; } @@ -350,6 +476,47 @@ static void *rwebinput_input_init(const char *joypad_driver) "[EMSCRIPTEN/INPUT] failed to create wheel callback: %d\n", r); } + r = emscripten_set_touchstart_callback("!canvas", rwebinput, false, + rwebinput_touch_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create touchstart callback: %d\n", r); + } + + r = emscripten_set_touchend_callback("!canvas", rwebinput, false, + rwebinput_touch_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create touchend callback: %d\n", r); + } + + r = emscripten_set_touchmove_callback("!canvas", rwebinput, false, + rwebinput_touch_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create touchmove callback: %d\n", r); + } + + r = emscripten_set_touchcancel_callback("!canvas", rwebinput, false, + rwebinput_touch_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create touchcancel callback: %d\n", r); + } + + r = emscripten_set_pointerlockchange_callback( + EMSCRIPTEN_EVENT_TARGET_DOCUMENT, rwebinput, false, + rwebinput_pointerlockchange_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create pointerlockchange callback: %d\n", r); + } + input_keymaps_init_keyboard_lut(rarch_key_map_rwebinput); return rwebinput; @@ -505,24 +672,41 @@ static int16_t rwebinput_input_state( return rwebinput_mouse_state(&rwebinput->mouse, id, device == RARCH_DEVICE_MOUSE_SCREEN); case RETRO_DEVICE_POINTER: case RARCH_DEVICE_POINTER_SCREEN: - if (idx == 0) { struct video_viewport vp = {0}; rwebinput_mouse_state_t *mouse = &rwebinput->mouse; - bool screen = device == - RARCH_DEVICE_POINTER_SCREEN; + bool pointer_down = false; + unsigned pointer_count = rwebinput->pointer_count; + int x = 0; + int y = 0; int16_t res_x = 0; int16_t res_y = 0; int16_t res_screen_x = 0; int16_t res_screen_y = 0; + if (pointer_count && idx < pointer_count) + { + x = rwebinput->pointer[idx].x; + y = rwebinput->pointer[idx].y; + pointer_down = true; + } + else if (idx == 0) + { + x = mouse->x; + y = mouse->y; + pointer_down = !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTNL)); + pointer_count = 1; + } + else + return 0; + if (!(video_driver_translate_coord_viewport_confined_wrap( - &vp, mouse->x, mouse->y, + &vp, x, y, &res_x, &res_y, &res_screen_x, &res_screen_y))) return 0; - if (screen) + if (device == RARCH_DEVICE_POINTER_SCREEN) { res_x = res_screen_x; res_y = res_screen_y; @@ -535,7 +719,9 @@ static int16_t rwebinput_input_state( case RETRO_DEVICE_ID_POINTER_Y: return res_y; case RETRO_DEVICE_ID_POINTER_PRESSED: - return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTNL)); + return (pointer_down && !input_driver_pointer_is_offscreen(res_x, res_y)); + case RETRO_DEVICE_ID_POINTER_COUNT: + return pointer_count; case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN: return input_driver_pointer_is_offscreen(res_x, res_y); default: diff --git a/libretro-common/gfx/gl_capabilities.c b/libretro-common/gfx/gl_capabilities.c index f7e4838000a4..1829eb490fea 100644 --- a/libretro-common/gfx/gl_capabilities.c +++ b/libretro-common/gfx/gl_capabilities.c @@ -287,15 +287,12 @@ bool gl_check_capability(enum gl_capability_enum enum_idx) } break; case GL_CAPS_FP_FBO: - /* GLES - No extensions for float FBO currently. */ -#ifndef HAVE_OPENGLES if (gl_check_capability(GL_CAPS_FBO)) { /* Float FBO is core in 3.2. */ - if (gl_query_core_context_in_use() || gl_query_extension("ARB_texture_float")) + if (gl_query_core_context_in_use() || gl_query_extension("ARB_texture_float") || gl_query_extension("OES_texture_float_linear")) return true; } -#endif break; case GL_CAPS_BGRA8888: #ifdef HAVE_OPENGLES diff --git a/menu/drivers/ozone.c b/menu/drivers/ozone.c index c14868f0cfc7..00a1ebbbea38 100644 --- a/menu/drivers/ozone.c +++ b/menu/drivers/ozone.c @@ -447,7 +447,8 @@ enum ozone_handle_flags2 OZONE_FLAG2_LAST_USE_PREFERRED_SYSTEM_COLOR_THEME = (1 << 7), OZONE_FLAG2_RESET_DEPTH = (1 << 8), OZONE_FLAG2_PENDING_CURSOR_IN_SIDEBAR = (1 << 9), - OZONE_FLAG2_IS_QUICK_MENU = (1 << 10) + OZONE_FLAG2_IS_QUICK_MENU = (1 << 10), + OZONE_FLAG2_IGNORE_MISSING_ASSETS = (1 << 11) }; struct ozone_handle @@ -1726,7 +1727,7 @@ static void ozone_set_color_theme( ozone->theme->message_background, sizeof(ozone->theme_dynamic.message_background)); - if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS) + if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS || ozone->flags2 & OZONE_FLAG2_IGNORE_MISSING_ASSETS) ozone_restart_cursor_animation(ozone); ozone_last_color_theme = color_theme; @@ -3209,7 +3210,7 @@ static void ozone_draw_cursor( /* Draw the cursor */ if ( (ozone->theme->name) - && (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS)) + && (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS || ozone->flags2 & OZONE_FLAG2_IGNORE_MISSING_ASSETS)) ozone_draw_cursor_slice(ozone, p_disp, userdata, @@ -7019,7 +7020,7 @@ static void ozone_draw_messagebox( dispctx->blend_begin(userdata); /* Avoid drawing a black box if there's no assets */ - if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS) + if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS || ozone->flags2 & OZONE_FLAG2_IGNORE_MISSING_ASSETS) { /* Note: The fact that we use a texture slice here * makes things very messy @@ -9341,9 +9342,14 @@ static void ozone_context_reset(void *data, bool is_threaded) if (ozone) { + settings_t *settings = config_get_ptr(); + ozone->flags |= OZONE_FLAG_HAS_ALL_ASSETS; - ozone_set_layout(ozone, config_get_ptr()->bools.ozone_collapse_sidebar, is_threaded); + if (settings->bools.menu_ignore_missing_assets) + ozone->flags2 |= OZONE_FLAG2_IGNORE_MISSING_ASSETS; + + ozone_set_layout(ozone, settings->bools.ozone_collapse_sidebar, is_threaded); /* Textures init */ for (i = 0; i < OZONE_TEXTURE_LAST; i++) @@ -9436,7 +9442,7 @@ static void ozone_context_reset(void *data, bool is_threaded) ozone_update_thumbnail_image(ozone); ozone_update_savestate_thumbnail_image(ozone); - if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS) + if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS || ozone->flags2 & OZONE_FLAG2_IGNORE_MISSING_ASSETS) ozone_restart_cursor_animation(ozone); /* Screensaver */ diff --git a/pkg/emscripten/README.md b/pkg/emscripten/README.md index 30dd73ed5876..ba5787d48f32 100644 --- a/pkg/emscripten/README.md +++ b/pkg/emscripten/README.md @@ -1,10 +1,10 @@ # RetroArch Web Player -The RetroArch Web Player is RetroArch compiled through [Emscripten](http://kripken.github.io/emscripten-site/). The following outlines how to compile RetroArch using Emscripten, and running it in your browser. +The RetroArch Web Player is RetroArch compiled through [Emscripten](https://emscripten.org/). The following outlines how to compile RetroArch using Emscripten, and running it in your browser. ## Compiling -To compile RetroArch with Emscripten, you'll first have to [download and install the Emscripten SDK](http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html) at 3.1.46: +To compile RetroArch with Emscripten, you'll first have to [download and install the Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html) at 3.1.46: ``` git clone https://github.com/emscripten-core/emsdk.git diff --git a/pkg/emscripten/libretro/embed.html b/pkg/emscripten/libretro/embed.html index 00e1dc0d60d8..77deb34fa85d 100644 --- a/pkg/emscripten/libretro/embed.html +++ b/pkg/emscripten/libretro/embed.html @@ -1,91 +1,83 @@ - - - - RetroArch Web Player - - - - - - - - - - - - - - -
-
-
- - RetroArch Logo +
+
+ +
+
+
+ + RetroArch Logo +
+
+
+ + + + + + + + +
+ + Become a patron
-
- - - - - - - - - -
- - -
- + diff --git a/pkg/emscripten/libretro/index.html b/pkg/emscripten/libretro/index.html index f4cdc5f4f6c9..9e2f41882ca3 100644 --- a/pkg/emscripten/libretro/index.html +++ b/pkg/emscripten/libretro/index.html @@ -1,30 +1,28 @@ - - - RetroArch Web Player - - - - - - - - - - - - - - - -
-
-
- -
- - RetroArch Logo +
+ + +
+ + +
+
+
+ +
+ + RetroArch Logo +
- - - - - - - - - - - + + + + + + + + + diff --git a/pkg/emscripten/libretro/libretro.css b/pkg/emscripten/libretro/libretro.css index 4d08dc2eb4eb..ec3d514e3d0e 100644 --- a/pkg/emscripten/libretro/libretro.css +++ b/pkg/emscripten/libretro/libretro.css @@ -81,12 +81,22 @@ } } -/** - * Disable the border around the player. - */ canvas.webplayer { border: none; outline: none; + width: 800px; + height: 600px; +} + +/** + * Hack to make emscripten stop messing with the canvas size while in fullscreen. + * Foiled again! + */ +:fullscreen canvas.webplayer { + min-width: 100vw; + max-width: 100vw; + min-height: 100vh; + max-height: 100vh; } textarea { diff --git a/pkg/emscripten/libretro/libretro.js b/pkg/emscripten/libretro/libretro.js index 7eda8fa194f7..a2fd96b8e55a 100644 --- a/pkg/emscripten/libretro/libretro.js +++ b/pkg/emscripten/libretro/libretro.js @@ -6,24 +6,23 @@ var BrowserFS = BrowserFS; var afs; var initializationCount = 0; -var setImmediate; var Module = { noInitialRun: true, arguments: ["-v", "--menu"], encoder: new TextEncoder(), - message_queue:[], - message_out:[], - message_accum:"", + message_queue: [], + message_out: [], + message_accum: "", retroArchSend: function(msg) { - let bytes = this.encoder.encode(msg+"\n"); - this.message_queue.push([bytes,0]); + 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 != "") { + if (out == null && this.message_accum != "") { out = this.message_accum; this.message_accum = ""; } @@ -33,35 +32,36 @@ var Module = { function(module) { function stdin() { // Return ASCII code of character, or null if no input - while(module.message_queue.length > 0){ + while (module.message_queue.length > 0) { var msg = module.message_queue[0][0]; var index = module.message_queue[0][1]; - if(index >= msg.length) { + if (index >= msg.length) { module.message_queue.shift(); } else { - module.message_queue[0][1] = index+1; + module.message_queue[0][1] = index + 1; // assumption: msg is a uint8array return msg[index]; } } return null; } + function stdout(c) { - if(c == null) { + if (c == null) { // flush - if(module.message_accum != "") { + 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 != "") { + 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.message_accum = module.message_accum + s; } } } @@ -69,88 +69,71 @@ var Module = { } ], postRun: [], - onRuntimeInitialized: function() - { - appInitialized(); - }, - print: function(text) - { - console.log(text); - }, - printErr: function(text) - { - console.error(text); - }, - canvas: document.getElementById("canvas"), + onRuntimeInitialized: function() { + appInitialized(); + }, + print: function(text) { + console.log("stdout:", text); + }, + printErr: function(text) { + console.log("stderr:", text); + }, + canvas: document.getElementById("canvas"), totalDependencies: 0, - monitorRunDependencies: function(left) - { - this.totalDependencies = Math.max(this.totalDependencies, left); - } + monitorRunDependencies: function(left) { + this.totalDependencies = Math.max(this.totalDependencies, left); + } }; -function cleanupStorage() -{ +function cleanupStorage() { localStorage.clear(); - if (BrowserFS.FileSystem.IndexedDB.isAvailable()) - { + if (BrowserFS.FileSystem.IndexedDB.isAvailable()) { var req = indexedDB.deleteDatabase("RetroArch"); - req.onsuccess = function () { + req.onsuccess = function() { console.log("Deleted database successfully"); }; - req.onerror = function () { - console.log("Couldn't delete database"); + req.onerror = function() { + console.error("Couldn't delete database"); }; - req.onblocked = function () { - console.log("Couldn't delete database due to the operation being blocked"); + req.onblocked = function() { + console.error("Couldn't delete database due to the operation being blocked"); }; } document.getElementById("btnClean").disabled = true; } -function idbfsInit() -{ +function idbfsInit() { $('#icnLocal').removeClass('fa-globe'); $('#icnLocal').addClass('fa-spinner fa-spin'); var imfs = new BrowserFS.FileSystem.InMemory(); - if (BrowserFS.FileSystem.IndexedDB.isAvailable()) - { + if (BrowserFS.FileSystem.IndexedDB.isAvailable()) { afs = new BrowserFS.FileSystem.AsyncMirror(imfs, - new BrowserFS.FileSystem.IndexedDB(function(e, fs) - { - if (e) - { - //fallback to imfs - afs = new BrowserFS.FileSystem.InMemory(); - console.log("WEBPLAYER: error: " + e + " falling back to in-memory filesystem"); - appInitialized(); - } - else - { - // initialize afs by copying files from async storage to sync storage. - afs.initialize(function (e) - { - if (e) - { + new BrowserFS.FileSystem.IndexedDB(function(e, fs) { + if (e) { + // fallback to imfs afs = new BrowserFS.FileSystem.InMemory(); - console.log("WEBPLAYER: error: " + e + " falling back to in-memory filesystem"); + console.error("WEBPLAYER: error: " + e + " falling back to in-memory filesystem"); appInitialized(); + } else { + // initialize afs by copying files from async storage to sync storage. + afs.initialize(function(e) { + if (e) { + afs = new BrowserFS.FileSystem.InMemory(); + console.error("WEBPLAYER: error: " + e + " falling back to in-memory filesystem"); + appInitialized(); + } else { + idbfsSyncComplete(); + } + }); } - else - { - idbfsSyncComplete(); - } - }); - } - }, - "RetroArch")); + }, + "RetroArch")); } } -function idbfsSyncComplete() -{ +function idbfsSyncComplete() { $('#icnLocal').removeClass('fa-spinner').removeClass('fa-spin'); $('#icnLocal').addClass('fa-check'); console.log("WEBPLAYER: idbfs setup successful"); @@ -158,69 +141,66 @@ function idbfsSyncComplete() appInitialized(); } -function appInitialized() -{ - /* Need to wait for the file system, the wasm runtime, and the zip download - to complete before enabling the Run button. */ - initializationCount++; - if (initializationCount == 3) - { - setupFileSystem("browser"); - preLoadingComplete(); - } - } - -function preLoadingComplete() -{ - /* Make the Preview image clickable to start RetroArch. */ - $('.webplayer-preview').addClass('loaded').click(function () { +function appInitialized() { + /* Need to wait for the file system, the wasm runtime, and the zip download + to complete before enabling the Run button. */ + initializationCount++; + if (initializationCount == 3) { + setupFileSystem("browser"); + preLoadingComplete(); + } +} + +function preLoadingComplete() { + // Make the Preview image clickable to start RetroArch. + $('.webplayer-preview').addClass('loaded').click(function() { + startRetroArch(); + return false; + }); + $('#btnRun').removeClass('disabled').removeAttr("disabled").click(function() { startRetroArch(); return false; - }); - document.getElementById("btnRun").disabled = false; - $('#btnRun').removeClass('disabled'); + }); } var zipTOC; function zipfsInit() { - // 256 MB max bundle size - let buffer = new ArrayBuffer(256*1024*1024); - let bufferView = new Uint8Array(buffer); - let idx = 0; - // bundle should be in five parts (this can be changed later) - Promise.all([fetch("assets/frontend/bundle.zip.aa"), - fetch("assets/frontend/bundle.zip.ab"), - fetch("assets/frontend/bundle.zip.ac"), - fetch("assets/frontend/bundle.zip.ad"), - fetch("assets/frontend/bundle.zip.ae") - ]).then(function(resps) { - Promise.all(resps.map((r) => r.arrayBuffer())).then(function(buffers) { - for (let buf of buffers) { - if (idx+buf.byteLength > buffer.maxByteLength) { - console.log("WEBPLAYER: error: bundle.zip is too large"); - } - bufferView.set(new Uint8Array(buf), idx, buf.byteLength); - idx += buf.byteLength; - } - BrowserFS.FileSystem.ZipFS.computeIndex(BrowserFS.BFSRequire('buffer').Buffer(new Uint8Array(buffer, 0, idx)), function(toc) { - zipTOC = toc; - appInitialized(); - }); - }) - }); + // 256 MB max bundle size + let buffer = new ArrayBuffer(256 * 1024 * 1024); + let bufferView = new Uint8Array(buffer); + let idx = 0; + // bundle should be in five parts (this can be changed later) + Promise.all([fetch("assets/frontend/bundle.zip.aa"), + fetch("assets/frontend/bundle.zip.ab"), + fetch("assets/frontend/bundle.zip.ac"), + fetch("assets/frontend/bundle.zip.ad"), + fetch("assets/frontend/bundle.zip.ae") + ]).then(function(resps) { + Promise.all(resps.map((r) => r.arrayBuffer())).then(function(buffers) { + for (let buf of buffers) { + if (idx + buf.byteLength > buffer.maxByteLength) { + console.error("WEBPLAYER: error: bundle.zip is too large"); + } + bufferView.set(new Uint8Array(buf), idx, buf.byteLength); + idx += buf.byteLength; + } + BrowserFS.FileSystem.ZipFS.computeIndex(BrowserFS.BFSRequire('buffer').Buffer(new Uint8Array(buffer, 0, idx)), function(toc) { + zipTOC = toc; + appInitialized(); + }); + }) + }); } -function setupFileSystem(backend) -{ - /* create a mountable filesystem that will server as a root - mountpoint for browserfs */ - var mfs = new BrowserFS.FileSystem.MountableFileSystem(); - /* create an XmlHttpRequest filesystem for the bundled data */ +function setupFileSystem(backend) { + // create a mountable filesystem that will server as a root mountpoint for browserfs + var mfs = new BrowserFS.FileSystem.MountableFileSystem(); + + // create an XmlHttpRequest filesystem for the bundled data var xfs1 = new BrowserFS.FileSystem.ZipFS(zipTOC); - /* create an XmlHttpRequest filesystem for core assets */ - var xfs2 = new BrowserFS.FileSystem.XmlHttpRequest - (".index-xhr", "assets/cores/"); + // create an XmlHttpRequest filesystem for core assets + var xfs2 = new BrowserFS.FileSystem.XmlHttpRequest(".index-xhr", "assets/cores/"); console.log("WEBPLAYER: initializing filesystem: " + backend); mfs.mount('/home/web_user/retroarch/userdata', afs); @@ -229,75 +209,81 @@ function setupFileSystem(backend) mfs.mount('/home/web_user/retroarch/userdata/content/downloads', xfs2); BrowserFS.initialize(mfs); var BFS = new BrowserFS.EmscriptenFS(Module.FS, Module.PATH, Module.ERRNO_CODES); - Module.FS.mount(BFS, {root: '/home'}, '/home'); + Module.FS.mount(BFS, { + root: '/home' + }, '/home'); console.log("WEBPLAYER: " + backend + " filesystem initialization successful"); } -/** - * Retrieve the value of the given GET parameter. - */ +// Retrieve the value of the given GET parameter. function getParam(name) { - var results = new RegExp('[?&]' + name + '=([^&#]*)').exec(window.location.href); - if (results) { - return results[1] || null; - } + var results = new RegExp('[?&]' + name + '=([^&#]*)').exec(window.location.href); + if (results) { + return results[1] || null; + } } -function startRetroArch() -{ +function startRetroArch() { $('.webplayer').show(); $('.webplayer-preview').hide(); document.getElementById("btnRun").disabled = true; - $('#btnFullscreen').removeClass('disabled'); - $('#btnMenu').removeClass('disabled'); - $('#btnAdd').removeClass('disabled'); - $('#btnRom').removeClass('disabled'); - - document.getElementById("btnAdd").disabled = false; - document.getElementById("btnRom").disabled = false; - document.getElementById("btnMenu").disabled = false; - document.getElementById("btnFullscreen").disabled = false; - - Module["canvas"] = document.getElementById("canvas"); - Module["canvas"].addEventListener("click", () => Module["canvas"].focus()); - Module['callMain'](Module['arguments']); - Module['resumeMainLoop'](); - Module['canvas'].focus(); + $('#btnAdd').removeClass("disabled").removeAttr("disabled").click(function() { + $('#btnRom').click(); + }); + $('#btnRom').removeAttr("disabled").change(function(e) { + selectFiles(e.target.files); + }); + $('#btnMenu').removeClass("disabled").removeAttr("disabled").click(function() { + Module._cmd_toggle_menu(); + Module.canvas.focus(); + }); + $('#btnFullscreen').removeClass("disabled").removeAttr("disabled").click(function() { + Module.requestFullscreen(false); + Module.canvas.focus(); + }); + + Module.canvas.focus(); + Module.canvas.addEventListener("pointerdown", function() { + Module.canvas.focus(); + }, false); + Module.callMain(Module.arguments); } -function selectFiles(files) -{ + +function selectFiles(files) { $('#btnAdd').addClass('disabled'); $('#icnAdd').removeClass('fa-plus'); $('#icnAdd').addClass('fa-spinner spinning'); var count = files.length; - for (var i = 0; i < count; i++) - { + for (var i = 0; i < count; i++) { filereader = new FileReader(); filereader.file_name = files[i].name; filereader.readAsArrayBuffer(files[i]); - filereader.onload = function(){uploadData(this.result, this.file_name)}; - filereader.onloadend = function(evt) - { + filereader.onload = function() { + uploadData(this.result, this.file_name) + }; + filereader.onloadend = function(evt) { console.log("WEBPLAYER: file: " + this.file_name + " upload complete"); - if (evt.target.readyState == FileReader.DONE) - { + if (evt.target.readyState == FileReader.DONE) { $('#btnAdd').removeClass('disabled'); $('#icnAdd').removeClass('fa-spinner spinning'); $('#icnAdd').addClass('fa-plus'); } - } + } } } -function uploadData(data,name) -{ +function uploadData(data, name) { var dataView = new Uint8Array(data); Module.FS.createDataFile('/', name, dataView, true, false); - var data = Module.FS.readFile(name,{ encoding: 'binary' }); - Module.FS.writeFile('/home/web_user/retroarch/userdata/content/' + name, data ,{ encoding: 'binary' }); + var data = Module.FS.readFile(name, { + encoding: 'binary' + }); + Module.FS.writeFile('/home/web_user/retroarch/userdata/content/' + name, data, { + encoding: 'binary' + }); Module.FS.unlink(name); } @@ -306,8 +292,7 @@ function switchCore(corename) { } function switchStorage(backend) { - if (backend != localStorage.getItem("backend")) - { + if (backend != localStorage.getItem("backend")) { localStorage.setItem("backend", backend); location.reload(); } @@ -315,21 +300,24 @@ function switchStorage(backend) { // When the browser has loaded everything. $(function() { - // Enable all available ToolTips. + // Enable data clear + $('#btnClean').click(function() { + cleanupStorage(); + }); + + // Enable all available ToolTips. $('.tooltip-enable').tooltip({ placement: 'right' }); // Allow hiding the top menu. $('.showMenu').hide(); - $('#btnHideMenu, .showMenu').click(function () { + $('#btnHideMenu, .showMenu').click(function() { $('nav').slideToggle('slow'); $('.showMenu').toggle('slow'); }); - /** - * Attempt to disable some default browser keys. - */ + // Attempt to disable some default browser keys. var keys = { 9: "tab", 13: "enter", @@ -351,20 +339,20 @@ $(function() { 116: "F5", 117: "F6", 118: "F7", - 119: "F8", - 120: "F9", - 121: "F10", - 122: "F11", - 123: "F12" - }; - window.addEventListener('keydown', function (e) { + 119: "F8", + 120: "F9", + 121: "F10", + 122: "F11", + 123: "F12" + }; + window.addEventListener('keydown', function(e) { if (keys[e.which]) { e.preventDefault(); } }); // Switch the core when selecting one. - $('#core-selector a').click(function () { + $('#core-selector a').click(function() { var coreChoice = $(this).data('core'); switchCore(coreChoice); }); @@ -381,8 +369,8 @@ function loadCore(core) { var coreTitle = $('#core-selector a[data-core="' + core + '"]').addClass('active').text(); $('#dropdownMenu1').text(coreTitle); // Load the Core's related JavaScript. - import("./"+core+"_libretro.js").then(script => { - script.default(Module).then(mod => { + import("./" + core + "_libretro.js").then(script => { + script.default(Module).then(mod => { Module = mod; $('#icnRun').removeClass('fa-spinner').removeClass('fa-spin'); $('#icnRun').addClass('fa-play'); @@ -390,18 +378,12 @@ function loadCore(core) { $('#lblLocal').addClass('active'); idbfsInit(); zipfsInit(); - }).catch(err => { console.error("Couldn't instantiate module",err); throw err; }); - }).catch(err => { console.error("Couldn't load script",err); throw err; }); -} - -function keyPress(k) -{ - function kp(k, event) { - var oEvent = new KeyboardEvent(event, { code: k }); - - document.dispatchEvent(oEvent); - document.getElementById('canvas').focus(); - } - kp(k, "keydown"); - setTimeout(function(){kp(k, "keyup")}, 50); -} + }).catch(err => { + console.error("Couldn't instantiate module", err); + throw err; + }); + }).catch(err => { + console.error("Couldn't load script", err); + throw err; + }); +} \ No newline at end of file diff --git a/retroarch.c b/retroarch.c index 27aa4d2a0f2a..ac278959e2fa 100644 --- a/retroarch.c +++ b/retroarch.c @@ -3941,6 +3941,9 @@ bool command_event(enum event_command cmd, void *data) } #endif break; + case CMD_EVENT_RELOAD_CONFIG: + config_load(global_get_ptr()); + break; case CMD_EVENT_DSP_FILTER_INIT: #ifdef HAVE_DSP_FILTER {