mirror of
https://github.com/libretro/RetroArch
synced 2025-03-14 10:21:46 +00:00
Emscripten improvements (#17422)
This commit is contained in:
parent
104561e7aa
commit
ed1810de86
2
.gitignore
vendored
2
.gitignore
vendored
@ -196,6 +196,8 @@ retroarch.js
|
||||
retroarch.js.mem
|
||||
*.bc
|
||||
*.wasm
|
||||
*.wasm.map
|
||||
obj-emscripten/
|
||||
|
||||
# only ignore .js files in the repo root
|
||||
/*.js
|
||||
|
@ -7,6 +7,9 @@
|
||||
- CHEEVOS: Include achievement state in netplay states
|
||||
- CLOUDSYNC: Fix Windows path issues
|
||||
- CLOUDSYNC: Workaround for duplicated requests bug
|
||||
- EMSCRIPTEN: Scale window to correct size
|
||||
- EMSCRIPTEN: Additional platform functions
|
||||
- EMSCRIPTEN/RWEBINPUT: Add touch input support
|
||||
- GENERAL: Fix save state auto increment
|
||||
- GENERAL: Fix softpatching with periods/dots in the file name
|
||||
- GENERAL: Fix compilation with --enable-videocore
|
||||
@ -34,6 +37,7 @@
|
||||
- SAVESTATES: Reset state index when loading new content
|
||||
- UWP: Fix slang shader compilation
|
||||
- VIDEO: Enable BFI setting for mobile platforms (mind the warnings)
|
||||
- VIDEO/OpenGLES: Fix FP/sRGB FBO support
|
||||
- VIDEO/SHADERS: Allow exact refresh rate sync with shader subframes
|
||||
- WEBPLAYER: Update core list for 1.20.0
|
||||
|
||||
|
@ -1570,10 +1570,11 @@ ifeq ($(HAVE_GL_CONTEXT), 1)
|
||||
DEF_FLAGS += $(OPENGLES_CFLAGS)
|
||||
ifeq ($(HAVE_OPENGLES3), 1)
|
||||
DEFINES += -DHAVE_OPENGLES3
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/glsym/glsym_es3.o
|
||||
else
|
||||
DEFINES += -DHAVE_OPENGLES2
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/glsym/glsym_es2.o
|
||||
endif
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/glsym/glsym_es2.o
|
||||
else
|
||||
DEFINES += -DHAVE_GL_SYNC
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/glsym/glsym_gl.o
|
||||
|
@ -25,7 +25,6 @@ HAVE_REWIND = 1
|
||||
HAVE_AUDIOMIXER = 1
|
||||
HAVE_CC_RESAMPLER = 1
|
||||
HAVE_EGL = 1
|
||||
HAVE_OPENGLES = 1
|
||||
HAVE_RJPEG = 0
|
||||
HAVE_RPNG = 1
|
||||
HAVE_EMSCRIPTEN = 1
|
||||
@ -58,34 +57,49 @@ HAVE_CHD ?= 0
|
||||
# You have been warned.
|
||||
HAVE_RWEBAUDIO = 0
|
||||
|
||||
# help diagnose GL problems (can cause issues in normal operation)
|
||||
GL_DEBUG ?= 0
|
||||
|
||||
# enable javascript filesystem tracking
|
||||
FS_DEBUG = 1
|
||||
|
||||
HAVE_OPENGLES ?= 1
|
||||
HAVE_OPENGLES3 ?= 0
|
||||
|
||||
ASYNC ?= 0
|
||||
ifeq ($(LIBRETRO), mupen64plus)
|
||||
ASYNC = 1
|
||||
endif
|
||||
|
||||
LTO ?= 0
|
||||
ifeq ($(LIBRETRO), tyrquake)
|
||||
LTO = 0
|
||||
endif
|
||||
|
||||
PTHREAD ?= 0
|
||||
|
||||
MEMORY ?= 134217728
|
||||
STACK_SIZE ?= 4194304
|
||||
INITIAL_HEAP ?= 134217728
|
||||
|
||||
PRECISE_F32 = 1
|
||||
# 4194304 ----- 4 MiB (Stack: recommended)
|
||||
# 8388608 ----- 8 MiB
|
||||
# 16777216 ---- 16 MiB
|
||||
# 33554432 ---- 32 MiB
|
||||
# 67108864 ---- 64 MiB
|
||||
# 134217728 --- 128 MiB (Heap: recommended) (Stack: recommended for some cores [mupen64plus_next])
|
||||
# 268435456 --- 256 MiB (Heap: recommended for some cores [mupen64plus_next])
|
||||
# 536870912 --- 512 MiB (Heap: needed for some cores [mednafen_psx(_hw)])
|
||||
# 1073741824 -- 1 GiB
|
||||
# 1610612736 -- 1.5 GiB
|
||||
# 2147483648 -- 2 GiB
|
||||
|
||||
OBJDIR := obj-emscripten
|
||||
|
||||
#if you compile with SDL2 flag add this Emscripten flag "-s USE_SDL=2" to LDFLAGS:
|
||||
EXPORTED_FUNCTIONS = _main,_malloc,_free,_cmd_savefiles,_cmd_save_state,_cmd_load_state,_cmd_undo_save_state,_cmd_undo_load_state,_cmd_take_screenshot,\
|
||||
_cmd_toggle_menu,_cmd_reload_config,_cmd_toggle_grab_mouse,_cmd_toggle_game_focus,_cmd_reset,_cmd_toggle_pause,_cmd_pause,_cmd_unpause,\
|
||||
_cmd_set_volume,_cmd_set_shader,_cmd_cheat_set_code,_cmd_cheat_get_code,_cmd_cheat_toggle_index,_cmd_cheat_get_code_state,_cmd_cheat_realloc,\
|
||||
_cmd_cheat_get_size,_cmd_cheat_apply_cheats
|
||||
|
||||
LIBS := -s USE_ZLIB=1
|
||||
LDFLAGS := -L. --no-heap-copy -s $(LIBS) -s TOTAL_MEMORY=$(MEMORY) -s NO_EXIT_RUNTIME=0 -s FULL_ES2=1 \
|
||||
-s "EXPORTED_RUNTIME_METHODS=['callMain', 'FS', 'PATH', 'ERRNO_CODES']" \
|
||||
-s ALLOW_MEMORY_GROWTH=1 -s "EXPORTED_FUNCTIONS=['_main', '_malloc', '_cmd_savefiles', '_cmd_save_state', '_cmd_load_state', '_cmd_take_screenshot']" \
|
||||
LDFLAGS := -L. --no-heap-copy -s $(LIBS) -s STACK_SIZE=$(STACK_SIZE) -s INITIAL_MEMORY=$(INITIAL_HEAP) \
|
||||
-s EXPORTED_RUNTIME_METHODS=callMain,FS,PATH,ERRNO_CODES,stringToNewUTF8,UTF8ToString \
|
||||
-s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS="$(EXPORTED_FUNCTIONS)" \
|
||||
-s MODULARIZE=1 -s EXPORT_ES6=1 -s EXPORT_NAME="libretro_$(subst -,_,$(LIBRETRO))" \
|
||||
-s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 \
|
||||
--js-library emscripten/library_errno_codes.js \
|
||||
--js-library emscripten/library_rwebcam.js
|
||||
--extern-pre-js emscripten/pre.js \
|
||||
--js-library emscripten/library_rwebcam.js \
|
||||
--js-library emscripten/library_platform_emscripten.js
|
||||
|
||||
ifeq ($(HAVE_RWEBAUDIO), 1)
|
||||
LDFLAGS += --js-library emscripten/library_rwebaudio.js
|
||||
@ -94,11 +108,11 @@ endif
|
||||
ifeq ($(HAVE_AL), 1)
|
||||
LDFLAGS += -lopenal
|
||||
DEFINES += -DHAVE_AL
|
||||
ASYNC = 1
|
||||
override ASYNC = 1
|
||||
endif
|
||||
|
||||
ifneq ($(PTHREAD), 0)
|
||||
LDFLAGS += -s WASM_MEM_MAX=1073741824 -pthread -s PTHREAD_POOL_SIZE=$(PTHREAD)
|
||||
LDFLAGS += -s MAXIMUM_MEMORY=1073741824 -pthread -s PTHREAD_POOL_SIZE=$(PTHREAD)
|
||||
CFLAGS += -pthread
|
||||
HAVE_THREADS=1
|
||||
else
|
||||
@ -108,10 +122,26 @@ endif
|
||||
ifeq ($(ASYNC), 1)
|
||||
LDFLAGS += -s ASYNCIFY=$(ASYNC) -s ASYNCIFY_STACK_SIZE=8192
|
||||
ifeq ($(DEBUG), 1)
|
||||
LDFLAGS += -s ASYNCIFY_DEBUG=1 # -s ASYNCIFY_ADVISE
|
||||
LDFLAGS += -s ASYNCIFY_DEBUG=1 # -s ASYNCIFY_ADVISE
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_OPENGLES), 1)
|
||||
ifeq ($(HAVE_OPENGLES3), 1)
|
||||
LDFLAGS += -s FULL_ES3=1 -s MIN_WEBGL_VERSION=2 -s MAX_WEBGL_VERSION=2
|
||||
else
|
||||
LDFLAGS += -s FULL_ES2=1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(GL_DEBUG), 1)
|
||||
LDFLAGS += -s GL_ASSERTIONS=1 -s GL_DEBUG=1
|
||||
endif
|
||||
|
||||
ifeq ($(FS_DEBUG), 1)
|
||||
LDFLAGS += -s FS_DEBUG=1
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SDL2), 1)
|
||||
LIBS += -s USE_SDL=2
|
||||
DEFINES += -DHAVE_SDL2
|
||||
@ -121,12 +151,14 @@ include Makefile.common
|
||||
|
||||
CFLAGS += $(DEF_FLAGS) -Ideps -Ideps/stb
|
||||
|
||||
libretro :=
|
||||
libretro =
|
||||
libretro_new =
|
||||
|
||||
ifeq ($(HAVE_STATIC_DUMMY),1)
|
||||
DEFINES += -DHAVE_STATIC_DUMMY
|
||||
else
|
||||
libretro += libretro_emscripten.bc
|
||||
libretro = libretro_emscripten.bc
|
||||
libretro_new = libretro_emscripten.a
|
||||
endif
|
||||
|
||||
ifneq ($(V), 1)
|
||||
@ -135,22 +167,16 @@ endif
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
LDFLAGS += -O0 -g -gsource-map -s SAFE_HEAP=1 -s STACK_OVERFLOW_CHECK=2 -s ASSERTIONS=1
|
||||
CFLAGS += -O0 -g -gsource-map -s SAFE_HEAP=1 -s SAFE_HEAP_LOG=1 -s STACK_OVERFLOW_CHECK=2 -s ASSERTIONS=1
|
||||
CFLAGS += -O0 -g -gsource-map
|
||||
else
|
||||
LDFLAGS += -O3 -s WASM=1
|
||||
LDFLAGS += -O3
|
||||
# WARNING: some optimizations can break some cores (ex: LTO breaks tyrquake)
|
||||
LDFLAGS += -s PRECISE_F32=$(PRECISE_F32)
|
||||
ifeq ($(LTO), 1)
|
||||
LDFLAGS += --llvm-lto 3
|
||||
LDFLAGS += -flto
|
||||
endif
|
||||
CFLAGS += -O3
|
||||
endif
|
||||
|
||||
# 128 * 1024, double the usual emscripten stack size
|
||||
LDFLAGS += -s STACK_SIZE=131072
|
||||
|
||||
LDFLAGS += --extern-pre-js emscripten/pre.js
|
||||
|
||||
CFLAGS += -Wall -I. -Ilibretro-common/include -Ideps/7zip -std=gnu99
|
||||
|
||||
RARCH_OBJ := $(addprefix $(OBJDIR)/,$(OBJ))
|
||||
@ -158,8 +184,9 @@ RARCH_OBJ := $(addprefix $(OBJDIR)/,$(OBJ))
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(RARCH_OBJ) $(libretro)
|
||||
@$(if $(Q), $(shell echo echo LD $@),)
|
||||
$(Q)$(LD) -o $@ $(RARCH_OBJ) $(libretro) $(LIBS) $(LDFLAGS)
|
||||
@$(if $(libretro), mv -f $(libretro) $(libretro_new),)
|
||||
@$(if $(Q), $(shell echo echo "LD $@ \<obj\> $(libretro_new) $(LIBS) $(LDFLAGS)"),)
|
||||
$(Q)$(LD) -o $@ $(RARCH_OBJ) $(libretro_new) $(LIBS) $(LDFLAGS)
|
||||
|
||||
$(OBJDIR)/%.o: %.c
|
||||
@mkdir -p $(dir $@)
|
||||
|
@ -122,11 +122,13 @@ void cheat_manager_apply_cheats(void)
|
||||
void cheat_manager_set_code(unsigned i, const char *str)
|
||||
{
|
||||
cheat_manager_t *cheat_st = &cheat_manager_state;
|
||||
if (!cheat_st->cheats)
|
||||
if (!cheat_st->cheats || string_is_empty(str))
|
||||
return;
|
||||
|
||||
if (!string_is_empty(str))
|
||||
strcpy(cheat_st->cheats[i].code, str);
|
||||
if (cheat_st->cheats[i].code)
|
||||
free(cheat_st->cheats[i].code);
|
||||
|
||||
cheat_st->cheats[i].code = strdup(str);
|
||||
|
||||
cheat_st->cheats[i].state = true;
|
||||
}
|
||||
|
@ -260,6 +260,7 @@ enum event_command
|
||||
CMD_EVENT_PRESENCE_UPDATE,
|
||||
CMD_EVENT_OVERLAY_NEXT,
|
||||
CMD_EVENT_OSK_TOGGLE,
|
||||
CMD_EVENT_RELOAD_CONFIG,
|
||||
#ifdef HAVE_MICROPHONE
|
||||
/* Stops all enabled microphones. */
|
||||
CMD_EVENT_MICROPHONE_STOP,
|
||||
|
@ -764,6 +764,9 @@
|
||||
#define DEFAULT_MENU_TICKER_SPEED 2.0f
|
||||
#define DEFAULT_MENU_TICKER_SMOOTH true
|
||||
|
||||
/* Don't skip rendering assets based on the absence of other assets */
|
||||
#define DEFAULT_MENU_IGNORE_MISSING_ASSETS false
|
||||
|
||||
#if defined(HAVE_THREADS)
|
||||
#define DEFAULT_MENU_SAVESTATE_RESUME true
|
||||
#else
|
||||
|
@ -1933,6 +1933,7 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("menu_dynamic_wallpaper_enable", &settings->bools.menu_dynamic_wallpaper_enable, true, DEFAULT_MENU_DYNAMIC_WALLPAPER_ENABLE, false);
|
||||
SETTING_BOOL("menu_ticker_smooth", &settings->bools.menu_ticker_smooth, true, DEFAULT_MENU_TICKER_SMOOTH, false);
|
||||
SETTING_BOOL("menu_scroll_fast", &settings->bools.menu_scroll_fast, true, DEFAULT_MENU_SCROLL_FAST, false);
|
||||
SETTING_BOOL("menu_ignore_missing_assets", &settings->bools.menu_ignore_missing_assets, true, DEFAULT_MENU_IGNORE_MISSING_ASSETS, false);
|
||||
|
||||
SETTING_BOOL("settings_show_drivers", &settings->bools.settings_show_drivers, true, DEFAULT_SETTINGS_SHOW_DRIVERS, false);
|
||||
SETTING_BOOL("settings_show_video", &settings->bools.settings_show_video, true, DEFAULT_SETTINGS_SHOW_VIDEO, false);
|
||||
|
@ -832,6 +832,7 @@ typedef struct settings
|
||||
bool menu_disable_left_analog;
|
||||
bool menu_disable_right_analog;
|
||||
bool menu_ticker_smooth;
|
||||
bool menu_ignore_missing_assets;
|
||||
bool settings_show_drivers;
|
||||
bool settings_show_video;
|
||||
bool settings_show_audio;
|
||||
|
@ -61,6 +61,7 @@ mkdir -p ../pkg/${platform}/build/rom
|
||||
# Emscripten
|
||||
elif [ $PLATFORM = "emscripten" ] ; then
|
||||
platform=emscripten
|
||||
# todo: change this to a
|
||||
EXT=bc
|
||||
|
||||
if [ -z "$EMSCRIPTEN" ] ; then
|
||||
@ -199,15 +200,10 @@ for f in `ls -v *_${platform}.${EXT}`; do
|
||||
|
||||
echo Buildbot: building ${name} for ${platform}
|
||||
name=`echo "$f" | sed "s/\(_libretro_${platform}\|\).${EXT}$//"`
|
||||
async=0
|
||||
pthread=${pthread:-0}
|
||||
lto=0
|
||||
whole_archive=
|
||||
big_stack=
|
||||
|
||||
if [ $PLATFORM = "emscripten" ]; then
|
||||
async=1 #emscripten needs async to sleep
|
||||
fi
|
||||
if [ $name = "nxengine" ] ; then
|
||||
echo "Applying whole archive linking..."
|
||||
whole_archive="WHOLE_ARCHIVE_LINK=1"
|
||||
@ -215,10 +211,31 @@ for f in `ls -v *_${platform}.${EXT}`; do
|
||||
echo "Applying big stack..."
|
||||
lto=0
|
||||
big_stack="BIG_STACK=1"
|
||||
elif [ $name = "mupen64plus" ] ; then
|
||||
async=1
|
||||
elif [ $name = "dosbox" ] ; then
|
||||
fi
|
||||
if [ $PLATFORM = "emscripten" ]; then
|
||||
async=0
|
||||
pthread=${pthread:-0}
|
||||
gles3=0
|
||||
stack_mem=4194304
|
||||
heap_mem=134217728
|
||||
if [ $name = "mupen64plus_next" ] ; then
|
||||
gles3=1
|
||||
async=1
|
||||
stack_mem=134217728
|
||||
heap_mem=268435456
|
||||
elif [ $name = "parallel_n64" ] ; then
|
||||
gles3=1
|
||||
async=1
|
||||
elif [ $name = "mednafen_psx" ] ; then
|
||||
heap_mem=536870912
|
||||
elif [ $name = "mednafen_psx_hw" ] ; then
|
||||
gles3=1
|
||||
heap_mem=536870912
|
||||
elif [ $name = "dosbox" ] ; then
|
||||
async=1
|
||||
elif [ $name = "scummvm" ] ; then
|
||||
async=1
|
||||
fi
|
||||
fi
|
||||
echo "-- Building core: $name --"
|
||||
if [ $PLATFORM = "unix" ]; then
|
||||
@ -227,15 +244,21 @@ for f in `ls -v *_${platform}.${EXT}`; do
|
||||
cp -f "$f" ../libretro_${platform}.${EXT}
|
||||
fi
|
||||
echo NAME: $name
|
||||
echo ASYNC: $async
|
||||
echo LTO: $lto
|
||||
if [ $PLATFORM = "emscripten" ]; then
|
||||
echo ASYNC: $async
|
||||
echo PTHREAD: $pthread
|
||||
echo GLES3: $gles3
|
||||
echo STACK_MEMORY: $stack_mem
|
||||
echo HEAP_MEMORY: $heap_mem
|
||||
fi
|
||||
|
||||
# Do cleanup if this is a big stack core
|
||||
if [ "$big_stack" = "BIG_STACK=1" ] ; then
|
||||
if [ $MAKEFILE_GRIFFIN = "yes" ]; then
|
||||
make -C ../ -f Makefile.griffin platform=${platform} clean || exit 1
|
||||
elif [ $PLATFORM = "emscripten" ]; then
|
||||
make -C ../ -f Makefile.emscripten PTHREAD=$pthread ASYNC=$async LTO=$lto -j7 clean || exit 1
|
||||
make -C ../ -f Makefile.emscripten PTHREAD=$pthread ASYNC=$async LTO=$lto HAVE_OPENGLES3=$gles3 -j7 clean || exit 1
|
||||
elif [ $PLATFORM = "unix" ]; then
|
||||
make -C ../ -f Makefile LINK=g++ LTO=$lto -j7 clean || exit 1
|
||||
else
|
||||
@ -247,8 +270,8 @@ for f in `ls -v *_${platform}.${EXT}`; do
|
||||
if [ $MAKEFILE_GRIFFIN = "yes" ]; then
|
||||
make -C ../ -f Makefile.griffin $OPTS platform=${platform} $whole_archive $big_stack -j3 || exit 1
|
||||
elif [ $PLATFORM = "emscripten" ]; then
|
||||
echo "BUILD COMMAND: make -C ../ -f Makefile.emscripten PTHREAD=$pthread ASYNC=$async LTO=$lto -j7 LIBRETRO=${name} TARGET=${name}_libretro.js"
|
||||
make -C ../ -f Makefile.emscripten $OPTS PTHREAD=$pthread ASYNC=$async LTO=$lto -j7 LIBRETRO=${name} TARGET=${name}_libretro.js || exit 1
|
||||
echo "BUILD COMMAND: make -C ../ -f Makefile.emscripten PTHREAD=$pthread ASYNC=$async LTO=$lto HAVE_OPENGLES3=$gles3 STACK_SIZE=$stack_mem INITIAL_HEAP=$heap_mem -j7 LIBRETRO=${name} TARGET=${name}_libretro.js"
|
||||
make -C ../ -f Makefile.emscripten $OPTS PTHREAD=$pthread ASYNC=$async LTO=$lto HAVE_OPENGLES3=$gles3 STACK_SIZE=$stack_mem INITIAL_HEAP=$heap_mem -j7 LIBRETRO=${name} TARGET=${name}_libretro.js || exit 1
|
||||
elif [ $PLATFORM = "unix" ]; then
|
||||
make -C ../ -f Makefile LINK=g++ $whole_archive $big_stack -j3 || exit 1
|
||||
elif [ $PLATFORM = "ctr" ]; then
|
||||
@ -318,6 +341,9 @@ for f in `ls -v *_${platform}.${EXT}`; do
|
||||
if [ $pthread != 0 ] ; then
|
||||
mv -f ../${name}_libretro.worker.js ../pkg/emscripten/${name}_libretro.worker.js
|
||||
fi
|
||||
if [ -f ../${name}_libretro.wasm.map ] ; then
|
||||
mv -f ../${name}_libretro.wasm.map ../pkg/emscripten/${name}_libretro.wasm.map
|
||||
fi
|
||||
fi
|
||||
|
||||
# Do manual executable step
|
||||
|
@ -1,15 +0,0 @@
|
||||
//"use strict";
|
||||
|
||||
// HACK: This is a dummy library that forces ERRNO_CODES to be used, so it's not optimized away.
|
||||
// Needed for BrowserFS.
|
||||
|
||||
var LibraryErrnoCodes = {
|
||||
dummyErrnoCodes__deps: ['$ERRNO_CODES'],
|
||||
dummyErrnoCodes: function() {
|
||||
if (!ERRNO_CODES) {
|
||||
console.error("ERRNO_CODES not imported!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mergeInto(LibraryManager.library, LibraryErrnoCodes);
|
78
emscripten/library_platform_emscripten.js
Normal file
78
emscripten/library_platform_emscripten.js
Normal file
@ -0,0 +1,78 @@
|
||||
//"use strict";
|
||||
|
||||
var LibraryPlatformEmscripten = {
|
||||
$RPE: {
|
||||
powerState: {
|
||||
supported: false,
|
||||
dischargeTime: 0,
|
||||
level: 0,
|
||||
charging: false
|
||||
},
|
||||
powerStateChange: function(e) {
|
||||
RPE.powerState.dischargeTime = Number.isFinite(e.target.dischargingTime) ? e.target.dischargingTime : 0x7FFFFFFF;
|
||||
RPE.powerState.level = e.target.level;
|
||||
RPE.powerState.charging = e.target.charging;
|
||||
}
|
||||
},
|
||||
|
||||
PlatformEmscriptenWatchCanvasSize: function() {
|
||||
RPE.observer = new ResizeObserver(function(e) {
|
||||
var width, height;
|
||||
var entry = e.find(i => i.target == Module.canvas);
|
||||
if (!entry) return;
|
||||
if (entry.devicePixelContentBoxSize) {
|
||||
width = entry.devicePixelContentBoxSize[0].inlineSize;
|
||||
height = entry.devicePixelContentBoxSize[0].blockSize;
|
||||
} else {
|
||||
width = Math.round(entry.contentRect.width * window.devicePixelRatio);
|
||||
height = Math.round(entry.contentRect.height * window.devicePixelRatio);
|
||||
}
|
||||
Module.setCanvasSize(width, height);
|
||||
Module.print("Setting real canvas size: " + width + " x " + height);
|
||||
});
|
||||
RPE.observer.observe(Module.canvas);
|
||||
window.addEventListener("resize", function(e) {
|
||||
RPE.observer.unobserve(Module.canvas);
|
||||
RPE.observer.observe(Module.canvas);
|
||||
}, false);
|
||||
},
|
||||
|
||||
PlatformEmscriptenPowerStateInit: function() {
|
||||
if (!navigator.getBattery) return;
|
||||
navigator.getBattery().then(function(battery) {
|
||||
battery.addEventListener("chargingchange", RPE.powerStateChange);
|
||||
battery.addEventListener("levelchange", RPE.powerStateChange);
|
||||
RPE.powerStateChange({target: battery});
|
||||
RPE.powerState.supported = true;
|
||||
});
|
||||
},
|
||||
|
||||
PlatformEmscriptenPowerStateGetSupported: function() {
|
||||
return RPE.powerState.supported;
|
||||
},
|
||||
|
||||
PlatformEmscriptenPowerStateGetDischargeTime: function() {
|
||||
return RPE.powerState.dischargeTime;
|
||||
},
|
||||
|
||||
PlatformEmscriptenPowerStateGetLevel: function() {
|
||||
return RPE.powerState.level;
|
||||
},
|
||||
|
||||
PlatformEmscriptenPowerStateGetCharging: function() {
|
||||
return RPE.powerState.charging;
|
||||
},
|
||||
|
||||
PlatformEmscriptenGetTotalMem: function() {
|
||||
if (!performance.memory) return 0;
|
||||
return performance.memory.jsHeapSizeLimit || 0;
|
||||
},
|
||||
|
||||
PlatformEmscriptenGetFreeMem: function() {
|
||||
if (!performance.memory) return 0;
|
||||
return (performance.memory.jsHeapSizeLimit || 0) - (performance.memory.usedJSHeapSize || 0);
|
||||
}
|
||||
};
|
||||
|
||||
autoAddDeps(LibraryPlatformEmscripten, '$RPE');
|
||||
mergeInto(LibraryManager.library, LibraryPlatformEmscripten);
|
@ -17,18 +17,13 @@ var LibraryRWebCam = {
|
||||
RWebCamInit__deps: ['malloc'],
|
||||
RWebCamInit: function(caps1, caps2, width, height) {
|
||||
if (!navigator) return 0;
|
||||
|
||||
navigator.getMedia = navigator.getUserMedia ||
|
||||
navigator.webkitGetUserMedia ||
|
||||
navigator.mozGetUserMedia ||
|
||||
navigator.msGetUserMedia;
|
||||
|
||||
if (!navigator.getMedia) return 0;
|
||||
if (!navigator.mediaDevices.getUserMedia) return 0;
|
||||
|
||||
var c = ++RWC.counter;
|
||||
|
||||
RWC.contexts[c] = [];
|
||||
RWC.contexts[c].videoElement = document.createElement("video");
|
||||
RWC.contexts[c].videoElement.classList.add("retroarchWebcamVideo");
|
||||
if (width !== 0 && height !== 0) {
|
||||
RWC.contexts[c].videoElement.width = width;
|
||||
RWC.contexts[c].videoElement.height = height;
|
||||
@ -37,11 +32,11 @@ var LibraryRWebCam = {
|
||||
RWC.contexts[c].glTex = caps1 & (1 << RWC.RETRO_CAMERA_BUFFER_OPENGL_TEXTURE);
|
||||
RWC.contexts[c].rawFb = caps1 & (1 << RWC.RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER);
|
||||
|
||||
navigator.getMedia({video: true, audio: false}, function(stream) {
|
||||
navigator.mediaDevices.getUserMedia({video: true, audio: false}).then(function(stream) {
|
||||
RWC.contexts[c].videoElement.autoplay = true;
|
||||
RWC.contexts[c].videoElement.src = URL.createObjectURL(stream);
|
||||
RWC.contexts[c].videoElement.srcObject = stream;
|
||||
RWC.contexts[c].runMode = 2;
|
||||
}, function (err) {
|
||||
}).catch(function (err) {
|
||||
console.log("webcam request failed", err);
|
||||
RWC.runMode = 0;
|
||||
});
|
||||
@ -53,7 +48,6 @@ var LibraryRWebCam = {
|
||||
|
||||
RWebCamFree: function(data) {
|
||||
RWC.contexts[data].videoElement.pause();
|
||||
URL.revokeObjectURL(RWC.contexts[data].videoElement.src);
|
||||
RWC.contexts[data].videoElement = null;
|
||||
RWC.contexts[data] = null;
|
||||
},
|
||||
@ -81,6 +75,7 @@ var LibraryRWebCam = {
|
||||
}
|
||||
if (RWC.contexts[data].rawFb) {
|
||||
RWC.contexts[data].rawFbCanvas = document.createElement("canvas");
|
||||
RWC.contexts[data].rawFbCanvas.classList.add("retroarchWebcamCanvas");
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
|
@ -46,9 +46,22 @@
|
||||
#include "../../retroarch.h"
|
||||
#include "../../verbosity.h"
|
||||
#include "../../tasks/tasks_internal.h"
|
||||
#include "../../cheat_manager.h"
|
||||
#include "../../audio/audio_driver.h"
|
||||
|
||||
void dummyErrnoCodes(void);
|
||||
void emscripten_mainloop(void);
|
||||
void PlatformEmscriptenWatchCanvasSize(void);
|
||||
void PlatformEmscriptenPowerStateInit(void);
|
||||
bool PlatformEmscriptenPowerStateGetSupported(void);
|
||||
int PlatformEmscriptenPowerStateGetDischargeTime(void);
|
||||
float PlatformEmscriptenPowerStateGetLevel(void);
|
||||
bool PlatformEmscriptenPowerStateGetCharging(void);
|
||||
uint64_t PlatformEmscriptenGetTotalMem(void);
|
||||
uint64_t PlatformEmscriptenGetFreeMem(void);
|
||||
|
||||
//// begin exported functions
|
||||
|
||||
// saves and states
|
||||
|
||||
void cmd_savefiles(void)
|
||||
{
|
||||
@ -65,11 +78,113 @@ void cmd_load_state(void)
|
||||
command_event(CMD_EVENT_LOAD_STATE, NULL);
|
||||
}
|
||||
|
||||
void cmd_undo_save_state(void)
|
||||
{
|
||||
command_event(CMD_EVENT_UNDO_SAVE_STATE, NULL);
|
||||
}
|
||||
|
||||
void cmd_undo_load_state(void)
|
||||
{
|
||||
command_event(CMD_EVENT_UNDO_LOAD_STATE, NULL);
|
||||
}
|
||||
|
||||
// misc
|
||||
|
||||
void cmd_take_screenshot(void)
|
||||
{
|
||||
command_event(CMD_EVENT_TAKE_SCREENSHOT, NULL);
|
||||
}
|
||||
|
||||
void cmd_toggle_menu(void)
|
||||
{
|
||||
command_event(CMD_EVENT_MENU_TOGGLE, NULL);
|
||||
}
|
||||
|
||||
void cmd_reload_config(void)
|
||||
{
|
||||
command_event(CMD_EVENT_RELOAD_CONFIG, NULL);
|
||||
}
|
||||
|
||||
void cmd_toggle_grab_mouse(void)
|
||||
{
|
||||
command_event(CMD_EVENT_GRAB_MOUSE_TOGGLE, NULL);
|
||||
}
|
||||
|
||||
void cmd_toggle_game_focus(void)
|
||||
{
|
||||
command_event(CMD_EVENT_GAME_FOCUS_TOGGLE, NULL);
|
||||
}
|
||||
|
||||
void cmd_reset(void)
|
||||
{
|
||||
command_event(CMD_EVENT_RESET, NULL);
|
||||
}
|
||||
|
||||
void cmd_toggle_pause(void)
|
||||
{
|
||||
command_event(CMD_EVENT_PAUSE_TOGGLE, NULL);
|
||||
}
|
||||
|
||||
void cmd_pause(void)
|
||||
{
|
||||
command_event(CMD_EVENT_PAUSE, NULL);
|
||||
}
|
||||
|
||||
void cmd_unpause(void)
|
||||
{
|
||||
command_event(CMD_EVENT_UNPAUSE, NULL);
|
||||
}
|
||||
|
||||
void cmd_set_volume(float volume) {
|
||||
audio_set_float(AUDIO_ACTION_VOLUME_GAIN, volume);
|
||||
}
|
||||
|
||||
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
|
||||
bool cmd_set_shader(const char *path)
|
||||
{
|
||||
return command_set_shader(NULL, path);
|
||||
}
|
||||
#endif
|
||||
|
||||
// cheats
|
||||
|
||||
void cmd_cheat_set_code(unsigned index, const char *str)
|
||||
{
|
||||
cheat_manager_set_code(index, str);
|
||||
}
|
||||
|
||||
const char *cmd_cheat_get_code(unsigned index)
|
||||
{
|
||||
return cheat_manager_get_code(index);
|
||||
}
|
||||
|
||||
void cmd_cheat_toggle_index(bool apply_cheats_after_toggle, unsigned index)
|
||||
{
|
||||
cheat_manager_toggle_index(apply_cheats_after_toggle, index);
|
||||
}
|
||||
|
||||
bool cmd_cheat_get_code_state(unsigned index)
|
||||
{
|
||||
return cheat_manager_get_code_state(index);
|
||||
}
|
||||
|
||||
bool cmd_cheat_realloc(unsigned new_size)
|
||||
{
|
||||
return cheat_manager_realloc(new_size, CHEAT_HANDLER_TYPE_EMU);
|
||||
}
|
||||
|
||||
unsigned cmd_cheat_get_size(void)
|
||||
{
|
||||
return cheat_manager_get_size();
|
||||
}
|
||||
|
||||
void cmd_cheat_apply_cheats(void)
|
||||
{
|
||||
cheat_manager_apply_cheats();
|
||||
}
|
||||
|
||||
//// end exported functions
|
||||
|
||||
static void frontend_emscripten_get_env(int *argc, char *argv[],
|
||||
void *args, void *params_data)
|
||||
{
|
||||
@ -154,16 +269,45 @@ static void frontend_emscripten_get_env(int *argc, char *argv[],
|
||||
#endif
|
||||
}
|
||||
|
||||
static enum frontend_powerstate frontend_emscripten_get_powerstate(int *seconds, int *percent)
|
||||
{
|
||||
enum frontend_powerstate ret = FRONTEND_POWERSTATE_NONE;
|
||||
|
||||
if (!PlatformEmscriptenPowerStateGetSupported())
|
||||
return ret;
|
||||
|
||||
if (!PlatformEmscriptenPowerStateGetCharging())
|
||||
ret = FRONTEND_POWERSTATE_ON_POWER_SOURCE;
|
||||
else if (PlatformEmscriptenPowerStateGetLevel() == 1)
|
||||
ret = FRONTEND_POWERSTATE_CHARGED;
|
||||
else
|
||||
ret = FRONTEND_POWERSTATE_CHARGING;
|
||||
|
||||
*seconds = PlatformEmscriptenPowerStateGetDischargeTime();
|
||||
*percent = (int)(PlatformEmscriptenPowerStateGetLevel() * 100);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint64_t frontend_emscripten_get_total_mem(void)
|
||||
{
|
||||
return PlatformEmscriptenGetTotalMem();
|
||||
}
|
||||
|
||||
static uint64_t frontend_emscripten_get_free_mem(void)
|
||||
{
|
||||
return PlatformEmscriptenGetFreeMem();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
dummyErrnoCodes();
|
||||
PlatformEmscriptenWatchCanvasSize();
|
||||
PlatformEmscriptenPowerStateInit();
|
||||
|
||||
EM_ASM({
|
||||
specialHTMLTargets["!canvas"] = Module.canvas;
|
||||
});
|
||||
|
||||
emscripten_set_canvas_element_size("!canvas", 800, 600);
|
||||
emscripten_set_element_css_size("!canvas", 800.0, 600.0);
|
||||
emscripten_set_main_loop(emscripten_mainloop, 0, 0);
|
||||
rarch_main(argc, argv, NULL);
|
||||
|
||||
@ -171,39 +315,39 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
frontend_ctx_driver_t frontend_ctx_emscripten = {
|
||||
frontend_emscripten_get_env, /* environment_get */
|
||||
NULL, /* init */
|
||||
NULL, /* deinit */
|
||||
NULL, /* exitspawn */
|
||||
NULL, /* process_args */
|
||||
NULL, /* exec */
|
||||
NULL, /* set_fork */
|
||||
NULL, /* shutdown */
|
||||
NULL, /* get_name */
|
||||
NULL, /* get_os */
|
||||
NULL, /* get_rating */
|
||||
NULL, /* load_content */
|
||||
NULL, /* get_architecture */
|
||||
NULL, /* get_powerstate */
|
||||
NULL, /* parse_drive_list */
|
||||
NULL, /* get_total_mem */
|
||||
NULL, /* get_free_mem */
|
||||
NULL, /* install_sighandlers */
|
||||
NULL, /* get_signal_handler_state */
|
||||
NULL, /* set_signal_handler_state */
|
||||
NULL, /* destroy_signal_handler_state */
|
||||
NULL, /* attach_console */
|
||||
NULL, /* detach_console */
|
||||
NULL, /* get_lakka_version */
|
||||
NULL, /* set_screen_brightness */
|
||||
NULL, /* watch_path_for_changes */
|
||||
NULL, /* check_for_path_changes */
|
||||
NULL, /* set_sustained_performance_mode */
|
||||
NULL, /* get_cpu_model_name */
|
||||
NULL, /* get_user_language */
|
||||
NULL, /* is_narrator_running */
|
||||
NULL, /* accessibility_speak */
|
||||
NULL, /* set_gamemode */
|
||||
"emscripten", /* ident */
|
||||
NULL /* get_video_driver */
|
||||
frontend_emscripten_get_env, /* environment_get */
|
||||
NULL, /* init */
|
||||
NULL, /* deinit */
|
||||
NULL, /* exitspawn */
|
||||
NULL, /* process_args */
|
||||
NULL, /* exec */
|
||||
NULL, /* set_fork */
|
||||
NULL, /* shutdown */
|
||||
NULL, /* get_name */
|
||||
NULL, /* get_os */
|
||||
NULL, /* get_rating */
|
||||
NULL, /* load_content */
|
||||
NULL, /* get_architecture */
|
||||
frontend_emscripten_get_powerstate, /* get_powerstate */
|
||||
NULL, /* parse_drive_list */
|
||||
frontend_emscripten_get_total_mem, /* get_total_mem */
|
||||
frontend_emscripten_get_free_mem, /* get_free_mem */
|
||||
NULL, /* install_sighandlers */
|
||||
NULL, /* get_signal_handler_state */
|
||||
NULL, /* set_signal_handler_state */
|
||||
NULL, /* destroy_signal_handler_state */
|
||||
NULL, /* attach_console */
|
||||
NULL, /* detach_console */
|
||||
NULL, /* get_lakka_version */
|
||||
NULL, /* set_screen_brightness */
|
||||
NULL, /* watch_path_for_changes */
|
||||
NULL, /* check_for_path_changes */
|
||||
NULL, /* set_sustained_performance_mode */
|
||||
NULL, /* get_cpu_model_name */
|
||||
NULL, /* get_user_language */
|
||||
NULL, /* is_narrator_running */
|
||||
NULL, /* accessibility_speak */
|
||||
NULL, /* set_gamemode */
|
||||
"emscripten", /* ident */
|
||||
NULL /* get_video_driver */
|
||||
};
|
||||
|
@ -185,9 +185,8 @@ enum gl2_renderchain_flags
|
||||
{
|
||||
GL2_CHAIN_FLAG_EGL_IMAGES = (1 << 0),
|
||||
GL2_CHAIN_FLAG_HAS_FP_FBO = (1 << 1),
|
||||
GL2_CHAIN_FLAG_HAS_SRGB_FBO_GLES3 = (1 << 2),
|
||||
GL2_CHAIN_FLAG_HAS_SRGB_FBO = (1 << 3),
|
||||
GL2_CHAIN_FLAG_HW_RENDER_DEPTH_INIT = (1 << 4)
|
||||
GL2_CHAIN_FLAG_HAS_SRGB_FBO = (1 << 2),
|
||||
GL2_CHAIN_FLAG_HW_RENDER_DEPTH_INIT = (1 << 3)
|
||||
};
|
||||
|
||||
typedef struct video_shader_ctx_scale
|
||||
@ -1616,9 +1615,7 @@ static void gl2_create_fbo_texture(gl2_t *gl,
|
||||
if (video_ctx_scaling)
|
||||
video_smooth = false;
|
||||
#endif
|
||||
#ifndef HAVE_OPENGLES
|
||||
bool force_srgb_disable = settings->bools.video_force_srgb_disable;
|
||||
#endif
|
||||
GLuint base_filt = video_smooth ? GL_LINEAR : GL_NEAREST;
|
||||
GLuint base_mip_filt = video_smooth ?
|
||||
GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST;
|
||||
@ -1650,19 +1647,21 @@ static void gl2_create_fbo_texture(gl2_t *gl,
|
||||
RARCH_ERR("[GL]: Floating-point FBO was requested, but is not supported. Falling back to UNORM. Result may band/clip/etc.!\n");
|
||||
}
|
||||
|
||||
#if !defined(HAVE_OPENGLES2)
|
||||
if ( fp_fbo
|
||||
&& (chain->flags & GL2_CHAIN_FLAG_HAS_FP_FBO))
|
||||
{
|
||||
RARCH_LOG("[GL]: FBO pass #%d is floating-point.\n", i);
|
||||
gl2_load_texture_image(GL_TEXTURE_2D, 0, GL_RGBA32F,
|
||||
gl2_load_texture_image(GL_TEXTURE_2D, 0,
|
||||
#ifdef HAVE_OPENGLES2
|
||||
GL_RGBA,
|
||||
#else
|
||||
GL_RGBA32F,
|
||||
#endif
|
||||
gl->fbo_rect[i].width, gl->fbo_rect[i].height,
|
||||
0, GL_RGBA, GL_FLOAT, NULL);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifndef HAVE_OPENGLES
|
||||
bool srgb_fbo = (chain->fbo_scale[i].flags & FBO_SCALE_FLAG_SRGB_FBO) ? true : false;
|
||||
|
||||
if (!fp_fbo && srgb_fbo)
|
||||
@ -1678,27 +1677,23 @@ static void gl2_create_fbo_texture(gl2_t *gl,
|
||||
&& (chain->flags & GL2_CHAIN_FLAG_HAS_SRGB_FBO))
|
||||
{
|
||||
RARCH_LOG("[GL]: FBO pass #%d is sRGB.\n", i);
|
||||
gl2_load_texture_image(GL_TEXTURE_2D, 0,
|
||||
#ifdef HAVE_OPENGLES2
|
||||
/* EXT defines are same as core GLES3 defines,
|
||||
* but GLES3 variant requires different arguments. */
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0, GL_SRGB_ALPHA_EXT,
|
||||
gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0,
|
||||
(chain->flags & GL2_CHAIN_FLAG_HAS_SRGB_FBO_GLES3)
|
||||
? GL_RGBA
|
||||
: GL_SRGB_ALPHA_EXT,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
GL_SRGB_ALPHA_EXT,
|
||||
#else
|
||||
gl2_load_texture_image(GL_TEXTURE_2D,
|
||||
0, GL_SRGB8_ALPHA8,
|
||||
gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
GL_SRGB8_ALPHA8,
|
||||
#endif
|
||||
gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0,
|
||||
#ifdef HAVE_OPENGLES2
|
||||
GL_SRGB_ALPHA_EXT,
|
||||
#else
|
||||
GL_RGBA,
|
||||
#endif
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#if defined(HAVE_OPENGLES2)
|
||||
#if defined(HAVE_OPENGLES)
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0, GL_RGBA,
|
||||
gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0,
|
||||
@ -2479,11 +2474,6 @@ static void gl2_renderchain_resolve_extensions(gl2_t *gl,
|
||||
chain->flags |= GL2_CHAIN_FLAG_HAS_FP_FBO;
|
||||
else
|
||||
chain->flags &= ~GL2_CHAIN_FLAG_HAS_FP_FBO;
|
||||
/* GLES3 has unpack_subimage and sRGB in core. */
|
||||
if (gl_check_capability(GL_CAPS_SRGB_FBO_ES3))
|
||||
chain->flags |= GL2_CHAIN_FLAG_HAS_SRGB_FBO_GLES3;
|
||||
else
|
||||
chain->flags &= ~GL2_CHAIN_FLAG_HAS_SRGB_FBO_GLES3;
|
||||
|
||||
if (!force_srgb_disable)
|
||||
{
|
||||
@ -3892,7 +3882,6 @@ static bool gl2_resolve_extensions(gl2_t *gl, const char *context_ident, const v
|
||||
RARCH_WARN("[GL]: GLES implementation does not have BGRA8888 extension.\n"
|
||||
"[GL]: 32-bit path will require conversion.\n");
|
||||
}
|
||||
/* TODO/FIXME - No extensions for float FBO currently. */
|
||||
#endif
|
||||
|
||||
#ifdef GL_DEBUG
|
||||
|
@ -38,8 +38,6 @@ typedef struct
|
||||
#ifdef HAVE_EGL
|
||||
egl_ctx_data_t egl;
|
||||
#endif
|
||||
int initial_width;
|
||||
int initial_height;
|
||||
unsigned fb_width;
|
||||
unsigned fb_height;
|
||||
} emscripten_ctx_data_t;
|
||||
@ -54,76 +52,29 @@ static void gfx_ctx_emscripten_swap_interval(void *data, int interval)
|
||||
|
||||
static void gfx_ctx_emscripten_get_canvas_size(int *width, int *height)
|
||||
{
|
||||
EmscriptenFullscreenChangeEvent fullscreen_status;
|
||||
bool is_fullscreen = false;
|
||||
EMSCRIPTEN_RESULT r = emscripten_get_fullscreen_status(&fullscreen_status);
|
||||
EMSCRIPTEN_RESULT r = emscripten_get_canvas_element_size("!canvas", width, height);
|
||||
|
||||
if (r == EMSCRIPTEN_RESULT_SUCCESS)
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
if (fullscreen_status.isFullscreen)
|
||||
{
|
||||
is_fullscreen = true;
|
||||
*width = fullscreen_status.screenWidth;
|
||||
*height = fullscreen_status.screenHeight;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_fullscreen)
|
||||
{
|
||||
r = emscripten_get_canvas_element_size("!canvas", width, height);
|
||||
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
*width = 800;
|
||||
*height = 600;
|
||||
RARCH_ERR("[EMSCRIPTEN/EGL]: Could not get screen dimensions: %d\n",r);
|
||||
}
|
||||
*width = 800;
|
||||
*height = 600;
|
||||
RARCH_ERR("[EMSCRIPTEN/EGL]: Could not get screen dimensions: %d\n",r);
|
||||
}
|
||||
}
|
||||
|
||||
static void gfx_ctx_emscripten_check_window(void *data, bool *quit,
|
||||
bool *resize, unsigned *width, unsigned *height)
|
||||
{
|
||||
EMSCRIPTEN_RESULT r;
|
||||
int input_width;
|
||||
int input_height;
|
||||
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
|
||||
|
||||
gfx_ctx_emscripten_get_canvas_size(&input_width, &input_height);
|
||||
|
||||
if (input_width == 0 || input_height == 0)
|
||||
{
|
||||
input_width = emscripten->initial_width;
|
||||
input_height = emscripten->initial_height;
|
||||
emscripten->fb_width = emscripten->fb_height = 0;
|
||||
}
|
||||
|
||||
*width = (unsigned)input_width;
|
||||
*height = (unsigned)input_height;
|
||||
*resize = false;
|
||||
|
||||
if ( (input_width != emscripten->fb_width)
|
||||
|| (input_height != emscripten->fb_height))
|
||||
{
|
||||
r = emscripten_set_canvas_element_size("!canvas",
|
||||
input_width, input_height);
|
||||
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
RARCH_ERR("[EMSCRIPTEN/EGL]: error resizing canvas: %d\n", r);
|
||||
|
||||
/* fix Module.requestFullscreen messing with the canvas size */
|
||||
r = emscripten_set_element_css_size("!canvas",
|
||||
(double)input_width, (double)input_height);
|
||||
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
RARCH_ERR("[EMSCRIPTEN/EGL]: error resizing canvas css: %d\n", r);
|
||||
|
||||
*resize = true;
|
||||
}
|
||||
|
||||
emscripten->fb_width = (unsigned)input_width;
|
||||
emscripten->fb_height = (unsigned)input_height;
|
||||
*quit = false;
|
||||
*width = emscripten->fb_width = (unsigned)input_width;
|
||||
*height = emscripten->fb_height = (unsigned)input_height;
|
||||
*quit = false;
|
||||
*resize = false;
|
||||
}
|
||||
|
||||
static void gfx_ctx_emscripten_swap_buffers(void *data)
|
||||
@ -148,6 +99,25 @@ static void gfx_ctx_emscripten_get_video_size(void *data,
|
||||
*height = emscripten->fb_height;
|
||||
}
|
||||
|
||||
static bool gfx_ctx_emscripten_get_metrics(void *data,
|
||||
enum display_metric_types type, float *value)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
// there is no way to get the actual DPI in emscripten, so return a standard value instead.
|
||||
// this is needed for menu touch/pointer swipe scrolling to work.
|
||||
case DISPLAY_METRIC_DPI:
|
||||
*value = 150.0f;
|
||||
break;
|
||||
|
||||
default:
|
||||
*value = 0.0f;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gfx_ctx_emscripten_destroy(void *data)
|
||||
{
|
||||
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
|
||||
@ -190,14 +160,6 @@ static void *gfx_ctx_emscripten_init(void *video_driver)
|
||||
if (!emscripten)
|
||||
return NULL;
|
||||
|
||||
/* TODO/FIXME - why is this conditional here - shouldn't these always
|
||||
* be grabbed? */
|
||||
if ( (emscripten->initial_width == 0)
|
||||
|| (emscripten->initial_height == 0))
|
||||
emscripten_get_canvas_element_size("!canvas",
|
||||
&emscripten->initial_width,
|
||||
&emscripten->initial_height);
|
||||
|
||||
#ifdef HAVE_EGL
|
||||
if (g_egl_inited)
|
||||
{
|
||||
@ -310,7 +272,7 @@ const gfx_ctx_driver_t gfx_ctx_emscripten = {
|
||||
NULL, /* get_video_output_size */
|
||||
NULL, /* get_video_output_prev */
|
||||
NULL, /* get_video_output_next */
|
||||
NULL, /* get_metrics */
|
||||
gfx_ctx_emscripten_get_metrics,
|
||||
gfx_ctx_emscripten_translate_aspect,
|
||||
NULL, /* update_title */
|
||||
gfx_ctx_emscripten_check_window,
|
||||
|
@ -23,14 +23,18 @@
|
||||
#include <encodings/crc32.h>
|
||||
#include <encodings/utf.h>
|
||||
|
||||
#include <emscripten/emscripten.h>
|
||||
#include <emscripten/html5.h>
|
||||
|
||||
#include "../input_driver.h"
|
||||
#include "../input_types.h"
|
||||
#include "../input_keymaps.h"
|
||||
|
||||
#include "../../tasks/tasks_internal.h"
|
||||
#include "../../configuration.h"
|
||||
#include "../../retroarch.h"
|
||||
#include "../../verbosity.h"
|
||||
#include "../../command.h"
|
||||
|
||||
/* https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button */
|
||||
#define RWEBINPUT_MOUSE_BTNL 0
|
||||
@ -39,6 +43,8 @@
|
||||
#define RWEBINPUT_MOUSE_BTN4 3
|
||||
#define RWEBINPUT_MOUSE_BTN5 4
|
||||
|
||||
#define MAX_TOUCH 32
|
||||
|
||||
typedef struct rwebinput_key_to_code_map_entry
|
||||
{
|
||||
const char *key;
|
||||
@ -58,26 +64,36 @@ typedef struct rwebinput_keyboard_event_queue
|
||||
size_t max_size;
|
||||
} rwebinput_keyboard_event_queue_t;
|
||||
|
||||
typedef struct rwebinput_pointer_states
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int id;
|
||||
} rwebinput_pointer_state_t;
|
||||
|
||||
typedef struct rwebinput_mouse_states
|
||||
{
|
||||
double pending_scroll_x;
|
||||
double pending_scroll_y;
|
||||
double scroll_x;
|
||||
double scroll_y;
|
||||
signed x;
|
||||
signed y;
|
||||
signed pending_delta_x;
|
||||
signed pending_delta_y;
|
||||
signed delta_x;
|
||||
signed delta_y;
|
||||
int x;
|
||||
int y;
|
||||
int pending_delta_x;
|
||||
int pending_delta_y;
|
||||
int delta_x;
|
||||
int delta_y;
|
||||
uint8_t buttons;
|
||||
} rwebinput_mouse_state_t;
|
||||
|
||||
typedef struct rwebinput_input
|
||||
{
|
||||
rwebinput_mouse_state_t mouse; /* double alignment */
|
||||
rwebinput_keyboard_event_queue_t keyboard; /* ptr alignment */
|
||||
rwebinput_mouse_state_t mouse; /* double alignment */
|
||||
rwebinput_keyboard_event_queue_t keyboard; /* ptr alignment */
|
||||
rwebinput_pointer_state_t pointer[MAX_TOUCH]; /* int alignment */
|
||||
unsigned pointer_count;
|
||||
bool keys[RETROK_LAST];
|
||||
bool pointerlock_active;
|
||||
} rwebinput_input_t;
|
||||
|
||||
/* KeyboardEvent.keyCode has been deprecated for a while and doesn't have
|
||||
@ -255,11 +271,39 @@ static EM_BOOL rwebinput_mouse_cb(int event_type,
|
||||
|
||||
uint8_t mask = 1 << mouse_event->button;
|
||||
|
||||
rwebinput->mouse.x = mouse_event->targetX;
|
||||
rwebinput->mouse.y = mouse_event->targetY;
|
||||
// note: movementX/movementY are pre-scaled in chromium (but not firefox)
|
||||
// see https://github.com/w3c/pointerlock/issues/42
|
||||
|
||||
rwebinput->mouse.pending_delta_x += mouse_event->movementX;
|
||||
rwebinput->mouse.pending_delta_y += mouse_event->movementY;
|
||||
|
||||
if (rwebinput->pointerlock_active)
|
||||
{
|
||||
unsigned video_width, video_height;
|
||||
video_driver_get_size(&video_width, &video_height);
|
||||
|
||||
rwebinput->mouse.x += mouse_event->movementX;
|
||||
rwebinput->mouse.y += mouse_event->movementY;
|
||||
|
||||
/* Clamp X */
|
||||
if (rwebinput->mouse.x < 0)
|
||||
rwebinput->mouse.x = 0;
|
||||
if (rwebinput->mouse.x >= video_width)
|
||||
rwebinput->mouse.x = (int)(video_width - 1);
|
||||
|
||||
/* Clamp Y */
|
||||
if (rwebinput->mouse.y < 0)
|
||||
rwebinput->mouse.y = 0;
|
||||
if (rwebinput->mouse.y >= video_height)
|
||||
rwebinput->mouse.y = (int)(video_height - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
double dpr = emscripten_get_device_pixel_ratio();
|
||||
rwebinput->mouse.x = (int)(mouse_event->targetX * dpr);
|
||||
rwebinput->mouse.y = (int)(mouse_event->targetY * dpr);
|
||||
}
|
||||
|
||||
if (event_type == EMSCRIPTEN_EVENT_MOUSEDOWN)
|
||||
rwebinput->mouse.buttons |= mask;
|
||||
else if (event_type == EMSCRIPTEN_EVENT_MOUSEUP)
|
||||
@ -273,8 +317,90 @@ static EM_BOOL rwebinput_wheel_cb(int event_type,
|
||||
{
|
||||
rwebinput_input_t *rwebinput = (rwebinput_input_t*)user_data;
|
||||
|
||||
rwebinput->mouse.pending_scroll_x += wheel_event->deltaX;
|
||||
rwebinput->mouse.pending_scroll_y += wheel_event->deltaY;
|
||||
double dpr = emscripten_get_device_pixel_ratio();
|
||||
rwebinput->mouse.pending_scroll_x += wheel_event->deltaX * dpr;
|
||||
rwebinput->mouse.pending_scroll_y += wheel_event->deltaY * dpr;
|
||||
|
||||
return EM_TRUE;
|
||||
}
|
||||
|
||||
static EM_BOOL rwebinput_touch_cb(int event_type,
|
||||
const EmscriptenTouchEvent *touch_event, void *user_data)
|
||||
{
|
||||
rwebinput_input_t *rwebinput = (rwebinput_input_t*)user_data;
|
||||
|
||||
unsigned touches_max = MIN(touch_event->numTouches, MAX_TOUCH);
|
||||
unsigned touches_released = 0;
|
||||
|
||||
switch (event_type)
|
||||
{
|
||||
case EMSCRIPTEN_EVENT_TOUCHSTART:
|
||||
case EMSCRIPTEN_EVENT_TOUCHMOVE:
|
||||
for (unsigned touch = 0; touch < touches_max; touch++)
|
||||
{
|
||||
if (!(touch_event->touches[touch].isChanged) && rwebinput->pointer[touch].id == touch_event->touches[touch].identifier)
|
||||
continue;
|
||||
|
||||
double dpr = emscripten_get_device_pixel_ratio();
|
||||
rwebinput->pointer[touch].x = (int)(touch_event->touches[touch].targetX * dpr);
|
||||
rwebinput->pointer[touch].y = (int)(touch_event->touches[touch].targetY * dpr);
|
||||
rwebinput->pointer[touch].id = touch_event->touches[touch].identifier;
|
||||
}
|
||||
break;
|
||||
case EMSCRIPTEN_EVENT_TOUCHEND:
|
||||
case EMSCRIPTEN_EVENT_TOUCHCANCEL:
|
||||
// note: touches_max/numTouches is out of date here - it uses the old value from before the release
|
||||
// note 2: I'm unsure if multiple touches can trigger the same touchend anyway...
|
||||
if (touches_max > 1)
|
||||
{
|
||||
for (unsigned touch_up = 0; touch_up < touches_max; touch_up++)
|
||||
{
|
||||
if (touch_event->touches[touch_up].isChanged)
|
||||
{
|
||||
memmove(rwebinput->pointer + touch_up - touches_released,
|
||||
rwebinput->pointer + touch_up - touches_released + 1,
|
||||
(touches_max - touch_up - 1) * sizeof(rwebinput_pointer_state_t));
|
||||
touches_released++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
touches_released = 1;
|
||||
|
||||
if (touches_max > touches_released)
|
||||
touches_max -= touches_released;
|
||||
else
|
||||
touches_max = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
rwebinput->pointer_count = touches_max;
|
||||
|
||||
return EM_TRUE;
|
||||
}
|
||||
|
||||
static EM_BOOL rwebinput_pointerlockchange_cb(int event_type,
|
||||
const EmscriptenPointerlockChangeEvent *pointerlock_change_event, void *user_data)
|
||||
{
|
||||
rwebinput_input_t *rwebinput = (rwebinput_input_t*)user_data;
|
||||
|
||||
rwebinput->pointerlock_active = pointerlock_change_event->isActive;
|
||||
|
||||
if (!pointerlock_change_event->isActive)
|
||||
{
|
||||
input_driver_state_t *input_st = input_state_get_ptr();
|
||||
|
||||
if (input_st->game_focus_state.enabled)
|
||||
{
|
||||
enum input_game_focus_cmd_type game_focus_cmd = GAME_FOCUS_CMD_OFF;
|
||||
command_event(CMD_EVENT_GAME_FOCUS_TOGGLE, &game_focus_cmd);
|
||||
}
|
||||
|
||||
if (input_st->flags & INP_FLAG_GRAB_MOUSE_STATE)
|
||||
{
|
||||
command_event(CMD_EVENT_GRAB_MOUSE_TOGGLE, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return EM_TRUE;
|
||||
}
|
||||
@ -350,6 +476,47 @@ static void *rwebinput_input_init(const char *joypad_driver)
|
||||
"[EMSCRIPTEN/INPUT] failed to create wheel callback: %d\n", r);
|
||||
}
|
||||
|
||||
r = emscripten_set_touchstart_callback("!canvas", rwebinput, false,
|
||||
rwebinput_touch_cb);
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
RARCH_ERR(
|
||||
"[EMSCRIPTEN/INPUT] failed to create touchstart callback: %d\n", r);
|
||||
}
|
||||
|
||||
r = emscripten_set_touchend_callback("!canvas", rwebinput, false,
|
||||
rwebinput_touch_cb);
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
RARCH_ERR(
|
||||
"[EMSCRIPTEN/INPUT] failed to create touchend callback: %d\n", r);
|
||||
}
|
||||
|
||||
r = emscripten_set_touchmove_callback("!canvas", rwebinput, false,
|
||||
rwebinput_touch_cb);
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
RARCH_ERR(
|
||||
"[EMSCRIPTEN/INPUT] failed to create touchmove callback: %d\n", r);
|
||||
}
|
||||
|
||||
r = emscripten_set_touchcancel_callback("!canvas", rwebinput, false,
|
||||
rwebinput_touch_cb);
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
RARCH_ERR(
|
||||
"[EMSCRIPTEN/INPUT] failed to create touchcancel callback: %d\n", r);
|
||||
}
|
||||
|
||||
r = emscripten_set_pointerlockchange_callback(
|
||||
EMSCRIPTEN_EVENT_TARGET_DOCUMENT, rwebinput, false,
|
||||
rwebinput_pointerlockchange_cb);
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
RARCH_ERR(
|
||||
"[EMSCRIPTEN/INPUT] failed to create pointerlockchange callback: %d\n", r);
|
||||
}
|
||||
|
||||
input_keymaps_init_keyboard_lut(rarch_key_map_rwebinput);
|
||||
|
||||
return rwebinput;
|
||||
@ -505,24 +672,41 @@ static int16_t rwebinput_input_state(
|
||||
return rwebinput_mouse_state(&rwebinput->mouse, id, device == RARCH_DEVICE_MOUSE_SCREEN);
|
||||
case RETRO_DEVICE_POINTER:
|
||||
case RARCH_DEVICE_POINTER_SCREEN:
|
||||
if (idx == 0)
|
||||
{
|
||||
struct video_viewport vp = {0};
|
||||
rwebinput_mouse_state_t
|
||||
*mouse = &rwebinput->mouse;
|
||||
bool screen = device ==
|
||||
RARCH_DEVICE_POINTER_SCREEN;
|
||||
bool pointer_down = false;
|
||||
unsigned pointer_count = rwebinput->pointer_count;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int16_t res_x = 0;
|
||||
int16_t res_y = 0;
|
||||
int16_t res_screen_x = 0;
|
||||
int16_t res_screen_y = 0;
|
||||
|
||||
if (pointer_count && idx < pointer_count)
|
||||
{
|
||||
x = rwebinput->pointer[idx].x;
|
||||
y = rwebinput->pointer[idx].y;
|
||||
pointer_down = true;
|
||||
}
|
||||
else if (idx == 0)
|
||||
{
|
||||
x = mouse->x;
|
||||
y = mouse->y;
|
||||
pointer_down = !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTNL));
|
||||
pointer_count = 1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
if (!(video_driver_translate_coord_viewport_confined_wrap(
|
||||
&vp, mouse->x, mouse->y,
|
||||
&vp, x, y,
|
||||
&res_x, &res_y, &res_screen_x, &res_screen_y)))
|
||||
return 0;
|
||||
|
||||
if (screen)
|
||||
if (device == RARCH_DEVICE_POINTER_SCREEN)
|
||||
{
|
||||
res_x = res_screen_x;
|
||||
res_y = res_screen_y;
|
||||
@ -535,7 +719,9 @@ static int16_t rwebinput_input_state(
|
||||
case RETRO_DEVICE_ID_POINTER_Y:
|
||||
return res_y;
|
||||
case RETRO_DEVICE_ID_POINTER_PRESSED:
|
||||
return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTNL));
|
||||
return (pointer_down && !input_driver_pointer_is_offscreen(res_x, res_y));
|
||||
case RETRO_DEVICE_ID_POINTER_COUNT:
|
||||
return pointer_count;
|
||||
case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
|
||||
return input_driver_pointer_is_offscreen(res_x, res_y);
|
||||
default:
|
||||
|
@ -287,15 +287,12 @@ bool gl_check_capability(enum gl_capability_enum enum_idx)
|
||||
}
|
||||
break;
|
||||
case GL_CAPS_FP_FBO:
|
||||
/* GLES - No extensions for float FBO currently. */
|
||||
#ifndef HAVE_OPENGLES
|
||||
if (gl_check_capability(GL_CAPS_FBO))
|
||||
{
|
||||
/* Float FBO is core in 3.2. */
|
||||
if (gl_query_core_context_in_use() || gl_query_extension("ARB_texture_float"))
|
||||
if (gl_query_core_context_in_use() || gl_query_extension("ARB_texture_float") || gl_query_extension("OES_texture_float_linear"))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case GL_CAPS_BGRA8888:
|
||||
#ifdef HAVE_OPENGLES
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include <psp2/kernel/threadmgr.h>
|
||||
#elif defined(_3DS)
|
||||
#include <3ds.h>
|
||||
#elif defined(EMSCRIPTEN_FIXME)
|
||||
#elif defined(EMSCRIPTEN)
|
||||
#include <emscripten/emscripten.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
@ -100,7 +100,7 @@ static int nanosleepDOS(const struct timespec *rqtp, struct timespec *rmtp)
|
||||
#define retro_sleep(msec) (usleep(1000 * (msec)))
|
||||
#elif defined(WIIU)
|
||||
#define retro_sleep(msec) (OSSleepTicks(ms_to_ticks((msec))))
|
||||
#elif defined(EMSCRIPTEN_FIXME)
|
||||
#elif defined(EMSCRIPTEN)
|
||||
#define retro_sleep(msec) (emscripten_sleep(msec))
|
||||
#else
|
||||
static INLINE void retro_sleep(unsigned msec)
|
||||
|
@ -452,7 +452,8 @@ enum ozone_handle_flags2
|
||||
OZONE_FLAG2_RESET_DEPTH = (1 << 8),
|
||||
OZONE_FLAG2_PENDING_CURSOR_IN_SIDEBAR = (1 << 9),
|
||||
OZONE_FLAG2_IS_QUICK_MENU = (1 << 10),
|
||||
OZONE_FLAG2_IS_PLAYLISTS_TAB = (1 << 11)
|
||||
OZONE_FLAG2_IS_PLAYLISTS_TAB = (1 << 11),
|
||||
OZONE_FLAG2_IGNORE_MISSING_ASSETS = (1 << 12)
|
||||
};
|
||||
|
||||
struct ozone_handle
|
||||
@ -1732,7 +1733,7 @@ static void ozone_set_color_theme(
|
||||
ozone->theme->message_background,
|
||||
sizeof(ozone->theme_dynamic.message_background));
|
||||
|
||||
if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS)
|
||||
if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS || ozone->flags2 & OZONE_FLAG2_IGNORE_MISSING_ASSETS)
|
||||
ozone_restart_cursor_animation(ozone);
|
||||
|
||||
ozone_last_color_theme = color_theme;
|
||||
@ -3215,7 +3216,7 @@ static void ozone_draw_cursor(
|
||||
|
||||
/* Draw the cursor */
|
||||
if ( (ozone->theme->name)
|
||||
&& (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS))
|
||||
&& (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS || ozone->flags2 & OZONE_FLAG2_IGNORE_MISSING_ASSETS))
|
||||
ozone_draw_cursor_slice(ozone,
|
||||
p_disp,
|
||||
userdata,
|
||||
@ -7024,7 +7025,7 @@ static void ozone_draw_messagebox(
|
||||
dispctx->blend_begin(userdata);
|
||||
|
||||
/* Avoid drawing a black box if there's no assets */
|
||||
if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS)
|
||||
if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS || ozone->flags2 & OZONE_FLAG2_IGNORE_MISSING_ASSETS)
|
||||
{
|
||||
/* Note: The fact that we use a texture slice here
|
||||
* makes things very messy
|
||||
@ -9432,9 +9433,14 @@ static void ozone_context_reset(void *data, bool is_threaded)
|
||||
|
||||
if (ozone)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
ozone->flags |= OZONE_FLAG_HAS_ALL_ASSETS;
|
||||
|
||||
ozone_set_layout(ozone, config_get_ptr()->bools.ozone_collapse_sidebar, is_threaded);
|
||||
if (settings->bools.menu_ignore_missing_assets)
|
||||
ozone->flags2 |= OZONE_FLAG2_IGNORE_MISSING_ASSETS;
|
||||
|
||||
ozone_set_layout(ozone, settings->bools.ozone_collapse_sidebar, is_threaded);
|
||||
|
||||
/* Textures init */
|
||||
for (i = 0; i < OZONE_TEXTURE_LAST; i++)
|
||||
@ -9527,7 +9533,7 @@ static void ozone_context_reset(void *data, bool is_threaded)
|
||||
ozone_update_thumbnail_image(ozone);
|
||||
ozone_update_savestate_thumbnail_image(ozone);
|
||||
|
||||
if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS)
|
||||
if (ozone->flags & OZONE_FLAG_HAS_ALL_ASSETS || ozone->flags2 & OZONE_FLAG2_IGNORE_MISSING_ASSETS)
|
||||
ozone_restart_cursor_animation(ozone);
|
||||
|
||||
/* Screensaver */
|
||||
|
@ -1,10 +1,10 @@
|
||||
# RetroArch Web Player
|
||||
|
||||
The RetroArch Web Player is RetroArch compiled through [Emscripten](http://kripken.github.io/emscripten-site/). The following outlines how to compile RetroArch using Emscripten, and running it in your browser.
|
||||
The RetroArch Web Player is RetroArch compiled through [Emscripten](https://emscripten.org/). The following outlines how to compile RetroArch using Emscripten, and running it in your browser.
|
||||
|
||||
## Compiling
|
||||
|
||||
To compile RetroArch with Emscripten, you'll first have to [download and install the Emscripten SDK](http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html) at 3.1.46:
|
||||
To compile RetroArch with Emscripten, you'll first have to [download and install the Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html) at 3.1.46:
|
||||
|
||||
```
|
||||
git clone https://github.com/emscripten-core/emsdk.git
|
||||
|
@ -1,91 +1,83 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>RetroArch Web Player</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.3/css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.0/css/font-awesome.min.css">
|
||||
<!-- Material Design Bootstrap -->
|
||||
<link href="//cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.1.1/css/mdb.min.css" rel="stylesheet">
|
||||
|
||||
<link href="libretro.css" rel="stylesheet" type="text/css">
|
||||
<link rel="shortcut icon" href="https://web.libretro.com/media/retroarch.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<!--Navbar-->
|
||||
<nav class="navbar navbar-dark bg-primary">
|
||||
<div class="container">
|
||||
<!--navbar content-->
|
||||
<div class="navbar-toggleable-xs">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>RetroArch Web Player</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.3/css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.0/css/font-awesome.min.css">
|
||||
<!-- Material Design Bootstrap -->
|
||||
<link href="//cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.1.1/css/mdb.min.css" rel="stylesheet">
|
||||
<link href="libretro.css" rel="stylesheet" type="text/css">
|
||||
<link rel="shortcut icon" href="https://web.libretro.com/media/retroarch.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<!--Navbar-->
|
||||
<nav class="navbar navbar-dark bg-primary">
|
||||
<div class="container">
|
||||
<!--navbar content-->
|
||||
<div class="navbar-toggleable-xs">
|
||||
<!--Links-->
|
||||
<ul class="nav navbar-nav">
|
||||
<div class="dropdown">
|
||||
<li class="nav-item dropdown">
|
||||
<li class="nav-item dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Core Selection</button>
|
||||
<div class="dropdown-menu dropdown-primary" aria-labelledby="dropdownMenu1" data-dropdown-in="fadeIn" data-dropdown-out="fadeOut" id="core-selector">
|
||||
<a class="dropdown-item" href="." data-core="chailove">ChaiLove</a>
|
||||
<a class="dropdown-item" href="." data-core="fceumm">FCEUmm</a>
|
||||
<a class="dropdown-item" href="." data-core="gambatte">Gambatte</a>
|
||||
<a class="dropdown-item" href="." data-core="genesis_plus_gx">Genesis Plus GX</a>
|
||||
<a class="dropdown-item" href="." data-core="lutro">Lutro</a>
|
||||
<a class="dropdown-item" href="." data-core="nestopia">Nestopia (NES)</a>
|
||||
<a class="dropdown-item" href="." data-core="snes9x2010">Snes9x 2010 (SNES)</a>
|
||||
<a class="dropdown-item" href="." data-core="theodore">Theodore (Thomson TO8/TO9)</a>
|
||||
<a class="dropdown-item" href="." data-core="vba_next">VBA Next (Gameboy Advance)</a>
|
||||
<a class="dropdown-item" href="." data-core="chailove">ChaiLove</a>
|
||||
<a class="dropdown-item" href="." data-core="fceumm">FCEUmm</a>
|
||||
<a class="dropdown-item" href="." data-core="gambatte">Gambatte</a>
|
||||
<a class="dropdown-item" href="." data-core="genesis_plus_gx">Genesis Plus GX</a>
|
||||
<a class="dropdown-item" href="." data-core="lutro">Lutro</a>
|
||||
<a class="dropdown-item" href="." data-core="nestopia">Nestopia (NES)</a>
|
||||
<a class="dropdown-item" href="." data-core="snes9x">Snes9x (SNES)</a>
|
||||
<a class="dropdown-item" href="." data-core="snes9x2010">Snes9x 2010 (SNES)</a>
|
||||
<a class="dropdown-item" href="." data-core="theodore">Theodore (Thomson TO8/TO9)</a>
|
||||
<a class="dropdown-item" href="." data-core="vba_next">VBA Next (Gameboy Advance)</a>
|
||||
</div>
|
||||
<button class="btn btn-primary disabled" id="btnRun" onclick="startRetroArch()" disabled>
|
||||
<span class="fa fa-spinner fa-spin" id="icnRun"></span> Run
|
||||
<button class="btn btn-primary disabled" id="btnRun" disabled>
|
||||
<span class="fa fa-spinner fa-spin" id="icnRun"></span> Run
|
||||
</button>
|
||||
|
||||
<button class="btn btn-primary disabled" id="btnAdd" onclick="document.getElementById('btnRom').click()" disabled>
|
||||
<span class="fa fa-plus" id="icnAdd"></span> Add Content
|
||||
<button class="btn btn-primary disabled" id="btnAdd" disabled>
|
||||
<span class="fa fa-plus" id="icnAdd"></span> Add Content
|
||||
</button>
|
||||
|
||||
<button class="btn btn-primary tooltip-enable" id="btnClean" onclick="cleanupStorage();" title="Cleanup storage">
|
||||
<span class="fa fa-trash-o" id="icnClean"></span> <span class="sr-only">Cleanup</span>
|
||||
<input style="display: none" type="file" id="btnRom" name="upload" multiple />
|
||||
<button class="btn btn-primary tooltip-enable" id="btnClean" title="Cleanup storage">
|
||||
<span class="fa fa-trash-o" id="icnClean"></span> <span class="sr-only">Cleanup</span>
|
||||
</button>
|
||||
|
||||
<input class="btn btn-primary disabled" style="display: none" type="file" id="btnRom" name="upload" onclick="document.getElementById('btnAdd').click();" onchange="selectFiles(event.target.files)" multiple />
|
||||
<button class="btn btn-primary disabled tooltip-enable" id="btnMenu" onclick="keyPress('F1');" title="Menu toggle" disabled>
|
||||
<span class="fa fa-bars" id="btnMenu"></span> <span class="sr-only">Menu</span>
|
||||
<button class="btn btn-primary disabled tooltip-enable" id="btnMenu" title="Menu toggle" disabled>
|
||||
<span class="fa fa-bars" id="icnMenu"></span> <span class="sr-only">Menu</span>
|
||||
</button>
|
||||
|
||||
<button class="btn btn-primary disabled tooltip-enable" id="btnFullscreen" onclick="Module.requestFullscreen(false)" title="Fullscreen" disabled>
|
||||
<span class="fa fa-desktop" id="icnAdd"></span> <span class="sr-only">Fullscreen</span>
|
||||
<button class="btn btn-primary disabled tooltip-enable" id="btnFullscreen" title="Fullscreen" disabled>
|
||||
<span class="fa fa-desktop" id="icnFullscreen"></span> <span class="sr-only">Fullscreen</span>
|
||||
</button>
|
||||
|
||||
</li>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
<!--/.navbar content-->
|
||||
</div>
|
||||
</nav>
|
||||
<div class="bg-inverse webplayer-container">
|
||||
<div class="container">
|
||||
<div class="webplayer_border text-xs-center" id="canvas_div">
|
||||
<canvas class="webplayer" id="canvas" tabindex="1" oncontextmenu="event.preventDefault()" style="display: none"></canvas>
|
||||
<img class="webplayer-preview img-fluid" src="media/canvas.png" width="960px" height="720px" alt="RetroArch Logo">
|
||||
</div>
|
||||
<!--/.navbar content-->
|
||||
</div>
|
||||
</nav>
|
||||
<div class="bg-inverse webplayer-container">
|
||||
<div class="container">
|
||||
<div class="webplayer_border text-xs-center" id="canvas_div">
|
||||
<canvas class="webplayer" id="canvas" tabindex="1" oncontextmenu="event.preventDefault()" style="display: none"></canvas>
|
||||
<img class="webplayer-preview img-fluid" src="media/canvas.png" width="960" height="720" alt="RetroArch Logo">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="//code.jquery.com/jquery-3.1.0.min.js"></script>
|
||||
<script src="//rawgit.com/jeresig/jquery.hotkeys/master/jquery.hotkeys.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/tether/1.3.4/js/tether.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.3/js/bootstrap.min.js"></script>
|
||||
<script src="analytics.js"></script>
|
||||
<!--script src="//wzrd.in/standalone/browserfs@0.6.1"></script-->
|
||||
<script src="browserfs.min.js"></script>
|
||||
<script src="libretro.js"></script>
|
||||
<div align="center">
|
||||
<a href="https://www.patreon.com/libretro">
|
||||
<img src="https://patreon_public_assets.s3.amazonaws.com/sized/becomeAPatronBanner.png" width="350" height="116"></a>
|
||||
</div>
|
||||
</body>
|
||||
<script src="//code.jquery.com/jquery-3.1.0.min.js"></script>
|
||||
<script src="//rawgit.com/jeresig/jquery.hotkeys/master/jquery.hotkeys.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/tether/1.3.4/js/tether.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.3/js/bootstrap.min.js"></script>
|
||||
<script src="analytics.js"></script>
|
||||
<!--script src="//wzrd.in/standalone/browserfs@0.6.1"></script-->
|
||||
<script src="browserfs.min.js"></script>
|
||||
<script src="libretro.js"></script>
|
||||
<div align="center">
|
||||
<a href="https://www.patreon.com/libretro">
|
||||
<img src="https://patreon_public_assets.s3.amazonaws.com/sized/becomeAPatronBanner.png" alt="Become a patron" width="350" height="116"></a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,30 +1,28 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>RetroArch Web Player</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.3/css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.0/css/font-awesome.min.css">
|
||||
<!-- Material Design Bootstrap -->
|
||||
<link href="//cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.1.1/css/mdb.min.css" rel="stylesheet">
|
||||
|
||||
<link href="libretro.css" rel="stylesheet" type="text/css">
|
||||
<link rel="shortcut icon" href="media/retroarch.ico" />
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<!--Navbar-->
|
||||
<nav class="navbar navbar-dark bg-primary">
|
||||
<div class="container">
|
||||
<!--navbar content-->
|
||||
<div class="navbar-toggleable-xs">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>RetroArch Web Player</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.3/css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.0/css/font-awesome.min.css">
|
||||
<!-- Material Design Bootstrap -->
|
||||
<link href="//cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.1.1/css/mdb.min.css" rel="stylesheet">
|
||||
<link href="libretro.css" rel="stylesheet" type="text/css">
|
||||
<link rel="shortcut icon" href="media/retroarch.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<!--Navbar-->
|
||||
<nav class="navbar navbar-dark bg-primary">
|
||||
<div class="container">
|
||||
<!--navbar content-->
|
||||
<div class="navbar-toggleable-xs">
|
||||
<!--Links-->
|
||||
<ul class="nav navbar-nav">
|
||||
<div class="dropdown">
|
||||
<li class="nav-item dropdown">
|
||||
<li class="nav-item dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Core Selection</button>
|
||||
<div class="dropdown-menu dropdown-primary" aria-labelledby="dropdownMenu1" data-dropdown-in="fadeIn" data-dropdown-out="fadeOut" id="core-selector">
|
||||
<a class="dropdown-item" href="." data-core="2048">2048</a>
|
||||
@ -117,92 +115,90 @@
|
||||
<a class="dropdown-item" href="." data-core="x1">Sharp X1 (X Millenium)</a>
|
||||
<a class="dropdown-item" href="." data-core="xrick">Rick Dangerous (XRick)</a>
|
||||
</div>
|
||||
<button class="btn btn-primary disabled" id="btnRun" onclick="startRetroArch()" disabled>
|
||||
<span class="fa fa-spinner fa-spin" id="icnRun"></span> Run
|
||||
<button class="btn btn-primary disabled" id="btnRun" disabled>
|
||||
<span class="fa fa-spinner fa-spin" id="icnRun"></span> Run
|
||||
</button>
|
||||
<button class="btn btn-primary disabled" id="btnAdd" onclick="document.getElementById('btnRom').click()" disabled>
|
||||
<span class="fa fa-plus" id="icnAdd"></span> Add Content
|
||||
<button class="btn btn-primary disabled" id="btnAdd" disabled>
|
||||
<span class="fa fa-plus" id="icnAdd"></span> Add Content
|
||||
</button>
|
||||
<button class="btn btn-primary tooltip-enable" id="btnClean" onclick="cleanupStorage();" title="Cleanup storage">
|
||||
<span class="fa fa-trash-o" id="icnClean"></span> <span class="sr-only">Cleanup</span>
|
||||
<input style="display: none" type="file" id="btnRom" name="upload" multiple />
|
||||
<button class="btn btn-primary tooltip-enable" id="btnClean" title="Cleanup storage">
|
||||
<span class="fa fa-trash-o" id="icnClean"></span> <span class="sr-only">Cleanup</span>
|
||||
</button>
|
||||
<input class="btn btn-primary disabled" style="display: none" type="file" id="btnRom" name="upload" onclick="document.getElementById('btnAdd').click();" onchange="selectFiles(event.target.files)" multiple />
|
||||
<button class="btn btn-primary disabled tooltip-enable" id="btnMenu" onclick="keyPress('F1');" title="Menu toggle" disabled>
|
||||
<span class="fa fa-bars" id="btnMenu"></span> <span class="sr-only">Menu</span>
|
||||
</button>
|
||||
<button class="btn btn-primary disabled tooltip-enable" id="btnFullscreen" onclick="Module.requestFullscreen(false)" title="Fullscreen" disabled>
|
||||
<span class="fa fa-desktop" id="icnAdd"></span> <span class="sr-only">Fullscreen</span>
|
||||
<button class="btn btn-primary disabled tooltip-enable" id="btnMenu" title="Menu toggle" disabled>
|
||||
<span class="fa fa-bars" id="icnMenu"></span> <span class="sr-only">Menu</span>
|
||||
</button>
|
||||
<button class="btn btn-primary disabled tooltip-enable" id="btnFullscreen" title="Fullscreen" disabled>
|
||||
<span class="fa fa-desktop" id="icnFullscreen"></span> <span class="sr-only">Fullscreen</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary tooltip-enable" data-toggle="modal" data-target="#helpModal">Help</button>
|
||||
</li>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
<div class="toggleMenu">
|
||||
<button class="btn btn-primary" id="btnHideMenu" title="Toggle Menu">
|
||||
<span class="fa fa-chevron-up" id="icnHideMenu"></span> <span class="sr-only">Hide Top Navigation</span>
|
||||
<span class="fa fa-chevron-up" id="icnHideMenu"></span> <span class="sr-only">Hide Top Navigation</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Basics steps modal for Web Libretro -->
|
||||
<div class="modal fade" id="helpModal" role="dialog" style="color:black;">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h1 class="modal-title">Basics</h1>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h3><b>Load Core</b></h3>
|
||||
<p>Load your core by clicking on the first tab. Scroll down until you reach the desired Core. We will use Nestopia for now. Don't forget - Content must be compatible with the matched Core.</p>
|
||||
<li>Nes: <i>NESTOPIA</i></li>
|
||||
<li>Game Boy / Color: <i>Gambatte</i></li>
|
||||
</ul>
|
||||
<p>etc.</p>
|
||||
<p></p>
|
||||
<h3><b>Load Content</b></h3>
|
||||
<p>After selecting Core, click Run. After RetroArch opens, click Add Content and select your compatible ROM.</p>
|
||||
<li>Nestopia > <i>YourGame.nes</i></li>
|
||||
<li>Gambatte > <i>YourGame.gbc</i></li>
|
||||
</ul>
|
||||
<p>etc.</p>
|
||||
<p></p>
|
||||
<h3><b><span class="fa fa-trash-o"></span> Cleanup Storage</b></h3>
|
||||
<p>The trashcan erases your existing configuration and presets. If the Web Player doesn't start, you should click the trashcan and refresh the cache in your browser (usually F5 or Shift+F5).</p>
|
||||
<p></p>
|
||||
<h3><b><span class="fa fa-bars"></span> Quick Menu</b></h3>
|
||||
<p>If you click on the three line icons, the Quick Menu will open here as in RetroArch.</p>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Basics steps modal for Web Libretro -->
|
||||
<div class="modal fade" id="helpModal" role="dialog" style="color:black;">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h1 class="modal-title">Basics</h1>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h3><b>Load Core</b></h3>
|
||||
<p>Load your core by clicking on the first tab. Scroll down until you reach the desired Core. We will use Nestopia for now. Don't forget - Content must be compatible with the matched Core.</p>
|
||||
<ul>
|
||||
<li>Nes: <i>NESTOPIA</i></li>
|
||||
<li>Game Boy / Color: <i>Gambatte</i></li>
|
||||
</ul>
|
||||
<p>etc.</p>
|
||||
<p></p>
|
||||
<h3><b>Load Content</b></h3>
|
||||
<p>After selecting Core, click Run. After RetroArch opens, click Add Content and select your compatible ROM.</p>
|
||||
<ul>
|
||||
<li>Nestopia > <i>YourGame.nes</i></li>
|
||||
<li>Gambatte > <i>YourGame.gbc</i></li>
|
||||
</ul>
|
||||
<p>etc.</p>
|
||||
<p></p>
|
||||
<h3><b><span class="fa fa-trash-o"></span> Cleanup Storage</b></h3>
|
||||
<p>The trashcan erases your existing configuration and presets. If the Web Player doesn't start, you should click the trashcan and refresh the cache in your browser (usually F5 or Shift+F5).</p>
|
||||
<p></p>
|
||||
<h3><b><span class="fa fa-bars"></span> Quick Menu</b></h3>
|
||||
<p>If you click on the three line icons, the Quick Menu will open here as in RetroArch.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--/.navbar content-->
|
||||
</nav>
|
||||
<div class="bg-inverse webplayer-container">
|
||||
<div class="webplayer_border text-xs-center" id="canvas_div">
|
||||
<div class="showMenu">
|
||||
<button type="button" class="btn btn-link">
|
||||
<span class="fa fa-chevron-down" id="icnShowMenu"></span> <span class="sr-only">Show Top Navigation</span>
|
||||
</button>
|
||||
</div>
|
||||
<canvas class="webplayer" id="canvas" tabindex="1" oncontextmenu="event.preventDefault()" style="display: none"></canvas>
|
||||
<img class="webplayer-preview img-fluid" src="media/canvas.png" width="960" height="720" alt="RetroArch Logo">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--/.navbar content-->
|
||||
</div>
|
||||
</nav>
|
||||
<div class="bg-inverse webplayer-container">
|
||||
<div class="webplayer_border text-xs-center" id="canvas_div">
|
||||
<div class="showMenu">
|
||||
<button type="button" class="btn btn-link">
|
||||
<span class="fa fa-chevron-down" id="icnShowMenu"></span> <span class="sr-only">Show Top Navigation</span>
|
||||
</button>
|
||||
</div>
|
||||
<canvas class="webplayer" id="canvas" tabindex="1" oncontextmenu="event.preventDefault()" style="display: none"></canvas>
|
||||
<img class="webplayer-preview img-fluid" src="media/canvas.png" width="960px" height="720px" alt="RetroArch Logo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="//code.jquery.com/jquery-3.1.0.min.js"></script>
|
||||
<script src="//rawgit.com/jeresig/jquery.hotkeys/master/jquery.hotkeys.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/tether/1.3.4/js/tether.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.3/js/bootstrap.min.js"></script>
|
||||
<script src="analytics.js"></script>
|
||||
<!--script src="//wzrd.in/standalone/browserfs@0.6.1"></script-->
|
||||
<script src="browserfs.min.js"></script>
|
||||
<script src="libretro.js"></script>
|
||||
</body>
|
||||
<script src="//code.jquery.com/jquery-3.1.0.min.js"></script>
|
||||
<script src="//rawgit.com/jeresig/jquery.hotkeys/master/jquery.hotkeys.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/tether/1.3.4/js/tether.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.3/js/bootstrap.min.js"></script>
|
||||
<script src="analytics.js"></script>
|
||||
<!--script src="//wzrd.in/standalone/browserfs@0.6.1"></script-->
|
||||
<script src="browserfs.min.js"></script>
|
||||
<script src="libretro.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -81,12 +81,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the border around the player.
|
||||
*/
|
||||
canvas.webplayer {
|
||||
border: none;
|
||||
outline: none;
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hack to make emscripten stop messing with the canvas size while in fullscreen.
|
||||
* Foiled again!
|
||||
*/
|
||||
:fullscreen canvas.webplayer {
|
||||
min-width: 100vw;
|
||||
max-width: 100vw;
|
||||
min-height: 100vh;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
textarea {
|
||||
|
@ -6,24 +6,23 @@
|
||||
var BrowserFS = BrowserFS;
|
||||
var afs;
|
||||
var initializationCount = 0;
|
||||
var setImmediate;
|
||||
|
||||
var Module = {
|
||||
noInitialRun: true,
|
||||
arguments: ["-v", "--menu"],
|
||||
|
||||
encoder: new TextEncoder(),
|
||||
message_queue:[],
|
||||
message_out:[],
|
||||
message_accum:"",
|
||||
message_queue: [],
|
||||
message_out: [],
|
||||
message_accum: "",
|
||||
|
||||
retroArchSend: function(msg) {
|
||||
let bytes = this.encoder.encode(msg+"\n");
|
||||
this.message_queue.push([bytes,0]);
|
||||
let bytes = this.encoder.encode(msg + "\n");
|
||||
this.message_queue.push([bytes, 0]);
|
||||
},
|
||||
retroArchRecv: function() {
|
||||
let out = this.message_out.shift();
|
||||
if(out == null && this.message_accum != "") {
|
||||
if (out == null && this.message_accum != "") {
|
||||
out = this.message_accum;
|
||||
this.message_accum = "";
|
||||
}
|
||||
@ -33,35 +32,36 @@ var Module = {
|
||||
function(module) {
|
||||
function stdin() {
|
||||
// Return ASCII code of character, or null if no input
|
||||
while(module.message_queue.length > 0){
|
||||
while (module.message_queue.length > 0) {
|
||||
var msg = module.message_queue[0][0];
|
||||
var index = module.message_queue[0][1];
|
||||
if(index >= msg.length) {
|
||||
if (index >= msg.length) {
|
||||
module.message_queue.shift();
|
||||
} else {
|
||||
module.message_queue[0][1] = index+1;
|
||||
module.message_queue[0][1] = index + 1;
|
||||
// assumption: msg is a uint8array
|
||||
return msg[index];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stdout(c) {
|
||||
if(c == null) {
|
||||
if (c == null) {
|
||||
// flush
|
||||
if(module.message_accum != "") {
|
||||
if (module.message_accum != "") {
|
||||
module.message_out.push(module.message_accum);
|
||||
module.message_accum = "";
|
||||
}
|
||||
} else {
|
||||
let s = String.fromCharCode(c);
|
||||
if(s == "\n") {
|
||||
if(module.message_accum != "") {
|
||||
if (s == "\n") {
|
||||
if (module.message_accum != "") {
|
||||
module.message_out.push(module.message_accum);
|
||||
module.message_accum = "";
|
||||
}
|
||||
} else {
|
||||
module.message_accum = module.message_accum+s;
|
||||
module.message_accum = module.message_accum + s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,88 +69,71 @@ var Module = {
|
||||
}
|
||||
],
|
||||
postRun: [],
|
||||
onRuntimeInitialized: function()
|
||||
{
|
||||
appInitialized();
|
||||
},
|
||||
print: function(text)
|
||||
{
|
||||
console.log(text);
|
||||
},
|
||||
printErr: function(text)
|
||||
{
|
||||
console.error(text);
|
||||
},
|
||||
canvas: document.getElementById("canvas"),
|
||||
onRuntimeInitialized: function() {
|
||||
appInitialized();
|
||||
},
|
||||
print: function(text) {
|
||||
console.log("stdout:", text);
|
||||
},
|
||||
printErr: function(text) {
|
||||
console.log("stderr:", text);
|
||||
},
|
||||
canvas: document.getElementById("canvas"),
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies: function(left)
|
||||
{
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
}
|
||||
monitorRunDependencies: function(left) {
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function cleanupStorage()
|
||||
{
|
||||
function cleanupStorage() {
|
||||
localStorage.clear();
|
||||
if (BrowserFS.FileSystem.IndexedDB.isAvailable())
|
||||
{
|
||||
if (BrowserFS.FileSystem.IndexedDB.isAvailable()) {
|
||||
var req = indexedDB.deleteDatabase("RetroArch");
|
||||
req.onsuccess = function () {
|
||||
req.onsuccess = function() {
|
||||
console.log("Deleted database successfully");
|
||||
};
|
||||
req.onerror = function () {
|
||||
console.log("Couldn't delete database");
|
||||
req.onerror = function() {
|
||||
console.error("Couldn't delete database");
|
||||
};
|
||||
req.onblocked = function () {
|
||||
console.log("Couldn't delete database due to the operation being blocked");
|
||||
req.onblocked = function() {
|
||||
console.error("Couldn't delete database due to the operation being blocked");
|
||||
};
|
||||
}
|
||||
|
||||
document.getElementById("btnClean").disabled = true;
|
||||
}
|
||||
|
||||
function idbfsInit()
|
||||
{
|
||||
function idbfsInit() {
|
||||
$('#icnLocal').removeClass('fa-globe');
|
||||
$('#icnLocal').addClass('fa-spinner fa-spin');
|
||||
var imfs = new BrowserFS.FileSystem.InMemory();
|
||||
if (BrowserFS.FileSystem.IndexedDB.isAvailable())
|
||||
{
|
||||
if (BrowserFS.FileSystem.IndexedDB.isAvailable()) {
|
||||
afs = new BrowserFS.FileSystem.AsyncMirror(imfs,
|
||||
new BrowserFS.FileSystem.IndexedDB(function(e, fs)
|
||||
{
|
||||
if (e)
|
||||
{
|
||||
//fallback to imfs
|
||||
afs = new BrowserFS.FileSystem.InMemory();
|
||||
console.log("WEBPLAYER: error: " + e + " falling back to in-memory filesystem");
|
||||
appInitialized();
|
||||
}
|
||||
else
|
||||
{
|
||||
// initialize afs by copying files from async storage to sync storage.
|
||||
afs.initialize(function (e)
|
||||
{
|
||||
if (e)
|
||||
{
|
||||
new BrowserFS.FileSystem.IndexedDB(function(e, fs) {
|
||||
if (e) {
|
||||
// fallback to imfs
|
||||
afs = new BrowserFS.FileSystem.InMemory();
|
||||
console.log("WEBPLAYER: error: " + e + " falling back to in-memory filesystem");
|
||||
console.error("WEBPLAYER: error: " + e + " falling back to in-memory filesystem");
|
||||
appInitialized();
|
||||
} else {
|
||||
// initialize afs by copying files from async storage to sync storage.
|
||||
afs.initialize(function(e) {
|
||||
if (e) {
|
||||
afs = new BrowserFS.FileSystem.InMemory();
|
||||
console.error("WEBPLAYER: error: " + e + " falling back to in-memory filesystem");
|
||||
appInitialized();
|
||||
} else {
|
||||
idbfsSyncComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
idbfsSyncComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
"RetroArch"));
|
||||
},
|
||||
"RetroArch"));
|
||||
}
|
||||
}
|
||||
|
||||
function idbfsSyncComplete()
|
||||
{
|
||||
function idbfsSyncComplete() {
|
||||
$('#icnLocal').removeClass('fa-spinner').removeClass('fa-spin');
|
||||
$('#icnLocal').addClass('fa-check');
|
||||
console.log("WEBPLAYER: idbfs setup successful");
|
||||
@ -158,69 +141,66 @@ function idbfsSyncComplete()
|
||||
appInitialized();
|
||||
}
|
||||
|
||||
function appInitialized()
|
||||
{
|
||||
/* Need to wait for the file system, the wasm runtime, and the zip download
|
||||
to complete before enabling the Run button. */
|
||||
initializationCount++;
|
||||
if (initializationCount == 3)
|
||||
{
|
||||
setupFileSystem("browser");
|
||||
preLoadingComplete();
|
||||
}
|
||||
}
|
||||
function appInitialized() {
|
||||
/* Need to wait for the file system, the wasm runtime, and the zip download
|
||||
to complete before enabling the Run button. */
|
||||
initializationCount++;
|
||||
if (initializationCount == 3) {
|
||||
setupFileSystem("browser");
|
||||
preLoadingComplete();
|
||||
}
|
||||
}
|
||||
|
||||
function preLoadingComplete()
|
||||
{
|
||||
/* Make the Preview image clickable to start RetroArch. */
|
||||
$('.webplayer-preview').addClass('loaded').click(function () {
|
||||
function preLoadingComplete() {
|
||||
// Make the Preview image clickable to start RetroArch.
|
||||
$('.webplayer-preview').addClass('loaded').click(function() {
|
||||
startRetroArch();
|
||||
return false;
|
||||
});
|
||||
document.getElementById("btnRun").disabled = false;
|
||||
$('#btnRun').removeClass('disabled');
|
||||
});
|
||||
$('#btnRun').removeClass('disabled').removeAttr("disabled").click(function() {
|
||||
startRetroArch();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
var zipTOC;
|
||||
|
||||
function zipfsInit() {
|
||||
// 256 MB max bundle size
|
||||
let buffer = new ArrayBuffer(256*1024*1024);
|
||||
let bufferView = new Uint8Array(buffer);
|
||||
let idx = 0;
|
||||
// bundle should be in five parts (this can be changed later)
|
||||
Promise.all([fetch("assets/frontend/bundle.zip.aa"),
|
||||
fetch("assets/frontend/bundle.zip.ab"),
|
||||
fetch("assets/frontend/bundle.zip.ac"),
|
||||
fetch("assets/frontend/bundle.zip.ad"),
|
||||
fetch("assets/frontend/bundle.zip.ae")
|
||||
]).then(function(resps) {
|
||||
Promise.all(resps.map((r) => r.arrayBuffer())).then(function(buffers) {
|
||||
for (let buf of buffers) {
|
||||
if (idx+buf.byteLength > buffer.maxByteLength) {
|
||||
console.log("WEBPLAYER: error: bundle.zip is too large");
|
||||
}
|
||||
bufferView.set(new Uint8Array(buf), idx, buf.byteLength);
|
||||
idx += buf.byteLength;
|
||||
}
|
||||
BrowserFS.FileSystem.ZipFS.computeIndex(BrowserFS.BFSRequire('buffer').Buffer(new Uint8Array(buffer, 0, idx)), function(toc) {
|
||||
zipTOC = toc;
|
||||
appInitialized();
|
||||
});
|
||||
})
|
||||
});
|
||||
// 256 MB max bundle size
|
||||
let buffer = new ArrayBuffer(256 * 1024 * 1024);
|
||||
let bufferView = new Uint8Array(buffer);
|
||||
let idx = 0;
|
||||
// bundle should be in five parts (this can be changed later)
|
||||
Promise.all([fetch("assets/frontend/bundle.zip.aa"),
|
||||
fetch("assets/frontend/bundle.zip.ab"),
|
||||
fetch("assets/frontend/bundle.zip.ac"),
|
||||
fetch("assets/frontend/bundle.zip.ad"),
|
||||
fetch("assets/frontend/bundle.zip.ae")
|
||||
]).then(function(resps) {
|
||||
Promise.all(resps.map((r) => r.arrayBuffer())).then(function(buffers) {
|
||||
for (let buf of buffers) {
|
||||
if (idx + buf.byteLength > buffer.maxByteLength) {
|
||||
console.error("WEBPLAYER: error: bundle.zip is too large");
|
||||
}
|
||||
bufferView.set(new Uint8Array(buf), idx, buf.byteLength);
|
||||
idx += buf.byteLength;
|
||||
}
|
||||
BrowserFS.FileSystem.ZipFS.computeIndex(BrowserFS.BFSRequire('buffer').Buffer(new Uint8Array(buffer, 0, idx)), function(toc) {
|
||||
zipTOC = toc;
|
||||
appInitialized();
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
function setupFileSystem(backend)
|
||||
{
|
||||
/* create a mountable filesystem that will server as a root
|
||||
mountpoint for browserfs */
|
||||
var mfs = new BrowserFS.FileSystem.MountableFileSystem();
|
||||
|
||||
/* create an XmlHttpRequest filesystem for the bundled data */
|
||||
function setupFileSystem(backend) {
|
||||
// create a mountable filesystem that will server as a root mountpoint for browserfs
|
||||
var mfs = new BrowserFS.FileSystem.MountableFileSystem();
|
||||
|
||||
// create an XmlHttpRequest filesystem for the bundled data
|
||||
var xfs1 = new BrowserFS.FileSystem.ZipFS(zipTOC);
|
||||
/* create an XmlHttpRequest filesystem for core assets */
|
||||
var xfs2 = new BrowserFS.FileSystem.XmlHttpRequest
|
||||
(".index-xhr", "assets/cores/");
|
||||
// create an XmlHttpRequest filesystem for core assets
|
||||
var xfs2 = new BrowserFS.FileSystem.XmlHttpRequest(".index-xhr", "assets/cores/");
|
||||
|
||||
console.log("WEBPLAYER: initializing filesystem: " + backend);
|
||||
mfs.mount('/home/web_user/retroarch/userdata', afs);
|
||||
@ -229,75 +209,81 @@ function setupFileSystem(backend)
|
||||
mfs.mount('/home/web_user/retroarch/userdata/content/downloads', xfs2);
|
||||
BrowserFS.initialize(mfs);
|
||||
var BFS = new BrowserFS.EmscriptenFS(Module.FS, Module.PATH, Module.ERRNO_CODES);
|
||||
Module.FS.mount(BFS, {root: '/home'}, '/home');
|
||||
Module.FS.mount(BFS, {
|
||||
root: '/home'
|
||||
}, '/home');
|
||||
console.log("WEBPLAYER: " + backend + " filesystem initialization successful");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the value of the given GET parameter.
|
||||
*/
|
||||
// Retrieve the value of the given GET parameter.
|
||||
function getParam(name) {
|
||||
var results = new RegExp('[?&]' + name + '=([^&#]*)').exec(window.location.href);
|
||||
if (results) {
|
||||
return results[1] || null;
|
||||
}
|
||||
var results = new RegExp('[?&]' + name + '=([^&#]*)').exec(window.location.href);
|
||||
if (results) {
|
||||
return results[1] || null;
|
||||
}
|
||||
}
|
||||
|
||||
function startRetroArch()
|
||||
{
|
||||
function startRetroArch() {
|
||||
$('.webplayer').show();
|
||||
$('.webplayer-preview').hide();
|
||||
document.getElementById("btnRun").disabled = true;
|
||||
|
||||
$('#btnFullscreen').removeClass('disabled');
|
||||
$('#btnMenu').removeClass('disabled');
|
||||
$('#btnAdd').removeClass('disabled');
|
||||
$('#btnRom').removeClass('disabled');
|
||||
$('#btnAdd').removeClass("disabled").removeAttr("disabled").click(function() {
|
||||
$('#btnRom').click();
|
||||
});
|
||||
$('#btnRom').removeAttr("disabled").change(function(e) {
|
||||
selectFiles(e.target.files);
|
||||
});
|
||||
$('#btnMenu').removeClass("disabled").removeAttr("disabled").click(function() {
|
||||
Module._cmd_toggle_menu();
|
||||
Module.canvas.focus();
|
||||
});
|
||||
$('#btnFullscreen').removeClass("disabled").removeAttr("disabled").click(function() {
|
||||
Module.requestFullscreen(false);
|
||||
Module.canvas.focus();
|
||||
});
|
||||
|
||||
document.getElementById("btnAdd").disabled = false;
|
||||
document.getElementById("btnRom").disabled = false;
|
||||
document.getElementById("btnMenu").disabled = false;
|
||||
document.getElementById("btnFullscreen").disabled = false;
|
||||
|
||||
Module["canvas"] = document.getElementById("canvas");
|
||||
Module["canvas"].addEventListener("click", () => Module["canvas"].focus());
|
||||
Module['callMain'](Module['arguments']);
|
||||
Module['resumeMainLoop']();
|
||||
Module['canvas'].focus();
|
||||
Module.canvas.focus();
|
||||
Module.canvas.addEventListener("pointerdown", function() {
|
||||
Module.canvas.focus();
|
||||
}, false);
|
||||
Module.callMain(Module.arguments);
|
||||
}
|
||||
function selectFiles(files)
|
||||
{
|
||||
|
||||
function selectFiles(files) {
|
||||
$('#btnAdd').addClass('disabled');
|
||||
$('#icnAdd').removeClass('fa-plus');
|
||||
$('#icnAdd').addClass('fa-spinner spinning');
|
||||
var count = files.length;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
for (var i = 0; i < count; i++) {
|
||||
filereader = new FileReader();
|
||||
filereader.file_name = files[i].name;
|
||||
filereader.readAsArrayBuffer(files[i]);
|
||||
filereader.onload = function(){uploadData(this.result, this.file_name)};
|
||||
filereader.onloadend = function(evt)
|
||||
{
|
||||
filereader.onload = function() {
|
||||
uploadData(this.result, this.file_name)
|
||||
};
|
||||
filereader.onloadend = function(evt) {
|
||||
console.log("WEBPLAYER: file: " + this.file_name + " upload complete");
|
||||
if (evt.target.readyState == FileReader.DONE)
|
||||
{
|
||||
if (evt.target.readyState == FileReader.DONE) {
|
||||
$('#btnAdd').removeClass('disabled');
|
||||
$('#icnAdd').removeClass('fa-spinner spinning');
|
||||
$('#icnAdd').addClass('fa-plus');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function uploadData(data,name)
|
||||
{
|
||||
function uploadData(data, name) {
|
||||
var dataView = new Uint8Array(data);
|
||||
Module.FS.createDataFile('/', name, dataView, true, false);
|
||||
|
||||
var data = Module.FS.readFile(name,{ encoding: 'binary' });
|
||||
Module.FS.writeFile('/home/web_user/retroarch/userdata/content/' + name, data ,{ encoding: 'binary' });
|
||||
var data = Module.FS.readFile(name, {
|
||||
encoding: 'binary'
|
||||
});
|
||||
Module.FS.writeFile('/home/web_user/retroarch/userdata/content/' + name, data, {
|
||||
encoding: 'binary'
|
||||
});
|
||||
Module.FS.unlink(name);
|
||||
}
|
||||
|
||||
@ -306,8 +292,7 @@ function switchCore(corename) {
|
||||
}
|
||||
|
||||
function switchStorage(backend) {
|
||||
if (backend != localStorage.getItem("backend"))
|
||||
{
|
||||
if (backend != localStorage.getItem("backend")) {
|
||||
localStorage.setItem("backend", backend);
|
||||
location.reload();
|
||||
}
|
||||
@ -315,21 +300,24 @@ function switchStorage(backend) {
|
||||
|
||||
// When the browser has loaded everything.
|
||||
$(function() {
|
||||
// Enable all available ToolTips.
|
||||
// Enable data clear
|
||||
$('#btnClean').click(function() {
|
||||
cleanupStorage();
|
||||
});
|
||||
|
||||
// Enable all available ToolTips.
|
||||
$('.tooltip-enable').tooltip({
|
||||
placement: 'right'
|
||||
});
|
||||
|
||||
// Allow hiding the top menu.
|
||||
$('.showMenu').hide();
|
||||
$('#btnHideMenu, .showMenu').click(function () {
|
||||
$('#btnHideMenu, .showMenu').click(function() {
|
||||
$('nav').slideToggle('slow');
|
||||
$('.showMenu').toggle('slow');
|
||||
});
|
||||
|
||||
/**
|
||||
* Attempt to disable some default browser keys.
|
||||
*/
|
||||
// Attempt to disable some default browser keys.
|
||||
var keys = {
|
||||
9: "tab",
|
||||
13: "enter",
|
||||
@ -351,20 +339,20 @@ $(function() {
|
||||
116: "F5",
|
||||
117: "F6",
|
||||
118: "F7",
|
||||
119: "F8",
|
||||
120: "F9",
|
||||
121: "F10",
|
||||
122: "F11",
|
||||
123: "F12"
|
||||
};
|
||||
window.addEventListener('keydown', function (e) {
|
||||
119: "F8",
|
||||
120: "F9",
|
||||
121: "F10",
|
||||
122: "F11",
|
||||
123: "F12"
|
||||
};
|
||||
window.addEventListener('keydown', function(e) {
|
||||
if (keys[e.which]) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// Switch the core when selecting one.
|
||||
$('#core-selector a').click(function () {
|
||||
$('#core-selector a').click(function() {
|
||||
var coreChoice = $(this).data('core');
|
||||
switchCore(coreChoice);
|
||||
});
|
||||
@ -381,8 +369,8 @@ function loadCore(core) {
|
||||
var coreTitle = $('#core-selector a[data-core="' + core + '"]').addClass('active').text();
|
||||
$('#dropdownMenu1').text(coreTitle);
|
||||
// Load the Core's related JavaScript.
|
||||
import("./"+core+"_libretro.js").then(script => {
|
||||
script.default(Module).then(mod => {
|
||||
import("./" + core + "_libretro.js").then(script => {
|
||||
script.default(Module).then(mod => {
|
||||
Module = mod;
|
||||
$('#icnRun').removeClass('fa-spinner').removeClass('fa-spin');
|
||||
$('#icnRun').addClass('fa-play');
|
||||
@ -390,18 +378,12 @@ function loadCore(core) {
|
||||
$('#lblLocal').addClass('active');
|
||||
idbfsInit();
|
||||
zipfsInit();
|
||||
}).catch(err => { console.error("Couldn't instantiate module",err); throw err; });
|
||||
}).catch(err => { console.error("Couldn't load script",err); throw err; });
|
||||
}
|
||||
|
||||
function keyPress(k)
|
||||
{
|
||||
function kp(k, event) {
|
||||
var oEvent = new KeyboardEvent(event, { code: k });
|
||||
|
||||
document.dispatchEvent(oEvent);
|
||||
document.getElementById('canvas').focus();
|
||||
}
|
||||
kp(k, "keydown");
|
||||
setTimeout(function(){kp(k, "keyup")}, 50);
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error("Couldn't instantiate module", err);
|
||||
throw err;
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error("Couldn't load script", err);
|
||||
throw err;
|
||||
});
|
||||
}
|
@ -3945,6 +3945,9 @@ bool command_event(enum event_command cmd, void *data)
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case CMD_EVENT_RELOAD_CONFIG:
|
||||
config_load(global_get_ptr());
|
||||
break;
|
||||
case CMD_EVENT_DSP_FILTER_INIT:
|
||||
#ifdef HAVE_DSP_FILTER
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user