diff --git a/.github/workflows/3DS.yml b/.github/workflows/3DS.yml index cd046d7694b..3ab6104380b 100644 --- a/.github/workflows/3DS.yml +++ b/.github/workflows/3DS.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile Salamander run: | diff --git a/.github/workflows/Android.yml b/.github/workflows/Android.yml index c147eb36be7..f7e905f3017 100644 --- a/.github/workflows/Android.yml +++ b/.github/workflows/Android.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile RA run: | diff --git a/.github/workflows/DOS-DJGPP.yml b/.github/workflows/DOS-DJGPP.yml index 5e4f21a96a1..24f8fa91084 100644 --- a/.github/workflows/DOS-DJGPP.yml +++ b/.github/workflows/DOS-DJGPP.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile RA run: | diff --git a/.github/workflows/Emscripten.yml b/.github/workflows/Emscripten.yml index 737533141e7..a67dd6dc3e0 100644 --- a/.github/workflows/Emscripten.yml +++ b/.github/workflows/Emscripten.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile RA run: | diff --git a/.github/workflows/GameCube.yml b/.github/workflows/GameCube.yml index 215bc7b2b91..14c558e6c4b 100644 --- a/.github/workflows/GameCube.yml +++ b/.github/workflows/GameCube.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile RA run: | diff --git a/.github/workflows/MSVC.yml b/.github/workflows/MSVC.yml index 0df1db83ba5..51fa5261422 100644 --- a/.github/workflows/MSVC.yml +++ b/.github/workflows/MSVC.yml @@ -21,7 +21,7 @@ jobs: - configuration: ReleaseANGLE platform: x64 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.1 @@ -50,7 +50,7 @@ jobs: platform: x64 # Qt and Cg builds are excluded for now steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.1 diff --git a/.github/workflows/MacOS.yml b/.github/workflows/MacOS.yml index e757705b38d..b496f192c81 100644 --- a/.github/workflows/MacOS.yml +++ b/.github/workflows/MacOS.yml @@ -12,7 +12,7 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile RA run: | diff --git a/.github/workflows/Miyoo.yml b/.github/workflows/Miyoo.yml index c74aed53841..85c4862d861 100644 --- a/.github/workflows/Miyoo.yml +++ b/.github/workflows/Miyoo.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile RA run: | diff --git a/.github/workflows/PS2.yml b/.github/workflows/PS2.yml index c825a595841..0930844e756 100644 --- a/.github/workflows/PS2.yml +++ b/.github/workflows/PS2.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile Salamander run: | diff --git a/.github/workflows/PS4-ORBIS.yml b/.github/workflows/PS4-ORBIS.yml index 5460503120b..1478e848d9e 100644 --- a/.github/workflows/PS4-ORBIS.yml +++ b/.github/workflows/PS4-ORBIS.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies run: | diff --git a/.github/workflows/PSP.yml b/.github/workflows/PSP.yml index 7bc66566c16..3a6fce1b38a 100644 --- a/.github/workflows/PSP.yml +++ b/.github/workflows/PSP.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile bootstrap run: | diff --git a/.github/workflows/PSVita.yml b/.github/workflows/PSVita.yml index 7f8e2ae8e14..7e70797e077 100644 --- a/.github/workflows/PSVita.yml +++ b/.github/workflows/PSVita.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile Salamander run: | diff --git a/.github/workflows/RS90.yml b/.github/workflows/RS90.yml index 99a630c1f1d..12496a60e21 100644 --- a/.github/workflows/RS90.yml +++ b/.github/workflows/RS90.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile RA run: | diff --git a/.github/workflows/RetroFW.yml b/.github/workflows/RetroFW.yml index 3aa3cf2eeea..43812544911 100644 --- a/.github/workflows/RetroFW.yml +++ b/.github/workflows/RetroFW.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile RA run: | diff --git a/.github/workflows/Switch-libnx.yml b/.github/workflows/Switch-libnx.yml index d3041f8244c..165c87ca4d9 100644 --- a/.github/workflows/Switch-libnx.yml +++ b/.github/workflows/Switch-libnx.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile RA run: | diff --git a/.github/workflows/Wii.yml b/.github/workflows/Wii.yml index dfcfa88379e..e5b84826830 100644 --- a/.github/workflows/Wii.yml +++ b/.github/workflows/Wii.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile Salamander run: | diff --git a/.github/workflows/WiiU.yml b/.github/workflows/WiiU.yml index fca65b108ac..5d3cbbd12bb 100644 --- a/.github/workflows/WiiU.yml +++ b/.github/workflows/WiiU.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile Salamander run: | diff --git a/.github/workflows/Windows-i686-MXE.yml b/.github/workflows/Windows-i686-MXE.yml index 9dfe31cd51e..740a7a5e974 100644 --- a/.github/workflows/Windows-i686-MXE.yml +++ b/.github/workflows/Windows-i686-MXE.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile RA run: | diff --git a/.github/workflows/Windows-x64-MXE.yml b/.github/workflows/Windows-x64-MXE.yml index 3ce50603a49..0b6fda6721a 100644 --- a/.github/workflows/Windows-x64-MXE.yml +++ b/.github/workflows/Windows-x64-MXE.yml @@ -17,7 +17,7 @@ jobs: options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile RA run: | diff --git a/.github/workflows/crowdin-daily.yml b/.github/workflows/crowdin-daily.yml index cb2eb994e20..5eba30cce53 100644 --- a/.github/workflows/crowdin-daily.yml +++ b/.github/workflows/crowdin-daily.yml @@ -25,7 +25,7 @@ jobs: with: python-version: '3.10' - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Crowdin Sync shell: bash env: diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml index 2e1079528de..66049d46737 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -27,7 +27,7 @@ jobs: with: python-version: '3.10' - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Crowdin Sync shell: bash env: diff --git a/.github/workflows/retroarch.yml b/.github/workflows/retroarch.yml index b1de465100e..259fc1a5394 100644 --- a/.github/workflows/retroarch.yml +++ b/.github/workflows/retroarch.yml @@ -22,7 +22,7 @@ jobs: sudo apt-get update -y sudo apt-get install build-essential libxkbcommon-dev libx11-xcb-dev zlib1g-dev libfreetype6-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev nvidia-cg-toolkit nvidia-cg-dev libavcodec-dev libsdl2-dev libsdl-image1.2-dev libxml2-dev yasm - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Configure run: ./configure - name: Build diff --git a/.gitignore b/.gitignore index 0c5d9a3e05c..b0ff3a59c96 100644 --- a/.gitignore +++ b/.gitignore @@ -237,4 +237,11 @@ param.sfo /deps/SPIRV-Cross/out/build/x64-Debug # Visual Studio Code -.vscode/ \ No newline at end of file +.vscode/ + + +*.wasm.* +*.tmp* +*.tmp +obj-emscripten/* +obj-emscripten/ diff --git a/Makefile.common b/Makefile.common index 3adbecfab37..773f055114c 100644 --- a/Makefile.common +++ b/Makefile.common @@ -763,6 +763,9 @@ ifeq ($(HAVE_EMSCRIPTEN), 1) OBJ += audio/drivers/rwebaudio.o endif endif +ifeq ($(EMULATORJS), 1) + OBJ += input/drivers/emulatorjs_input.o +endif ifeq ($(HAVE_BLUETOOTH), 1) OBJ += bluetooth/bluetooth_driver.o @@ -1516,10 +1519,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 88637560b3e..5ab51065447 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -25,6 +25,7 @@ HAVE_AUDIOMIXER = 1 HAVE_CC_RESAMPLER = 1 HAVE_EGL = 1 HAVE_OPENGLES = 1 +HAVE_OPENGLES = 1 HAVE_RJPEG = 0 HAVE_RPNG = 1 HAVE_EMSCRIPTEN = 1 @@ -57,12 +58,47 @@ HAVE_AL = 1 # You have been warned. HAVE_RWEBAUDIO = 0 -ASYNC ?= 0 -ifeq ($(LIBRETRO), mupen64plus) - ASYNC = 1 -endif +EMULATORJS = 1 +ASYNC ?= 0 +PTHREAD ?= 0 LTO ?= 0 +WASM ?= 1 +DEBUG ?= 0 + +DEFINES := -DRARCH_INTERNAL -DHAVE_MAIN +DEFINES += -DHAVE_FILTERS_BUILTIN -DNO_MISSING_ASSET_WARNING -DNO_AUTO_CANVAS_RESIZE -DWEB_SCALING -DNO_CANVAS_FULLSCREEN -DNO_INITIAL_CANVAS_RESIZE -DEMULATORJS + +HAVE_OPENGL = 0 # not supported +EM_OPENGL ?= 0 +HAVE_REGAL ?= 0 + +HAVE_OPENGLES ?= 1 +HAVE_OPENGLES3 ?= 0 #3D cores only + +GL_DEBUG ?= 0 # help diagnose GLSL problems (can cause errors in normal operation) +GENERATE_SOURCEMAP ?= 0 +FS_DEBUG = 0 + +MEMORY_PADDING ?= 0 + +# default value: 5242880 (5 MiB) +STACK_MEMORY ?= 8388608 +# default value: 16777216 (16 MiB) +HEAP_MEMORY ?= 536870912 + +# 8388608 ----- 8 MiB (Stack: recommended) +# 16777216 ---- 16 MiB +# 33554432 ---- 32 MiB +# 67108864 ---- 64 MiB +# 134217728 --- 128 MiB +# 268435456 --- 256 MiB (Heap: recommended) (Stack: recommended for some cores [mupen64plus_next]) +# 536870912 --- 512 MiB (Heap: needed for some cores [mednafen_psx(_hw), mupen64plus_next]) +# 1073741824 -- 1 GiB +# 1610612736 -- 1.5 GiB +# 2147483648 -- 2 GiB +# 4294967296 -- 4 GiB + ifeq ($(LIBRETRO), tyrquake) LTO = 0 endif @@ -76,12 +112,24 @@ PRECISE_F32 = 1 OBJDIR := obj-emscripten #if you compile with SDL2 flag add this Emscripten flag "-s USE_SDL=2" to LDFLAGS: +ifeq ($(HAVE_SDL2), 1) + LIBS += -s USE_SDL=2 + DEFINES += -DHAVE_SDL2 +endif 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 "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain']" \ - -s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS="['_main', '_malloc', '_cmd_savefiles', '_cmd_save_state', '_cmd_load_state', '_cmd_take_screenshot']" \ - --js-library emscripten/library_errno_codes.js \ - --js-library emscripten/library_rwebcam.js +LDFLAGS := -L. --no-heap-copy $(LIBS) -s TOTAL_STACK=$(STACK_MEMORY) -s TOTAL_MEMORY=$(HEAP_MEMORY) -s NO_EXIT_RUNTIME=1 -s EXPORTED_RUNTIME_METHODS="['callMain', 'cwrap', 'getValue', 'FS']" -s EXPORTED_FUNCTIONS=$(EXPORTED_FUNCTIONS) \ + -s EXPORTED_FUNCTIONS=['_main','_malloc','_load_state','_set_volume','_set_variable','_simulate_input','_shader_enable','_get_state_info','_save_state_info','_set_cheat','_cmd_take_screenshot','_system_restart','_cmd_savefiles','_get_core_options','_cmd_save_state','_supports_states','_reset_cheat','_toggleMainLoop','_save_file_path','_get_disk_count','_set_current_disk','_get_current_disk','_refresh_save_files','_toggle_fastforward','_set_ff_ratio','_toggle_slow_motion','_set_sm_ratio','_toggle_rewind','_set_rewind_granularity'] \ + -lidbfs.js \ + -s ERROR_ON_UNDEFINED_SYMBOLS=0 \ + -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 \ + -s ALLOW_MEMORY_GROWTH=1 \ + --js-library emscripten/library_rwebcam.js \ + --js-library emscripten/library_errno_codes.js + +ifneq ($(MEMORY_PADDING), 0) + LDFLAGS += -s GLOBAL_BASE=$(MEMORY_PADDING)mb +endif ifeq ($(HAVE_RWEBAUDIO), 1) LDFLAGS += --js-library emscripten/library_rwebaudio.js @@ -92,8 +140,35 @@ ifeq ($(HAVE_AL), 1) DEFINES += -DHAVE_AL 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 ($(EM_OPENGL), 1) + DEFINES += -DEM_OPENGL + ifeq ($(HAVE_REGAL), 1) + LDFLAGS += -s USE_REGAL=1 + DEFINES += -DHAVE_REGAL + else + LDFLAGS += -s LEGACY_GL_EMULATION=1 -s GL_UNSAFE_OPTS=0 + 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 + + ifneq ($(PTHREAD), 0) - LDFLAGS += -s WASM_MEM_MAX=1073741824 -pthread -s PTHREAD_POOL_SIZE=$(PTHREAD) + LDFLAGS += -s WASM_MEM_MAX=4294967296 -pthread -s PTHREAD_POOL_SIZE=$(PTHREAD) CFLAGS += -pthread HAVE_THREADS=1 else @@ -101,12 +176,13 @@ else endif ifeq ($(ASYNC), 1) - LDFLAGS += -s ASYNCIFY=$(ASYNC) + LDFLAGS += -s ASYNCIFY=1 -s ASYNCIFY_STACK_SIZE=131072 endif -ifeq ($(HAVE_SDL2), 1) - LIBS += -s USE_SDL=2 - DEFINES += -DHAVE_SDL2 +ifeq ($(GENERATE_SOURCEMAP), 1) + CFLAGS += -g4 + CXXFLAGS += -g4 + LDFLAGS += -g4 --source-map-base http://localhost:8000/cores/ # -gsource-map endif include Makefile.common @@ -121,15 +197,22 @@ else libretro += libretro_emscripten.bc endif +ifeq ($(WASM), 1) + LDFLAGS += -s WASM=1 +else + LDFLAGS += -s WASM=0 +endif + + ifneq ($(V), 1) Q := @ endif ifeq ($(DEBUG), 1) - LDFLAGS += -O0 -g + LDFLAGS += -O0 -g -s ASSERTIONS=2 CFLAGS += -O0 -g 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) @@ -138,8 +221,7 @@ else CFLAGS += -O3 endif -CFLAGS += -Wall -I. -Ilibretro-common/include -std=gnu99 $(LIBS) #\ -# -s EXPORTED_FUNCTIONS="['_main', '_malloc', '_cmd_savefiles', '_cmd_save_state', '_cmd_take_screenshot']" +CFLAGS += -Wall -Wunused-function -Wno-unused-command-line-argument -I. -Ilibretro-common/include -std=gnu99 $(LIBS) RARCH_OBJ := $(addprefix $(OBJDIR)/,$(OBJ)) @@ -160,7 +242,7 @@ $(OBJDIR)/%.o: %.cpp $(Q)$(CXX) $(CXXFLAGS) $(DEFINES) $(EOPTS) -c -o $@ $< clean: - rm -rf $(OBJDIR) + #rm -rf $(OBJDIR) rm -f $(TARGET) .PHONY: all clean diff --git a/command.c b/command.c index 93cb11bf6df..6b6cf873d5a 100644 --- a/command.c +++ b/command.c @@ -1065,6 +1065,13 @@ void command_event_set_volume( audio_set_float(AUDIO_ACTION_VOLUME_GAIN, new_volume); } +#ifdef EMULATORJS +void set_volume(float new_volume) +{ + audio_set_float(AUDIO_ACTION_VOLUME_GAIN, new_volume); +} +#endif + /** * event_set_mixer_volume: * @gain : amount of gain to be applied to current volume level. @@ -1723,6 +1730,18 @@ bool command_set_shader(command_t *cmd, const char *arg) } #endif +#ifdef EMULATORJS +void shader_enable(int enabled) +{ + if (enabled) { + command_set_shader(NULL, "/shader/shader.glslp"); + } else { + command_set_shader(NULL, ""); + } +} +#endif + + #ifdef HAVE_CONFIGFILE bool command_event_save_core_config( const char *dir_menu_config, diff --git a/config.def.h b/config.def.h index 9ad699b4271..2def3887e36 100644 --- a/config.def.h +++ b/config.def.h @@ -24,6 +24,8 @@ #include "gfx/video_defines.h" #include "input/input_defines.h" +#define DEFAULT_TOP_PORTRAIT_VIEWPORT false + #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/configuration.c b/configuration.c index 14ac60819ae..594f1397e45 100644 --- a/configuration.c +++ b/configuration.c @@ -185,6 +185,9 @@ enum input_driver_enum INPUT_COCOA, INPUT_QNX, INPUT_RWEBINPUT, +#ifdef EMULATORJS + INPUT_EMULATORJS, +#endif INPUT_DOS, INPUT_WINRAW, INPUT_NULL @@ -598,7 +601,11 @@ static const enum input_driver_enum INPUT_DEFAULT_DRIVER = INPUT_SDL2; #elif defined(WEBOS) && defined(HAVE_SDL2) static const enum input_driver_enum INPUT_DEFAULT_DRIVER = INPUT_SDL2; #elif defined(EMSCRIPTEN) +#ifdef EMULATORJS +static const enum input_driver_enum INPUT_DEFAULT_DRIVER = INPUT_EMULATORJS; +#else static const enum input_driver_enum INPUT_DEFAULT_DRIVER = INPUT_RWEBINPUT; +#endif #elif defined(_WIN32) && defined(HAVE_DINPUT) static const enum input_driver_enum INPUT_DEFAULT_DRIVER = INPUT_DINPUT; #elif defined(_WIN32) && !defined(HAVE_DINPUT) && _WIN32_WINNT >= 0x0501 @@ -1175,6 +1182,10 @@ const char *config_get_default_input(void) return "qnx_input"; case INPUT_RWEBINPUT: return "rwebinput"; +#ifdef EMULATORJS + case INPUT_EMULATORJS: + return "emulatorjs"; +#endif case INPUT_DOS: return "dos"; case INPUT_NULL: @@ -1689,6 +1700,7 @@ static struct config_bool_setting *populate_settings_bool( struct config_bool_setting *tmp = (struct config_bool_setting*)calloc(1, (*size + 1) * sizeof(struct config_bool_setting)); unsigned count = 0; + SETTING_BOOL("video_top_portrait_viewport", &settings->bools.video_top_portrait_viewport, true, DEFAULT_TOP_PORTRAIT_VIEWPORT, false); SETTING_BOOL("accessibility_enable", &settings->bools.accessibility_enable, true, DEFAULT_ACCESSIBILITY_ENABLE, false); SETTING_BOOL("driver_switch_enable", &settings->bools.driver_switch_enable, true, DEFAULT_DRIVER_SWITCH_ENABLE, false); SETTING_BOOL("frame_time_counter_reset_after_fastforwarding", &settings->bools.frame_time_counter_reset_after_fastforwarding, true, false, false); diff --git a/configuration.h b/configuration.h index cc7f9def38c..9e55a81ad2f 100644 --- a/configuration.h +++ b/configuration.h @@ -557,6 +557,7 @@ typedef struct settings bool placeholder; /* Video */ + bool video_top_portrait_viewport; bool video_fullscreen; bool video_windowed_fullscreen; bool video_vsync; diff --git a/dist-scripts/dist-cores.sh b/dist-scripts/dist-cores.sh index 13f603c9b0a..6c0d585f226 100755 --- a/dist-scripts/dist-cores.sh +++ b/dist-scripts/dist-cores.sh @@ -2,9 +2,20 @@ . ../version.all PLATFORM=$1 +CLEAN=$2 +PTHREADS=$3 SALAMANDER=no MAKEFILE_GRIFFIN=no +clean=no +if [ "$CLEAN" = "clean" ] ; then +clean=yes +fi +threads=0 +if [ "$PTHREADS" = "yes" ] ; then +threads=8 +fi + # PSP if [ $PLATFORM = "unix" ] ; then platform=unix @@ -194,16 +205,28 @@ if [ $PLATFORM = "libnx" ]; then make -C ../ -f Makefile.${platform} clean || exit 1 fi +if [ $clean = "yes" ]; then +rm -rf ../obj-emscripten +fi +lastPthreads=0 +lastGles=0 + #for f in *_${platform}.${EXT} ; do 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} + pthread="$threads" + wasm=1 lto=0 whole_archive= big_stack= + memory_padding=0 + + gles3=0 + stack_mem=8388608 + heap_mem=268435456 if [ $PLATFORM = "emscripten" ]; then async=1 #emscripten needs async to sleep @@ -215,7 +238,30 @@ for f in `ls -v *_${platform}.${EXT}`; do echo "Applying big stack..." lto=0 big_stack="BIG_STACK=1" - elif [ $name = "mupen64plus" ] ; then + elif [ $name = "mupen64plus_next" ] ; then + gles3=1 + async=1 + stack_mem=268435456 + heap_mem=536870912 + if [ $wasm = 0 ]; then + continue; + fi + elif [ $name = "picodrive" ] ; then + heap_mem=536870912 + elif [ $name = "pcsx_rearmed" ] || [ $name = "genesis_plus_gx" ]; then + heap_mem=536870912 + elif [ $name = "mednafen_psx" ] || [ $name = "mednafen_psx_hw" ]; then + gles3=1 + heap_mem=536870912 + elif [ $name = "parallel_n64" ] ; then + gles3=1 + async=1 + heap_mem=536870912 + elif [ $name = "ppsspp" ] ; then + gles3=1 + pthread=12 + heap_mem=536870912 + elif [ $name = "scummvm" ] ; then async=1 elif [ $name = "dosbox" ] ; then async=0 @@ -228,14 +274,32 @@ for f in `ls -v *_${platform}.${EXT}`; do fi echo NAME: $name echo ASYNC: $async + echo PTHREAD: $pthread echo LTO: $lto + echo WASM: $wasm + echo GLES3: $gles3 + echo STACK_MEMORY: $stack_mem + echo HEAP_MEMORY: $heap_mem + echo MEMORY_PADDING: $memory_padding + + if [ $clean = "yes" ]; then + if [ $lastPthreads != $pthread ] ; then + rm -rf ../obj-emscripten + fi + if [ $lastGles != $gles3 ] ; then + rm -rf ../obj-emscripten + fi + fi + lastPthreads=$pthread + lastGles=$gles3 + # 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 WASM=$wasm -j7 clean || exit 1 elif [ $PLATFORM = "unix" ]; then make -C ../ -f Makefile LINK=g++ LTO=$lto -j7 clean || exit 1 else @@ -247,8 +311,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 TARGET=${name}_libretro.js" - make -C ../ -f Makefile.emscripten $OPTS PTHREAD=$pthread ASYNC=$async LTO=$lto -j7 TARGET=${name}_libretro.js || exit 1 + echo "BUILD COMMAND: make -C ../ -f Makefile.emscripten $OPTS PTHREAD=$pthread ASYNC=$async WASM=$wasm LTO=$lto HAVE_OPENGLES3=$gles3 STACK_MEMORY=$stack_mem HEAP_MEMORY=$heap_mem MEMORY_PADDING=$memory_padding -j7 TARGET=${name}_libretro.js" + make -C ../ -f Makefile.emscripten $OPTS PTHREAD=$pthread ASYNC=$async WASM=$wasm LTO=$lto HAVE_OPENGLES3=$gles3 STACK_MEMORY=$stack_mem HEAP_MEMORY=$heap_mem -j7 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 @@ -313,11 +377,39 @@ for f in `ls -v *_${platform}.${EXT}`; do mv -f ../retrodos.exe ../pkg/${platform}/cores/${name}.exe elif [ $PLATFORM = "emscripten" ] ; then mkdir -p ../pkg/emscripten/ - mv -f ../${name}_libretro.js ../pkg/emscripten/${name}_libretro.js - mv -f ../${name}_libretro.wasm ../pkg/emscripten/${name}_libretro.wasm + + out_dir="../../EmulatorJS/data/cores" + + mkdir -p $out_dir + + core="" + if [ $name = "mednafen_vb" ]; then + core="beetle_vb" + else + core=${name} + fi + if [ $pthread != 0 ] ; then - mv -f ../${name}_libretro.worker.js ../pkg/emscripten/${name}_libretro.worker.js + 7z a ${out_dir}/${core}-thread-wasm.data ../${name}_libretro.wasm ../${name}_libretro.js ../${name}_libretro.worker.js + else + if [ $wasm = 0 ]; then + 7z a ${out_dir}/${core}-asmjs.data ../${name}_libretro.js.mem ../${name}_libretro.js + rm ../${name}_libretro.js.mem + else + 7z a ${out_dir}/${core}-wasm.data ../${name}_libretro.wasm ../${name}_libretro.js + rm ../${name}_libretro.wasm + fi fi + rm ../${name}_libretro.js + + +# mv -f ../${name}_libretro.js ../pkg/emscripten/${name}_libretro.js +# mv -f ../${name}_libretro.wasm ../pkg/emscripten/${name}_libretro.wasm +# if [ $pthread != 0 ] ; then +# mv -f ../${name}_libretro.worker.js ../pkg/emscripten/${name}_libretro.worker.js +# fi + + fi # Do manual executable step diff --git a/emscripten/library_rwebaudio.js b/emscripten/library_rwebaudio.js index dfbca1e228d..e14ced53016 100644 --- a/emscripten/library_rwebaudio.js +++ b/emscripten/library_rwebaudio.js @@ -3,7 +3,7 @@ var LibraryRWebAudio = { $RA__deps: ['$Browser'], $RA: { - BUFFER_SIZE: 2048, + BUFFER_SIZE: 4096, context: null, buffers: [], @@ -88,7 +88,7 @@ var LibraryRWebAudio = { RA.context = new ac(); RA.numBuffers = ((latency * RA.context.sampleRate) / (1000 * RA.BUFFER_SIZE))|0; - if (RA.numBuffers < 2) RA.numBuffers = 2; + if (RA.numBuffers < 3) RA.numBuffers = 3; for (var i = 0; i < RA.numBuffers; i++) { RA.buffers[i] = RA.context.createBuffer(2, RA.BUFFER_SIZE, RA.context.sampleRate); diff --git a/frontend/drivers/platform_emscripten.c b/frontend/drivers/platform_emscripten.c index c842a49854a..5201512763b 100644 --- a/frontend/drivers/platform_emscripten.c +++ b/frontend/drivers/platform_emscripten.c @@ -60,6 +60,16 @@ void cmd_save_state(void) command_event(CMD_EVENT_SAVE_STATE, NULL); } +#ifdef EMULATORJS +void set_cheat(unsigned index, bool enabled, const char *code) +{ + command_event(CMD_EVENT_LOAD_STATE, NULL); +} +void reset_cheat(void) +{ + retro_cheat_reset(); +} +#else void cmd_load_state(void) { command_event(CMD_EVENT_LOAD_STATE, NULL); @@ -69,6 +79,8 @@ void cmd_take_screenshot(void) { command_event(CMD_EVENT_TAKE_SCREENSHOT, NULL); } +#endif + static void frontend_emscripten_get_env(int *argc, char *argv[], void *args, void *params_data) @@ -134,8 +146,12 @@ static void frontend_emscripten_get_env(int *argc, char *argv[], "screenshots", sizeof(g_defaults.dirs[DEFAULT_DIR_SCREENSHOT])); fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SAVESTATE], user_path, "states", sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE])); +#ifdef EMULATORJS + fill_pathname_join("/", user_path, "system", sizeof("/")); +#else fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SYSTEM], user_path, "system", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM])); +#endif fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS], user_path, "thumbnails", sizeof(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS])); fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_LOGS], user_path, @@ -159,9 +175,12 @@ static void frontend_emscripten_get_env(int *argc, char *argv[], int main(int argc, char *argv[]) { dummyErrnoCodes(); + printf("Built for EmulatorJS Version 4.0.6\nDownload a copy from https://github.com/EmulatorJS/EmulatorJS\nView the licence here: https://github.com/EmulatorJS/EmulatorJS/blob/main/LICENSE\n"); +#ifndef NO_INITIAL_CANVAS_RESIZE emscripten_set_canvas_element_size("#canvas", 800, 600); emscripten_set_element_css_size("#canvas", 800.0, 600.0); +#endif emscripten_set_main_loop(emscripten_mainloop, 0, 0); rarch_main(argc, argv, NULL); diff --git a/gfx/drivers/gl1.c b/gfx/drivers/gl1.c index 6f3e58153f1..18da0464620 100644 --- a/gfx/drivers/gl1.c +++ b/gfx/drivers/gl1.c @@ -1235,6 +1235,7 @@ static void gl1_set_viewport(gl1_t *gl1, int x = 0; int y = 0; float device_aspect = (float)viewport_width / viewport_height; + bool video_top_portrait_viewport = settings->bools.video_top_portrait_viewport; if (gl1->ctx_driver->translate_aspect) device_aspect = gl1->ctx_driver->translate_aspect( @@ -1303,9 +1304,10 @@ static void gl1_set_viewport(gl1_t *gl1, #if defined(RARCH_MOBILE) /* In portrait mode, we want viewport to gravitate to top of screen. */ - if (device_aspect < 1.0f) - gl1->vp.y *= 2; + video_top_portrait_viewport = true; #endif + if (video_top_portrait_viewport && device_aspect < 1.0f) + gl1->vp.y *= 2; glViewport(gl1->vp.x, gl1->vp.y, gl1->vp.width, gl1->vp.height); gl1_set_projection(gl1, &gl1_default_ortho, allow_rotate); diff --git a/gfx/drivers/gl2.c b/gfx/drivers/gl2.c index 4c97275a0e5..eaac6ec81cf 100644 --- a/gfx/drivers/gl2.c +++ b/gfx/drivers/gl2.c @@ -1281,6 +1281,7 @@ static void gl2_set_viewport(gl2_t *gl, int x = 0; int y = 0; float device_aspect = (float)viewport_width / viewport_height; + bool video_top_portrait_viewport = settings->bools.video_top_portrait_viewport; if (gl->ctx_driver->translate_aspect) device_aspect = gl->ctx_driver->translate_aspect( @@ -1349,9 +1350,10 @@ static void gl2_set_viewport(gl2_t *gl, #if defined(RARCH_MOBILE) /* In portrait mode, we want viewport to gravitate to top of screen. */ - if (device_aspect < 1.0f) - gl->vp.y *= 2; + video_top_portrait_viewport = true; #endif + if (video_top_portrait_viewport && device_aspect < 1.0f) + gl->vp.y *= 2; glViewport(gl->vp.x, gl->vp.y, gl->vp.width, gl->vp.height); gl2_set_projection(gl, &default_ortho, allow_rotate); diff --git a/gfx/drivers/gl3.c b/gfx/drivers/gl3.c index bd60ab796db..7634167c948 100644 --- a/gfx/drivers/gl3.c +++ b/gfx/drivers/gl3.c @@ -1386,6 +1386,7 @@ static void gl3_set_viewport(gl3_t *gl, float device_aspect = (float)viewport_width / viewport_height; bool video_scale_integer = settings->bools.video_scale_integer; unsigned video_aspect_ratio_idx = settings->uints.video_aspect_ratio_idx; + bool video_top_portrait_viewport = settings->bools.video_top_portrait_viewport; if (gl->ctx_driver->translate_aspect) device_aspect = gl->ctx_driver->translate_aspect( @@ -1454,9 +1455,10 @@ static void gl3_set_viewport(gl3_t *gl, #if defined(RARCH_MOBILE) /* In portrait mode, we want viewport to gravitate to top of screen. */ - if (device_aspect < 1.0f) - gl->vp.y *= 2; + video_top_portrait_viewport = true; #endif + if (video_top_portrait_viewport && device_aspect < 1.0f) + gl->vp.y *= 2; glViewport(gl->vp.x, gl->vp.y, gl->vp.width, gl->vp.height); gl3_set_projection(gl, &gl3_default_ortho, allow_rotate); diff --git a/gfx/drivers_context/emscriptenegl_ctx.c b/gfx/drivers_context/emscriptenegl_ctx.c index 44805130857..30df616b552 100644 --- a/gfx/drivers_context/emscriptenegl_ctx.c +++ b/gfx/drivers_context/emscriptenegl_ctx.c @@ -46,10 +46,7 @@ typedef struct static void gfx_ctx_emscripten_swap_interval(void *data, int interval) { - if (interval == 0) - emscripten_set_main_loop_timing(EM_TIMING_SETIMMEDIATE, 0); - else - emscripten_set_main_loop_timing(EM_TIMING_RAF, interval); + emscripten_set_main_loop_timing(EM_TIMING_SETIMMEDIATE, 0); } static void gfx_ctx_emscripten_get_canvas_size(int *width, int *height) @@ -58,6 +55,7 @@ static void gfx_ctx_emscripten_get_canvas_size(int *width, int *height) bool is_fullscreen = false; EMSCRIPTEN_RESULT r = emscripten_get_fullscreen_status(&fullscreen_status); +#ifndef NO_CANVAS_FULLSCREEN if (r == EMSCRIPTEN_RESULT_SUCCESS) { if (fullscreen_status.isFullscreen) @@ -67,6 +65,7 @@ static void gfx_ctx_emscripten_get_canvas_size(int *width, int *height) *height = fullscreen_status.screenHeight; } } +#endif if (!is_fullscreen) { @@ -84,7 +83,6 @@ static void gfx_ctx_emscripten_get_canvas_size(int *width, int *height) 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; @@ -102,9 +100,11 @@ static void gfx_ctx_emscripten_check_window(void *data, bool *quit, *height = (unsigned)input_height; *resize = false; +#ifndef NO_AUTO_CANVAS_RESIZE if ( (input_width != emscripten->fb_width) || (input_height != emscripten->fb_height)) { + EMSCRIPTEN_RESULT r; r = emscripten_set_canvas_element_size("#canvas", input_width, input_height); @@ -120,9 +120,15 @@ static void gfx_ctx_emscripten_check_window(void *data, bool *quit, *resize = true; } - +#endif +#ifdef WEB_SCALING + double dpr = emscripten_get_device_pixel_ratio(); + emscripten->fb_width = (unsigned)(input_width * dpr); + emscripten->fb_height = (unsigned)(input_height * dpr); +#else emscripten->fb_width = (unsigned)input_width; emscripten->fb_height = (unsigned)input_height; +#endif *quit = false; } @@ -262,9 +268,15 @@ static void gfx_ctx_emscripten_input_driver(void *data, const char *name, input_driver_t **input, void **input_data) { +#ifdef EMULATORJS + void *emulatorjs = input_driver_init_wrap(&input_emulatorjs, name); + *input = emulatorjs ? &input_emulatorjs : NULL; + *input_data = emulatorjs; +#else void *rwebinput = input_driver_init_wrap(&input_rwebinput, name); *input = rwebinput ? &input_rwebinput : NULL; *input_data = rwebinput; +#endif } static bool gfx_ctx_emscripten_has_focus(void *data) { return g_egl_inited; } diff --git a/gfx/gfx_widgets.c b/gfx/gfx_widgets.c index 698fdf3ad68..47c4969934a 100644 --- a/gfx/gfx_widgets.c +++ b/gfx/gfx_widgets.c @@ -742,6 +742,9 @@ static void gfx_widgets_font_init( { int glyph_width = 0; float scaled_size = font_size * +#ifdef EMULATORJS + 1.25 * +#endif p_dispwidget->last_scale_factor; /* Free existing font */ @@ -1617,7 +1620,11 @@ void gfx_widgets_frame(void *data) int text_width = font_driver_get_message_width( p_dispwidget->gfx_widget_fonts.regular.font, text, +#ifdef EMULATORJS + strlen(text), 1.5f); +#else strlen(text), 1.0f); +#endif int total_width = text_width + p_dispwidget->simple_widget_padding * 2; @@ -1725,6 +1732,7 @@ void gfx_widgets_frame(void *data) widget->frame(data, p_dispwidget); } +#ifndef EMULATORJS /* Draw all messages */ if (p_dispwidget->current_msgs_size) { @@ -1759,6 +1767,7 @@ void gfx_widgets_frame(void *data) slock_unlock(p_dispwidget->current_msgs_lock); #endif } +#endif /* Ensure all text is flushed */ gfx_widgets_flush_text(video_width, video_height, diff --git a/gfx/video_shader_parse.c b/gfx/video_shader_parse.c index 3267695175c..73e99c88ad5 100644 --- a/gfx/video_shader_parse.c +++ b/gfx/video_shader_parse.c @@ -3007,6 +3007,7 @@ bool video_shader_apply_shader( sizeof(msg) - _len); } +#ifndef EMULATORJS #ifdef HAVE_GFX_WIDGETS if (dispwidget_get_ptr()->active) gfx_widget_set_generic_message(msg, 2000); @@ -3014,6 +3015,7 @@ bool video_shader_apply_shader( #endif runloop_msg_queue_push(msg, 1, 120, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); +#endif } RARCH_LOG("[Shaders]: %s: \"%s\".\n", diff --git a/griffin/griffin.c b/griffin/griffin.c index 3c194a3e2ad..09520783c98 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -660,8 +660,12 @@ INPUT #include "../input/drivers/qnx_input.c" #include "../input/drivers_joypad/qnx_joypad.c" #elif defined(EMSCRIPTEN) +#ifdef EMULATORJS +#include "../input/drivers/emulatorjs_input.c" +#else #include "../input/drivers/rwebinput_input.c" #include "../input/drivers_joypad/rwebpad_joypad.c" +#endif #elif defined(DJGPP) #include "../input/drivers/dos_input.c" #include "../input/drivers_joypad/dos_joypad.c" diff --git a/input/drivers/emulatorjs_input.c b/input/drivers/emulatorjs_input.c new file mode 100644 index 00000000000..7ebb14854cc --- /dev/null +++ b/input/drivers/emulatorjs_input.c @@ -0,0 +1,754 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2018 - Michael Lelli + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef WEB_SCALING +#include +#endif + +#include + +#include "../input_keymaps.h" + +#include "../../tasks/tasks_internal.h" +#include "../../configuration.h" +#include "../../retroarch.h" +#include "../../verbosity.h" + +/* https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button */ +#define RWEBINPUT_MOUSE_BTNL 0 +#define RWEBINPUT_MOUSE_BTNM 1 +#define RWEBINPUT_MOUSE_BTNR 2 +#define RWEBINPUT_MOUSE_BTN4 3 +#define RWEBINPUT_MOUSE_BTN5 4 + +typedef struct rwebinput_key_to_code_map_entry +{ + const char *key; + enum retro_key rk; +} rwebinput_key_to_code_map_entry_t; + +typedef struct rwebinput_keyboard_event +{ + int type; + EmscriptenKeyboardEvent event; +} rwebinput_keyboard_event_t; + +typedef struct rwebinput_keyboard_event_queue +{ + rwebinput_keyboard_event_t *events; + size_t count; + size_t max_size; +} rwebinput_keyboard_event_queue_t; + +typedef struct rwebinput_touch +{ + long touch_id; + long last_canvasX; + long last_canvasY; + bool down; + long last_touchdown_id; + long last_touchdown_location; + bool clicked_yet; +} rwebinput_touch_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; + uint8_t buttons; +} rwebinput_mouse_state_t; + +typedef struct rwebinput_input +{ + rwebinput_touch_t touch; + rwebinput_mouse_state_t mouse; /* double alignment */ + rwebinput_keyboard_event_queue_t keyboard; /* ptr alignment */ + bool keys[RETROK_LAST]; +} rwebinput_input_t; + +/* KeyboardEvent.keyCode has been deprecated for a while and doesn't have + * separate left/right modifer codes, so we have to map string labels from + * KeyboardEvent.code to retro keys */ +static const rwebinput_key_to_code_map_entry_t rwebinput_key_to_code_map[] = +{ + { "KeyA", RETROK_a }, + { "KeyB", RETROK_b }, + { "KeyC", RETROK_c }, + { "KeyD", RETROK_d }, + { "KeyE", RETROK_e }, + { "KeyF", RETROK_f }, + { "KeyG", RETROK_g }, + { "KeyH", RETROK_h }, + { "KeyI", RETROK_i }, + { "KeyJ", RETROK_j }, + { "KeyK", RETROK_k }, + { "KeyL", RETROK_l }, + { "KeyM", RETROK_m }, + { "KeyN", RETROK_n }, + { "KeyO", RETROK_o }, + { "KeyP", RETROK_p }, + { "KeyQ", RETROK_q }, + { "KeyR", RETROK_r }, + { "KeyS", RETROK_s }, + { "KeyT", RETROK_t }, + { "KeyU", RETROK_u }, + { "KeyV", RETROK_v }, + { "KeyW", RETROK_w }, + { "KeyX", RETROK_x }, + { "KeyY", RETROK_y }, + { "KeyZ", RETROK_z }, + { "ArrowLeft", RETROK_LEFT }, + { "ArrowRight", RETROK_RIGHT }, + { "ArrowUp", RETROK_UP }, + { "ArrowDown", RETROK_DOWN }, + { "Enter", RETROK_RETURN }, + { "NumpadEnter", RETROK_KP_ENTER }, + { "Tab", RETROK_TAB }, + { "Insert", RETROK_INSERT }, + { "Delete", RETROK_DELETE }, + { "End", RETROK_END }, + { "Home", RETROK_HOME }, + { "ShiftRight", RETROK_RSHIFT }, + { "ShiftLeft", RETROK_LSHIFT }, + { "ControlLeft", RETROK_LCTRL }, + { "AltLeft", RETROK_LALT }, + { "Space", RETROK_SPACE }, + { "Escape", RETROK_ESCAPE }, + { "NumpadAdd", RETROK_KP_PLUS }, + { "NumpadSubtract", RETROK_KP_MINUS }, + { "F1", RETROK_F1 }, + { "F2", RETROK_F2 }, + { "F3", RETROK_F3 }, + { "F4", RETROK_F4 }, + { "F5", RETROK_F5 }, + { "F6", RETROK_F6 }, + { "F7", RETROK_F7 }, + { "F8", RETROK_F8 }, + { "F9", RETROK_F9 }, + { "F10", RETROK_F10 }, + { "F11", RETROK_F11 }, + { "F12", RETROK_F12 }, + { "Digit0", RETROK_0 }, + { "Digit1", RETROK_1 }, + { "Digit2", RETROK_2 }, + { "Digit3", RETROK_3 }, + { "Digit4", RETROK_4 }, + { "Digit5", RETROK_5 }, + { "Digit6", RETROK_6 }, + { "Digit7", RETROK_7 }, + { "Digit8", RETROK_8 }, + { "Digit9", RETROK_9 }, + { "PageUp", RETROK_PAGEUP }, + { "PageDown", RETROK_PAGEDOWN }, + { "Numpad0", RETROK_KP0 }, + { "Numpad1", RETROK_KP1 }, + { "Numpad2", RETROK_KP2 }, + { "Numpad3", RETROK_KP3 }, + { "Numpad4", RETROK_KP4 }, + { "Numpad5", RETROK_KP5 }, + { "Numpad6", RETROK_KP6 }, + { "Numpad7", RETROK_KP7 }, + { "Numpad8", RETROK_KP8 }, + { "Numpad9", RETROK_KP9 }, + { "Period", RETROK_PERIOD }, + { "CapsLock", RETROK_CAPSLOCK }, + { "NumLock", RETROK_NUMLOCK }, + { "Backspace", RETROK_BACKSPACE }, + { "NumpadMultiply", RETROK_KP_MULTIPLY }, + { "NumpadDivide", RETROK_KP_DIVIDE }, + { "PrintScreen", RETROK_PRINT }, + { "ScrollLock", RETROK_SCROLLOCK }, + { "Backquote", RETROK_BACKQUOTE }, + { "Pause", RETROK_PAUSE }, + { "Quote", RETROK_QUOTE }, + { "Comma", RETROK_COMMA }, + { "Minus", RETROK_MINUS }, + { "Slash", RETROK_SLASH }, + { "Semicolon", RETROK_SEMICOLON }, + { "Equal", RETROK_EQUALS }, + { "BracketLeft", RETROK_LEFTBRACKET }, + { "Backslash", RETROK_BACKSLASH }, + { "BracketRight", RETROK_RIGHTBRACKET }, + { "NumpadDecimal", RETROK_KP_PERIOD }, + { "NumpadEqual", RETROK_KP_EQUALS }, + { "ControlRight", RETROK_RCTRL }, + { "AltRight", RETROK_RALT }, + { "F13", RETROK_F13 }, + { "F14", RETROK_F14 }, + { "F15", RETROK_F15 }, + { "MetaRight", RETROK_RMETA }, + { "MetaLeft", RETROK_LMETA }, + { "Help", RETROK_HELP }, + { "ContextMenu", RETROK_MENU }, + { "Power", RETROK_POWER }, +}; + +/* to make the string labels for codes from JavaScript work, we convert them + * to CRC32 hashes for the LUT */ +static void rwebinput_generate_lut(void) +{ + int i; + struct rarch_key_map *key_map; + + retro_assert(ARRAY_SIZE(rarch_key_map_rwebinput) == + ARRAY_SIZE(rwebinput_key_to_code_map) + 1); + + for (i = 0; i < ARRAY_SIZE(rwebinput_key_to_code_map); i++) + { + int j; + uint32_t crc; + const rwebinput_key_to_code_map_entry_t *key_to_code = + &rwebinput_key_to_code_map[i]; + key_map = &rarch_key_map_rwebinput[i]; + crc = encoding_crc32(0, (const uint8_t *)key_to_code->key, + strlen(key_to_code->key)); + + /* sanity check: make sure there's no collisions */ + for (j = 0; j < i; j++) + retro_assert(rarch_key_map_rwebinput[j].sym != crc); + + key_map->rk = key_to_code->rk; + key_map->sym = crc; + } + + /* set terminating entry */ + key_map = &rarch_key_map_rwebinput[ + ARRAY_SIZE(rarch_key_map_rwebinput) - 1]; + key_map->rk = RETROK_UNKNOWN; + key_map->sym = 0; +} +static EM_BOOL rwebinput_mouse_cb(int event_type, + const EmscriptenMouseEvent *mouse_event, void *user_data) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)user_data; + + uint8_t mask = 1 << mouse_event->button; + +#ifdef WEB_SCALING + double dpr = emscripten_get_device_pixel_ratio(); + rwebinput->mouse.x = (long)(mouse_event->targetX * dpr); + rwebinput->mouse.y = (long)(mouse_event->targetY * dpr); + rwebinput->mouse.pending_delta_x += (long)(mouse_event->movementX * dpr); + rwebinput->mouse.pending_delta_y += (long)(mouse_event->movementY * dpr); +#else + rwebinput->mouse.x = mouse_event->targetX; + rwebinput->mouse.y = mouse_event->targetY; + rwebinput->mouse.pending_delta_x += mouse_event->movementX; + rwebinput->mouse.pending_delta_y += mouse_event->movementY; +#endif + + if (event_type == EMSCRIPTEN_EVENT_MOUSEDOWN) + rwebinput->mouse.buttons |= mask; + else if (event_type == EMSCRIPTEN_EVENT_MOUSEUP) + rwebinput->mouse.buttons &= ~mask; + + return EM_FALSE; +} + +static EM_BOOL rwebinput_wheel_cb(int event_type, + const EmscriptenWheelEvent *wheel_event, void *user_data) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)user_data; + +#ifdef WEB_SCALING + 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; +#else + rwebinput->mouse.pending_scroll_x += wheel_event->deltaX; + rwebinput->mouse.pending_scroll_y += wheel_event->deltaY; +#endif + + 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; + rwebinput_touch_t *touch_handler = &rwebinput->touch; + + EmscriptenTouchPoint changed_touch; + bool touch_changed = false; + for (int i=0; inumTouches; i++) { + if (touch_event->touches[i].isChanged) { + changed_touch = touch_event->touches[i]; + touch_changed = true; + } + } + if (!touch_changed) return EM_TRUE; + if (event_type == EMSCRIPTEN_EVENT_TOUCHSTART && touch_handler->last_touchdown_id != changed_touch.identifier) { + touch_handler->clicked_yet = false; + touch_handler->last_touchdown_id = changed_touch.identifier; + touch_handler->last_touchdown_location = (changed_touch.canvasX + changed_touch.canvasY); + } + if (event_type == EMSCRIPTEN_EVENT_TOUCHSTART && touch_handler->clicked_yet) { + rwebinput->mouse.buttons |= 1 << 0; + } + if (event_type == EMSCRIPTEN_EVENT_TOUCHMOVE && touch_handler->last_touchdown_id == changed_touch.identifier) { + long u = touch_handler->last_touchdown_location - (changed_touch.canvasX + changed_touch.canvasY); + //25 may be too much of an offset... + if (((u<0)?-u:u) > 25) { + touch_handler->last_touchdown_id = -1; + } + } + + if (event_type == EMSCRIPTEN_EVENT_TOUCHCANCEL || event_type == EMSCRIPTEN_EVENT_TOUCHEND) { + if (changed_touch.identifier == touch_handler->touch_id) { + touch_handler->down = false; + } + if (touch_handler->last_touchdown_id == changed_touch.identifier && !touch_handler->clicked_yet) { + touch_handler->clicked_yet = true; + } else if (touch_handler->clicked_yet) { + rwebinput->mouse.buttons &= ~(1 << 0); + touch_handler->clicked_yet = false; + touch_handler->last_touchdown_id = -1; + } + return EM_TRUE; + } else if (touch_handler->down && changed_touch.identifier != touch_handler->touch_id) { + return EM_TRUE; //I am not supporting multi touch + } + if (event_type == EMSCRIPTEN_EVENT_TOUCHSTART) { + touch_handler->down = true; + touch_handler->touch_id = changed_touch.identifier; + touch_handler->last_canvasX = changed_touch.canvasX; + touch_handler->last_canvasY = changed_touch.canvasY; + } else if (event_type == EMSCRIPTEN_EVENT_TOUCHMOVE) { + long diffX = changed_touch.canvasX - touch_handler->last_canvasX; + long diffY = changed_touch.canvasY - touch_handler->last_canvasY; + touch_handler->last_canvasX = changed_touch.canvasX; + touch_handler->last_canvasY = changed_touch.canvasY; + +#ifdef WEB_SCALING + double dpr = emscripten_get_device_pixel_ratio(); + rwebinput->mouse.x = (long)(changed_touch.canvasX * dpr); + rwebinput->mouse.y = (long)(changed_touch.canvasY * dpr); + rwebinput->mouse.pending_delta_x += (long)(diffX * dpr); + rwebinput->mouse.pending_delta_y += (long)(diffY * dpr); +#else + rwebinput->mouse.x = changed_touch.canvasX; + rwebinput->mouse.y = changed_touch.canvasY; + rwebinput->mouse.pending_delta_x += diffX; + rwebinput->mouse.pending_delta_y += diffY; +#endif + + //printf("diff: %li\n", diffX); + } + + return EM_TRUE; +} + +static void *rwebinput_input_init(const char *joypad_driver) +{ + EMSCRIPTEN_RESULT r; + rwebinput_input_t *rwebinput = + (rwebinput_input_t*)calloc(1, sizeof(*rwebinput)); + + if (!rwebinput) + return NULL; + + rwebinput_generate_lut(); + + r = emscripten_set_mousedown_callback("#canvas", rwebinput, false, rwebinput_mouse_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create mousedown callback: %d\n", r); + } + + r = emscripten_set_mouseup_callback("#canvas", rwebinput, false, rwebinput_mouse_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create mouseup callback: %d\n", r); + } + + r = emscripten_set_mousemove_callback("#canvas", rwebinput, false, rwebinput_mouse_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create mousemove callback: %d\n", r); + } + + r = emscripten_set_wheel_callback("#canvas", rwebinput, false, rwebinput_wheel_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[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 wheel 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 wheel 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 wheel 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 wheel callback: %d\n", r); + } + + input_keymaps_init_keyboard_lut(rarch_key_map_rwebinput); + + return rwebinput; +} +static int16_t rwebinput_mouse_state( + rwebinput_mouse_state_t *mouse, + unsigned id, bool screen) +{ + switch (id) + { + case RETRO_DEVICE_ID_MOUSE_X: + return (int16_t)(screen ? mouse->x : mouse->delta_x); + case RETRO_DEVICE_ID_MOUSE_Y: + return (int16_t)(screen ? mouse->y : mouse->delta_y); + case RETRO_DEVICE_ID_MOUSE_LEFT: + return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTNL)); + case RETRO_DEVICE_ID_MOUSE_RIGHT: + return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTNR)); + case RETRO_DEVICE_ID_MOUSE_MIDDLE: + return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTNM)); + case RETRO_DEVICE_ID_MOUSE_BUTTON_4: + return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTN4)); + case RETRO_DEVICE_ID_MOUSE_BUTTON_5: + return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTN5)); + case RETRO_DEVICE_ID_MOUSE_WHEELUP: + return mouse->scroll_y < 0.0; + case RETRO_DEVICE_ID_MOUSE_WHEELDOWN: + return mouse->scroll_y > 0.0; + case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP: + return mouse->scroll_x < 0.0; + case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN: + return mouse->scroll_x > 0.0; + } + + return 0; +} +struct rwebinput_code_to_key +{ + const int id; + int down_1; + int down_2; + int down_3; + int down_4; +}; + +static struct rwebinput_code_to_key stuff[] = +{ + { 0, 0, 0, 0, 0 }, //b + { 1, 0, 0, 0, 0 }, //y + { 2, 0, 0, 0, 0 }, //select + { 3, 0, 0, 0, 0 }, //start + { 4, 0, 0, 0, 0 }, //up + { 5, 0, 0, 0, 0 }, //down + { 6, 0, 0, 0, 0 }, //left + { 7, 0, 0, 0, 0 }, //right + { 8, 0, 0, 0, 0 }, //a + { 9, 0, 0, 0, 0 }, //x + { 10, 0, 0, 0, 0 }, //l + { 11, 0, 0, 0, 0 }, //r + { 12, 0, 0, 0, 0 }, //l2 + { 13, 0, 0, 0, 0 }, //r2 + { 14, 0, 0, 0, 0 }, //l3 + { 15, 0, 0, 0, 0 }, //r3 + { 16, 0, 0, 0, 0 }, //L STICK RIGHT + { 17, 0, 0, 0, 0 }, //L STICK LEFT + { 18, 0, 0, 0, 0 }, //L STICK DOWN + { 19, 0, 0, 0, 0 }, //L STICK UP + { 20, 0, 0, 0, 0 }, //R STICK RIGHT + { 21, 0, 0, 0, 0 }, //R STICK LEFT + { 22, 0, 0, 0, 0 }, //R STICK DOWN + { 23, 0, 0, 0, 0 }, //R STICK UP + { 24, 0, 0, 0, 0 }, + { 25, 0, 0, 0, 0 }, + { 26, 0, 0, 0, 0 }, +}; + +void simulate_input(int user, int key, int down) +{ + int i; + for (i=0; i=24) return 0; + for (int i=0; ikeys[id]); + case RETRO_DEVICE_MOUSE: + case RARCH_DEVICE_MOUSE_SCREEN: + 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; + rwebinput_mouse_state_t + *mouse = &rwebinput->mouse; + const int edge_detect = 32700; + bool screen = device == + RARCH_DEVICE_POINTER_SCREEN; + bool inside = false; + int16_t res_x = 0; + int16_t res_y = 0; + int16_t res_screen_x = 0; + int16_t res_screen_y = 0; + + vp.x = 0; + vp.y = 0; + vp.width = 0; + vp.height = 0; + vp.full_width = 0; + vp.full_height = 0; + + if (!(video_driver_translate_coord_viewport_wrap( + &vp, mouse->x, mouse->y, + &res_x, &res_y, &res_screen_x, &res_screen_y))) + return 0; + + if (screen) + { + res_x = res_screen_x; + res_y = res_screen_y; + } + + inside = (res_x >= -edge_detect) + && (res_y >= -edge_detect) + && (res_x <= edge_detect) + && (res_y <= edge_detect); + + switch (id) + { + case RETRO_DEVICE_ID_POINTER_X: + if (inside) + return res_x; + break; + case RETRO_DEVICE_ID_POINTER_Y: + if (inside) + return res_y; + break; + case RETRO_DEVICE_ID_POINTER_PRESSED: + return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTNL)); + case RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN: + return !inside; + default: + break; + } + } + break; + } + + return 0; +} + +static void rwebinput_input_free(void *data) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + + emscripten_html5_remove_all_event_listeners(); + + free(rwebinput->keyboard.events); + + free(data); +} + +static void rwebinput_process_keyboard_events( + rwebinput_input_t *rwebinput, + rwebinput_keyboard_event_t *event) +{ + uint32_t keycode; + unsigned translated_keycode; + const EmscriptenKeyboardEvent *key_event = &event->event; + bool keydown = + event->type == EMSCRIPTEN_EVENT_KEYDOWN; + + keycode = encoding_crc32(0, (const uint8_t *)key_event->code, + strnlen(key_event->code, sizeof(key_event->code))); + translated_keycode = input_keymaps_translate_keysym_to_rk(keycode); + + + if ( translated_keycode < RETROK_LAST + && translated_keycode != RETROK_UNKNOWN) + rwebinput->keys[translated_keycode] = keydown; +} + +static void rwebinput_input_poll(void *data) +{ + size_t i; + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + + for (i = 0; i < rwebinput->keyboard.count; i++) + rwebinput_process_keyboard_events(rwebinput, + &rwebinput->keyboard.events[i]); + + rwebinput->keyboard.count = 0; + + rwebinput->mouse.delta_x = rwebinput->mouse.pending_delta_x; + rwebinput->mouse.delta_y = rwebinput->mouse.pending_delta_y; + rwebinput->mouse.pending_delta_x = 0; + rwebinput->mouse.pending_delta_y = 0; + + rwebinput->mouse.scroll_x = rwebinput->mouse.pending_scroll_x; + rwebinput->mouse.scroll_y = rwebinput->mouse.pending_scroll_y; + rwebinput->mouse.pending_scroll_x = 0; + rwebinput->mouse.pending_scroll_y = 0; +} + +static uint64_t rwebinput_get_capabilities(void *data) +{ + uint64_t caps = 0; + + caps |= (1 << RETRO_DEVICE_JOYPAD); + caps |= (1 << RETRO_DEVICE_ANALOG); + caps |= (1 << RETRO_DEVICE_KEYBOARD); + caps |= (1 << RETRO_DEVICE_MOUSE); + caps |= (1 << RETRO_DEVICE_POINTER); + + return caps; +} + +input_driver_t input_emulatorjs = { + rwebinput_input_init, + rwebinput_input_poll, + rwebinput_input_state, + rwebinput_input_free, + NULL, + NULL, + rwebinput_get_capabilities, + "emulatorjs", + NULL, + NULL +}; diff --git a/input/input_driver.c b/input/input_driver.c index 46d4c0ccb4a..5fb9fbc5f60 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -278,7 +278,7 @@ input_device_driver_t *joypad_drivers[] = { #if defined(HAVE_HID) && !defined(WIIU) &hid_joypad, #endif -#ifdef EMSCRIPTEN +#if defined(EMSCRIPTEN) && !defined(EMULATORJS) &rwebpad_joypad, #endif &null_joypad, @@ -356,8 +356,12 @@ input_driver_t *input_drivers[] = { &input_qnx, #endif #ifdef EMSCRIPTEN +#ifdef EMULATORJS + &input_emulatorjs, +#else &input_rwebinput, #endif +#endif #ifdef DJGPP &input_dos, #endif diff --git a/input/input_driver.h b/input/input_driver.h index c353229902b..cbd9326fde9 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -1093,6 +1093,7 @@ extern input_driver_t input_udev; extern input_driver_t input_cocoa; extern input_driver_t input_qnx; extern input_driver_t input_rwebinput; +extern input_driver_t input_emulatorjs; extern input_driver_t input_dos; extern input_driver_t input_winraw; extern input_driver_t input_wayland; diff --git a/libretro-common/queues/task_queue.c b/libretro-common/queues/task_queue.c index 137e4adc4b4..5f7905ccff8 100644 --- a/libretro-common/queues/task_queue.c +++ b/libretro-common/queues/task_queue.c @@ -91,6 +91,7 @@ static void task_queue_msg_push(retro_task_t *task, static void task_queue_push_progress(retro_task_t *task) { + task->mute = true; #ifdef HAVE_THREADS /* msg_push callback interacts directly with the task properties (particularly title). * make sure another thread doesn't modify them while rendering @@ -978,7 +979,7 @@ retro_task_t *task_init(void) task->cleanup = NULL; task->finished = false; task->cancelled = false; - task->mute = false; + task->mute = true; task->task_data = NULL; task->user_data = NULL; task->state = NULL; diff --git a/retroarch.c b/retroarch.c index c4927a67e75..2f9c763a5f3 100644 --- a/retroarch.c +++ b/retroarch.c @@ -4924,9 +4924,18 @@ int rarch_main(int argc, char *argv[], void *data) #ifdef HAVE_RWEBAUDIO void RWebAudioRecalibrateTime(void); #endif +#ifdef EMULATORJS +bool EJS_PAUSED = false; +bool EJS_MAINLOOP_PAUSED = false; +#endif void emscripten_mainloop(void) { + if (EJS_PAUSED && !EJS_MAINLOOP_PAUSED) { + emscripten_pause_main_loop(); + EJS_MAINLOOP_PAUSED = true; + return; + } int ret; static unsigned emscripten_frame_count = 0; video_driver_state_t *video_st = video_state_get_ptr(); @@ -4973,6 +4982,137 @@ void emscripten_mainloop(void) main_exit(NULL); emscripten_force_exit(0); } + +#ifdef EMULATORJS + +char* save_file_path() { + runloop_state_t *runloop_st = runloop_state_get_ptr(); + return runloop_st->name.savefile; +} + +void toggleMainLoop(int running) { + if (running == 1 && EJS_MAINLOOP_PAUSED) { + emscripten_resume_main_loop(); + EJS_MAINLOOP_PAUSED = false; + } + EJS_PAUSED = (running == 0); +} + +void cmd_load_state(void) +{ + command_event(CMD_EVENT_LOAD_STATE, NULL);//done +} + +int load_state(char *path, int rv) +{ + content_load_state(path, false, false); + return rv; +} + +void system_restart(void) +{ + command_event(CMD_EVENT_RESET, NULL);//done +} + +void cmd_take_screenshot(void) +{ + const char *path = "/screenshot.png"; + video_driver_state_t *video_st = video_state_get_ptr(); + if (!take_screenshot(NULL, + path, true, + video_st->frame_cache_data && (video_st->frame_cache_data == RETRO_HW_FRAME_BUFFER_VALID), true, false)) + printf("Error taking screenshot"); +} + +int get_disk_count(void) +{ + runloop_state_t *runloop_st = runloop_state_get_ptr(); + rarch_system_info_t *sys_info = &runloop_st->system; + + if (!sys_info) + return -1; + + if (!disk_control_enabled(&sys_info->disk_control)) return -1; + + return (&sys_info->disk_control)->cb.get_num_images(); +} + +void set_current_disk(int index) +{ + runloop_state_t *runloop_st = runloop_state_get_ptr(); + rarch_system_info_t *sys_info = &runloop_st->system; + + if (!sys_info) + return; + + if (!disk_control_enabled(&sys_info->disk_control)) return; + + if (!disk_control_get_eject_state(&sys_info->disk_control)) { + disk_control_set_eject_state(&sys_info->disk_control, true, false); + } + disk_control_set_index(&sys_info->disk_control, index, false); + if (disk_control_get_eject_state(&sys_info->disk_control)) { + disk_control_set_eject_state(&sys_info->disk_control, false, false); + } +} + +int get_current_disk(void) +{ + runloop_state_t *runloop_st = runloop_state_get_ptr(); + rarch_system_info_t *sys_info = &runloop_st->system; + + if (!sys_info) + return -1; + + if (!disk_control_enabled(&sys_info->disk_control)) return -1; + + return (&sys_info->disk_control)->cb.get_image_index(); +} +bool FF_ACTIVE; +void toggle_fastforward(bool enabled) +{ + FF_ACTIVE = enabled; +} +bool EJS_IS_FASTFORWARD() +{ + return FF_ACTIVE; +} +void set_ff_ratio(float ratio) { + settings_t *settings = config_get_ptr(); + settings->floats.fastforward_ratio = ratio; +} + +bool SM_ACTIVE; +void toggle_slow_motion(bool enabled) +{ + SM_ACTIVE = enabled; +} +bool EJS_IS_SLOWMOTION() +{ + return SM_ACTIVE; +} +void set_sm_ratio(float ratio) +{ + settings_t *settings = config_get_ptr(); + settings->floats.slowmotion_ratio = ratio; +} + +bool REWIND_ACTIVE; +void toggle_rewind(bool enabled) +{ + REWIND_ACTIVE = enabled; +} +bool EJS_IS_REWIND() +{ + return REWIND_ACTIVE; +} +void set_rewind_granularity(uint granularity) +{ + settings_t *settings = config_get_ptr(); + settings->uints.rewind_granularity = granularity; +} +#endif + #endif #ifndef HAVE_MAIN diff --git a/runloop.c b/runloop.c index 3cede99b3d4..f35790d8d70 100644 --- a/runloop.c +++ b/runloop.c @@ -5356,6 +5356,12 @@ static void runloop_pause_toggle( command_event(CMD_EVENT_PAUSE, NULL); } +#ifdef EMULATORJS +bool EJS_IS_FASTFORWARD(); +bool EJS_IS_SLOWMOTION(); +bool EJS_IS_REWIND(); +#endif + static enum runloop_state_enum runloop_check_state( bool error_on_init, settings_t *settings, @@ -6049,7 +6055,11 @@ static enum runloop_state_enum runloop_check_state( char s[128]; bool rewinding = false; static bool old_rewind_pressed = false; +#ifdef EMULATORJS + bool rewind_pressed = EJS_IS_REWIND(); +#else bool rewind_pressed = BIT256_GET(current_bits, RARCH_REWIND); +#endif unsigned t = 0; s[0] = '\0'; @@ -6325,10 +6335,15 @@ static enum runloop_state_enum runloop_check_state( { static bool old_button_state = false; static bool old_hold_button_state = false; +#ifdef EMULATORJS + bool new_button_state = EJS_IS_FASTFORWARD(); + bool new_hold_button_state = EJS_IS_FASTFORWARD(); +#else bool new_button_state = BIT256_GET( current_bits, RARCH_FAST_FORWARD_KEY); bool new_hold_button_state = BIT256_GET( current_bits, RARCH_FAST_FORWARD_HOLD_KEY); +#endif bool check2 = new_button_state && !old_button_state; @@ -6421,10 +6436,16 @@ static enum runloop_state_enum runloop_check_state( /* Check slowmotion hotkeys */ static bool old_slowmotion_button_state = false; static bool old_slowmotion_hold_button_state = false; + +#ifdef EMULATORJS + bool new_slowmotion_button_state = EJS_IS_SLOWMOTION(); + bool new_slowmotion_hold_button_state = EJS_IS_SLOWMOTION(); +#else bool new_slowmotion_button_state = BIT256_GET( current_bits, RARCH_SLOWMOTION_KEY); bool new_slowmotion_hold_button_state = BIT256_GET( current_bits, RARCH_SLOWMOTION_HOLD_KEY); +#endif /* Don't allow slowmotion while paused */ if (runloop_paused) @@ -8183,3 +8204,64 @@ void runloop_path_set_special(char **argv, unsigned num_content) runloop_st->name.savestate); } } + + +#ifdef EMULATORJS +void set_variable(char key[], char value[]) +{ + if (strcmp(key, "fps") == 0) { + settings_t *settings = config_get_ptr(); + if (strcmp(value, "show") == 0) { + settings->bools.video_fps_show = true; + } else { + settings->bools.video_fps_show = false; + } + return; + } + runloop_state_t *runloop_st = &runloop_state; + size_t opt_idx; + size_t val_idx; + if (!core_option_manager_get_idx(runloop_st->core_options, key, &opt_idx)) { + printf("invalid core option %s. This is not an error.\n", key); + return; + } + if (!core_option_manager_get_val_idx(runloop_st->core_options, opt_idx, value, &val_idx)) { + printf("invalid core value %s for %s\n", value, key); + return; + } + if (val_idx != runloop_st->core_options->opts[opt_idx].index) { + core_option_manager_set_val(runloop_st->core_options, opt_idx, val_idx, true); + } +} + +char rv[10000]; //bruh +char* get_core_options(void) +{ + memset(rv, '\0', sizeof(rv)); + runloop_state_t *runloop_st = &runloop_state; + int o = 0; + for (int i=0; icore_options->size; i++) { + size_t opt_idx; + size_t val_idx1; + struct core_option *option = (struct core_option*)&runloop_st->core_options->opts[i]; + if (core_option_manager_get_idx(runloop_st->core_options, runloop_st->core_options->opts[i].key, &opt_idx) && core_option_manager_get_val_idx(runloop_st->core_options, i, option->val_labels->elems[runloop_st->core_options->opts[i].default_index].data, &val_idx1)) { + if (o>0) strcat(rv, "\n"); + o++; + strcat(rv, runloop_st->core_options->opts[i].key); + strcat(rv, "|"); + strcat(rv, option->val_labels->elems[runloop_st->core_options->opts[i].default_index].data); + strcat(rv, "; "); + int w = 0; + for (int j=0; jval_labels->size; j++) { + size_t val_idx; + if (core_option_manager_get_val_idx(runloop_st->core_options, i, option->val_labels->elems[j].data, &val_idx)) { + if (w>0) strcat(rv, "|"); + strcat(rv, option->val_labels->elems[j].data); + w++; + } + } + } + } + return rv; +} +#endif diff --git a/tasks/task_save.c b/tasks/task_save.c index 08bcf1f67fd..c540d2a431b 100644 --- a/tasks/task_save.c +++ b/tasks/task_save.c @@ -65,7 +65,8 @@ #if defined(HAVE_LIBNX) || defined(_3DS) #define SAVE_STATE_CHUNK 4096 * 10 #else -#define SAVE_STATE_CHUNK 4096 +//why so slow by default +#define SAVE_STATE_CHUNK 4096 * 1000 #endif #define RASTATE_VERSION 1 @@ -519,7 +520,9 @@ bool content_undo_load_state(void) /* Swap the current state with the backup state. This way, we can undo what we're undoing */ +#ifndef EMULATORJS content_save_state("RAM", false, false); +#endif ret = content_deserialize_state(temp_data, temp_data_size); @@ -1358,7 +1361,9 @@ static void content_load_state_cb(retro_task_t *task, } /* Backup the current state so we can undo this load */ +#ifndef EMULATORJS content_save_state("RAM", false, false); +#endif ret = content_deserialize_state(buf, size); @@ -1589,6 +1594,49 @@ static void task_push_load_and_save_state(const char *path, void *data, } } +#ifdef EMULATORJS +void* state_data; +char myString[1000]; + +void save_state_info(void) +{ + memset(myString, '\0', sizeof(myString)); + if (state_data) + free(state_data); + if (!core_info_current_supports_savestate()) { + strcpy(myString, "Not Supported||0"); + return; + } + size_t serial_size = core_serialize_size(); + if (serial_size == 0) { + strcpy(myString, "Size is zero||0"); + return; + } + state_data = content_get_serialized_data(&serial_size); + if (!state_data) { + strcpy(myString, "Error writing data||0"); + return; + } + sprintf(myString, "%zu|%zu|1", serial_size, (unsigned long)state_data); +} + + +char* get_state_info(void) { + return myString; +} + +bool supports_states(void) +{ + return core_info_current_supports_savestate(); +} +void refresh_save_files(void) +{ + event_load_save_files(false); +} + +#endif + + /** * content_save_state: * @path : path of saved state that shall be written to. @@ -2015,7 +2063,9 @@ bool content_load_state_from_ram(void) /* Swap the current state with the backup state. This way, we can undo what we're undoing */ +#ifndef EMULATORJS content_save_state("RAM", false, false); +#endif ret = content_deserialize_state(temp_data, temp_data_size);