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);