Merge remote-tracking branch 'libretro/master' into warmenhoven/dev/MicCoreAudio

This commit is contained in:
Eric Warmenhoven 2025-06-06 14:09:19 -04:00
commit 720f5bca59
240 changed files with 12733 additions and 10188 deletions

View File

@ -30,7 +30,7 @@ jobs:
- name: Compile RA
run: |
make -f Makefile.ctr -j$(getconf _NPROCESSORS_ONLN) USE_CTRULIB_2=1 clean
make -f Makefile.ctr -j$(getconf _NPROCESSORS_ONLN) USE_CTRULIB_2=1 HAVE_STATIC_DUMMY=1
make -f Makefile.ctr -j$(getconf _NPROCESSORS_ONLN) USE_CTRULIB_2=1 HAVE_STATIC_DUMMY=1 info all
- name: Get short SHA
id: slug
run: echo "::set-output name=sha8::$(echo ${GITHUB_SHA} | cut -c1-8)"

View File

@ -25,7 +25,7 @@ jobs:
- name: Compile RA
run: |
make -f Makefile.dos -j$(getconf _NPROCESSORS_ONLN) clean
make -f Makefile.dos -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1
make -f Makefile.dos -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1 info all
- name: Get short SHA
id: slug

View File

@ -25,7 +25,7 @@ jobs:
- name: Compile RA
run: |
make -f Makefile.ngc -j$(getconf _NPROCESSORS_ONLN) clean
make -f Makefile.ngc -j$(getconf _NPROCESSORS_ONLN) EXTERNAL_LIBOGC=1 GX_PTHREAD_LEGACY=0 HAVE_STATIC_DUMMY=1
make -f Makefile.ngc -j$(getconf _NPROCESSORS_ONLN) EXTERNAL_LIBOGC=1 GX_PTHREAD_LEGACY=0 HAVE_STATIC_DUMMY=1 info all
- name: Get short SHA
id: slug
run: echo "::set-output name=sha8::$(echo ${GITHUB_SHA} | cut -c1-8)"

View File

@ -30,7 +30,7 @@ jobs:
- name: Compile RA
run: |
make -j$(getconf _NPROCESSORS_ONLN) clean
make -j$(getconf _NPROCESSORS_ONLN)
make -j$(getconf _NPROCESSORS_ONLN) info all
- name: Get short SHA
id: slug

View File

@ -59,7 +59,7 @@ jobs:
run: |
echo "Building RetroArch in ${{ matrix.sys }} environment"
./configure
make -j$(nproc)
make -j$(nproc) info all
- name: Collect DLLs and binaries
shell: msys2 {0}

View File

@ -25,7 +25,7 @@ jobs:
- name: Compile RA
run: |
make -j$(getconf _NPROCESSORS_ONLN) -f Makefile.miyoo clean
make -j$(getconf _NPROCESSORS_ONLN) -f Makefile.miyoo
make -j$(getconf _NPROCESSORS_ONLN) -f Makefile.miyoo info all
- name: Get short SHA
id: slug

View File

@ -30,7 +30,7 @@ jobs:
- name: Compile RA
run: |
make -f Makefile.ps2 -j$(getconf _NPROCESSORS_ONLN) clean
make -f Makefile.ps2 -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1 release
make -f Makefile.ps2 -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1 info release
- name: Get short SHA
id: slug

View File

@ -30,7 +30,7 @@ jobs:
- name: Compile RA
run: |
make -f Makefile.psl1ght -j$(getconf _NPROCESSORS_ONLN) clean
make -f Makefile.psl1ght -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1
make -f Makefile.psl1ght -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1 info all
- name: Get short SHA
id: slug

View File

@ -31,7 +31,7 @@ jobs:
- name: Compile RA
run: |
export PATH=~/cli:$PATH # .net cli
make -f Makefile.orbis -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1
make -f Makefile.orbis -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1 info all
- name: Get short SHA
id: slug

View File

@ -30,7 +30,7 @@ jobs:
- name: Compile RA
run: |
make -f Makefile.vita -j$(getconf _NPROCESSORS_ONLN) clean
make -f Makefile.vita -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1 HAVE_VITAGLES=1
make -f Makefile.vita -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1 HAVE_VITAGLES=1 info all
- name: Get short SHA
id: slug
run: echo "::set-output name=sha8::$(echo ${GITHUB_SHA} | cut -c1-8)"

View File

@ -25,7 +25,7 @@ jobs:
- name: Compile RA
run: |
make -j$(getconf _NPROCESSORS_ONLN) -f Makefile.rs90 clean
make -j$(getconf _NPROCESSORS_ONLN) -f Makefile.rs90
make -j$(getconf _NPROCESSORS_ONLN) -f Makefile.rs90 info all
- name: Get short SHA
id: slug

View File

@ -25,7 +25,7 @@ jobs:
- name: Compile RA
run: |
make -j$(getconf _NPROCESSORS_ONLN) -f Makefile.retrofw clean
make -j$(getconf _NPROCESSORS_ONLN) -f Makefile.retrofw
make -j$(getconf _NPROCESSORS_ONLN) -f Makefile.retrofw info all
- name: Get short SHA
id: slug

View File

@ -30,7 +30,7 @@ jobs:
- name: Compile RA
run: |
make -f Makefile.wii -j$(getconf _NPROCESSORS_ONLN) clean
make -f Makefile.wii -j$(getconf _NPROCESSORS_ONLN) EXTERNAL_LIBOGC=1 GX_PTHREAD_LEGACY=0 HAVE_STATIC_DUMMY=1
make -f Makefile.wii -j$(getconf _NPROCESSORS_ONLN) EXTERNAL_LIBOGC=1 GX_PTHREAD_LEGACY=0 HAVE_STATIC_DUMMY=1 info all
- name: Get short SHA
id: slug
run: echo "::set-output name=sha8::$(echo ${GITHUB_SHA} | cut -c1-8)"

View File

@ -30,7 +30,7 @@ jobs:
- name: Compile RA
run: |
make -f Makefile.wiiu -j$(getconf _NPROCESSORS_ONLN) clean
make -f Makefile.wiiu -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1
make -f Makefile.wiiu -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1 info all
- name: Get short SHA
id: slug
run: echo "::set-output name=sha8::$(echo ${GITHUB_SHA} | cut -c1-8)"

View File

@ -33,4 +33,4 @@ jobs:
- name: Build Debug
run: |
make clean # making sure we don't have leftovers from previous build
make DEBUG=1 GL_DEBUG=1 C89_BUILD=1
make DEBUG=1 GL_DEBUG=1 C89_BUILD=1 info all

View File

@ -2,14 +2,18 @@ name: CI webOS
on:
push:
tags-ignore:
- '*'
branches:
- '*'
pull_request:
release:
types: [ published ]
repository_dispatch:
types: [run_build]
permissions:
contents: read
types: [ run_build ]
env:
PACKAGE_NAME: com.retroarch.webos
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
jobs:
@ -28,6 +32,14 @@ jobs:
fileName: "ares-package_*.deb"
out-file-path: "temp"
- name: Download Manifest Generator
uses: robinraju/release-downloader@v1.9
with:
repository: "webosbrew/dev-toolbox-cli"
latest: true
fileName: "webosbrew-toolbox-gen-manifest_*.deb"
out-file-path: "temp"
- name: Update packages
run: sudo apt-get -yq update
@ -50,6 +62,38 @@ jobs:
./arm-webos-linux-gnueabi_sdk-buildroot/relocate-sdk.sh
- name: Compile RA
shell: bash
run: |
. /tmp/arm-webos-linux-gnueabi_sdk-buildroot/environment-setup
make -f Makefile.webos ADD_SDL2_LIB=1 -j$(getconf _NPROCESSORS_ONLN)
make -f Makefile.webos ipk PACKAGE_NAME=${PACKAGE_NAME} ADD_SDL2_LIB=1 -j$(getconf _NPROCESSORS_ONLN)
- name: Get short SHA
id: slug
run: echo "sha8=$(echo ${GITHUB_SHA} | cut -c1-8)" >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@v4
with:
name: com.retroarch.webos_${{ steps.slug.outputs.sha8 }}_arm.ipk
path: |
webos/*.ipk
- name: Generate Manifest
shell: bash
run: |
. version.all
webosbrew-gen-manifest -o webos/${PACKAGE_NAME}.manifest.json \
-p webos/${PACKAGE_NAME}_${RARCH_VERSION}_arm.ipk \
-i https://github.com/webosbrew/RetroArch/raw/webos/webos/icon160.png \
-l https://github.com/webosbrew/RetroArch
- name: Release
if: github.event_name == 'release'
uses: ncipollo/release-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.event.release.tag_name }}
allowUpdates: true
omitNameDuringUpdate: true
omitBody: true
omitPrereleaseDuringUpdate: true
artifacts: webos/*.ipk,webos/*.manifest.json

2
.gitignore vendored
View File

@ -241,6 +241,8 @@ gfx/common/wayland/content-type-v1.h
gfx/common/wayland/content-type-v1.c
gfx/common/wayland/single-pixel-buffer-v1.h
gfx/common/wayland/single-pixel-buffer-v1.c
gfx/common/wayland/xdg-toplevel-icon-v1.h
gfx/common/wayland/xdg-toplevel-icon-v1.c
# libretro-common samples
libretro-common/samples/streams/rzip/rzip

View File

@ -1,8 +1,16 @@
# Future
- EMSCRIPTEN: Support suspend screensaver
- EMSCRIPTEN/RWEBCAM: Fix camera driver
- EMSCRIPTEN/RWEBINPUT: Add accelerometer/gyroscope support
- EMSCRIPTEN/RWEBPAD: Add rumble support
- EMSCRIPTEN/RWEBAUDIO: Rewrite driver, set as default audio driver
# 1.21.0
- 3DS: Fix unique IDs for newer cores
- 3DS: Fixes
- 3DS: Enable TLS (SSL)
- 3DS: Fix UI freeze when threaded rendering is enabled
- 3DS: Fix crash on load content
- 3DS: Other minor fixes
- APPLE: Enable Vulkan emulated mailbox
- APPLE: Include b2 core in App Store builds
- APPLE: CoreMIDI driver for IOS/MacOS

View File

@ -45,7 +45,6 @@ else
OBJDIR := $(OBJDIR_BASE)/release
CFLAGS ?= -O3
CXXFLAGS ?= -O3
DEF_FLAGS += -ffast-math
endif
DEF_FLAGS += -Wall -Wsign-compare
@ -171,7 +170,7 @@ ifneq ($(MOC_HEADERS),)
RARCH_OBJ += $(MOC_OBJ)
endif
all: info $(TARGET) config.mk
all: $(TARGET) config.mk
define INFO
ASFLAGS: $(ASFLAGS)

View File

@ -351,7 +351,6 @@ endif
ifeq ($(HAVE_MICROPHONE), 1)
DEFINES += -DHAVE_MICROPHONE
OBJ += audio/microphone_driver.o
endif
ifeq ($(HAVE_REWIND), 1)
@ -1284,6 +1283,7 @@ ifeq ($(HAVE_WAYLAND), 1)
gfx/common/wayland_common.o \
gfx/common/wayland/fractional-scale-v1.o \
gfx/common/wayland/viewporter.o \
gfx/common/wayland/xdg-toplevel-icon-v1.o \
gfx/common/wayland/xdg-shell.o \
gfx/common/wayland/idle-inhibit-unstable-v1.o \
gfx/common/wayland/xdg-decoration-unstable-v1.o \

View File

@ -98,11 +98,10 @@ else
HAVE_NETWORKING = 1
HAVE_IFINFO = 1
HAVE_CHEEVOS = 1
#HAVE_SOCKET_LEGACY = 1
HAVE_THREADS = 1
#HAVE_SSL = 1
HAVE_BUILTINMBEDTLS = 1
HAVE_CORE_INFO_CACHE = 1
HAVE_CLOUDSYNC = 1
include Makefile.common
CFLAGS += $(DEF_FLAGS)
@ -155,11 +154,7 @@ LIBDIRS := -L. -L$(CTRULIB)/lib
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -marm -mfpu=vfp -mtp=soft
CFLAGS += -mword-relocations \
-ffast-math \
$(ARCH)
#CFLAGS += -Wall
CFLAGS += -mword-relocations $(ARCH)
CFLAGS += -DARM11 -D_3DS
ifeq ($(strip $(USE_CTRULIB_2)),1)
@ -203,7 +198,7 @@ CFLAGS += -Werror=implicit-function-declaration
ASFLAGS := -g $(ARCH) -O3
LDFLAGS += -specs=ctr/3dsx_custom.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
CFLAGS += -std=gnu99 -ffast-math
CFLAGS += -std=gnu99
LIB_CORE :=
LIB_CORE_FULL :=
@ -275,7 +270,7 @@ endif
.PHONY: $(BUILD) clean all
all: info $(TARGET)
all: $(TARGET)
define INFO
AR: $(AR)

View File

@ -83,11 +83,7 @@ LIBDIRS := -L. -L$(CTRULIB)/lib
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -marm -mfpu=vfp -mtp=soft
CFLAGS += -mword-relocations \
-fomit-frame-pointer -ffast-math \
$(ARCH)
#CFLAGS += -Wall
CFLAGS += -mword-relocations -fomit-frame-pointer $(ARCH)
CFLAGS += -DARM11 -D_3DS
ifeq ($(strip $(USE_CTRULIB_2)),1)
@ -115,7 +111,7 @@ CFLAGS += -Werror=implicit-function-declaration
ASFLAGS := -g $(ARCH) -O3
LDFLAGS += -specs=ctr/3dsx_custom.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
CFLAGS += -std=gnu99 -ffast-math
CFLAGS += -std=gnu99
LIBS := -lctru -lm

View File

@ -107,7 +107,7 @@ OPK_NAME = retroarch.opk
OBJ :=
LINK := $(CXX)
DEF_FLAGS := -march=mips32 -mtune=mips32r2 -mhard-float -ffast-math -fomit-frame-pointer
DEF_FLAGS := -march=mips32 -mtune=mips32r2 -mhard-float -fomit-frame-pointer
DEF_FLAGS += -mplt -mno-shared
DEF_FLAGS += -ffunction-sections -fdata-sections
DEF_FLAGS += -I. -Ideps -Ideps/stb -DDINGUX=1 -MMD

View File

@ -120,7 +120,7 @@ APP_ICON := pkg/libnx/retroarch.jpg
#---------------------------------------------------------------------------------
ARCH :=
CFLAGS := -g -Wall -O3 -fcommon -ffast-math -ffunction-sections \
CFLAGS := -g -Wall -O3 -fcommon -ffunction-sections \
$(ARCH) $(DEFINES) $(INCLUDE_DIRS)
CFLAGS += $(INCLUDE)
@ -207,7 +207,7 @@ DEPENDS := $(filter-out libretro_libnx.a,$(DEPENDS_TMP))
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all: info $(OUTPUT)
all: $(OUTPUT)
define INFO
CC: $(CC)

View File

@ -11,7 +11,7 @@ TARGET_BASE := $(subst .js,,$(TARGET))
OS = Emscripten
OBJ :=
DEFINES := -DRARCH_INTERNAL -DHAVE_MAIN -DEMSCRIPTEN -DNO_CANVAS_RESIZE
DEFINES := -DRARCH_INTERNAL -DHAVE_MAIN -DEMSCRIPTEN
DEFINES += -DHAVE_FILTERS_BUILTIN -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_UPDATE_CORE_INFO
HAVE_PATCH = 1
@ -27,7 +27,7 @@ HAVE_GLSL = 1
HAVE_SCREENSHOTS = 1
HAVE_REWIND = 1
HAVE_AUDIOMIXER = 1
HAVE_CC_RESAMPLER = 1
HAVE_CC_RESAMPLER ?= 1
HAVE_EGL ?= 0
HAVE_OPENGLES = 1
HAVE_RJPEG = 0
@ -54,8 +54,6 @@ HAVE_BSV_MOVIE = 1
HAVE_CHD ?= 0
HAVE_NETPLAYDISCOVERY ?= 0
HAVE_AL ?= 1
# enables pthreads, requires special headers on the web server:
# see https://web.dev/articles/coop-coep
HAVE_THREADS ?= 0
@ -63,18 +61,14 @@ HAVE_THREADS ?= 0
# requires HAVE_THREADS
HAVE_AUDIOWORKLET ?= 0
# WARNING -- READ BEFORE ENABLING
# The rwebaudio driver is known to have several audio bugs, such as
# minor crackling, or the entire page freezing/crashing.
# It works perfectly on chrome, but even firefox has really bad audio quality.
# I should also note, the driver on iOS is completely broken (crashes the page).
# You have been warned.
HAVE_RWEBAUDIO ?= 0
# doesn't work on PROXY_TO_PTHREAD
HAVE_RWEBAUDIO ?= 1
# whether the browser thread is allowed to block to wait for audio to play,
# may lead to the issues mentioned above.
# currently this variable is only used by audioworklet;
# rwebaudio will always busywait and openal will never busywait.
# requires ASYNC or PROXY_TO_PTHREAD
HAVE_AL ?= 0
# whether the browser thread is allowed to block to wait for audio to play, not CPU usage-friendly!
# currently this variable is only used by rwebaudio and audioworklet; openal will never busywait.
ALLOW_AUDIO_BUSYWAIT ?= 0
# minimal asyncify; better performance than full asyncify,
@ -106,6 +100,9 @@ ASYNC ?= 0
LTO ?= 0
PTHREAD_POOL_SIZE ?= 4
ASYNCIFY_ADD ?= dynCall_*,emscripten_mainloop
ASYNCIFY_REMOVE ?= threaded_worker
STACK_SIZE ?= 4194304
INITIAL_HEAP ?= 134217728
@ -123,7 +120,7 @@ INITIAL_HEAP ?= 134217728
OBJDIR := obj-emscripten
EXPORTED_FUNCTIONS = _main,_malloc,_free,_cmd_savefiles,_cmd_save_state,_cmd_load_state,_cmd_undo_save_state,_cmd_undo_load_state,_cmd_take_screenshot,\
EXPORTED_FUNCTIONS = _main,_malloc,_free,_cmd_savefiles,_cmd_save_state,_cmd_load_state,_cmd_undo_save_state,_cmd_undo_load_state,_cmd_toggle_fullscreen,_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,EmscriptenSendCommand,EmscriptenReceiveCommandReply
@ -168,7 +165,7 @@ LDFLAGS := -L. --no-heap-copy -s STACK_SIZE=$(STACK_SIZE) -s INITIAL_MEMORY=$(IN
-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=0 \
-s ENVIRONMENT=web,worker \
-s ENVIRONMENT=web,worker -s WASM_BIGINT=1 \
--extern-pre-js emscripten/pre.js \
--js-library emscripten/library_rwebcam.js \
--js-library emscripten/library_platform_emscripten.js
@ -193,6 +190,9 @@ endif
ifeq ($(HAVE_RWEBAUDIO), 1)
LDFLAGS += --js-library emscripten/library_rwebaudio.js
DEFINES += -DHAVE_RWEBAUDIO
ifeq ($(PROXY_TO_PTHREAD), 1)
$(error ERROR: RWEBAUDIO is incompatible with PROXY_TO_PTHREAD)
endif
endif
ifeq ($(HAVE_AUDIOWORKLET), 1)
@ -202,18 +202,24 @@ ifeq ($(HAVE_AUDIOWORKLET), 1)
ifeq ($(HAVE_THREADS), 0)
$(error ERROR: AUDIOWORKLET requires HAVE_THREADS)
endif
ifeq ($(PROXY_TO_PTHREAD), 1)
else ifeq ($(ASYNC), 1)
endif
ifeq ($(HAVE_AL), 1)
LDFLAGS += -lopenal
DEFINES += -DHAVE_AL
endif
ifeq ($(PROXY_TO_PTHREAD), 1)
else ifeq ($(ASYNC), 1)
else
DEFINES += -DEMSCRIPTEN_AUDIO_EXTERNAL_BLOCK
ifeq ($(MIN_ASYNC), 1)
DEFINES += -DEMSCRIPTEN_AUDIO_ASYNC_BLOCK
else
DEFINES += -DEMSCRIPTEN_AUDIO_EXTERNAL_BLOCK
ifeq ($(MIN_ASYNC), 1)
DEFINES += -DEMSCRIPTEN_AUDIO_ASYNC_BLOCK
else
DEFINES += -DEMSCRIPTEN_AUDIO_FAKE_BLOCK
endif
ifneq ($(ALLOW_AUDIO_BUSYWAIT), 1)
DEFINES += -DEMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK
endif
DEFINES += -DEMSCRIPTEN_AUDIO_FAKE_BLOCK
endif
ifneq ($(ALLOW_AUDIO_BUSYWAIT), 1)
DEFINES += -DEMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK
endif
endif
@ -224,23 +230,18 @@ endif
# explanation of some of these defines:
# EMSCRIPTEN_AUDIO_EXTERNAL_BLOCK: audio blocking occurs in the main loop instead of in the audio driver functions.
# EMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK: along with above, enables external blocking in the write function.
# ALLOW_AUDIO_BUSYWAIT: write function will busywait. init function may still use an external block.
# EMSCRIPTEN_AUDIO_BUSYWAIT: write function will busywait. init function may still use an external block.
# EMSCRIPTEN_AUDIO_ASYNC_BLOCK: external block uses emscripten_sleep (requires MIN_ASYNC).
# EMSCRIPTEN_AUDIO_FAKE_BLOCK: external block uses main loop timing (doesn't require asyncify).
# when building with either PROXY_TO_PTHREAD or ASYNC (full asyncify), none of the above are required.
ifeq ($(HAVE_AL), 1)
LDFLAGS += -lopenal
DEFINES += -DHAVE_AL
endif
ifeq ($(HAVE_THREADS), 1)
LDFLAGS += -pthread -s PTHREAD_POOL_SIZE=$(PTHREAD_POOL_SIZE)
CFLAGS += -pthread -s SHARED_MEMORY
endif
ifeq ($(WASM_WORKERS), 1)
LDFLAGS += -s WASM_WORKERS=2
LDFLAGS += -s WASM_WORKERS=1
endif
ifeq ($(ASYNC), 1)
@ -251,7 +252,7 @@ ifeq ($(ASYNC), 1)
endif
else ifeq ($(MIN_ASYNC), 1)
DEFINES += -DEMSCRIPTEN_ASYNCIFY -DEMSCRIPTEN_MIN_ASYNCIFY
LDFLAGS += -s ASYNCIFY=1 -s ASYNCIFY_STACK_SIZE=8192 -s ASYNCIFY_IGNORE_INDIRECT=1 -s ASYNCIFY_ADD='dynCall_*,emscripten_mainloop' -s ASYNCIFY_REMOVE='threaded_worker'
LDFLAGS += -s ASYNCIFY=1 -s ASYNCIFY_STACK_SIZE=8192 -s ASYNCIFY_IGNORE_INDIRECT=1 -s ASYNCIFY_ADD='$(ASYNCIFY_ADD)' -s ASYNCIFY_REMOVE='$(ASYNCIFY_REMOVE)'
ifeq ($(DEBUG), 1)
LDFLAGS += -s ASYNCIFY_ADVISE #-s ASYNCIFY_DEBUG=1
endif
@ -297,21 +298,11 @@ all: $(TARGET)
$(libretro_new): ;
mv_libretro:
mv -f $(libretro) $(libretro_new) || true
$(Q)mv -f $(libretro) $(libretro_new) || true
# until emscripten adds something like WASM_WORKERS=2 but for audio worklets, DIY.
ifeq ($(HAVE_AUDIOWORKLET), 1)
$(TARGET): $(RARCH_OBJ) $(libretro_new) mv_libretro
@$(if $(Q), $(shell echo echo "LD $@ \<obj\> $(libretro_new) $(LIBS) $(LDFLAGS)"),)
$(Q)$(LD) -o $@ $(RARCH_OBJ) $(libretro_new) $(LIBS) $(LDFLAGS)
$(Q)tr -d '\n' < "$(TARGET_BASE).aw.js" | sed -e "s/[\/&]/\\\\&/g" -e "s/'/\\\\\\\\&/g" > _audioworklet.js
$(Q)sed -i.bak -e "s/\"$(TARGET_BASE)\.aw\.js\"/URL.createObjectURL(new Blob(['$$(cat _audioworklet.js)'],{type:'text\/javascript'}))/" -- "$@"
$(Q)rm -f "$(TARGET_BASE).aw.js" _audioworklet.js "$@".bak
else
$(TARGET): $(RARCH_OBJ) $(libretro_new) mv_libretro
@$(if $(Q), $(shell echo echo "LD $@ \<obj\> $(libretro_new) $(LIBS) $(LDFLAGS)"),)
$(Q)$(LD) -o $@ $(RARCH_OBJ) $(libretro_new) $(LIBS) $(LDFLAGS)
endif
$(OBJDIR)/%.o: %.c
@mkdir -p $(dir $@)

View File

@ -127,7 +127,7 @@ APP_ICON := pkg/libnx/retroarch.jpg
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -mcpu=cortex-a57+crc+fp+simd
CFLAGS := -g -Wall -O3 -fcommon -ffast-math -ffunction-sections \
CFLAGS := -g -Wall -O3 -fcommon -ffunction-sections \
$(ARCH) $(DEFINES) $(INCLUDE_DIRS) -I$(LIBNX)/include -I$(PORTLIBS)/include/ -include $(LIBNX)/include/switch.h #$(shell $(PORTLIBS)/bin/freetype-config --cflags)
CFLAGS += $(INCLUDE) -DSWITCH=1 -DHAVE_LIBNX=1 -DNXLINK=1 -DHAVE_SHADERPIPELINE -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_UPDATE_CORES -DHAVE_UPDATE_CORE_INFO -DHAVE_STB_FONT #-DHAVE_FREETYPE

View File

@ -118,7 +118,7 @@ TARGET = retroarch
OBJ :=
LINK := $(CXX)
DEF_FLAGS := -march=armv5te -mtune=arm926ej-s -ffast-math -fomit-frame-pointer
DEF_FLAGS := -march=armv5te -mtune=arm926ej-s -fomit-frame-pointer
DEF_FLAGS += -ffunction-sections -fdata-sections
DEF_FLAGS += -I. -Ideps -Ideps/stb -DMIYOO=1 -DDINGUX -MMD
DEF_FLAGS += -Wall -Wno-unused-variable -flto
@ -166,7 +166,7 @@ endif
RARCH_OBJ := $(addprefix $(OBJDIR)/,$(OBJ))
all: info $(TARGET)
all: $(TARGET)
define INFO
ASFLAGS: $(ASFLAGS)

View File

@ -241,7 +241,7 @@ OBJOUT = -o
LINKOUT = -o
LINK = $(CXX)
all: info $(EXT_TARGET)
all: $(EXT_TARGET)
define INFO
CC: $(CC)

View File

@ -182,7 +182,7 @@ endif
TARGETS := $(TARGET).self
all: info $(TARGETS)
all: $(TARGETS)
define INFO
CC: $(CC)

View File

@ -94,7 +94,7 @@ LDFLAGS += -L$(PS2DEV)/gsKit/lib -L$(PS2SDK)/ports/lib -L.
# Lib cdvd is needed to get proper time
LIBS += -lpatches -lgskit -ldmakit -lps2_drivers -lz -lelf-loader
CFLAGS = $(OPTIMIZE_LV) $(DISABLE_WARNINGS) $(DEFINES) -DPS2 -ffast-math -fsingle-precision-constant
CFLAGS = $(OPTIMIZE_LV) $(DISABLE_WARNINGS) $(DEFINES) -DPS2 -fsingle-precision-constant
ASFLAGS = $(CFLAGS)
EE_OBJS += $(OBJ)
@ -108,7 +108,7 @@ EE_INCS = $(INCDIR)
EE_BIN = $(TARGET).elf
EE_GPVAL = $(GPVAL)
all: info $(EE_BIN)
all: $(EE_BIN)
define INFO
EE_BIN: $(EE_BIN)

View File

@ -21,7 +21,7 @@ endif
INCDIR = -Ilibretro-common/include
INCDIR += -I$(PS2SDK)/ports/include
CFLAGS = $(OPTIMIZE_LV) $(DISABLE_WARNINGS) -ffast-math -fsingle-precision-constant
CFLAGS = $(OPTIMIZE_LV) $(DISABLE_WARNINGS) -fsingle-precision-constant
ASFLAGS = $(CFLAGS)
RARCH_DEFINES += -DPS2 -DIS_SALAMANDER -DRARCH_CONSOLE

View File

@ -130,7 +130,7 @@ ifneq ($(V),1)
Q := @
endif
all: info $(SELF_TARGET)
all: $(SELF_TARGET)
define INFO
CXX: $(CXX)
@ -159,11 +159,8 @@ pkg: create-core
# $(PACKAGE_FINALIZE) $(PACKAGE_BASENAME).gnpdrm.pkg
clean:
ifneq ($(V),1)
@echo RM
else
rm -f $(ELF_TARGET)
rm -f $(OBJ)
endif
@$(if $(Q), echo $@,)
$(Q)rm -f $(ELF_TARGET)
$(Q)rm -f $(OBJ)
.PHONY: clean

View File

@ -16,7 +16,7 @@ else
endif
INCDIR = deps deps/stb deps/7zip libretro-common/include libretro-common/include/compat/zlib
CFLAGS = $(OPTIMIZE_LV) -ffast-math -fsingle-precision-constant
CFLAGS = $(OPTIMIZE_LV) -fsingle-precision-constant
ASFLAGS = $(CFLAGS)
RARCH_DEFINES = -DPSP \

View File

@ -12,7 +12,7 @@ else
endif
INCDIR = $(PSPPATH)/include libretro-common/include
CFLAGS = $(OPTIMIZE_LV) -ffast-math -fsingle-precision-constant
CFLAGS = $(OPTIMIZE_LV) -fsingle-precision-constant
ASFLAGS = $(CFLAGS)
RARCH_DEFINES = -DPSP -DIS_SALAMANDER -DRARCH_CONSOLE

View File

@ -120,7 +120,7 @@ OPK_NAME = retroarch_retrofw.opk
OBJ :=
LINK := $(CXX)
DEF_FLAGS := -march=mips32 -mtune=mips32 -mhard-float -ffast-math -fomit-frame-pointer
DEF_FLAGS := -march=mips32 -mtune=mips32 -mhard-float -fomit-frame-pointer
DEF_FLAGS += -mplt -mno-shared
DEF_FLAGS += -ffunction-sections -fdata-sections
DEF_FLAGS += -I. -Ideps -Ideps/stb -DDINGUX=1 -DRETROFW=1 -MMD
@ -184,7 +184,7 @@ X-OD-NeedsDownscaling=true
endef
export DESKTOP_ENTRY
all: info $(TARGET) opk
all: $(TARGET) opk
define INFO
ASFLAGS: $(ASFLAGS)

View File

@ -186,7 +186,7 @@ X-OD-NeedsDownscaling=true
endef
export DESKTOP_ENTRY
all: info $(TARGET) opk
all: $(TARGET) opk
define INFO
ASFLAGS: $(ASFLAGS)

View File

@ -149,7 +149,7 @@ endif
ASFLAGS := $(CFLAGS)
LDFLAGS := -Wl,-q,--pic-veneer
CFLAGS += -Wall -ffast-math
CFLAGS += -Wall
CFLAGS += -DRARCH_INTERNAL -DHAVE_SCREENSHOTS -DRARCH_CONSOLE
CFLAGS += -DHAVE_DSP_FILTER
CFLAGS += -DHAVE_VIDEO_FILTER
@ -201,7 +201,7 @@ TARGETS := $(TARGET).vpk
DEPFLAGS = -MT $@ -MMD -MP -MF $*.Tdepend
POSTCOMPILE = mv -f $*.Tdepend $*.depend
all: info $(TARGETS)
all: $(TARGETS)
define INFO
ASFLAGS: $(ASFLAGS)
@ -274,11 +274,10 @@ $(TARGET).elf: $(OBJ) liblibScePiglet_stub.a
vita-pack-vpk -s param.sfo -b $< $@
clean:
ifneq ($(V),1)
rm -f $(OBJ) $(TARGET).elf $(TARGET).elf.unstripped.elf $(TARGET).velf $(TARGET).self param.sfo $(TARGET).vpk
rm -rf deps/Pigs-In-A-Blanket/piglet_stub/libScePiglet
rm -f $(OBJ:.o=.depend)
endif
@$(if $(Q), echo $@,)
$(Q)rm -f $(OBJ) $(TARGET).elf $(TARGET).elf.unstripped.elf $(TARGET).velf $(TARGET).self param.sfo $(TARGET).vpk
$(Q)rm -rf deps/Pigs-In-A-Blanket/piglet_stub/libScePiglet
$(Q)rm -f $(OBJ:.o=.depend)
# Useful for developers
vpksend: $(TARGET).vpk

View File

@ -14,7 +14,7 @@ PREFIX = arm-vita-eabi
CC = $(PREFIX)-gcc
INCDIR = libretro-common/include
CFLAGS = -Wl,-q $(OPTIMIZE_LV) -I$(INCDIR) -std=gnu99 -mfloat-abi=hard -ffast-math -fsingle-precision-constant -mword-relocations
CFLAGS = -Wl,-q $(OPTIMIZE_LV) -I$(INCDIR) -std=gnu99 -mfloat-abi=hard -fsingle-precision-constant -mword-relocations
ASFLAGS = $(CFLAGS)
RARCH_DEFINES = -DVITA -DIS_SALAMANDER -DRARCH_CONSOLE

View File

@ -5,10 +5,10 @@ ifneq ($(CROSS_COMPILE),arm-webos-linux-gnueabi-)
endif
ifdef SDKTARGETSYSROOT
$(warning OE-based toolchain isn't supported anymore. Please use https://github.com/webosbrew/native-toolchain)
$(warning "OE-based toolchain isn't supported anymore. Please use https://github.com/webosbrew/native-toolchain")
STAGING_DIR = $(SDKTARGETSYSROOT)
else ifndef STAGING_DIR
$(error Can't find buildroot based toolchain. Please use https://github.com/webosbrew/native-toolchain)
$(error "Can't find buildroot based toolchain. Please use https://github.com/webosbrew/native-toolchain")
endif
WEBOS_FREETYPE_CONFIG ?= $(STAGING_DIR)/usr/bin/freetype-config
@ -17,12 +17,12 @@ WEBOS_INC_DIR ?= $(STAGING_DIR)/usr/include
WEBOS_LIB_DIR ?= $(STAGING_DIR)/usr/lib
ADD_SDL2_LIB ?= 0
SDL2_PREBUILT_ARCHIVE ?= https://github.com/webosbrew/SDL-webOS/releases/download/release-2.30.0-webos.2/SDL2-2.30.0-webos.tar.gz
SDL2_PREBUILT_ARCHIVE ?= https://github.com/webosbrew/SDL-webOS/releases/download/release-2.30.8-webos.3/SDL2-2.30.8-webos-abi.tar.gz
#########################
#########################
PACKAGE_NAME = com.retroarch
APP_PACKAGE_NAME ?= com.retroarch.webos
PACKAGE_VERSION := $(patsubst "%",%,$(RARCH_VERSION))
DEBUG ?= 0
@ -60,8 +60,8 @@ HAVE_DR_MP3 = 1
HAVE_DYNAMIC = 1
HAVE_DYLIB = 1
HAVE_EGL = 0
HAVE_FREETYPE = 0
HAVE_GDI = 1
HAVE_FREETYPE = 1
HAVE_GDI = 0
HAVE_GETADDRINFO = 1
HAVE_GETOPT_LONG = 1
HAVE_GLSL = 1
@ -107,7 +107,7 @@ HAVE_RUNAHEAD = 1
HAVE_SDL = 0
HAVE_SDL2 = 1
HAVE_SHADERPIPELINE = 1
HAVE_STB_FONT = 1
HAVE_STB_FONT = 0
HAVE_STB_IMAGE = 1
HAVE_STB_VORBIS = 1
HAVE_STDIN_CMD = 1
@ -138,9 +138,10 @@ DEF_FLAGS += -ffunction-sections -fdata-sections
DEF_FLAGS += -I. -Ideps -Ideps/stb -DWEBOS=1 -MMD
DEF_FLAGS += -Wall -Wno-unused-variable
LIBS := -ldl -lz -lrt -pthread
CFLAGS :=
CXXFLAGS := -fno-exceptions -fno-rtti -std=c++11 -D__STDC_CONSTANT_MACROS
ASFLAGS :=
ARCHFLAGS := -mcpu=cortex-a9 -mtune=cortex-a53 -mfloat-abi=softfp
CFLAGS := $(ARCHFLAGS)
CXXFLAGS := $(ARCHFLAGS) -fno-exceptions -fno-rtti -std=c++11 -D__STDC_CONSTANT_MACROS
ASFLAGS := $(ARCHFLAGS)
LDFLAGS := -Wl,-rpath=\$$ORIGIN/lib,--gc-sections
INCLUDE_DIRS = -I$(WEBOS_INC_DIR)
LIBRARY_DIRS = -L$(WEBOS_LIB_DIR)
@ -151,6 +152,8 @@ DEFINES += -DHAVE_FILTERS_BUILTIN
DEFINES += -DHAVE_SDL2
DEFINES += -DHAVE_PULSE
DEFINES += -DHAVE_NETWORKING -DHAVE_IFINFO -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_UPDATE_CORES
DEFINES += -DHAVE_NETWORKGAMEPAD
DEFINES += -DHAVE_FREETYPE
DEFINES += -DHAVE_UPDATE_CORE_INFO
PKG_CONFIG=pkg-config
@ -159,6 +162,10 @@ SDL2_CFLAGS := $(shell $(PKG_CONFIG) --cflags sdl2)
SDL2_LIBS := $(shell $(PKG_CONFIG) --libs sdl2)
OPENGLES_LIBS = -lGLESv2
PULSE_LIBS = $(shell $(PKG_CONFIG) --libs libpulse)
FREETYPE_CFLAGS := $(shell $(PKG_CONFIG) --cflags freetype2)
FREETYPE_LIBS := $(shell $(PKG_CONFIG) --libs freetype2)
FONTCONFIG_CFLAGS := $(shell $(PKG_CONFIG) --cflags fontconfig)
FONTCONFIG_LIBS := $(shell $(PKG_CONFIG) --libs fontconfig)
MMAP_LIBS = -lc
NEON_CFLAGS = -mfpu=neon
NEON_ASFLAGS = -mfpu=neon
@ -191,7 +198,7 @@ RARCH_OBJ := $(addprefix $(OBJDIR)/,$(OBJ))
define APPINFO
{
"id": "$(PACKAGE_NAME)",
"id": "$(APP_PACKAGE_NAME)",
"version": "$(PACKAGE_VERSION)",
"vendor": "webosbrew.org",
"title": "RetroArch",
@ -204,7 +211,7 @@ define APPINFO
endef
export APPINFO
all: info $(TARGET) ipk
all: $(TARGET) ipk
define INFO
ASFLAGS: $(ASFLAGS)
@ -285,10 +292,10 @@ endif
cd webos && ares-package dist
install: ipk
ares-install webos/$(PACKAGE_NAME)_$(PACKAGE_VERSION)_$(ARCH).ipk
ares-install webos/$(APP_PACKAGE_NAME)_$(PACKAGE_VERSION)_$(ARCH).ipk
launch: install
ares-launch com.retroarch
ares-launch $(APP_PACKAGE_NAME)
.PHONY: all clean ipk

View File

@ -273,7 +273,7 @@ OBJOUT = -o
LINKOUT = -o
LINK = $(CXX)
all: info $(EXT_TARGET)
all: $(EXT_TARGET)
define INFO
CC: $(CC)

View File

@ -203,7 +203,7 @@ INCDIRS += -Iwiiu
INCDIRS += -Iwiiu/include
CFLAGS := -mcpu=750 -meabi -mhard-float
CFLAGS += -ffast-math -Werror=implicit-function-declaration
CFLAGS += -Werror=implicit-function-declaration
CFLAGS += -ffunction-sections -fdata-sections
#CFLAGS += -fomit-frame-pointer -mword-relocations
CFLAGS += -Wall
@ -302,7 +302,7 @@ endif
DEPFLAGS = -MT $@ -MMD -MP -MF $(BUILD_DIR)/$*.depend
all: info $(TARGETS)
all: $(TARGETS)
define INFO
AR: $(AR)

View File

@ -132,8 +132,8 @@ ifeq ($(DEBUG), 1)
CFLAGS += -O0 -g
CXXFLAGS += -O0 -g
else
CFLAGS += -O3 -ffast-math
CXXFLAGS += -O3 -ffast-math
CFLAGS += -O3
CXXFLAGS += -O3
endif
CFLAGS += $(DEF_FLAGS) -Wall -Wno-unused-result -Wno-unused-variable -I. -Ideps

View File

@ -21,23 +21,11 @@
RETRO_BEGIN_DECLS
#define AUDIO_CHUNK_SIZE_BLOCKING 512
/* So we don't get complete line-noise when fast-forwarding audio. */
#define AUDIO_CHUNK_SIZE_NONBLOCKING 2048
#define AUDIO_MAX_RATIO 16
#define AUDIO_MIN_RATIO 0.0625
#define AUDIO_MIXER_MAX_STREAMS 16
#define AUDIO_MIXER_MAX_SYSTEM_STREAMS (AUDIO_MIXER_MAX_STREAMS + 8)
/* Fastforward timing calculations running average samples. Helps with a
consistent pitch when fast-forwarding. */
#define AUDIO_FF_EXP_AVG_SAMPLES 16
/* do not define more than (MAX_SYSTEM_STREAMS - MAX_STREAMS) */
/* Do not define more than (MAX_SYSTEM_STREAMS - MAX_STREAMS) */
enum audio_mixer_system_slot
{
AUDIO_MIXER_SYSTEM_SLOT_OK = AUDIO_MIXER_MAX_STREAMS,

File diff suppressed because it is too large Load Diff

View File

@ -169,8 +169,8 @@ typedef struct audio_driver
typedef struct
{
double source_ratio_original;
double source_ratio_current;
double src_ratio_orig;
double src_ratio_curr;
uint64_t free_samples_count;
@ -320,17 +320,6 @@ bool audio_driver_start(bool is_shutdown);
bool audio_driver_stop(void);
/**
* If you need to query the size of audio samples,
* use this function instead of checking the flags directly.
*
* @return The size of a single audio sample in bytes,
* as determined by the presence of the \c AUDIO_FLAG_USE_FLOAT flag.
* Will currently return either 2 (for \c uint16_t) or 4 (for \c float),
* although this may change if we add support for more sample types.
*/
unsigned audio_driver_get_sample_size(void);
#ifdef HAVE_TRANSLATE
/* TODO/FIXME - Doesn't currently work. Fix this. */
bool audio_driver_is_ai_service_speech_running(void);
@ -344,9 +333,7 @@ bool audio_driver_is_ai_service_speech_running(void);
**/
bool audio_compute_buffer_statistics(audio_statistics_t *stats);
bool audio_driver_init_internal(
void *settings_data,
bool audio_cb_inited);
bool audio_driver_init_internal(void *data, bool audio_cb_inited);
bool audio_driver_deinit(void);

View File

@ -382,6 +382,9 @@ bool audioworklet_external_block(void)
{
audioworklet_data_t *audioworklet = audioworklet_static_data;
if (!audioworklet)
return false;
#ifdef EMSCRIPTEN_AUDIO_FAKE_BLOCK
if (!audioworklet->block_requested)
return false;

View File

@ -1,6 +1,7 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2015 - Michael Lelli
* Copyright (C) 2011-2017 - Daniel De Matteis
* Copyright (C) 2025 - OlyB
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
@ -17,81 +18,230 @@
#include <stdlib.h>
#include <unistd.h>
#include <boolean.h>
#include <retro_timers.h>
#include "../audio_driver.h"
#include "../../verbosity.h"
#include "../../frontend/drivers/platform_emscripten.h"
#define RWEBAUDIO_BUFFER_SIZE_MS 10
/* forward declarations */
unsigned RWebAudioSampleRate(void);
void *RWebAudioInit(unsigned latency);
ssize_t RWebAudioWrite(const void *s, size_t len);
ssize_t RWebAudioQueueBuffer(size_t num_frames, float *left, float *right);
bool RWebAudioStop(void);
bool RWebAudioStart(void);
void RWebAudioSetNonblockState(bool state);
void RWebAudioFree(void);
size_t RWebAudioWriteAvail(void);
size_t RWebAudioBufferSize(void);
size_t RWebAudioWriteAvailFrames(void);
size_t RWebAudioBufferSizeFrames(void);
void RWebAudioRecalibrateTime(void);
bool RWebAudioResumeCtx(void);
typedef struct rweb_audio
typedef struct rwebaudio_data
{
bool is_paused;
} rweb_audio_t;
size_t tmpbuf_frames;
size_t tmpbuf_offset;
float *tmpbuf_left;
float *tmpbuf_right;
bool nonblock;
bool running;
#ifdef EMSCRIPTEN_AUDIO_FAKE_BLOCK
bool block_requested;
#endif
} rwebaudio_data_t;
static rwebaudio_data_t *rwebaudio_static_data = NULL;
static void rwebaudio_free(void *data)
{
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
if (!rwebaudio)
return;
RWebAudioFree();
free(data);
if (rwebaudio->tmpbuf_left)
free(rwebaudio->tmpbuf_left);
if (rwebaudio->tmpbuf_right)
free(rwebaudio->tmpbuf_right);
free(rwebaudio);
rwebaudio_static_data = NULL;
}
static void *rwebaudio_init(const char *device, unsigned rate, unsigned latency,
unsigned block_frames,
unsigned *new_rate)
{
rweb_audio_t *rwebaudio = (rweb_audio_t*)calloc(1, sizeof(rweb_audio_t));
rwebaudio_data_t *rwebaudio;
if (rwebaudio_static_data)
{
RARCH_ERR("[RWebAudio] Tried to start already running driver!\n");
return NULL;
}
rwebaudio = (rwebaudio_data_t*)calloc(1, sizeof(rwebaudio_data_t));
if (!rwebaudio)
return NULL;
if (RWebAudioInit(latency))
*new_rate = RWebAudioSampleRate();
if (!RWebAudioInit(latency))
{
RARCH_ERR("[RWebAudio] Failed to initialize driver!\n");
return NULL;
}
rwebaudio_static_data = rwebaudio;
*new_rate = RWebAudioSampleRate();
rwebaudio->tmpbuf_frames = RWEBAUDIO_BUFFER_SIZE_MS * *new_rate / 1000;
rwebaudio->tmpbuf_left = memalign(sizeof(float), rwebaudio->tmpbuf_frames * sizeof(float));
rwebaudio->tmpbuf_right = memalign(sizeof(float), rwebaudio->tmpbuf_frames * sizeof(float));
RARCH_LOG("[RWebAudio] Device rate: %d Hz.\n", *new_rate);
RARCH_LOG("[RWebAudio] Buffer size: %lu bytes.\n", RWebAudioBufferSizeFrames() * 2 * sizeof(float));
return rwebaudio;
}
static ssize_t rwebaudio_write(void *data, const void *s, size_t len)
{
return RWebAudioWrite(s, len);
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
const float *samples = (const float*)s;
size_t num_frames = len / 2 / sizeof(float);
size_t written = 0;
if (!rwebaudio)
return -1;
while (num_frames)
{
rwebaudio->tmpbuf_left[rwebaudio->tmpbuf_offset] = *(samples++);
rwebaudio->tmpbuf_right[rwebaudio->tmpbuf_offset] = *(samples++);
num_frames--;
if (++rwebaudio->tmpbuf_offset == rwebaudio->tmpbuf_frames)
{
size_t queued = RWebAudioQueueBuffer(rwebaudio->tmpbuf_frames, rwebaudio->tmpbuf_left, rwebaudio->tmpbuf_right);
rwebaudio->tmpbuf_offset = 0;
/* fast-forward or context is suspended */
if (queued < rwebaudio->tmpbuf_frames)
break;
written += queued;
}
}
if (rwebaudio->nonblock)
return written;
#ifdef EMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK
#ifdef EMSCRIPTEN_AUDIO_FAKE_BLOCK
if (RWebAudioWriteAvailFrames() == 0)
{
rwebaudio->block_requested = true;
platform_emscripten_enter_fake_block(1);
}
#endif
/* async external block doesn't need to do anything else */
#else
while (RWebAudioWriteAvailFrames() == 0)
{
#ifdef EMSCRIPTEN_FULL_ASYNCIFY
retro_sleep(1);
#endif
RWebAudioResumeCtx();
}
#endif
return written;
}
#ifdef EMSCRIPTEN_AUDIO_EXTERNAL_BLOCK
/* returns true if fake block should continue */
bool rwebaudio_external_block(void)
{
rwebaudio_data_t *rwebaudio = rwebaudio_static_data;
if (!rwebaudio)
return false;
#ifdef EMSCRIPTEN_AUDIO_FAKE_BLOCK
if (!rwebaudio->block_requested)
return false;
#endif
#ifdef EMSCRIPTEN_AUDIO_EXTERNAL_WRITE_BLOCK
while (!rwebaudio->nonblock && RWebAudioWriteAvailFrames() == 0)
{
RWebAudioResumeCtx();
#ifdef EMSCRIPTEN_AUDIO_ASYNC_BLOCK
retro_sleep(1);
#else
return true;
#endif
}
#endif
#ifdef EMSCRIPTEN_AUDIO_FAKE_BLOCK
rwebaudio->block_requested = false;
platform_emscripten_exit_fake_block();
return true; /* return to RAF if needed */
#endif
return false;
}
#endif
void rwebaudio_recalibrate_time(void)
{
if (rwebaudio_static_data)
RWebAudioRecalibrateTime();
}
static bool rwebaudio_stop(void *data)
{
rweb_audio_t *rwebaudio = (rweb_audio_t*)data;
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
if (!rwebaudio)
return false;
rwebaudio->is_paused = true;
rwebaudio->running = false;
return RWebAudioStop();
}
static void rwebaudio_set_nonblock_state(void *data, bool state)
{
RWebAudioSetNonblockState(state);
}
static bool rwebaudio_alive(void *data)
{
rweb_audio_t *rwebaudio = (rweb_audio_t*)data;
if (!rwebaudio)
return false;
return !rwebaudio->is_paused;
}
static bool rwebaudio_start(void *data, bool is_shutdown)
{
rweb_audio_t *rwebaudio = (rweb_audio_t*)data;
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
if (!rwebaudio)
return false;
rwebaudio->is_paused = false;
rwebaudio->running = true;
return RWebAudioStart();
}
static size_t rwebaudio_write_avail(void *data) {return RWebAudioWriteAvail();}
static size_t rwebaudio_buffer_size(void *data) {return RWebAudioBufferSize();}
static bool rwebaudio_alive(void *data)
{
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
if (!rwebaudio)
return false;
return rwebaudio->running;
}
static void rwebaudio_set_nonblock_state(void *data, bool state)
{
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
if (!rwebaudio)
return;
rwebaudio->nonblock = state;
RWebAudioSetNonblockState(state);
}
static size_t rwebaudio_write_avail(void *data)
{
rwebaudio_data_t *rwebaudio = (rwebaudio_data_t*)data;
size_t avail_frames;
if (!rwebaudio)
return 0;
avail_frames = RWebAudioWriteAvailFrames();
if (avail_frames > rwebaudio->tmpbuf_offset)
return (avail_frames - rwebaudio->tmpbuf_offset) * 2 * sizeof(float);
return 0;
}
static size_t rwebaudio_buffer_size(void *data)
{
return RWebAudioBufferSizeFrames() * 2 * sizeof(float);
}
static bool rwebaudio_use_float(void *data) { return true; }
audio_driver_t audio_rwebaudio = {

View File

@ -443,10 +443,13 @@ static size_t wasapi_write_avail(void *wh)
{
wasapi_t *w = (wasapi_t*)wh;
UINT32 padding = 0;
if (w->flags & WASAPI_FLG_EXCLUSIVE && w->buffer)
return FIFO_WRITE_AVAIL(w->buffer);
if (FAILED(_IAudioClient_GetCurrentPadding(w->client, &padding)))
return 0;
if (w->buffer) /* Exaggerate available size for best results.. */
return FIFO_WRITE_AVAIL(w->buffer) + padding * 2;
return FIFO_WRITE_AVAIL(w->buffer) + padding;
return w->engine_buffer_size - padding * w->frame_size;
}

View File

@ -1,852 +0,0 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2023 Jesse Talavera-Greenberg
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h>
#include <memalign.h>
#include <audio/conversion/s16_to_float.h>
#include <audio/conversion/float_to_s16.h>
#include <retro_assert.h>
#include <string/stdstring.h>
#include <lists/string_list.h>
#include <audio/conversion/dual_mono.h>
#include "microphone_driver.h"
#include "audio_defines.h"
#include "../configuration.h"
#include "../driver.h"
#include "../list_special.h"
#include "../runloop.h"
#include "../verbosity.h"
static microphone_driver_state_t mic_driver_st;
microphone_driver_t microphone_null = {
NULL,
NULL,
NULL,
NULL,
"null",
NULL,
NULL,
NULL,
NULL
};
microphone_driver_t *microphone_drivers[] = {
#ifdef HAVE_ALSA
&microphone_alsa,
#if !defined(__QNX__) && !defined(MIYOO) && defined(HAVE_THREADS)
&microphone_alsathread,
#endif
#endif
#ifdef HAVE_WASAPI
&microphone_wasapi,
#endif
#ifdef HAVE_SDL2
&microphone_sdl, /* Microphones are not supported in SDL 1 */
#endif
#ifdef HAVE_PIPEWIRE
&microphone_pipewire,
#endif
#ifdef HAVE_COREAUDIO
&microphone_coreaudio,
#endif
&microphone_null,
NULL,
};
microphone_driver_state_t *microphone_state_get_ptr(void)
{
return &mic_driver_st;
}
#define mic_driver_get_sample_size(microphone) \
(((microphone)->flags & MICROPHONE_FLAG_USE_FLOAT) ? sizeof(float) : sizeof(int16_t))
static bool mic_driver_open_mic_internal(retro_microphone_t* microphone);
bool microphone_driver_start(void)
{
microphone_driver_state_t *mic_st = &mic_driver_st;
retro_microphone_t *microphone = &mic_st->microphone;
if (microphone->flags & MICROPHONE_FLAG_ACTIVE)
{ /* If there's an opened microphone that the core turned on... */
if (microphone->flags & MICROPHONE_FLAG_PENDING)
{ /* If this microphone was requested before the driver was ready...*/
retro_assert(microphone->microphone_context == NULL);
/* The microphone context shouldn't have been created yet */
/* Now that the driver and driver context are ready, let's initialize the mic */
if (mic_driver_open_mic_internal(microphone))
{
/* open_mic_internal will start the microphone if it's enabled */
RARCH_DBG("[Microphone]: Initialized a previously-pending microphone.\n");
}
else
{
RARCH_ERR("[Microphone]: Failed to initialize a previously pending microphone; microphone will not be used.\n");
microphone_driver_close_mic(microphone);
/* Not returning false because a mic failure shouldn't take down the driver;
* what if the player just unplugged their mic? */
}
}
else
{ /* The mic was already created, so let's just unpause it */
microphone_driver_set_mic_state(microphone, true);
RARCH_DBG("[Microphone]: Started a microphone that was enabled when the driver was last stopped.\n");
}
}
return true;
}
bool microphone_driver_stop(void)
{
microphone_driver_state_t *mic_st = &mic_driver_st;
retro_microphone_t *microphone = &mic_st->microphone;
bool result = true;
if ((microphone->flags & MICROPHONE_FLAG_ACTIVE)
&& (microphone->flags & MICROPHONE_FLAG_ENABLED)
&& !(microphone->flags & MICROPHONE_FLAG_PENDING))
{ /* If there's an opened microphone that the core turned on and received... */
result = mic_st->driver->stop_mic(mic_st->driver_context, microphone->microphone_context);
}
/* If the mic is pending, then we don't need to do anything. */
return result;
}
/**
* config_get_microphone_driver_options:
*
* Get an enumerated list of all microphone driver names, separated by '|'.
*
* Returns: string listing of all microphone driver names, separated by '|'.
**/
const char *config_get_microphone_driver_options(void)
{
return char_list_new_special(STRING_LIST_MICROPHONE_DRIVERS, NULL);
}
bool microphone_driver_find_driver(void *settings_data, const char *prefix,
bool verbosity_enabled)
{
settings_t *settings = (settings_t*)settings_data;
int i = (int)driver_find_index(
"microphone_driver",
settings->arrays.microphone_driver);
if (i >= 0)
mic_driver_st.driver = (const microphone_driver_t *)
microphone_drivers[i];
else
{
const microphone_driver_t *tmp = NULL;
if (verbosity_enabled)
{
unsigned d;
RARCH_ERR("Couldn't find any %s named \"%s\".\n", prefix,
settings->arrays.microphone_driver);
RARCH_LOG_OUTPUT("Available %ss are:\n", prefix);
for (d = 0; microphone_drivers[d]; d++)
{
if (microphone_drivers[d])
RARCH_LOG_OUTPUT("\t%s\n", microphone_drivers[d]->ident);
}
RARCH_WARN("Going to default to first %s..\n", prefix);
}
tmp = (const microphone_driver_t *)microphone_drivers[0];
if (!tmp)
return false;
mic_driver_st.driver = tmp;
}
return true;
}
static void mic_driver_microphone_handle_init(retro_microphone_t *microphone,
const retro_microphone_params_t *params)
{
if (microphone)
{
const settings_t *settings = config_get_ptr();
unsigned microphone_sample_rate = settings->uints.microphone_sample_rate;
microphone->microphone_context = NULL;
microphone->flags = MICROPHONE_FLAG_ACTIVE;
microphone->sample_buffer = NULL;
microphone->sample_buffer_length = 0;
microphone->requested_params.rate = params ? params->rate : microphone_sample_rate;
microphone->actual_params.rate = 0;
/* We don't set the actual parameters until we actually open the mic.
* (Remember, the core can request one before the driver is ready.) */
microphone->effective_params.rate = params ? params->rate : microphone_sample_rate;
/* We set the effective parameters because
* the frontend has to do what it can
* to give the core what it asks for. */
}
}
static void mic_driver_microphone_handle_free(retro_microphone_t *microphone, bool is_reset)
{
microphone_driver_state_t *mic_st = &mic_driver_st;
const microphone_driver_t *mic_driver = mic_st->driver;
void *driver_context = mic_st->driver_context;
if (!microphone)
return;
if (!driver_context)
RARCH_WARN("[Microphone]: Attempted to free a microphone without an active driver context.\n");
if (microphone->microphone_context)
{
mic_driver->close_mic(driver_context, microphone->microphone_context);
microphone->microphone_context = NULL;
}
if (microphone->sample_buffer)
{
memalign_free(microphone->sample_buffer);
microphone->sample_buffer = NULL;
microphone->sample_buffer_length = 0;
}
if (microphone->outgoing_samples)
{
fifo_free(microphone->outgoing_samples);
microphone->outgoing_samples = NULL;
}
if (microphone->resampler && microphone->resampler->free && microphone->resampler_data)
microphone->resampler->free(microphone->resampler_data);
microphone->resampler = NULL;
microphone->resampler_data = NULL;
/* If the mic driver is being reset and the microphone was already valid... */
if ((microphone->flags & MICROPHONE_FLAG_ACTIVE) && is_reset)
microphone->flags |= MICROPHONE_FLAG_PENDING;
/* ...then we need to keep the handle itself valid
* so it can be reinitialized.
* Otherwise the core will lose mic input. */
else
memset(microphone, 0, sizeof(*microphone));
/* Do NOT free the microphone handle itself! It's allocated statically! */
}
bool microphone_driver_init_internal(void *settings_data)
{
settings_t *settings = (settings_t*)settings_data;
microphone_driver_state_t *mic_st = &mic_driver_st;
bool verbosity_enabled = verbosity_is_enabled();
size_t max_frames = AUDIO_CHUNK_SIZE_NONBLOCKING * AUDIO_MAX_RATIO;
/* If the user has mic support turned off... */
if (!settings->bools.microphone_enable)
{
mic_st->flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE;
return false;
}
convert_s16_to_float_init_simd();
convert_float_to_s16_init_simd();
if (!(microphone_driver_find_driver(settings,
"microphone driver", verbosity_enabled)))
{
RARCH_ERR("[Microphone]: Failed to initialize microphone driver. Will continue without mic input.\n");
goto error;
}
mic_st->input_frames_length = max_frames * sizeof(float);
mic_st->input_frames = (float*)memalign_alloc(64, mic_st->input_frames_length);
if (!mic_st->input_frames)
goto error;
mic_st->converted_input_frames_length = max_frames * sizeof(float);
mic_st->converted_input_frames = (float*)memalign_alloc(64, mic_st->converted_input_frames_length);
if (!mic_st->converted_input_frames)
goto error;
/* Need room for dual-mono frames */
mic_st->dual_mono_frames_length = max_frames * sizeof(float) * 2;
mic_st->dual_mono_frames = (float*)memalign_alloc(64, mic_st->dual_mono_frames_length);
if (!mic_st->dual_mono_frames)
goto error;
mic_st->resampled_frames_length = max_frames * sizeof(float) * 2;
mic_st->resampled_frames = (float*) memalign_alloc(64, mic_st->resampled_frames_length);
if (!mic_st->resampled_frames)
goto error;
mic_st->resampled_mono_frames_length = max_frames * sizeof(float);
mic_st->resampled_mono_frames = (float*) memalign_alloc(64, mic_st->resampled_mono_frames_length);
if (!mic_st->resampled_mono_frames)
goto error;
mic_st->final_frames_length = max_frames * sizeof(int16_t);
mic_st->final_frames = (int16_t*) memalign_alloc(64, mic_st->final_frames_length);
if (!mic_st->final_frames)
goto error;
if (!mic_st->driver || !mic_st->driver->init)
goto error;
if (!(mic_st->driver_context = mic_st->driver->init()))
goto error;
if (!string_is_empty(settings->arrays.microphone_resampler))
strlcpy(mic_st->resampler_ident,
settings->arrays.microphone_resampler,
sizeof(mic_st->resampler_ident));
else
mic_st->resampler_ident[0] = '\0';
mic_st->resampler_quality = (enum resampler_quality)settings->uints.microphone_resampler_quality;
RARCH_LOG("[Microphone]: Initialized microphone driver.\n");
/* The mic driver was initialized, now we're ready to open mics */
mic_st->flags |= MICROPHONE_DRIVER_FLAG_ACTIVE;
if (!microphone_driver_start())
goto error;
return true;
error:
RARCH_ERR("[Microphone]: Failed to start microphone driver. Will continue without audio input.\n");
mic_st->flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE;
return microphone_driver_deinit(false);
}
/**
*
* @param microphone Handle to the microphone to init with a context
*/
static bool mic_driver_open_mic_internal(retro_microphone_t* microphone)
{
microphone_driver_state_t *mic_st = &mic_driver_st;
settings_t *settings = config_get_ptr();
const microphone_driver_t *mic_driver = mic_st->driver;
void *driver_context = mic_st->driver_context;
unsigned runloop_audio_latency = runloop_state_get_ptr()->audio_latency;
unsigned setting_audio_latency = settings->uints.microphone_latency;
unsigned audio_latency = MAX(runloop_audio_latency, setting_audio_latency);
size_t max_samples = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO;
if (!microphone || !mic_driver || !(mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE))
return false;
microphone->sample_buffer_length = max_samples * sizeof(int16_t);
microphone->sample_buffer =
(int16_t*)memalign_alloc(64, microphone->sample_buffer_length);
if (!microphone->sample_buffer)
goto error;
microphone->outgoing_samples = fifo_new(max_samples * sizeof(int16_t));
if (!microphone->outgoing_samples)
goto error;
microphone->microphone_context = mic_driver->open_mic(driver_context,
*settings->arrays.microphone_device ? settings->arrays.microphone_device : NULL,
microphone->requested_params.rate,
audio_latency,
&microphone->actual_params.rate);
if (!microphone->microphone_context)
goto error;
microphone_driver_set_mic_state(microphone, microphone->flags & MICROPHONE_FLAG_ENABLED);
RARCH_LOG("[Microphone]: Requested microphone sample rate of %uHz, got %uHz.\n",
microphone->requested_params.rate,
microphone->actual_params.rate
);
if ( mic_driver->mic_use_float
&& mic_driver->mic_use_float(mic_st->driver_context, microphone->microphone_context))
microphone->flags |= MICROPHONE_FLAG_USE_FLOAT;
microphone->original_ratio = (double)microphone->effective_params.rate / microphone->actual_params.rate;
if (!retro_resampler_realloc(
&microphone->resampler_data,
&microphone->resampler,
mic_st->resampler_ident,
mic_st->resampler_quality,
microphone->original_ratio))
{
RARCH_ERR("[Microphone]: Failed to initialize resampler \"%s\".\n", mic_st->resampler_ident);
goto error;
}
microphone->flags &= ~MICROPHONE_FLAG_PENDING;
RARCH_LOG("[Microphone]: Initialized microphone.\n");
return true;
error:
mic_driver_microphone_handle_free(microphone, false);
RARCH_ERR("[Microphone]: Driver attempted to initialize the microphone but failed.\n");
return false;
}
static void microphone_driver_close_mic_internal(retro_microphone_t *microphone, bool is_reset)
{
microphone_driver_state_t *mic_st = &mic_driver_st;
const microphone_driver_t *mic_driver = mic_st->driver;
void *driver_context = mic_st->driver_context;
if ( microphone
&& driver_context
&& mic_driver
&& mic_driver->close_mic)
mic_driver_microphone_handle_free(microphone, is_reset);
}
void microphone_driver_close_mic(retro_microphone_t *microphone)
{
mic_driver_microphone_handle_free(microphone, false);
}
bool microphone_driver_set_mic_state(retro_microphone_t *microphone, bool state)
{
microphone_driver_state_t *mic_st = &mic_driver_st;
const microphone_driver_t *mic_driver = mic_st->driver;
void *driver_context = mic_st->driver_context;
if (!microphone
|| !(microphone->flags & MICROPHONE_FLAG_ACTIVE)
|| !mic_driver
|| !mic_driver->start_mic
|| !mic_driver->stop_mic)
return false;
/* If the provided microphone was null or invalid, or the driver is incomplete, stop. */
/* If the driver is initialized... */
if (driver_context && microphone->microphone_context)
{
bool success;
/* If we want to enable this mic... */
if (state)
{
success = mic_driver->start_mic(driver_context, microphone->microphone_context);
/* Enable the mic. (Enabling an active mic is a successful noop.) */
if (success)
{
microphone->flags |= MICROPHONE_FLAG_ENABLED;
RARCH_LOG("[Microphone]: Enabled microphone.\n");
}
else
{
RARCH_ERR("[Microphone]: Failed to enable microphone.\n");
}
}
else
{ /* If we want to pause this mic... */
success = mic_driver->stop_mic(driver_context, microphone->microphone_context);
/* Disable the mic. (If the mic is already stopped, disabling it should still be successful.) */
if (success)
{
microphone->flags &= ~MICROPHONE_FLAG_ENABLED;
RARCH_LOG("[Microphone]: Disabled microphone.\n");
}
else
{
RARCH_ERR("[Microphone]: Failed to disable microphone.\n");
}
}
return success;
}
else
{ /* The driver's not ready yet, so we'll make a note
* of what the mic's state should be */
if (state)
microphone->flags |= MICROPHONE_FLAG_ENABLED;
else
microphone->flags &= ~MICROPHONE_FLAG_ENABLED;
RARCH_DBG("[Microphone]: Set pending state to %s.\n",
state ? "enabled" : "disabled");
return true;
/* This isn't an error */
}
}
bool microphone_driver_get_mic_state(const retro_microphone_t *microphone)
{
if (!microphone || !(microphone->flags & MICROPHONE_FLAG_ACTIVE))
return false;
return microphone->flags & MICROPHONE_FLAG_ENABLED;
}
/**
* Pull queued microphone samples from the driver
* and copy them to the provided buffer(s).
*
* Note that microphone samples are provided in mono,
* so a "sample" and a "frame" are equivalent here.
*
* @param mic_st The overall state of the audio driver.
* @param[out] frames The buffer in which the core will receive microphone samples.
* @param num_frames The size of \c frames, in samples.
*/
static size_t microphone_driver_flush(
microphone_driver_state_t *mic_st,
retro_microphone_t *microphone,
size_t num_frames)
{
struct resampler_data resampler_data;
unsigned sample_size = mic_driver_get_sample_size(microphone);
size_t bytes_to_read = MIN(mic_st->input_frames_length, num_frames * sample_size);
size_t frames_to_enqueue;
int bytes_read = mic_st->driver->read(
mic_st->driver_context,
microphone->microphone_context,
mic_st->input_frames,
bytes_to_read);
/* First, get the most recent mic data */
if (bytes_read <= 0)
return 0;
resampler_data.input_frames = bytes_read / sample_size;
/* This is in frames, not samples or bytes;
* we're up-channeling the audio to stereo,
* so this number still applies. */
resampler_data.output_frames = 0;
/* The resampler sets the value of output_frames */
resampler_data.data_in = mic_st->dual_mono_frames;
resampler_data.data_out = mic_st->resampled_frames;
/* The buffers that will be used for the resampler's input and output */
resampler_data.ratio = (double)microphone->effective_params.rate / (double)microphone->actual_params.rate;
if (fabs(resampler_data.ratio - 1.0f) < 1e-8)
{ /* If the mic's native rate is practically the same as the requested one... */
/* ...then skip the resampler, since it'll produce (more or less) identical results. */
frames_to_enqueue = MIN(FIFO_WRITE_AVAIL(microphone->outgoing_samples), resampler_data.input_frames);
/* If this mic provides floating-point samples... */
if (microphone->flags & MICROPHONE_FLAG_USE_FLOAT)
{
convert_float_to_s16(mic_st->final_frames, (const float*)mic_st->input_frames, resampler_data.input_frames);
fifo_write(microphone->outgoing_samples, mic_st->final_frames, frames_to_enqueue * sizeof(int16_t));
}
else
fifo_write(microphone->outgoing_samples, mic_st->input_frames, frames_to_enqueue * sizeof(int16_t));
return resampler_data.input_frames;
}
/* Couldn't take the fast path, so let's resample the mic input */
/* First we need to format the input for the resampler. */
/* If this mic provides floating-point samples... */
if (microphone->flags & MICROPHONE_FLAG_USE_FLOAT)
/* Samples are already in floating-point, so we just need to up-channel them. */
convert_to_dual_mono_float(mic_st->dual_mono_frames,
(const float*)mic_st->input_frames, resampler_data.input_frames);
else
{
/* Samples are 16-bit, so we need to convert them first. */
convert_s16_to_float(mic_st->converted_input_frames, (const int16_t*)mic_st->input_frames, resampler_data.input_frames, 1.0f);
convert_to_dual_mono_float(mic_st->dual_mono_frames, mic_st->converted_input_frames, resampler_data.input_frames);
}
/* Now we resample the mic data. */
microphone->resampler->process(microphone->resampler_data, &resampler_data);
/* Next, we convert the resampled data back to mono... */
convert_to_mono_float_left(mic_st->resampled_mono_frames, mic_st->resampled_frames, resampler_data.output_frames);
/* Why the left channel? No particular reason.
* Left and right channels are the same in this case anyway. */
/* Finally, we convert the audio back to 16-bit ints, as the mic interface requires. */
convert_float_to_s16(mic_st->final_frames, mic_st->resampled_mono_frames, resampler_data.output_frames);
frames_to_enqueue = MIN(FIFO_WRITE_AVAIL(microphone->outgoing_samples), resampler_data.output_frames);
fifo_write(microphone->outgoing_samples, mic_st->final_frames, frames_to_enqueue * sizeof(int16_t));
return resampler_data.output_frames;
}
int microphone_driver_read(retro_microphone_t *microphone, int16_t* frames, size_t num_frames)
{
uint32_t runloop_flags = runloop_get_flags();
size_t frames_remaining = num_frames;
microphone_driver_state_t *mic_st = &mic_driver_st;
const microphone_driver_t *driver = mic_st->driver;
bool core_paused = (runloop_flags & RUNLOOP_FLAG_PAUSED) ? true : false;
bool is_fastforward = (runloop_flags & RUNLOOP_FLAG_FASTMOTION) ? true : false;
bool is_slowmo = (runloop_flags & RUNLOOP_FLAG_SLOWMOTION) ? true : false;
bool is_rewind = state_manager_frame_is_reversed();
bool driver_active = (mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE) ? true : false;
/* If the provided arguments aren't valid... */
if (!frames || !microphone)
return -1;
/* If the microphone or driver aren't active... */
if (!driver_active || !(microphone->flags & MICROPHONE_FLAG_ACTIVE))
return -1;
/* If the driver is invalid or doesn't have the functions it needs... */
if (!driver || !driver->read || !driver->mic_alive)
return -1;
/* If the core didn't actually ask for any frames... */
if (num_frames == 0)
return 0;
if ( (microphone->flags & MICROPHONE_FLAG_PENDING)
|| (microphone->flags & MICROPHONE_FLAG_SUSPENDED)
|| !(microphone->flags & MICROPHONE_FLAG_ENABLED)
|| is_fastforward
|| is_slowmo
|| is_rewind
)
{ /* If the microphone is pending, suspended, or disabled...
...or if the core is in fast-forward, slow-mo, or rewind...*/
memset(frames, 0, num_frames * sizeof(*frames));
return (int)num_frames;
/* ...then copy silence to the provided buffer. Not an error if the mic is pending,
* because the user might have requested a microphone
* before the driver could provide it. */
}
/* Why mute the mic when the core isn't running at standard speed?
* Because I couldn't think of anything useful for the mic to do.
* If you can, send a PR! */
/* If the driver or microphone's state haven't been allocated... */
if (!mic_st->driver_context || !microphone->microphone_context)
return -1;
/* If the mic isn't active like it should be at this point... */
if (!driver->mic_alive(mic_st->driver_context, microphone->microphone_context))
{
RARCH_ERR("[Microphone]: Mic frontend has the mic enabled, but the backend has it disabled.\n");
return -1;
}
/* If the core asked for more frames than we can fit... */
if (num_frames > microphone->outgoing_samples->size)
return -1;
retro_assert(mic_st->input_frames != NULL);
while (FIFO_READ_AVAIL(microphone->outgoing_samples) < num_frames * sizeof(int16_t))
{ /* Until we can give the core the frames it asked for... */
size_t frames_to_read = MIN(AUDIO_CHUNK_SIZE_NONBLOCKING, frames_remaining);
size_t frames_read = 0;
/* If the game is running and the mic driver is active... */
if (!core_paused)
frames_read = microphone_driver_flush(mic_st, microphone, frames_to_read);
/* Otherwise, advance the counters. We're not gonna get new data,
* but we still need to finish this loop */
frames_remaining -= frames_read;
} /* If the queue already has enough samples to give, the loop will be skipped */
fifo_read(microphone->outgoing_samples, frames, num_frames * sizeof(int16_t));
return (int)num_frames;
}
bool microphone_driver_get_effective_params(const retro_microphone_t *microphone, retro_microphone_params_t *params)
{
/* If the arguments are null... */
if (!microphone || !params)
return false;
/* If this isn't an opened microphone... */
if (!(microphone->flags & MICROPHONE_FLAG_ACTIVE))
return false;
*params = microphone->effective_params;
return true;
}
/* NOTE: The core may request a microphone before the driver is ready.
* A pending handle will be provided in that case, and the frontend will
* initialize the microphone when the time is right;
* do not call this function twice on the same mic. */
retro_microphone_t *microphone_driver_open_mic(const retro_microphone_params_t *params)
{
microphone_driver_state_t *mic_st = &mic_driver_st;
const settings_t *settings = config_get_ptr();
const microphone_driver_t *mic_driver = mic_st->driver;
void *driver_context = mic_st->driver_context;
if (!settings)
return NULL;
/* Not checking mic_st->flags because they might not be set yet;
* don't forget, the core can ask for a mic
* before the audio driver is ready to create one. */
if (!settings->bools.microphone_enable)
{
RARCH_DBG("[Microphone]: Refused to open microphone because it's disabled in the settings.\n");
return NULL;
}
if (mic_driver == &microphone_null)
{
RARCH_WARN("[Microphone]: Cannot open microphone, null driver is configured.\n");
return NULL;
}
if ( !mic_driver
&& (string_is_equal(settings->arrays.microphone_driver, "null")
|| string_is_empty(settings->arrays.microphone_driver)))
{ /* If the mic driver hasn't been initialized, but it's not going to be... */
RARCH_ERR("[Microphone]: Cannot open microphone as the driver won't be initialized.\n");
return NULL;
}
/* If the core has requested a second microphone... */
if (mic_st->microphone.flags & MICROPHONE_FLAG_ACTIVE)
{
RARCH_ERR("[Microphone]: Failed to open a second microphone, frontend only supports one at a time right now.\n");
if (mic_st->microphone.flags & MICROPHONE_FLAG_PENDING)
/* If that mic is pending... */
RARCH_ERR("[Microphone]: A microphone is pending initialization.\n");
else
/* That mic is initialized */
RARCH_ERR("[Microphone]: An initialized microphone exists.\n");
return NULL;
}
/* Cores might ask for a microphone before the audio driver is ready to provide them;
* if that happens, we have to initialize the microphones later.
* But the user still wants a handle, so we'll give them one.
*/
mic_driver_microphone_handle_init(&mic_st->microphone, params);
/* If driver_context is NULL, the handle won't have
* a valid microphone context (but we'll create one later) */
if (driver_context)
{
/* If the microphone driver is ready to open a microphone... */
if (mic_driver_open_mic_internal(&mic_st->microphone)) /* If the microphone was successfully initialized... */
RARCH_LOG("[Microphone]: Opened the requested microphone successfully.\n");
else
goto error;
}
else
{ /* If the driver isn't ready to create a microphone... */
mic_st->microphone.flags |= MICROPHONE_FLAG_PENDING;
RARCH_LOG("[Microphone]: Microphone requested before driver context was ready; deferring initialization.\n");
}
return &mic_st->microphone;
error:
mic_driver_microphone_handle_free(&mic_st->microphone, false);
/* This function cleans up any resources and unsets all flags */
return NULL;
}
static bool microphone_driver_free_devices_list(void)
{
microphone_driver_state_t *mic_st = &mic_driver_st;
if (
!mic_st->driver
|| !mic_st->driver->device_list_free
|| !mic_st->driver_context
|| !mic_st->devices_list)
return false;
mic_st->driver->device_list_free(mic_st->driver_context, mic_st->devices_list);
mic_st->devices_list = NULL;
return true;
}
bool microphone_driver_deinit(bool is_reset)
{
microphone_driver_state_t *mic_st = &mic_driver_st;
const microphone_driver_t *driver = mic_st->driver;
microphone_driver_free_devices_list();
microphone_driver_close_mic_internal(&mic_st->microphone, is_reset);
if (driver && driver->free)
{
if (mic_st->driver_context)
driver->free(mic_st->driver_context);
mic_st->driver_context = NULL;
}
if (mic_st->input_frames)
memalign_free(mic_st->input_frames);
mic_st->input_frames = NULL;
mic_st->input_frames_length = 0;
if (mic_st->converted_input_frames)
memalign_free(mic_st->converted_input_frames);
mic_st->converted_input_frames = NULL;
mic_st->converted_input_frames_length = 0;
if (mic_st->dual_mono_frames)
memalign_free(mic_st->dual_mono_frames);
mic_st->dual_mono_frames = NULL;
mic_st->dual_mono_frames_length = 0;
if (mic_st->resampled_frames)
memalign_free(mic_st->resampled_frames);
mic_st->resampled_frames = NULL;
mic_st->resampled_frames_length = 0;
if (mic_st->resampled_mono_frames)
memalign_free(mic_st->resampled_mono_frames);
mic_st->resampled_mono_frames = NULL;
mic_st->resampled_mono_frames_length = 0;
if (mic_st->final_frames)
memalign_free(mic_st->final_frames);
mic_st->final_frames = NULL;
mic_st->final_frames_length = 0;
mic_st->resampler_quality = RESAMPLER_QUALITY_DONTCARE;
mic_st->flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE;
memset(mic_st->resampler_ident, '\0', sizeof(mic_st->resampler_ident));
return true;
}
bool microphone_driver_get_devices_list(void **data)
{
struct string_list**ptr = (struct string_list**)data;
if (!ptr)
return false;
*ptr = mic_driver_st.devices_list;
return true;
}

View File

@ -190,7 +190,7 @@ struct retro_microphone
* The ratio of the core-requested sample rate to the device's opened sample rate.
* If this is (almost) equal to 1, then resampling will be skipped.
*/
double original_ratio;
double orig_ratio;
};
/**

View File

@ -537,59 +537,66 @@ static void generateColorBars(uint32_t *buffer, size_t width, size_t height) {
static void *avfoundation_init(const char *device, uint64_t caps,
unsigned width, unsigned height)
{
RARCH_LOG("[Camera]: Initializing AVFoundation camera %ux%u\n", width, height);
avfoundation_t *avf = (avfoundation_t*)calloc(1, sizeof(avfoundation_t));
if (!avf) {
RARCH_LOG("[Camera]: Initializing AVFoundation camera %ux%u\n", width, height);
if (!avf)
{
RARCH_ERR("[Camera]: Failed to allocate avfoundation_t\n");
return NULL;
}
avf->manager = [AVCameraManager sharedInstance];
avf->width = width;
avf->height = height;
avf->manager.width = width;
avf->manager = [AVCameraManager sharedInstance];
avf->width = width;
avf->height = height;
avf->manager.width = width;
avf->manager.height = height;
// Check if we're on the main thread
if ([NSThread isMainThread]) {
/* Check if we're on the main thread */
if ([NSThread isMainThread])
{
RARCH_LOG("[Camera]: Initializing on main thread\n");
// Direct initialization on main thread
/* Direct initialization on main thread */
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (status != AVAuthorizationStatusAuthorized) {
if (status != AVAuthorizationStatusAuthorized)
{
RARCH_ERR("[Camera]: Camera access not authorized (status: %d)\n", (int)status);
free(avf);
return;
}
}];
} else {
}
else
{
RARCH_LOG("[Camera]: Initializing on background thread\n");
// Use dispatch_sync to run authorization check on main thread
/* Use dispatch_sync to run authorization check on main thread */
__block AVAuthorizationStatus status;
dispatch_sync(dispatch_get_main_queue(), ^{
status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
});
if (status != AVAuthorizationStatusAuthorized) {
if (status != AVAuthorizationStatusAuthorized)
{
RARCH_ERR("[Camera]: Camera access not authorized (status: %d)\n", (int)status);
free(avf);
return NULL;
}
}
// Allocate frame buffer
/* Allocate frame buffer */
avf->manager.frameBuffer = (uint32_t*)calloc(width * height, sizeof(uint32_t));
if (!avf->manager.frameBuffer) {
if (!avf->manager.frameBuffer)
{
RARCH_ERR("[Camera]: Failed to allocate frame buffer\n");
free(avf);
return NULL;
}
// Initialize capture session on main thread
/* Initialize capture session on main thread */
__block bool setupSuccess = false;
if ([NSThread isMainThread]) {
if ([NSThread isMainThread])
{
@autoreleasepool {
setupSuccess = [avf->manager setupCameraSession];
if (setupSuccess) {
@ -597,7 +604,9 @@ static void *avfoundation_init(const char *device, uint64_t caps,
RARCH_LOG("[Camera]: Started camera session\n");
}
}
} else {
}
else
{
dispatch_sync(dispatch_get_main_queue(), ^{
@autoreleasepool {
setupSuccess = [avf->manager setupCameraSession];
@ -609,15 +618,17 @@ static void *avfoundation_init(const char *device, uint64_t caps,
});
}
if (!setupSuccess) {
if (!setupSuccess)
{
RARCH_ERR("[Camera]: Failed to setup camera\n");
free(avf->manager.frameBuffer);
free(avf);
return NULL;
}
// Add a check to verify the session is actually running
if (!avf->manager.session.isRunning) {
/* Add a check to verify the session is actually running */
if (!avf->manager.session.isRunning)
{
RARCH_ERR("[Camera]: Failed to start camera session\n");
free(avf->manager.frameBuffer);
free(avf);
@ -636,11 +647,11 @@ static void avfoundation_free(void *data)
RARCH_LOG("[Camera]: Freeing AVFoundation camera\n");
if (avf->manager.session) {
if (avf->manager.session)
[avf->manager.session stopRunning];
}
if (avf->manager.frameBuffer) {
if (avf->manager.frameBuffer)
{
free(avf->manager.frameBuffer);
avf->manager.frameBuffer = NULL;
}
@ -651,8 +662,10 @@ static void avfoundation_free(void *data)
static bool avfoundation_start(void *data)
{
bool isRunning;
avfoundation_t *avf = (avfoundation_t*)data;
if (!avf || !avf->manager.session) {
if (!avf || !avf->manager.session)
{
RARCH_ERR("[Camera]: Cannot start - invalid data\n");
return false;
}
@ -664,10 +677,10 @@ static bool avfoundation_start(void *data)
RARCH_LOG("[Camera]: Camera session started on background thread\n");
});
// Give the session a moment to start
usleep(100000); // 100ms
/* Give the session a moment to start */
usleep(100000); /* 100ms */
bool isRunning = avf->manager.session.isRunning;
isRunning = avf->manager.session.isRunning;
RARCH_LOG("[Camera]: Camera session running: %s\n", isRunning ? "YES" : "NO");
return isRunning;
}
@ -691,15 +704,18 @@ static bool avfoundation_poll(void *data,
retro_camera_frame_opengl_texture_t frame_gl_cb)
{
avfoundation_t *avf = (avfoundation_t*)data;
if (!avf || !frame_raw_cb) {
if (!avf || !frame_raw_cb)
{
RARCH_ERR("[Camera]: Cannot poll - invalid data or callback\n");
return false;
}
if (!avf->manager.session.isRunning) {
if (!avf->manager.session.isRunning)
{
RARCH_LOG("[Camera]: Camera not running, generating color bars\n");
uint32_t *tempBuffer = (uint32_t*)calloc(avf->width * avf->height, sizeof(uint32_t));
if (tempBuffer) {
if (tempBuffer)
{
generateColorBars(tempBuffer, avf->width, avf->height);
frame_raw_cb(tempBuffer, avf->width, avf->height, avf->width * 4);
free(tempBuffer);
@ -722,4 +738,4 @@ camera_driver_t camera_avfoundation = {
avfoundation_stop,
avfoundation_poll,
"avfoundation"
};
};

View File

@ -40,6 +40,8 @@
#define FFMPEG_CAMERA_DEFAULT_BACKEND "dshow"
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
#define FFMPEG_CAMERA_DEFAULT_BACKEND "bktr"
#else
#define FFMPEG_CAMERA_DEFAULT_BACKEND "lavfi"
#endif
typedef struct ffmpeg_camera

View File

@ -14,6 +14,7 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdint.h>
#include <boolean.h>
@ -23,7 +24,7 @@
#include "../../retroarch.h"
/* forward declarations */
void *RWebCamInit(uint64_t caps, unsigned width, unsigned height);
void *RWebCamInit(uint64_t caps, unsigned width, unsigned height, bool debug);
void RWebCamFree(void *data);
bool RWebCamStart(void *data);
void RWebCamStop(void *data);
@ -34,7 +35,7 @@ static void *rwebcam_init(const char *device, uint64_t caps,
unsigned width, unsigned height)
{
(void)device;
return RWebCamInit(caps, width, height);
return RWebCamInit(caps, width, height, !!getenv("RWEBCAM_DEBUG"));
}
static void rwebcam_free(void *data)

View File

@ -310,7 +310,8 @@ static void *v4l_init(const char *device, uint64_t caps,
if (!path_is_character_special(v4l->dev_name))
{
RARCH_ERR("[V4L2]: %s is no device.\n", v4l->dev_name);
goto error;
free(v4l);
return NULL;
}
v4l->fd = open(v4l->dev_name, O_RDWR | O_NONBLOCK, 0);

View File

@ -1279,6 +1279,13 @@ static void rcheevos_show_game_placard(void)
runloop_msg_queue_push(msg, _len, 0, 3 * 60, false, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
if (summary.num_unsupported_achievements)
{
const char* warning = msg_hash_to_str(MSG_CHEEVOS_UNSUPPORTED_WARNING);
runloop_msg_queue_push(warning, strlen(warning), 0, 3 * 60, false, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_WARNING);
}
}
static uint32_t rcheevos_client_read_memory(uint32_t address,

View File

@ -809,6 +809,35 @@ bool command_play_replay_slot(command_t *cmd, const char *arg)
#endif
}
bool command_save_savefiles(command_t *cmd, const char* arg)
{
char reply[4];
bool ret;
size_t _len = strlcpy(reply, "OK", sizeof(reply));
reply[ _len] = '\n';
reply[++_len] = '\0';
/* In the future, this should probably send each saved file path
to the replier. */
ret = command_event(CMD_EVENT_SAVE_FILES, NULL);
if (!ret)
strlcpy(reply, "NO", sizeof(reply));
cmd->replier(cmd, reply, _len);
return ret;
}
bool command_load_savefiles(command_t *cmd, const char* arg)
{
char reply[4];
bool ret;
size_t _len = strlcpy(reply, "OK", sizeof(reply));
reply[ _len] = '\n';
reply[++_len] = '\0';
ret = command_event(CMD_EVENT_LOAD_FILES, NULL);
if (!ret)
strlcpy(reply, "NO", sizeof(reply));
cmd->replier(cmd, reply, _len);
return ret;
}
#if defined(HAVE_CHEEVOS)
bool command_read_ram(command_t *cmd, const char *arg)

View File

@ -258,6 +258,7 @@ enum event_command
CMD_EVENT_AI_SERVICE_CALL,
/* Misc. */
CMD_EVENT_SAVE_FILES,
CMD_EVENT_LOAD_FILES,
CMD_EVENT_CONTROLLER_INIT,
CMD_EVENT_DISCORD_INIT,
CMD_EVENT_PRESENCE_UPDATE,
@ -272,8 +273,6 @@ enum event_command
/* Reinitializes microphone driver. */
CMD_EVENT_MICROPHONE_REINIT,
#endif
/* Deprecated */
CMD_EVENT_SEND_DEBUG_INFO,
/* Add a playlist entry to another playlist. */
CMD_EVENT_ADD_TO_PLAYLIST
};
@ -420,6 +419,8 @@ bool command_get_config_param(command_t *cmd, const char* arg);
bool command_show_osd_msg(command_t *cmd, const char* arg);
bool command_load_state_slot(command_t *cmd, const char* arg);
bool command_play_replay_slot(command_t *cmd, const char* arg);
bool command_save_savefiles(command_t *cmd, const char* arg);
bool command_load_savefiles(command_t *cmd, const char* arg);
#ifdef HAVE_CHEEVOS
bool command_read_ram(command_t *cmd, const char *arg);
bool command_write_ram(command_t *cmd, const char *arg);
@ -446,6 +447,9 @@ static const struct cmd_action_map action_map[] = {
{ "LOAD_STATE_SLOT",command_load_state_slot, "<slot number>"},
{ "PLAY_REPLAY_SLOT",command_play_replay_slot, "<slot number>"},
{ "SAVE_FILES", command_save_savefiles, "No argument"},
{ "LOAD_FILES", command_load_savefiles, "No argument"},
};
static const struct cmd_map map[] = {
@ -482,6 +486,7 @@ static const struct cmd_map map[] = {
{ "DISK_PREV", RARCH_DISK_PREV },
{ "SHADER_TOGGLE", RARCH_SHADER_TOGGLE },
{ "SHADER_HOLD", RARCH_SHADER_HOLD },
{ "SHADER_NEXT", RARCH_SHADER_NEXT },
{ "SHADER_PREV", RARCH_SHADER_PREV },

View File

@ -156,6 +156,7 @@
* > Helps to unify menu appearance when viewing
* thumbnails of different sizes */
#define DEFAULT_MATERIALUI_THUMBNAIL_BACKGROUND_ENABLE true
#define DEFAULT_MENU_THUMBNAIL_BACKGROUND_ENABLE false
#define DEFAULT_SCREEN_BRIGHTNESS 100
@ -569,6 +570,7 @@
#define DEFAULT_REMAP_SAVE_ON_EXIT true
#define DEFAULT_SHOW_HIDDEN_FILES false
#define DEFAULT_CORE_SUGGEST_ALWAYS false
/* Initialise file browser with the last used start directory */
#define DEFAULT_USE_LAST_START_DIRECTORY false
@ -793,6 +795,7 @@
#define DEFAULT_CONTENT_SHOW_SETTINGS true
#define DEFAULT_CONTENT_SHOW_HISTORY true
#define DEFAULT_CONTENT_SHOW_FAVORITES true
#define DEFAULT_CONTENT_SHOW_FAVORITES_FIRST false
#ifdef HAVE_IMAGEVIEWER
#define DEFAULT_CONTENT_SHOW_IMAGES true
#endif
@ -808,7 +811,7 @@
#endif
#endif
#define DEFAULT_MENU_CONTENT_SHOW_ADD_ENTRY MENU_ADD_CONTENT_ENTRY_DISPLAY_MAIN_TAB
#define DEFAULT_MENU_CONTENT_SHOW_ADD_ENTRY MENU_ADD_CONTENT_ENTRY_DISPLAY_PLAYLISTS_TAB
#define DEFAULT_CONTENT_SHOW_PLAYLISTS true
#define DEFAULT_CONTENT_SHOW_PLAYLIST_TABS true
@ -1174,7 +1177,7 @@
/* Desired audio latency in milliseconds. Might not be honored
* if driver can't provide given latency. */
#if defined(ANDROID) || defined(RETROFW) || defined(MIYOO) || (defined(EMSCRIPTEN) && !defined(HAVE_AUDIOWORKLET))
#if defined(ANDROID) || defined(RETROFW) || defined(MIYOO) || (defined(EMSCRIPTEN) && defined(HAVE_AL))
/* For most Android devices, 64ms is way too low. */
#define DEFAULT_OUT_LATENCY 128
#define DEFAULT_IN_LATENCY 128
@ -1289,11 +1292,7 @@
#endif
/* Pause gameplay when window loses focus. */
#if defined(EMSCRIPTEN)
#define DEFAULT_PAUSE_NONACTIVE false
#else
#define DEFAULT_PAUSE_NONACTIVE true
#endif
/* Pause gameplay when controller disconnects. */
#define DEFAULT_PAUSE_ON_DISCONNECT false
@ -1402,7 +1401,12 @@
#define DEFAULT_SAVESTATE_AUTO_SAVE false
#define DEFAULT_SAVESTATE_AUTO_LOAD false
/* Take screenshots for save states */
#if defined(__x86_64__)
#define DEFAULT_SAVESTATE_THUMBNAIL_ENABLE true
#else
#define DEFAULT_SAVESTATE_THUMBNAIL_ENABLE false
#endif
/* When creating save (srm) files, compress
* written data */
@ -1684,7 +1688,7 @@
#if defined(__QNX__) || defined(_XBOX1) || defined(_XBOX360) || (defined(__MACH__) && defined(IOS)) || defined(ANDROID) || defined(WIIU) || defined(HAVE_NEON) || defined(GEKKO) || defined(__ARM_NEON__) || defined(__PS3__)
#define DEFAULT_AUDIO_RESAMPLER_QUALITY_LEVEL RESAMPLER_QUALITY_LOWER
#elif defined(PSP) || defined(_3DS) || defined(VITA) || defined(PS2) || defined(DINGUX) || defined(EMSCRIPTEN)
#elif defined(PSP) || defined(_3DS) || defined(VITA) || defined(PS2) || defined(DINGUX)
#define DEFAULT_AUDIO_RESAMPLER_QUALITY_LEVEL RESAMPLER_QUALITY_LOWEST
#else
#define DEFAULT_AUDIO_RESAMPLER_QUALITY_LEVEL RESAMPLER_QUALITY_NORMAL

View File

@ -472,6 +472,13 @@ static const struct retro_keybind retro_keybinds_1[] = {
RARCH_SHADER_TOGGLE, NO_BTN, NO_BTN, 0,
true
},
{
NULL, NULL,
AXIS_NONE, AXIS_NONE,
MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_HOLD, RETROK_UNKNOWN,
RARCH_SHADER_HOLD, NO_BTN, NO_BTN, 0,
true
},
{
NULL, NULL,
AXIS_NONE, AXIS_NONE,
@ -654,16 +661,6 @@ static const struct retro_keybind retro_keybinds_1[] = {
RARCH_OSK, NO_BTN, NO_BTN, 0,
true
},
#if 0
/* Deprecated */
{
NULL, NULL,
AXIS_NONE, AXIS_NONE,
MENU_ENUM_LABEL_VALUE_INPUT_META_SEND_DEBUG_INFO, RETROK_UNKNOWN,
RARCH_SEND_DEBUG_INFO, NO_BTN, NO_BTN, 0,
true
},
#endif
#elif defined(DINGUX)
{
NULL, NULL,
@ -1114,6 +1111,13 @@ static const struct retro_keybind retro_keybinds_1[] = {
RARCH_SHADER_TOGGLE, NO_BTN, NO_BTN, 0,
true
},
{
NULL, NULL,
AXIS_NONE, AXIS_NONE,
MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_HOLD, RETROK_UNKNOWN,
RARCH_SHADER_HOLD, NO_BTN, NO_BTN, 0,
true
},
{
NULL, NULL,
AXIS_NONE, AXIS_NONE,
@ -1296,16 +1300,6 @@ static const struct retro_keybind retro_keybinds_1[] = {
RARCH_OSK, NO_BTN, NO_BTN, 0,
true
},
#if 0
/* Deprecated */
{
NULL, NULL,
AXIS_NONE, AXIS_NONE,
MENU_ENUM_LABEL_VALUE_INPUT_META_SEND_DEBUG_INFO, RETROK_UNKNOWN,
RARCH_SEND_DEBUG_INFO, NO_BTN, NO_BTN, 0,
true
},
#endif
#else
{
NULL, NULL,
@ -1513,15 +1507,15 @@ static const struct retro_keybind retro_keybinds_1[] = {
{
NULL, NULL,
AXIS_NONE, AXIS_NONE,
MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_START, RETROK_UNKNOWN,
RARCH_LIGHTGUN_START, NO_BTN, NO_BTN, 0,
MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_START, RETROK_RETURN,
RARCH_LIGHTGUN_START, RETRO_DEVICE_ID_MOUSE_BUTTON_4, NO_BTN, 0,
true
},
{
NULL, NULL,
AXIS_NONE, AXIS_NONE,
MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_SELECT, RETROK_UNKNOWN,
RARCH_LIGHTGUN_SELECT, NO_BTN, NO_BTN, 0,
MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_SELECT, RETROK_RSHIFT,
RARCH_LIGHTGUN_SELECT, RETRO_DEVICE_ID_MOUSE_BUTTON_5, NO_BTN, 0,
true
},
{
@ -1766,6 +1760,13 @@ static const struct retro_keybind retro_keybinds_1[] = {
RARCH_SHADER_TOGGLE, NO_BTN, NO_BTN, 0,
true
},
{
NULL, NULL,
AXIS_NONE, AXIS_NONE,
MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_HOLD, RETROK_UNKNOWN,
RARCH_SHADER_HOLD, NO_BTN, NO_BTN, 0,
true
},
{
NULL, NULL,
AXIS_NONE, AXIS_NONE,
@ -1948,16 +1949,6 @@ static const struct retro_keybind retro_keybinds_1[] = {
RARCH_OSK, NO_BTN, NO_BTN, 0,
true
},
#if 0
/* Deprecated */
{
NULL, NULL,
AXIS_NONE, AXIS_NONE,
MENU_ENUM_LABEL_VALUE_INPUT_META_SEND_DEBUG_INFO, RETROK_UNKNOWN,
RARCH_SEND_DEBUG_INFO, NO_BTN, NO_BTN, 0,
true
},
#endif
#endif
};

View File

@ -374,6 +374,7 @@ const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = {
DECLARE_META_BIND(2, disk_prev, RARCH_DISK_PREV, MENU_ENUM_LABEL_VALUE_INPUT_META_DISK_PREV),
DECLARE_META_BIND(2, shader_toggle, RARCH_SHADER_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_TOGGLE),
DECLARE_META_BIND(2, shader_hold, RARCH_SHADER_HOLD, MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_HOLD),
DECLARE_META_BIND(2, shader_next, RARCH_SHADER_NEXT, MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_NEXT),
DECLARE_META_BIND(2, shader_prev, RARCH_SHADER_PREV, MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_PREV),
@ -408,10 +409,6 @@ const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = {
DECLARE_META_BIND(2, overlay_next, RARCH_OVERLAY_NEXT, MENU_ENUM_LABEL_VALUE_INPUT_META_OVERLAY_NEXT),
DECLARE_META_BIND(2, osk_toggle, RARCH_OSK, MENU_ENUM_LABEL_VALUE_INPUT_META_OSK),
#if 0
/* Deprecated */
DECLARE_META_BIND(2, send_debug_info, RARCH_SEND_DEBUG_INFO, MENU_ENUM_LABEL_VALUE_INPUT_META_SEND_DEBUG_INFO),
#endif
};
#if defined(HAVE_METAL)
@ -592,7 +589,7 @@ static const enum microphone_driver_enum MICROPHONE_DEFAULT_DRIVER = MICROPHONE_
#if defined(RS90) || defined(MIYOO)
static const enum audio_resampler_driver_enum AUDIO_DEFAULT_RESAMPLER_DRIVER = AUDIO_RESAMPLER_NEAREST;
#elif defined(PSP) || defined(EMSCRIPTEN)
#elif defined(PSP) || (defined(EMSCRIPTEN) && defined(HAVE_CC_RESAMPLER))
static const enum audio_resampler_driver_enum AUDIO_DEFAULT_RESAMPLER_DRIVER = AUDIO_RESAMPLER_CC;
#else
static const enum audio_resampler_driver_enum AUDIO_DEFAULT_RESAMPLER_DRIVER = AUDIO_RESAMPLER_SINC;
@ -1816,6 +1813,7 @@ static struct config_bool_setting *populate_settings_bool(
SETTING_BOOL("remap_save_on_exit", &settings->bools.remap_save_on_exit, true, DEFAULT_REMAP_SAVE_ON_EXIT, false);
SETTING_BOOL("show_hidden_files", &settings->bools.show_hidden_files, true, DEFAULT_SHOW_HIDDEN_FILES, false);
SETTING_BOOL("use_last_start_directory", &settings->bools.use_last_start_directory, true, DEFAULT_USE_LAST_START_DIRECTORY, false);
SETTING_BOOL("core_suggest_always", &settings->bools.core_suggest_always, true, DEFAULT_CORE_SUGGEST_ALWAYS, false);
SETTING_BOOL("camera_allow", &settings->bools.camera_allow, true, false, false);
SETTING_BOOL("location_allow", &settings->bools.location_allow, true, false, false);
SETTING_BOOL("cloud_sync_enable", &settings->bools.cloud_sync_enable, true, false, false);
@ -2025,6 +2023,7 @@ static struct config_bool_setting *populate_settings_bool(
SETTING_BOOL("content_show_settings", &settings->bools.menu_content_show_settings, true, DEFAULT_CONTENT_SHOW_SETTINGS, false);
SETTING_BOOL("content_show_favorites", &settings->bools.menu_content_show_favorites, true, DEFAULT_CONTENT_SHOW_FAVORITES, false);
SETTING_BOOL("content_show_favorites_first", &settings->bools.menu_content_show_favorites_first, true, DEFAULT_CONTENT_SHOW_FAVORITES_FIRST, false);
#ifdef HAVE_IMAGEVIEWER
SETTING_BOOL("content_show_images", &settings->bools.menu_content_show_images, true, DEFAULT_CONTENT_SHOW_IMAGES, false);
#endif
@ -2077,6 +2076,7 @@ static struct config_bool_setting *populate_settings_bool(
SETTING_BOOL("menu_navigation_wraparound_enable", &settings->bools.menu_navigation_wraparound_enable, true, true, false);
SETTING_BOOL("menu_navigation_browser_filter_supported_extensions_enable", &settings->bools.menu_navigation_browser_filter_supported_extensions_enable, true, true, false);
SETTING_BOOL("menu_show_advanced_settings", &settings->bools.menu_show_advanced_settings, true, DEFAULT_SHOW_ADVANCED_SETTINGS, false);
SETTING_BOOL("menu_thumbnail_background_enable", &settings->bools.menu_thumbnail_background_enable, true, DEFAULT_MENU_THUMBNAIL_BACKGROUND_ENABLE, false);
#ifdef HAVE_MATERIALUI
SETTING_BOOL("materialui_icons_enable", &settings->bools.menu_materialui_icons_enable, true, DEFAULT_MATERIALUI_ICONS_ENABLE, false);
SETTING_BOOL("materialui_switch_icons", &settings->bools.menu_materialui_switch_icons, true, DEFAULT_MATERIALUI_SWITCH_ICONS, false);
@ -4641,11 +4641,11 @@ bool config_load_override_file(const char *config_path)
*/
bool config_unload_override(void)
{
settings_t *settings = config_st;
bool fullscreen_prev = settings->bools.video_fullscreen;
uint32_t flags = runloop_get_flags();
settings_t *settings = config_st;
runloop_state_t *runloop_st = runloop_state_get_ptr();
bool fullscreen_prev = settings->bools.video_fullscreen;
runloop_state_get_ptr()->flags &= ~RUNLOOP_FLAG_OVERRIDES_ACTIVE;
runloop_st->flags &= ~RUNLOOP_FLAG_OVERRIDES_ACTIVE;
path_clear(RARCH_PATH_CONFIG_OVERRIDE);
/* Toggle has_save_path to false so it resets */
@ -4666,10 +4666,14 @@ bool config_unload_override(void)
&& !settings->bools.video_fullscreen)
settings->flags |= SETTINGS_FLG_SKIP_WINDOW_POSITIONS;
if (flags & RUNLOOP_FLAG_CORE_RUNNING)
if (runloop_st->flags & RUNLOOP_FLAG_CORE_RUNNING)
command_event(CMD_EVENT_REINIT, NULL);
}
/* Turbo fire settings must be reloaded from remap */
if (settings->bools.auto_remaps_enable)
config_load_remap(settings->paths.directory_input_remapping, &runloop_st->system);
RARCH_LOG("[Overrides]: Configuration overrides unloaded, original configuration restored.\n");
/* Reset save paths */
@ -5630,8 +5634,12 @@ int8_t config_save_overrides(enum override_type type,
if (conf)
{
/* Turbo fire settings are saved to remaps and therefore skipped from overrides */
for (i = 0; i < (unsigned)bool_settings_size; i++)
{
if (string_starts_with(bool_settings[i].ident, "input_turbo"))
continue;
if ((*bool_settings[i].ptr) != (*bool_overrides[i].ptr))
{
config_set_string(conf, bool_overrides[i].ident,
@ -5643,6 +5651,9 @@ int8_t config_save_overrides(enum override_type type,
}
for (i = 0; i < (unsigned)int_settings_size; i++)
{
if (string_starts_with(int_settings[i].ident, "input_turbo"))
continue;
if ((*int_settings[i].ptr) != (*int_overrides[i].ptr))
{
config_set_int(conf, int_overrides[i].ident,
@ -5653,6 +5664,9 @@ int8_t config_save_overrides(enum override_type type,
}
for (i = 0; i < (unsigned)uint_settings_size; i++)
{
if (string_starts_with(uint_settings[i].ident, "input_turbo"))
continue;
if ((*uint_settings[i].ptr) != (*uint_overrides[i].ptr))
{
config_set_int(conf, uint_overrides[i].ident,
@ -6073,30 +6087,17 @@ bool input_remapping_load_file(void *data, const char *path)
_len = strlcpy(s1, "input_remap_port_p", sizeof(s1));
strlcpy(s1 + _len, formatted_number, sizeof(s1) - _len);
CONFIG_GET_INT_BASE(conf, settings, uints.input_remap_ports[i], s1);
/* Turbo fire settings */
_len = strlcpy(s1, "input_turbo_enable", sizeof(s1));
CONFIG_GET_BOOL_BASE(conf, settings, bools.input_turbo_enable, s1);
_len = strlcpy(s1, "input_turbo_allow_dpad", sizeof(s1));
CONFIG_GET_BOOL_BASE(conf, settings, bools.input_turbo_allow_dpad, s1);
_len = strlcpy(s1, "input_turbo_mode", sizeof(s1));
CONFIG_GET_INT_BASE(conf, settings, uints.input_turbo_mode, s1);
_len = strlcpy(s1, "input_turbo_bind", sizeof(s1));
CONFIG_GET_INT_BASE(conf, settings, ints.input_turbo_bind, s1);
_len = strlcpy(s1, "input_turbo_button", sizeof(s1));
CONFIG_GET_INT_BASE(conf, settings, uints.input_turbo_button, s1);
_len = strlcpy(s1, "input_turbo_period", sizeof(s1));
CONFIG_GET_INT_BASE(conf, settings, uints.input_turbo_period, s1);
_len = strlcpy(s1, "input_turbo_duty_cycle", sizeof(s1));
CONFIG_GET_INT_BASE(conf, settings, uints.input_turbo_duty_cycle, s1);
}
/* Turbo fire settings */
CONFIG_GET_BOOL_BASE(conf, settings, bools.input_turbo_enable, "input_turbo_enable");
CONFIG_GET_BOOL_BASE(conf, settings, bools.input_turbo_allow_dpad, "input_turbo_allow_dpad");
CONFIG_GET_INT_BASE(conf, settings, uints.input_turbo_mode, "input_turbo_mode");
CONFIG_GET_INT_BASE(conf, settings, ints.input_turbo_bind, "input_turbo_bind");
CONFIG_GET_INT_BASE(conf, settings, uints.input_turbo_button, "input_turbo_button");
CONFIG_GET_INT_BASE(conf, settings, uints.input_turbo_period, "input_turbo_period");
CONFIG_GET_INT_BASE(conf, settings, uints.input_turbo_duty_cycle, "input_turbo_duty_cycle");
input_remapping_update_port_map();
/* Whenever a remap file is loaded, subsequent
@ -6284,30 +6285,17 @@ bool input_remapping_save_file(const char *path)
_len = strlcpy(s1, "input_remap_port_p", sizeof(s1));
strlcpy(s1 + _len, formatted_number, sizeof(s1) - _len);
config_set_int(conf, s1, settings->uints.input_remap_ports[i]);
/* Turbo fire settings */
_len = strlcpy(s1, "input_turbo_enable", sizeof(s1));
config_set_string(conf, s1, settings->bools.input_turbo_enable ? "true" : "false");
_len = strlcpy(s1, "input_turbo_allow_dpad", sizeof(s1));
config_set_string(conf, s1, settings->bools.input_turbo_allow_dpad ? "true" : "false");
_len = strlcpy(s1, "input_turbo_mode", sizeof(s1));
config_set_int(conf, s1, settings->uints.input_turbo_mode);
_len = strlcpy(s1, "input_turbo_bind", sizeof(s1));
config_set_int(conf, s1, settings->ints.input_turbo_bind);
_len = strlcpy(s1, "input_turbo_button", sizeof(s1));
config_set_int(conf, s1, settings->uints.input_turbo_button);
_len = strlcpy(s1, "input_turbo_period", sizeof(s1));
config_set_int(conf, s1, settings->uints.input_turbo_period);
_len = strlcpy(s1, "input_turbo_duty_cycle", sizeof(s1));
config_set_int(conf, s1, settings->uints.input_turbo_duty_cycle);
}
/* Turbo fire settings */
config_set_string(conf, "input_turbo_enable", settings->bools.input_turbo_enable ? "true" : "false");
config_set_string(conf, "input_turbo_allow_dpad", settings->bools.input_turbo_allow_dpad ? "true" : "false");
config_set_int(conf, "input_turbo_mode", settings->uints.input_turbo_mode);
config_set_int(conf, "input_turbo_bind", settings->ints.input_turbo_bind);
config_set_int(conf, "input_turbo_button", settings->uints.input_turbo_button);
config_set_int(conf, "input_turbo_period", settings->uints.input_turbo_period);
config_set_int(conf, "input_turbo_duty_cycle", settings->uints.input_turbo_duty_cycle);
ret = config_file_write(conf, path, true);
config_file_free(conf);

View File

@ -731,7 +731,6 @@ typedef struct settings
bool frame_time_counter_reset_after_save_state;
/* Menu */
bool filter_by_current_core;
bool menu_enable_widgets;
bool menu_show_load_content_animation;
bool notification_show_autoconfig;
@ -803,6 +802,7 @@ typedef struct settings
bool menu_materialui_auto_rotate_nav_bar;
bool menu_materialui_dual_thumbnail_list_view_enable;
bool menu_materialui_thumbnail_background_enable;
bool menu_thumbnail_background_enable;
bool menu_rgui_background_filler_thickness_enable;
bool menu_rgui_border_filler_thickness_enable;
bool menu_rgui_border_filler_enable;
@ -820,12 +820,12 @@ typedef struct settings
bool menu_xmb_vertical_thumbnails;
bool menu_content_show_settings;
bool menu_content_show_favorites;
bool menu_content_show_favorites_first;
bool menu_content_show_images;
bool menu_content_show_music;
bool menu_content_show_video;
bool menu_content_show_netplay;
bool menu_content_show_history;
bool menu_content_show_add;
bool menu_content_show_playlists;
bool menu_content_show_playlist_tabs;
bool menu_content_show_explore;
@ -1036,8 +1036,11 @@ typedef struct settings
bool sort_screenshots_by_content_enable;
bool config_save_on_exit;
bool remap_save_on_exit;
bool show_hidden_files;
bool filter_by_current_core;
bool use_last_start_directory;
bool core_suggest_always;
bool savefiles_in_content_dir;
bool savestates_in_content_dir;

View File

@ -1571,7 +1571,7 @@ static size_t core_info_get_file_id(const char *core_filename,
&& !string_is_equal(last_underscore, "_libretro"))
{
*last_underscore = '\0';
_len = strlen(s); /* TODO/FIXME - make this unnecessary later on */
_len = last_underscore - s;
}
return _len;
}

View File

@ -1330,7 +1330,7 @@ static bool init_media_info(void)
media.duration.hours = 0;
media.duration.minutes = 0;
media.duration.seconds = 0;
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Could not determine media duration\n");
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Could not determine media duration.\n");
}
}
@ -2161,7 +2161,7 @@ bool CORE_PREFIX(retro_load_game)(const struct retro_game_info *info)
if (!CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
{
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Cannot set pixel format.");
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Cannot set pixel format.\n");
goto error;
}
@ -2192,13 +2192,13 @@ bool CORE_PREFIX(retro_load_game)(const struct retro_game_info *info)
if (!open_codecs())
{
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Failed to find codec.");
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Failed to find codec.\n");
goto error;
}
if (!init_media_info())
{
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Failed to init media info.");
log_cb(RETRO_LOG_ERROR, "[FFMPEG] Failed to init media info.\n");
goto error;
}

View File

@ -97,7 +97,7 @@ static void process_mpv_events(mpv_event_id event_block)
{
struct mpv_event_log_message *msg =
(struct mpv_event_log_message *)mp_event->data;
log_cb(RETRO_LOG_INFO, "mpv: [%s] %s: %s",
log_cb(RETRO_LOG_INFO, "mpv: [%s] %s: %s\n",
msg->prefix, msg->level, msg->text);
}
else if(mp_event->event_id == MPV_EVENT_END_FILE)
@ -151,7 +151,7 @@ void CORE_PREFIX(retro_init)(void)
if(mpv_client_api_version() != MPV_CLIENT_API_VERSION)
{
log_cb(RETRO_LOG_WARN, "libmpv version mismatch. Please update or "
"recompile mpv-libretro after updating libmpv.");
"recompile mpv-libretro after updating libmpv.\n");
}
return;

View File

@ -1,232 +1,120 @@
# 1.20.0
- AUDIO: Fix audio handling in case of RARCH_NETPLAY_CTL_USE_CORE_PACKET_INTERFACE
- AUDIO: Include missing audio filters on some platforms
- AUDIO/PIPEWIRE: Add PipeWire audio driver
- AUDIO/PIPEWIRE: Add PipeWire microphone driver
- APPLE: Hide threaded video setting
- APPLE: Use mfi joypad driver by default
- APPLE: Include holani, noods, mrboom, yabause, bsnes-jg core in App Store builds
- CHEEVOS: Add rarity and points to achievement unlock widget
- CHEEVOS: Add rank to leaderboard submission notification
- CHEEVOS: Update to rcheevos 11.5
- CHEEVOS: Update to rcheevos 11.6
- CHEEVOS: Show rcheevos game image in Discord rich presence
- CHEEVOS: Use translated strings for achievement messages
- CLOUDSYNC: Allow saves and configs to be synced optionally
- CLOUDSYNC: Add iCloud cloud sync driver
- CLOUDSYNC: Speed up by upload/download in parallel
- CLOUDSYNC: Allow thumbnails and system dir to be synced optionally
- CLOUDSYNC: Enable CloudSync on Android (non-SSL)
- CLOUDSYNC: Add more logs in failure situations
- CLOUDSYNC: Fixes for reauthentication and parallel sync
- CLOUDSYNC: Fixes for file resurrection
- CLOUDSYNC: Enable CloudSync on Windows
- CRT/SWITCHRES: Update switchres to 2.2.1
- GENERAL: Support for mbedtls v3
- GENERAL: Automatic Frame Delay refactor
- GENERAL: Remove Frame Rest, obsoleted by Frame Delay refactor
- GENERAL: Wrap around auto increment save state indexes when amount of states is limited
- GENERAL: Enable CHD hashing for Switch and DOS
- GENERAL: Enable auto save state when new content is loaded
- GENERAL: Improve Preemptive Frames when pointing device is used
- GENERAL: Fix building with menu disabled
- HAIKU: Restore Haiku build
- INPUT: Allow to select a preferred/reserved device for each player
- INPUT: Enable Caps, Num, Scroll Lock modifiers on multiple platforms
- INPUT: Autoconfig extension with alternative name/vid/pid
- INPUT: Fix autoconfig profile saving when device is not in the default port
- INPUT: Change classic turbo mode to work independently of which key was pressed first
- INPUT: Pointer and lightgun handling sanitization on Windows and Linux desktop platforms. These input drivers will now report edge and offscreen positions in a harmonized way, and will not return 0 instead.
- INPUT/DINPUT: Fix detection of quick shift key presses
- INPUT/HID: Fix crash on macOS when disconnecting the controller a second time
- INPUT/LINUX: Add illuminance sensor support to the linuxraw, sdl2, udev, and x11 input drivers
- INPUT/Remaps: Sort and apply remaps based on the specific connected controller
- INPUT/UDEV: Enable mouse buttons 4 and 5
- INPUT/WAYLAND: Enable horizontal scroll and mouse buttons 4 and 5
- INPUT/WAYLAND: Simulate lightgun input for cores
- INPUT/WAYLAND: Support for cursor-shape-v1 and content-type-v1 protocol
- INPUT/X11: Enable mouse buttons 4 and 5
- iOS: Enable vibration by default
- iOS: Better handling of physical mice/magic keyboard trackpad
- iOS: Mouse grab fixes
- iOS: Fix mouse cursor movement when button is held down
- iOS: Fix microphone support request and entitlement
- iOS: Enable compilation back to iOS 12
- iOS: Fix OpenGL ES context usage on iOS 9
- iOS/TVOS: Add Opera to App Store build
- iOS/TVOS: Bring NEON defines in line with ARM64
- iOS/TVOS: Flush save files on backgrounding
- LIBRETRO: Support RETRO_ENVIRONMENT_GET_FILE_BROWSER_START_DIRECTORY
- LIBRETRO: Support "/" as a file extension for loading a directory as content
- FFMPEG: Fix crash when playing back a file with 96 kHz audio
- MACOS: New display server, including support for ProMotion 120Hz V-Sync
- MACOS: Create App Store build
- MACOS: Generate key up events for command keys
- MIDI: Fix long messages (SysEx) in WinMM driver
- MIDI: Fix lingering notes on close in Alsa driver
- MENU: Support local thumbnails in other image formats than png (jpg/jpeg, bmp, tga)
- MENU: Delete also savestate thumbnails when savestates are garbage collected
- MENU: Option to disable analog stick menu navigation
- MENU: Fix pause toggle to not clear fast forward state
- MENU: Fix search playlist index in XMB/Ozone
- MENU: Fix renamed entry display
- MENU: Filter unknown extensions also inside zip files
- MENU: Add icons for present / missing firmware on core info page
- MENU: Ignore other hotkeys when menu toggle is pressed
- MENU: Fix menu jumping when using L3+R3 combo
- MENU: System Information now only shows features relevant for the platform
- MENU/GLUI: Make Show Sublabels options effective
- MENU/GLUI: Icon fixes
- MENU/XMB: Allow playlist icons to be individually customized, by looking for images in Named_Logos
- MENU/OZONE: Add Selenium theme for Ozone
- MENU/OZONE: Touchscreen improvements
- MENU/OZONE: Add a touch-sensitive Resume button in the lower right corner
- NETPLAY: Add East Asian relay server
- OVERLAY: Add option to load overlay based on system name
- PS2: Fix several broken cores depending on pthread
- QT: Enable building with Qt6
- QT: Fix input panel
- RECORDING: New WAV recording driver (audio only)
- REMOTE RETROPAD: Add gyro/acceleration/light sensor test screen
- REMOTE RETROPAD: Add pointer test screen
- REPLAY: Replay format extended to support external tools
- TVOS: Support bluetooth keyboards on tvOS
- TVOS: Fixes to run correctly on TVOS13
- TVOS: Better handling of Siri remote
- TVOS: WebDAV server for adding files more easily
- TVOS: Add Settings.app option to reset retroarch.cfg
- TVOS: Bring minimum tvos version down to 13.0
- VIDEO: Show and use exact refresh rate (3 decimals) and interlace/doublestrike where available
- VIDEO: Allow setting viewport bias to offset viewport horizontally/vertically
- VIDEO: Support viewport bias also with integer overscale and custom aspect ratios
- VIDEO: Use shader path from CLI for shader cycling
- VIDEO: Pixel perfect integer scaling improvements: axis options, smart mode
- VIDEO: Add upscale 1.66x filter
- VIDEO/D3D: Fix GPU screenshots
- VIDEO/KMS: Force fullscreen when KMS is used
- VIDEO/OpenGLES: Improve version directive granularity
- VIDEO/SHADERS: Fix memory leak when shader parameter step is 0.0
- VIDEO/SHADERS: Add 2 uniforms, OriginalAspect and OriginalAspectRot.
- VIDEO/SHADERS: Add CoreFPS and FrameTimeDelta uniforms.
- VIDEO/SLANG: Support optional includes
- VIDEO/VULKAN: Fix Vulkan window freezes when swapchain becomes suboptimal
- VIDEO/VULKAN: Prefer IMMEDIATE mode without vsync
- VIDEO/X11: Support inhibit of Xss screensaver
- VITA: Enable analog L2/R2 triggers when a DS3 controller is used with PS Vita
- WAYLAND: Fix segfault when relative pointer is not supported
- WAYLAND: Use reverse DNS name for desktop file and icon
- WAYLAND: Commit viewport resizes for more responsive display when resizing window
- WINDOWS: Fix restart if path to executable contains non-ASCII symbols
- WINDOWS: Hide directories starting with $ from file browser
# 1.19.1
- Savestates: Revert savestate changes, was causing corruption and other issues
# 1.19.0
- AI: Revert AI translation to previous version (fix for translation not working with HW rendered cores)
- APPLE: Try to use system preferred language
- APPLE: Correctly register for filetypes uniquely
- APPLE/MFI: improved Switch Online controller support through MFi
- AUDIO: Bring back audio toggling on menu toggle
- CHEEVOS: Build a default RetroAchievements memory map when no RetroAchievements game is loaded
- CHEEVOS: Update to rcheevos 11.3
- CHEEVOS: fix hardcore acting as if it's enabled when it isn't
- CLANG: Fix clang error incompatible-pointer-types-discards-qualifiers
- CLOUDSYNC/LINUX: Enable Cloud Sync by default on Linux builds with network (#16456)
- CLOUDSYNC/WEBOS: Enable Cloud Sync by default on Linux builds with network (#16456)
- CORE: Set compute fps stats logging to debug level
- EMSCRIPTEN: Added M2000 to core selection dropdown
- FFMPEG: Add compatibility with FFMPEG 7.0
- GLSLANG: Remove unneeded ENABLE_HLSL code from glslang
- GENERAL: Memory leak: Dynamic allocation from msg_hash_get_help_us_enum was not freed.
- INPUT/KEYBOARD: Add support for multimedia keys - Extended RETROK_ values with 18 new items, commonly found on
"multimedia" keyboards. Mapping added for SDL, X11, Wayland, dinput, winraw keymaps.
- INPUT/MFI: Pressure sensitive left/right triggers
- INPUT/MFI: Fix Start + L1/L2/R2 combinations
- INPUT/MFI: Support strong and weak rumble
- INTL: Fetch translations from Crowdin
- INTL: Add Galician and Norwegian to list of languages
- LAKKA: Display reboot/shutdown message also when not saving config on exit
- LAKKA: Provide update URL and target name at buildtime
- LIBRETRO: Add a debug message for the SET_ROTATION callback
- macOS: Default Accessibility on if VoiceOver is on
- iOS: default audio sync on again, also more mfi logging
- iOS: Fix Import Content
- iOS: Fix ios-arm64 nightly build crash
- iOS: Import content from iCloud
- iOS: Fix #16485 crash on startup
- iOS: Display app icon in app icon picker in materialui
- iOS/tvOS: Various QoL improvements
- iOS/tvOS: Fix a couple more path name mangling bugs
- iOS/tvOS: Better way of packaging Frameworks
- iOS/tvOS: define PACKAGE_VERSION to be App Store MARKETING_VERSION
- iOS/tvOS: Fix keyboard handling for app store builds
- iOS/tvOS: Fix escaping the sandbox for jailbroken devices
- iOS/tvOS: default accessibility on if voice over is enabled
- iOS/tvOS: better way of reporting available memory
- macOS/iOS/tvOS: enable text-to-speech using AVSpeechSynthesizer.
- tvOS: Fix scaling for 720p
- MENU: New function in Quick Menu: Add to Playlist
- MENU/XMB: New theme: FlatUX, designed to merge FlatUI and Retroactive themes into a single, unified design
- NETWORKING/RETROPAD CORE: Fix socket close method
- PIXMAN: Update pixman-private.h - patch to fix build issue with musl
- PLAYLIST: Cleanup 'Add to Playlist' (#16495)
- SCANNING: Fix for scanning PSP ISOs (and probably few others)
- SAVES: Fix core config saving
- SAVES: Fix save new config name when core loaded
- SAVESTATES: Increase save state chunk size for all platforms - Even a class 6 or class 10 SD card can handle reads and writes on the order of MB/s, which means a 4KB chunk size is just wasting time in syscalls. This could maybe be fixed with a buffering reader but I don't feel comfortable tweaking libretro-common's VFS to handle that. Instead, I thought it would be good to both remove an ifdef and increase the chunk size to 128KB. For cores with small states this will should make state saving virtually instantaneous, and for cores with large states it should be a 32x speedup.
- VIDEO: Fix crash when using threaded video - for Mesa 23.2 and later
- VIDEO/GL: Fix reinitialization of the threaded gl drivers
- VIDEO/VULKAN: Add support for A2R10G10B10 HDR format
- VIDEO/VULKAN: Implement HDR readback - screenshot support
- WAYLAND: Ignore configure events during splash (fix not remembering window size)
- WAYLAND: Use frontend signal handler to quit (fix quit by window close)
- WAYLAND: Commit viewport resizes (window resize is more responsive)
- UWP: Align MESA to alpha-2-resfix - Remove wrong resolution special handling for OPENGL
- UWP: 4K fix: align MESA reading of ClientRect to retroarch procedure, this fixes max resolution being set to 1080p. As reading must be done inside an UI thread and is in fact an async operation which might delay frame generation, the reading itself is doen once and cached, give that changing resolution while the app is running is an unlikely corner-case use
- WINDOWS: Windows mouse ungrab must release the mouse instead of confine it to the current desktop (#16488)
- WINDOWS: Fix numlock/pause key release events
# 1.18.0
- AI: Fix narrator language when AI translation and menu languages are different
- DISK CONTROL: Add option to disable initial disk change
- DISK CONTROL: Visibility option for disk control notifications
- DRM: Fix mode vrefresh calculation. When using an interlaced/doublescan mode, the vertical refresh rate is mis-calculated.
- EMSCRIPTEN: Fix mouse Y parameter translation in rwebinput
- INPUT: Fix input state combos including R3 and false triggers of RETROK_UNKNOWN
- INPUT: Add a new turbo mode, "Classic (Toggle)"
- INPUT: Fix bind hold when axis does not rest at 0
- INPUT: Limit axis threshold setting to sensible values
- INPUT: Add Overlay Mouse, Lightgun, and Pointer
- INPUT/ANDROID: Fix mouse grab behavior on Android
- INPUT/LINUXRAW: Fix device name and hotplug reconnect
- IOS: Minor iOS JIT availability information
- IOS/TVOS: Pause application on applicationWillResignActive
- LIBRETRO: Add Doxygen-styled comments to parts of the libretro API
- LUA: Update Lua to version 5.3.6
- MENU: Add sublabels for input bind common entries
- MENU: Don't load history and favorites if size is 0
- MENU: Don't disable fast forward when entering menu
- MENU: Widget position, size, color, icon adjustments
- MENU: Fix savestate slots in Qt UI
- MENU: Reorder and reduce depth of User Interface menu
- MENU/OZONE: Fix sidebar wraparound, visibility after config load, crash after playlist delete
- MENU/OZONE: Fix sidebar and sublabel animations
- OSX/MACOS: Fix crash on non-Metal build
- OSX/MACOS: Add portable.txt as flag for portable install
- REMOTE RETROPAD: add display for analog axes, indication of inputs already pressed
- SAVES: Allow combining saves in content dir with save sorting
- SHADER: Added rolling scan line simulation based on the shader subframe feature. This is implemented with a scrolling scissor rect rather than in the shader itself as this is more efficient although may not work for every shader pass - we may need an option to exclude certain passes. The implementation simply divides the screen up by the number of sub frames and then moves the scissor rect down over the screen over the number of sub frames
- TVOS: Force asset re-extraction when cache is deleted
- TVOS: Add history and favorites to Top Shelf
- TVOS: Fix crash when history item does not have a label
- UWP: Enable HAVE_ACCESSIBILITY for UWP builds
- UWP: Allow UWP build to work with a modified version of Mesa Gallium D3D12
- VIDEO: Add subframe shader support for Vulkan/GLcore/DX10-11-12, enabling shaders to run at higher framerate than the content
- VIDEO: Fix restoring fullscreen/windowed setting when unloading override
- VIDEO/VULKAN: Fix HDR with Vulkan after reinit
- VIDEO/VULKAN: Remove the use of oldSwapchain
- VIDEO/GL2: Fix OpenGL ES version detection
- WEBDAV: Fixed SEGFAULT in WebDav task sync + type changes
- WEBOS: Fix build, add core location on webosbrew.org
- WIN32: Fix Alt+Enter not working when menubar is disabled
# 1.21.0
- 3DS: Fix unique IDs for newer cores
- 3DS: Enable TLS (SSL)
- 3DS: Fix UI freeze when threaded rendering is enabled
- 3DS: Fix crash on load content
- 3DS: Other minor fixes
- APPLE: Enable Vulkan emulated mailbox
- APPLE: Include b2 core in App Store builds
- APPLE: CoreMIDI driver for IOS/MacOS
- APPLE: CoreLocation driver for IOS/MacOS
- AUTOCONF: Enable alternative display name in autoconfig files
- AUTOCONF: Make autoconfig failure messages optional
- AUDIO: Option to mute on rewind
- AUDIO/PIPEWIRE: Fix app launch when pipewire service is stopped
- AUDIO/PIPEWIRE: Fix speedup with threaded video mode
- AUDIO/PIPEWIRE: Fix latency setting and microphone handling
- AUDIO/PIPEWIRE: Pass the new rate to the audio driver
- CAMERA: Add PipeWire camera driver
- CAMERA: Add ffmpeg camera driver
- CHEAT: Rewrite part of cheat_manager_load_cb_second_pass()
- CHEEVOS: Include achievement state in netplay states
- CHEEVOS: Fix crash when entering achievements in quick menu while client is not present
- CHEEVOS: Restore cheevos_badges_enable for HAVE_GFX_WIDGETS builds
- CLI: Allow --entryslot to fall back to normal states
- CLOUDSYNC: Fix Windows path issues
- CLOUDSYNC: Workaround for duplicated requests bug
- CLOUDSYNC: Workaround for 301 redirects
- CLOUDSYNC: Handle ignored directories properly
- EMSCRIPTEN: Added new AudioWorklet driver, a fast callback-based audio driver
- EMSCRIPTEN: Scale window to correct size
- EMSCRIPTEN: Additional platform functions
- EMSCRIPTEN: Add new default video context driver: emscriptenwebgl_ctx
- EMSCRIPTEN: Add new audio driver: AudioWorklet
- EMSCRIPTEN: Add new modernized web player which will eventually replace the existing one
- EMSCRIPTEN/RWEBINPUT: Add touch input support
- GAMECUBE: Fixes
- GENERAL: Fix save state auto increment
- GENERAL: Fix softpatching with periods/dots in the file name
- GENERAL: Fix compilation with --enable-videocore
- GENERAL: Allow asset directory redefinition and other directory overrides via environment variables
- GENERAL: Allow override of player 1/2 input with machine learning models (needs recompilation and external library)
- GENERAL: Fix performance counter option not remembered between sessions
- GENERAL: Create security statement
- GENERAL: Fix crash when core is not selected
- GENERAL: Use core fps instead of screen refresh for calculating dropped frames
- INPUT: Fix a crash when initializing illuminance sensor on Linux
- INPUT: Analog-to-digital refactor, fixing behavior when analogs are assigned to keys
- INPUT: Turbo fire overhaul. See https://github.com/libretro/RetroArch/pull/17633
- INPUT/ANDROID: Fix game focus and pause handling
- INPUT/COCOA: Include gravity in acceleration sensor values
- INPUT/COCOA: Fix relative mouse input
- INPUT/COCOA: Allow mouse input while mouse overlay is active
- INPUT/WINRAW: Invert mouse index order
- IOS: Ensure webserver notice can be dismissed
- IOS: Fix rescanning manual playlists after app update
- IOS: Fix clean playlist function
- IOS: Fix crash when scanning
- IOS: Fix jump back to selected item when closing content
- IOS: Fix shared GL context setup
- IOS: Update Launch Screen
- IOS: Screen orientation lock through display server
- IOS: Fix rescanning manual playlists after app update
- LAKKA: Remove bluetooth device after disconnection
- LINUX/X11: Extend X11 input driver with XInput2 extensions for multi-mouse
- MACOS: Fix some sandbox handling in App Store builds
- MACOS: Reset keyboard state when focus is lost
- MENU: Add SSL support to the information list
- MENU: Add warning to BFI and related menu items
- MENU: Fix latency statistics when using runahead
- MENU: Fix opening file inside archive with core selection
- MENU: Main menu unified between different menu drivers
- MENU: Visibility toggle for playlist tabs
- MENU: Color the notification icon by message category
- MENU: Gray Dark+Light theme adjustments
- MENU/GLUI: Menu back button switches tabs like in other menu drivers
- MENU/GLUI: Tab selection option is honored
- MENU/GLUI: Fix CD icon appearing when no icon is specified
- MENU/GLUI: Allow fullscreen thumbnail browsing
- MENU/GLUI: Save state thumbnails
- MENU/PLAYLISTS: Random selection/shuffle function
- MENU/QT: Fix desktop menu crash with Cheevos disabled
- MENU/RGUI: Cleanups of certain menu items
- MENU/RGUI: Thumbnail fixes
- MENU/OZONE: Fix messagebox background
- MENU/XMB: Fix Light theme, font shadow
- MENU/XMB: Appearance menu cleanup
- MENU/XMB: Icon thumbnail can be any of the existing types
- MISC: Guard nanosleep prototype behind _POSIX_TIMERS
- MISC: Use fabsf and intended threshold for refresh rate check
- MISC: Use platform-specific checks for invalid descriptors
- MIDI: Add dropdown items for midi device selection
- NETWORK: Refactor of net_http, improvements for task blocking and performance
- NETWORK: Follow http redirects in net_http
- NETWORK: Expire failed DNS lookups much faster
- NETWORK: Fix netplay when using netpacket interface with recent cheevos
- NETWORK/HTTP: Fix crash in net_http_resolve() in single-thread mode
- OVERLAY: Fix overlay lightgun, mouse & pointer
- OVERLAY: Preferred overlay loading is now default only on mobile platforms
- OVERLAY: Improve analog recentering when touching the area just outside the recentering zone
- QT: Enable non-png thumbnails also for Qt interface
- REPLAY: Fix issue when replaying old format input recordings in newer RetroArch
- TTS: Fix initial text-to-speech on Windows
- TVOS: Fix 720p display
- TVOS: Fix refresh rate fetching on tvOS 13/14
- TVOS: Update Top Shelf art
- 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
- VIDEO/SHADERS: FIX shader wildcards
- VIDEO/VULKAN: Enable adaptive vsync
- VIDEO/V4L2: Added resolution picker/forcing.
- VIDEO/V4L2: Rewrote logic for finding ALSA audio devices in enumerate_audio_devices function
- VIDEO/V4L2: Added a skip for some of the interface queries that fail and aren't required for magewell usb.
- VITA: Fixes
- WINDOWS: Win32 socket improvements
- WII: Fixes
- WIIU: Fixes
- WEBPLAYER: Update core list for 1.20.0

View File

@ -58,10 +58,10 @@ int mbedtls_padlock_has_support( int feature )
"cpuid \n\t"
"cmpl $0xC0000001, %%eax \n\t"
"movl $0, %%edx \n\t"
"jb unsupported \n\t"
"jb 1f \n\t"
"movl $0xC0000001, %%eax \n\t"
"cpuid \n\t"
"unsupported: \n\t"
"1: \n\t"
"movl %%edx, %1 \n\t"
"movl %2, %%ebx \n\t"
: "=m" (ebx), "=m" (edx)

View File

@ -0,0 +1,4 @@
xdg_toplevel_icon protocol
Maintainers:
Matthias Klumpp <matthias@tenstral.net> (@mak)

View File

@ -0,0 +1,205 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="xdg_toplevel_icon_v1">
<copyright>
Copyright © 2023-2024 Matthias Klumpp
Copyright © 2024 David Edmundson
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="protocol to assign icons to toplevels">
This protocol allows clients to set icons for their toplevel surfaces
either via the XDG icon stock (using an icon name), or from pixel data.
A toplevel icon represents the individual toplevel (unlike the application
or launcher icon, which represents the application as a whole), and may be
shown in window switchers, window overviews and taskbars that list
individual windows.
This document adheres to RFC 2119 when using words like "must",
"should", "may", etc.
Warning! The protocol described in this file is currently in the testing
phase. Backward compatible changes may be added together with the
corresponding interface version bump. Backward incompatible changes can
only be done by creating a new major version of the extension.
</description>
<interface name="xdg_toplevel_icon_manager_v1" version="1">
<description summary="interface to manage toplevel icons">
This interface allows clients to create toplevel window icons and set
them on toplevel windows to be displayed to the user.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the toplevel icon manager">
Destroy the toplevel icon manager.
This does not destroy objects created with the manager.
</description>
</request>
<request name="create_icon">
<description summary="create a new icon instance">
Creates a new icon object. This icon can then be attached to a
xdg_toplevel via the 'set_icon' request.
</description>
<arg name="id" type="new_id" interface="xdg_toplevel_icon_v1"/>
</request>
<request name="set_icon">
<description summary="set an icon on a toplevel window">
This request assigns the icon 'icon' to 'toplevel', or clears the
toplevel icon if 'icon' was null.
This state is double-buffered and is applied on the next
wl_surface.commit of the toplevel.
After making this call, the xdg_toplevel_icon_v1 provided as 'icon'
can be destroyed by the client without 'toplevel' losing its icon.
The xdg_toplevel_icon_v1 is immutable from this point, and any
future attempts to change it must raise the
'xdg_toplevel_icon_v1.immutable' protocol error.
The compositor must set the toplevel icon from either the pixel data
the icon provides, or by loading a stock icon using the icon name.
See the description of 'xdg_toplevel_icon_v1' for details.
If 'icon' is set to null, the icon of the respective toplevel is reset
to its default icon (usually the icon of the application, derived from
its desktop-entry file, or a placeholder icon).
If this request is passed an icon with no pixel buffers or icon name
assigned, the icon must be reset just like if 'icon' was null.
</description>
<arg name="toplevel" type="object" interface="xdg_toplevel" summary="the toplevel to act on"/>
<arg name="icon" type="object" interface="xdg_toplevel_icon_v1" allow-null="true"/>
</request>
<event name="icon_size">
<description summary="describes a supported &amp; preferred icon size">
This event indicates an icon size the compositor prefers to be
available if the client has scalable icons and can render to any size.
When the 'xdg_toplevel_icon_manager_v1' object is created, the
compositor may send one or more 'icon_size' events to describe the list
of preferred icon sizes. If the compositor has no size preference, it
may not send any 'icon_size' event, and it is up to the client to
decide a suitable icon size.
A sequence of 'icon_size' events must be finished with a 'done' event.
If the compositor has no size preferences, it must still send the
'done' event, without any preceding 'icon_size' events.
</description>
<arg name="size" type="int"
summary="the edge size of the square icon in surface-local coordinates, e.g. 64"/>
</event>
<event name="done">
<description summary="all information has been sent">
This event is sent after all 'icon_size' events have been sent.
</description>
</event>
</interface>
<interface name="xdg_toplevel_icon_v1" version="1">
<description summary="a toplevel window icon">
This interface defines a toplevel icon.
An icon can have a name, and multiple buffers.
In order to be applied, the icon must have either a name, or at least
one buffer assigned. Applying an empty icon (with no buffer or name) to
a toplevel should reset its icon to the default icon.
It is up to compositor policy whether to prefer using a buffer or loading
an icon via its name. See 'set_name' and 'add_buffer' for details.
</description>
<enum name="error">
<entry name="invalid_buffer"
summary="the provided buffer does not satisfy requirements"
value="1"/>
<entry name="immutable"
summary="the icon has already been assigned to a toplevel and must not be changed"
value="2"/>
<entry name="no_buffer"
summary="the provided buffer has been destroyed before the toplevel icon"
value="3"/>
</enum>
<request name="destroy" type="destructor">
<description summary="destroy the icon object">
Destroys the 'xdg_toplevel_icon_v1' object.
The icon must still remain set on every toplevel it was assigned to,
until the toplevel icon is reset explicitly.
</description>
</request>
<request name="set_name">
<description summary="set an icon name">
This request assigns an icon name to this icon.
Any previously set name is overridden.
The compositor must resolve 'icon_name' according to the lookup rules
described in the XDG icon theme specification[1] using the
environment's current icon theme.
If the compositor does not support icon names or cannot resolve
'icon_name' according to the XDG icon theme specification it must
fall back to using pixel buffer data instead.
If this request is made after the icon has been assigned to a toplevel
via 'set_icon', a 'immutable' error must be raised.
[1]: https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
</description>
<arg name="icon_name" type="string"/>
</request>
<request name="add_buffer">
<description summary="add icon data from a pixel buffer">
This request adds pixel data supplied as wl_buffer to the icon.
The client should add pixel data for all icon sizes and scales that
it can provide, or which are explicitly requested by the compositor
via 'icon_size' events on xdg_toplevel_icon_manager_v1.
The wl_buffer supplying pixel data as 'buffer' must be backed by wl_shm
and must be a square (width and height being equal).
If any of these buffer requirements are not fulfilled, a 'invalid_buffer'
error must be raised.
If this icon instance already has a buffer of the same size and scale
from a previous 'add_buffer' request, data from the last request
overrides the preexisting pixel data.
The wl_buffer must be kept alive for as long as the xdg_toplevel_icon
it is associated with is not destroyed, otherwise a 'no_buffer' error
is raised. The buffer contents must not be modified after it was
assigned to the icon. As a result, the region of the wl_shm_pool's
backing storage used for the wl_buffer must not be modified after this
request is sent. The wl_buffer.release event is unused.
If this request is made after the icon has been assigned to a toplevel
via 'set_icon', a 'immutable' error must be raised.
</description>
<arg name="buffer" type="object" interface="wl_buffer"/>
<arg name="scale" type="int"
summary="the scaling factor of the icon, e.g. 1"/>
</request>
</interface>
</protocol>

View File

@ -245,7 +245,7 @@ bool disk_index_file_init(
disk_index_file->file_path, disk_index_file_dir,
content_name, sizeof(disk_index_file->file_path));
strlcpy(disk_index_file->file_path + _len,
".ldci",
FILE_PATH_DISK_CONTROL_INDEX_EXTENSION,
sizeof(disk_index_file->file_path) - _len);
/* All is well - reset disk_index_file_t and

View File

@ -2,23 +2,14 @@
var LibraryPlatformEmscripten = {
$RPE: {
powerStateChange: function(e) {
_platform_emscripten_update_power_state(true, Number.isFinite(e.target.dischargingTime) ? e.target.dischargingTime : 0x7FFFFFFF, e.target.level, e.target.charging);
},
updateMemoryUsage: function() {
// unfortunately this will be innacurate in threaded (worker) builds
var used = BigInt(performance.memory.usedJSHeapSize || 0);
var limit = BigInt(performance.memory.jsHeapSizeLimit || 0);
// emscripten currently only supports passing 32 bit ints, so pack it
_platform_emscripten_update_memory_usage(Number(used & 0xFFFFFFFFn), Number(used >> 32n), Number(limit & 0xFFFFFFFFn), Number(limit >> 32n));
setTimeout(RPE.updateMemoryUsage, 5000);
},
canvasWidth: 0,
canvasHeight: 0,
sentinelPromise: null,
command_queue: [],
command_reply_queue: []
},
PlatformEmscriptenWatchCanvasSizeAndDpr__deps: ["platform_emscripten_update_canvas_dimensions"],
PlatformEmscriptenWatchCanvasSizeAndDpr__deps: ["platform_emscripten_update_canvas_dimensions_cb"],
PlatformEmscriptenWatchCanvasSizeAndDpr: function(dpr) {
if (RPE.observer) {
RPE.observer.unobserve(Module.canvas);
@ -36,9 +27,11 @@ var LibraryPlatformEmscripten = {
width = Math.round(entry.contentRect.width * window.devicePixelRatio);
height = Math.round(entry.contentRect.height * window.devicePixelRatio);
}
RPE.canvasWidth = width;
RPE.canvasHeight = height;
// doubles are too big to pass as an argument to exported functions
{{{ makeSetValue("dpr", "0", "window.devicePixelRatio", "double") }}};
_platform_emscripten_update_canvas_dimensions(width, height, dpr);
_platform_emscripten_update_canvas_dimensions_cb(width, height, dpr);
});
RPE.observer.observe(Module.canvas);
window.addEventListener("resize", function() {
@ -47,27 +40,112 @@ var LibraryPlatformEmscripten = {
}, false);
},
PlatformEmscriptenWatchWindowVisibility__deps: ["platform_emscripten_update_window_hidden"],
PlatformEmscriptenWatchWindowVisibility__deps: ["platform_emscripten_update_window_hidden_cb"],
PlatformEmscriptenWatchWindowVisibility: function() {
document.addEventListener("visibilitychange", function() {
_platform_emscripten_update_window_hidden(document.visibilityState == "hidden");
_platform_emscripten_update_window_hidden_cb(document.visibilityState == "hidden");
}, false);
},
PlatformEmscriptenPowerStateInit__deps: ["platform_emscripten_update_power_state"],
$PlatformEmscriptenPowerStateChange__deps: ["platform_emscripten_update_power_state_cb"],
$PlatformEmscriptenPowerStateChange: function(e) {
_platform_emscripten_update_power_state_cb(true, Number.isFinite(e.target.dischargingTime) ? e.target.dischargingTime : 0x7FFFFFFF, e.target.level, e.target.charging);
},
PlatformEmscriptenPowerStateInit__deps: ["$PlatformEmscriptenPowerStateChange"],
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});
battery.addEventListener("chargingchange", PlatformEmscriptenPowerStateChange);
battery.addEventListener("levelchange", PlatformEmscriptenPowerStateChange);
PlatformEmscriptenPowerStateChange({target: battery});
});
},
PlatformEmscriptenMemoryUsageInit__deps: ["platform_emscripten_update_memory_usage"],
$PlatformEmscriptenUpdateMemoryUsage__deps: ["platform_emscripten_update_memory_usage_cb"],
$PlatformEmscriptenUpdateMemoryUsage: function() {
// unfortunately this will be inaccurate in threaded (worker) builds
_platform_emscripten_update_memory_usage_cb(BigInt(performance.memory.usedJSHeapSize || 0), BigInt(performance.memory.jsHeapSizeLimit || 0));
setTimeout(PlatformEmscriptenUpdateMemoryUsage, 5000);
},
PlatformEmscriptenMemoryUsageInit__deps: ["$PlatformEmscriptenUpdateMemoryUsage"],
PlatformEmscriptenMemoryUsageInit: function() {
if (!performance.memory) return;
RPE.updateMemoryUsage();
PlatformEmscriptenUpdateMemoryUsage();
},
PlatformEmscriptenWatchFullscreen__deps: ["platform_emscripten_update_fullscreen_state_cb"],
PlatformEmscriptenWatchFullscreen: function() {
document.addEventListener("fullscreenchange", function() {
_platform_emscripten_update_fullscreen_state_cb(!!document.fullscreenElement);
}, false);
},
PlatformEmscriptenGLContextEventInit__deps: ["platform_emscripten_gl_context_lost_cb", "platform_emscripten_gl_context_restored_cb"],
PlatformEmscriptenGLContextEventInit: function() {
Module.canvas.addEventListener("webglcontextlost", function(e) {
e.preventDefault();
_platform_emscripten_gl_context_lost_cb();
});
Module.canvas.addEventListener("webglcontextrestored", function() {
_platform_emscripten_gl_context_restored_cb();
});
},
$PlatformEmscriptenDoSetCanvasSize: async function(width, height) {
// window.resizeTo is terrible; it uses window.outerWidth/outerHeight dimensions,
// which are on their own scale entirely, which also changes with the zoom level independently from the DPR.
// instead try 2 hardcoded sizes first and interpolate to find the correct size.
var expAX = 600;
var expAY = 450;
var expBX = expAX + 100;
var expBY = expAY + 100;
await new Promise(r => setTimeout(r, 0));
window.resizeTo(expAX, expAY);
await new Promise(r => setTimeout(r, 50));
var oldWidth = RPE.canvasWidth;
var oldHeight = RPE.canvasHeight;
window.resizeTo(expBX, expBY);
await new Promise(r => setTimeout(r, 50));
var projX = (expBX - expAX) * (width - oldWidth) / (RPE.canvasWidth - oldWidth) + expAX;
var projY = (expBY - expAY) * (height - oldHeight) / (RPE.canvasHeight - oldHeight) + expAY;
window.resizeTo(Math.round(projX), Math.round(projY));
},
PlatformEmscriptenSetCanvasSize__proxy: "sync",
PlatformEmscriptenSetCanvasSize__deps: ["$PlatformEmscriptenDoSetCanvasSize"],
PlatformEmscriptenSetCanvasSize: function(width, height) {
// c-accessible library functions should not be async
PlatformEmscriptenDoSetCanvasSize(width, height);
},
$PlatformEmscriptenDoSetWakeLock: async function(state) {
if (state && !RPE.sentinelPromise && navigator?.wakeLock?.request) {
try {
RPE.sentinelPromise = navigator.wakeLock.request("screen");
} catch (e) {}
} else if (!state && RPE.sentinelPromise) {
try {
var sentinel = await RPE.sentinelPromise;
sentinel.release();
} catch (e) {}
RPE.sentinelPromise = null;
}
},
PlatformEmscriptenSetWakeLock__proxy: "sync",
PlatformEmscriptenSetWakeLock__deps: ["$PlatformEmscriptenDoSetWakeLock"],
PlatformEmscriptenSetWakeLock: function(state) {
PlatformEmscriptenDoSetWakeLock(state);
},
PlatformEmscriptenGetSystemInfo: function() {
var userAgent = navigator?.userAgent?.toLowerCase?.();
if (!userAgent) return 0;
var browser = 1 + ["chrom", "firefox", "safari"].findIndex(i => userAgent.includes(i));
var os = 1 + [/windows/, /linux|cros|android/, /iphone|ipad/, /mac os/].findIndex(i => i.test(userAgent));
return browser | (os << 16);
},
$EmscriptenSendCommand__deps: ["platform_emscripten_command_raise_flag"],
@ -82,4 +160,4 @@ var LibraryPlatformEmscripten = {
};
autoAddDeps(LibraryPlatformEmscripten, '$RPE');
mergeInto(LibraryManager.library, LibraryPlatformEmscripten);
addToLibrary(LibraryPlatformEmscripten);

View File

@ -1,140 +1,81 @@
//"use strict";
var LibraryRWebAudio = {
$RA__deps: ['$Browser'],
$RA: {
BUFFER_SIZE: 2048,
$RWA: {
/* add 10 ms of silence on start, seems to prevent underrun on start/unpausing (terrible crackling in firefox) */
MIN_START_OFFSET_SEC: 0.01,
/* firefox and safari need more latency (transparent to audio driver) */
EXTRA_LATENCY_SEC_NONCHROME: 0.01,
EXTRA_LATENCY_SEC_CHROME: 0,
PLATFORM_EMSCRIPTEN_BROWSER_CHROMIUM: 1,
context: null,
buffers: [],
numBuffers: 0,
bufIndex: 0,
bufOffset: 0,
startTime: 0,
contextRunning: false,
nonblock: false,
currentTimeWorkaround: false,
setStartTime: function() {
if (RA.context.currentTime) {
RA.startTime = window['performance']['now']() - RA.context.currentTime * 1000;
Module["resumeMainLoop"]();
} else window['setTimeout'](RA.setStartTime, 0);
},
getCurrentPerfTime: function() {
if (RA.startTime) return (window['performance']['now']() - RA.startTime) / 1000;
else return 0;
},
process: function(queueBuffers) {
var currentTime = RA.getCurrentPerfTime();
for (var i = 0; i < RA.bufIndex; i++) {
if (RA.buffers[i].endTime !== 0 && RA.buffers[i].endTime < currentTime) {
RA.buffers[i].endTime = 0;
var buf = RA.buffers.splice(i, 1);
RA.buffers[RA.numBuffers - 1] = buf[0];
i--;
RA.bufIndex--;
} else if (!RA.startTime) {
RA.setStartTime();
}
}
},
fillBuffer: function(buf, samples) {
var count = 0;
const leftBuffer = RA.buffers[RA.bufIndex].getChannelData(0);
const rightBuffer = RA.buffers[RA.bufIndex].getChannelData(1);
while (samples && RA.bufOffset !== RA.BUFFER_SIZE) {
leftBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8', 'float') }}};
rightBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8 + 4', 'float') }}};
RA.bufOffset++;
count++;
samples--;
}
return count;
},
queueAudio: function() {
var index = RA.bufIndex;
var startTime;
if (RA.bufIndex) startTime = RA.buffers[RA.bufIndex - 1].endTime;
else startTime = RA.context.currentTime;
RA.buffers[index].endTime = startTime + RA.buffers[index].duration;
const bufferSource = RA.context.createBufferSource();
bufferSource.buffer = RA.buffers[index];
bufferSource.connect(RA.context.destination);
bufferSource.start(startTime);
RA.bufIndex++;
RA.bufOffset = 0;
},
block: function() {
do {
RA.process();
} while (RA.bufIndex === RA.numBuffers);
}
endTime: 0,
latency: 0,
virtualBufferFrames: 0,
currentTimeDiff: 0,
extraLatencySec: 0
},
RWebAudioInit: function(latency) {
var ac = window['AudioContext'] || window['webkitAudioContext'];
/* AudioContext.currentTime can be inaccurate: https://bugzilla.mozilla.org/show_bug.cgi?id=901247 */
$RWebAudioGetCurrentTime: function() {
return performance.now() / 1000 - RWA.currentTimeDiff;
},
$RWebAudioStateChangeCB: function() {
RWA.contextRunning = RWA.context.state == "running";
},
RWebAudioResumeCtx: function() {
if (!RWA.contextRunning) RWA.context.resume();
return RWA.contextRunning;
},
RWebAudioInit__deps: ["$RWebAudioStateChangeCB", "platform_emscripten_get_browser"],
RWebAudioInit: function(latency) {
var ac = window.AudioContext || window.webkitAudioContext;
if (!ac) return 0;
RA.context = new ac();
RWA.context = new ac();
RWA.currentTimeDiff = performance.now() / 1000 - RWA.context.currentTime;
RWA.nonblock = false;
RWA.endTime = 0;
RWA.latency = latency;
RWA.virtualBufferFrames = Math.round(RWA.latency * RWA.context.sampleRate / 1000);
RWA.context.addEventListener("statechange", RWebAudioStateChangeCB);
RWebAudioStateChangeCB();
RWA.extraLatencySec = (_platform_emscripten_get_browser() == RWA.PLATFORM_EMSCRIPTEN_BROWSER_CHROMIUM) ? RWA.EXTRA_LATENCY_SEC_CHROME : RWA.EXTRA_LATENCY_SEC_NONCHROME;
RA.numBuffers = ((latency * RA.context.sampleRate) / (1000 * RA.BUFFER_SIZE))|0;
if (RA.numBuffers < 2) RA.numBuffers = 2;
for (var i = 0; i < RA.numBuffers; i++) {
RA.buffers[i] = RA.context.createBuffer(2, RA.BUFFER_SIZE, RA.context.sampleRate);
RA.buffers[i].endTime = 0
}
RA.nonblock = false;
RA.startTime = 0;
// chrome hack to get currentTime running
RA.context.createGain();
window['setTimeout'](RA.setStartTime, 0);
Module["pauseMainLoop"]();
return 1;
},
RWebAudioSampleRate: function() {
return RA.context.sampleRate;
return RWA.context.sampleRate;
},
RWebAudioWrite: function (buf, size) {
RA.process();
var samples = size / 8;
var count = 0;
RWebAudioQueueBuffer__deps: ["$RWebAudioGetCurrentTime", "RWebAudioResumeCtx", "RWebAudioWriteAvailFrames"],
RWebAudioQueueBuffer: function(num_frames, left, right) {
if (RWA.nonblock && _RWebAudioWriteAvailFrames() < num_frames) return 0;
if (!_RWebAudioResumeCtx()) return 0;
while (samples) {
if (RA.bufIndex === RA.numBuffers) {
if (RA.nonblock) break;
else RA.block();
}
var buffer = RWA.context.createBuffer(2, num_frames, RWA.context.sampleRate);
buffer.getChannelData(0).set(HEAPF32.subarray(left >> 2, (left >> 2) + num_frames));
buffer.getChannelData(1).set(HEAPF32.subarray(right >> 2, (right >> 2) + num_frames));
var bufferSource = RWA.context.createBufferSource();
bufferSource.buffer = buffer;
bufferSource.connect(RWA.context.destination);
var fill = RA.fillBuffer(buf, samples);
samples -= fill;
count += fill;
buf += fill * 8;
var currentTime = RWebAudioGetCurrentTime();
/* when empty, start rounded up to nearest 1 ms, add MIN_START_OFFSET_SEC */
var startTime = RWA.endTime > currentTime ? RWA.endTime : Math.ceil(currentTime * 1000) / 1000 + RWA.MIN_START_OFFSET_SEC;
RWA.endTime = startTime + buffer.duration;
bufferSource.start(startTime + RWA.extraLatencySec);
if (RA.bufOffset === RA.BUFFER_SIZE) {
RA.queueAudio();
}
}
return count * 8;
return num_frames;
},
RWebAudioStop: function() {
RA.bufIndex = 0;
RA.bufOffset = 0;
return true;
},
@ -143,29 +84,32 @@ var LibraryRWebAudio = {
},
RWebAudioSetNonblockState: function(state) {
RA.nonblock = state;
RWA.nonblock = state;
},
RWebAudioFree__deps: ["$RWebAudioStateChangeCB"],
RWebAudioFree: function() {
RA.bufIndex = 0;
RA.bufOffset = 0;
RWA.context.removeEventListener("statechange", RWebAudioStateChangeCB);
RWA.context.close();
RWA.contextRunning = false;
},
RWebAudioBufferSize: function() {
return RA.numBuffers * RA.BUFFER_SIZE * 8;
RWebAudioBufferSizeFrames: function() {
return RWA.virtualBufferFrames;
},
RWebAudioWriteAvail: function() {
RA.process();
return ((RA.numBuffers - RA.bufIndex) * RA.BUFFER_SIZE - RA.bufOffset) * 8;
RWebAudioWriteAvailFrames__deps: ["$RWebAudioGetCurrentTime"],
RWebAudioWriteAvailFrames: function() {
var avail = Math.round(((RWA.latency / 1000) - RWA.endTime + RWebAudioGetCurrentTime()) * RWA.context.sampleRate);
if (avail <= 0) return 0;
if (avail >= RWA.virtualBufferFrames) return RWA.virtualBufferFrames;
return avail;
},
RWebAudioRecalibrateTime: function() {
if (RA.startTime) {
RA.startTime = window['performance']['now']() - RA.context.currentTime * 1000;
}
if (RWA.contextRunning) RWA.currentTimeDiff = performance.now() / 1000 - RWA.context.currentTime;
}
};
autoAddDeps(LibraryRWebAudio, '$RA');
mergeInto(LibraryManager.library, LibraryRWebAudio);
autoAddDeps(LibraryRWebAudio, '$RWA');
addToLibrary(LibraryRWebAudio);

View File

@ -5,133 +5,275 @@ var LibraryRWebCam = {
RETRO_CAMERA_BUFFER_OPENGL_TEXTURE: 0,
RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER: 1,
tmp: null,
contexts: [],
counter: 0,
ready: function(data) {
return RWC.contexts[data].runMode == 2 && !RWC.contexts[data].videoElement.paused && RWC.contexts[data].videoElement.videoWidth != 0 && RWC.contexts[data].videoElement.videoHeight != 0;
}
counter: 0
},
RWebCamInit__deps: ['malloc'],
RWebCamInit: function(caps1, caps2, width, height) {
if (!navigator) return 0;
if (!navigator.mediaDevices.getUserMedia) return 0;
$RWebCamReady: function(c) {
try {
/* try to start video it was paused */
if (RWC.contexts[c].videoElement.paused) RWC.contexts[c].videoElement.play();
} catch (e) {}
return RWC.contexts[c].cameraRunning && !RWC.contexts[c].videoElement.paused && RWC.contexts[c].videoElement.videoWidth != 0 && RWC.contexts[c].videoElement.videoHeight != 0;
},
$RWebCamInitBrowser__proxy: "sync",
$RWebCamInitBrowser: function(width, height, glTex, rawFb, debug, proxied) {
if (!navigator?.mediaDevices?.getUserMedia) return 0;
var c = ++RWC.counter;
RWC.contexts[c] = {width: width, height: height, glTex: glTex, rawFb: rawFb, debug: debug, proxied: proxied, idealWidth: width, idealHeight: height};
return c;
},
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;
RWebCamInit__deps: ["malloc", "$RWebCamInitBrowser"],
RWebCamInit: function(caps, width, height, debug) {
var glTex = Number(caps) & (1 << RWC.RETRO_CAMERA_BUFFER_OPENGL_TEXTURE);
var rawFb = Number(caps) & (1 << RWC.RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER);
var proxied = typeof ENVIRONMENT_IS_PTHREAD != "undefined" && ENVIRONMENT_IS_PTHREAD;
var c = RWebCamInitBrowser(width, height, glTex, rawFb, debug, proxied);
if (!c) return 0;
if (debug) console.log("RWebCamInit", c);
if (proxied) {
RWC.contexts[c] = {};
RWC.contexts[c].debug = debug;
RWC.contexts[c].glTex = glTex;
RWC.contexts[c].rawFb = rawFb;
RWC.contexts[c].proxied = true;
}
RWC.contexts[c].runMode = 1;
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.mediaDevices.getUserMedia({video: true, audio: false}).then(function(stream) {
RWC.contexts[c].videoElement.autoplay = true;
RWC.contexts[c].videoElement.srcObject = stream;
RWC.contexts[c].runMode = 2;
}).catch(function (err) {
console.log("webcam request failed", err);
RWC.runMode = 0;
});
// for getting/storing texture id in GL mode
/* for getting/storing texture id in GL mode */
if (!RWC.tmp) RWC.tmp = _malloc(4);
return c;
},
RWebCamFree: function(data) {
RWC.contexts[data].videoElement.pause();
RWC.contexts[data].videoElement = null;
RWC.contexts[data] = null;
$RWebCamFreeBrowser__proxy: "sync",
$RWebCamFreeBrowser: function(c) {
RWC.contexts[c] = null;
},
RWebCamStart__deps: ['glGenTextures', 'glBindTexture', 'glGetIntegerv', 'glTexParameteri', 'malloc'],
RWebCamStart: function(data) {
var ret = 0;
if (RWC.contexts[data].glTex) {
RWebCamFree__deps: ["$RWebCamFreeBrowser", "RWebCamStop"],
RWebCamFree: function(c) {
if (RWC.contexts[c].debug) console.log("RWebCamFree", c);
if (RWC.contexts[c].running) _RWebCamStop(c); /* need more checks in RA */
if (RWC.contexts[c].proxied) RWC.contexts[c] = null;
RWebCamFreeBrowser(c);
},
$RWebCamStartBrowser__proxy: "sync",
$RWebCamStartBrowser: function(c) {
RWC.contexts[c].videoElement = document.createElement("video");
RWC.contexts[c].videoElement.classList.add("retroarchWebcamVideo");
if (RWC.contexts[c].debug) document.body.appendChild(RWC.contexts[c].videoElement);
if (RWC.contexts[c].rawFb || RWC.contexts[c].proxied) {
RWC.contexts[c].rawFbCanvas = document.createElement("canvas");
RWC.contexts[c].rawFbCanvas.classList.add("retroarchWebcamCanvas");
if (RWC.contexts[c].debug) document.body.appendChild(RWC.contexts[c].rawFbCanvas);
}
var videoOpts = true;
if (RWC.contexts[c].idealWidth && RWC.contexts[c].idealHeight) {
/* save us some cropping/scaling, only honored by some browsers */
videoOpts = {width: RWC.contexts[c].idealWidth, height: RWC.contexts[c].idealHeight, aspectRatio: RWC.contexts[c].idealWidth / RWC.contexts[c].idealHeight};
}
navigator.mediaDevices.getUserMedia({audio: false, video: videoOpts}).then(function(stream) {
if (!RWC.contexts[c]?.running) {
/* too late */
for (var track of stream.getVideoTracks()) {
track.stop();
}
return;
}
RWC.contexts[c].videoElement.autoplay = true;
RWC.contexts[c].videoElement.srcObject = stream;
RWC.contexts[c].cameraRunning = true;
}).catch(function(e) {
console.log("[rwebcam] webcam request failed", e);
});
RWC.contexts[c].running = true;
},
RWebCamStart__deps: ["glGenTextures", "glBindTexture", "glGetIntegerv", "glTexParameteri", "malloc", "$RWebCamStartBrowser"],
RWebCamStart: function(c) {
if (RWC.contexts[c].debug) console.log("RWebCamStart", c);
if (RWC.contexts[c].running) return;
if (RWC.contexts[c].glTex) {
_glGenTextures(1, RWC.tmp);
RWC.contexts[data].glTexId = {{{ makeGetValue('RWC.tmp', '0', 'i32') }}};
if (RWC.contexts[data].glTexId !== 0) {
// save previous texture
RWC.contexts[c].glTexId = {{{ makeGetValue('RWC.tmp', 0, 'i32') }}};
if (RWC.contexts[c].glTexId !== 0) {
/* save previous texture */
_glGetIntegerv(0x8069 /* GL_TEXTURE_BINDING_2D */, RWC.tmp);
var prev = {{{ makeGetValue('RWC.tmp', '0', 'i32') }}};
_glBindTexture(0x0DE1 /* GL_TEXTURE_2D */, RWC.contexts[data].glTexId);
var prev = {{{ makeGetValue('RWC.tmp', 0, 'i32') }}};
_glBindTexture(0x0DE1 /* GL_TEXTURE_2D */, RWC.contexts[c].glTexId);
/* NPOT textures in WebGL must have these filters and clamping settings */
_glTexParameteri(0x0DE1 /* GL_TEXTURE_2D */, 0x2800 /* GL_TEXTURE_MAG_FILTER */, 0x2601 /* GL_LINEAR */);
_glTexParameteri(0x0DE1 /* GL_TEXTURE_2D */, 0x2801 /* GL_TEXTURE_MIN_FILTER */, 0x2601 /* GL_LINEAR */);
_glTexParameteri(0x0DE1 /* GL_TEXTURE_2D */, 0x2802 /* GL_TEXTURE_WRAP_S */, 0x812F /* GL_CLAMP_TO_EDGE */);
_glTexParameteri(0x0DE1 /* GL_TEXTURE_2D */, 0x2803 /*GL_TEXTURE_WRAP_T */, 0x812F /* GL_CLAMP_TO_EDGE */);
_glBindTexture(0x0DE1 /* GL_TEXTURE_2D */, prev);
RWC.contexts[data].glFirstFrame = true;
ret = 1;
RWC.contexts[c].glFirstFrame = true;
}
}
if (RWC.contexts[data].rawFb) {
RWC.contexts[data].rawFbCanvas = document.createElement("canvas");
RWC.contexts[data].rawFbCanvas.classList.add("retroarchWebcamCanvas");
ret = 1;
}
return ret;
RWebCamStartBrowser(c);
RWC.contexts[c].running = true;
return 1;
},
RWebCamStop__deps: ['glDeleteTextures', 'free'],
RWebCamStop: function(data) {
if (RWC.contexts[data].glTexId) {
_glDeleteTextures(1, RWC.contexts[data].glTexId);
}
if (RWC.contexts[data].rawFbCanvas) {
if (RWC.contexts[data].rawBuffer) {
_free(RWC.contexts[data].rawBuffer);
RWC.contexts[data].rawBuffer = 0;
RWC.contexts[data].rawFbCanvasCtx = null
$RWebCamStopBrowser__proxy: "sync",
$RWebCamStopBrowser: function(c) {
if (RWC.contexts[c].debug && RWC.contexts[c].rawFbCanvas) document.body.removeChild(RWC.contexts[c].rawFbCanvas);
RWC.contexts[c].rawFbCanvasCtx = null;
RWC.contexts[c].rawFbCanvas = null;
RWC.contexts[c].videoElement.pause();
if (RWC.contexts[c].cameraRunning) {
for (var track of RWC.contexts[c].videoElement.srcObject.getVideoTracks()) {
track.stop();
}
RWC.contexts[data].rawFbCanvas = null;
}
if (RWC.contexts[c].debug) document.body.removeChild(RWC.contexts[c].videoElement);
RWC.contexts[c].videoElement = null;
RWC.contexts[c].running = false;
},
RWebCamPoll__deps: ['glBindTexture', 'glGetIntegerv'],
RWebCamPoll: function(data, frame_raw_cb, frame_gl_cb) {
if (!RWC.ready(data)) return 0;
RWebCamStop__deps: ["free", "glDeleteTextures", "$RWebCamStopBrowser"],
RWebCamStop: function(c) {
if (RWC.contexts[c].debug) console.log("RWebCamStop", c);
if (!RWC.contexts[c].running) return;
if (RWC.contexts[c].glTexId) {
_glDeleteTextures(1, RWC.contexts[c].glTexId);
}
if (RWC.contexts[c].rawBuffer) {
_free(RWC.contexts[c].rawBuffer);
RWC.contexts[c].rawBuffer = 0;
}
RWebCamStopBrowser(c);
RWC.contexts[c].running = false;
},
$RWebCamCheckDimensions: function(c) {
if (!RWC.contexts[c].width) RWC.contexts[c].width = RWC.contexts[c].videoElement.videoWidth;
if (!RWC.contexts[c].height) RWC.contexts[c].height = RWC.contexts[c].videoElement.videoHeight;
},
$RWebCamStoreDimensions__proxy: "sync",
$RWebCamStoreDimensions__deps: ["$RWebCamReady", "$RWebCamCheckDimensions"],
$RWebCamStoreDimensions: function(c, ptr) {
if (!RWebCamReady(c)) return 0;
RWebCamCheckDimensions(c);
{{{ makeSetValue('ptr', 0, 'RWC.contexts[c].width', 'i32') }}};
{{{ makeSetValue('ptr', 4, 'RWC.contexts[c].height', 'i32') }}};
return 1;
},
$RWebCamGetImageData__deps: ["$RWebCamReady"],
$RWebCamGetImageData: function(c) {
if (!RWebCamReady(c)) return 0;
if (!RWC.contexts[c].rawFbCanvasCtx) {
RWC.contexts[c].rawFbCanvas.width = RWC.contexts[c].width;
RWC.contexts[c].rawFbCanvas.height = RWC.contexts[c].height;
RWC.contexts[c].rawFbCanvasCtx = RWC.contexts[c].rawFbCanvas.getContext("2d", {willReadFrequently: true});
}
/* crop to desired aspect ratio if necessary */
var oldAspect = RWC.contexts[c].videoElement.videoWidth / RWC.contexts[c].videoElement.videoHeight;
var newAspect = RWC.contexts[c].rawFbCanvas.width / RWC.contexts[c].rawFbCanvas.height;
var width = RWC.contexts[c].videoElement.videoWidth;
var height = RWC.contexts[c].videoElement.videoHeight;
var offsetX = 0;
var offsetY = 0;
if (oldAspect > newAspect) {
width = height * newAspect;
offsetX = (RWC.contexts[c].videoElement.videoWidth - width) / 2;
} else if (oldAspect < newAspect) {
height = width / newAspect;
offsetY = (RWC.contexts[c].videoElement.videoHeight - height) / 2;
}
RWC.contexts[c].rawFbCanvasCtx.drawImage(RWC.contexts[c].videoElement,
offsetX, offsetY, width, height,
0, 0, RWC.contexts[c].rawFbCanvas.width, RWC.contexts[c].rawFbCanvas.height);
return RWC.contexts[c].rawFbCanvasCtx.getImageData(0, 0, RWC.contexts[c].rawFbCanvas.width, RWC.contexts[c].rawFbCanvas.height).data;
},
$RWebCamStoreImageData__proxy: "sync",
$RWebCamStoreImageData__deps: ["$RWebCamGetImageData"],
$RWebCamStoreImageData: function(c, ptr) {
var data = RWebCamGetImageData(c);
if (!data) return 0;
HEAPU8.set(data, ptr);
return 1;
},
RWebCamPoll__deps: ["$RWebCamReady", "glBindTexture", "glGetIntegerv", "malloc", "free", "$RWebCamStoreDimensions", "$RWebCamCheckDimensions", "$RWebCamStoreImageData"],
RWebCamPoll: function(c, frame_raw_cb, frame_gl_cb) {
if (RWC.contexts[c].debug) console.log("RWebCamPoll", c, RWC.contexts[c].proxied, RWC.contexts[c].rawBuffer, RWC.contexts[c].running);
if (!RWC.contexts[c].running) return 0; /* need more checks in RA */
var ret = 0;
if (RWC.contexts[data].glTexId !== 0 && frame_gl_cb !== 0) {
_glGetIntegerv(0x8069 /* GL_TEXTURE_BINDING_2D */, RWC.tmp);
var prev = {{{ makeGetValue('RWC.tmp', '0', 'i32') }}};
_glBindTexture(0x0DE1 /* GL_TEXTURE_2D */, RWC.contexts[data].glTexId);
if (RWC.contexts[data].glFirstFrame) {
Module.ctx.texImage2D(Module.ctx.TEXTURE_2D, 0, Module.ctx.RGB, Module.ctx.RGB, Module.ctx.UNSIGNED_BYTE, RWC.contexts[data].videoElement);
RWC.contexts[data].glFirstFrame = false;
if ((RWC.contexts[c].rawFb && frame_raw_cb !== 0) || RWC.contexts[c].proxied) {
if (!RWC.contexts[c].rawBuffer) {
if (RWC.contexts[c].proxied) {
/* pull dimensions into this thread */
var dimensions = _malloc(8);
var res = RWebCamStoreDimensions(c, dimensions); /* also checks ready(c) */
if (!res) {
_free(dimensions);
return 0;
}
RWC.contexts[c].width = {{{ makeGetValue('dimensions', 0, 'i32') }}};
RWC.contexts[c].height = {{{ makeGetValue('dimensions', 4, 'i32') }}};
_free(dimensions);
RWC.contexts[c].length = RWC.contexts[c].width * RWC.contexts[c].height * 4;
} else {
if (!RWebCamReady(c)) return 0;
RWebCamCheckDimensions(c);
}
RWC.contexts[c].rawBuffer = _malloc(RWC.contexts[c].width * RWC.contexts[c].height * 4 + 1);
}
/* store at +1 so we can read data as XRGB */
if (!RWebCamStoreImageData(c, RWC.contexts[c].rawBuffer + 1)) return 0;
}
if (RWC.contexts[c].glTexId !== 0 && frame_gl_cb !== 0) {
var imageSrcGL;
if (RWC.contexts[c].proxied) {
imageSrcGL = HEAPU8.subarray(RWC.contexts[c].rawBuffer + 1, RWC.contexts[c].rawBuffer + RWC.contexts[c].length + 1);
} else {
Module.ctx.texSubImage2D(Module.ctx.TEXTURE_2D, 0, 0, 0, Module.ctx.RGB, Module.ctx.UNSIGNED_BYTE, RWC.contexts[data].videoElement);
if (!RWebCamReady(c)) return 0;
imageSrcGL = RWC.contexts[c].videoElement;
}
_glGetIntegerv(0x8069 /* GL_TEXTURE_BINDING_2D */, RWC.tmp);
var prev = {{{ makeGetValue('RWC.tmp', 0, 'i32') }}};
_glBindTexture(0x0DE1 /* GL_TEXTURE_2D */, RWC.contexts[c].glTexId);
if (RWC.contexts[c].glFirstFrame) {
if (RWC.contexts[c].proxied)
Module.ctx.texImage2D(Module.ctx.TEXTURE_2D, 0, Module.ctx.RGBA, RWC.contexts[c].width, RWC.contexts[c].height, 0, Module.ctx.RGBA, Module.ctx.UNSIGNED_BYTE, imageSrcGL);
else
Module.ctx.texImage2D(Module.ctx.TEXTURE_2D, 0, Module.ctx.RGB, Module.ctx.RGB, Module.ctx.UNSIGNED_BYTE, imageSrcGL);
RWC.contexts[c].glFirstFrame = false;
} else {
if (RWC.contexts[c].proxied)
Module.ctx.texSubImage2D(Module.ctx.TEXTURE_2D, 0, 0, 0, RWC.contexts[c].width, RWC.contexts[c].height, Module.ctx.RGBA, Module.ctx.UNSIGNED_BYTE, imageSrcGL);
else
Module.ctx.texSubImage2D(Module.ctx.TEXTURE_2D, 0, 0, 0, Module.ctx.RGB, Module.ctx.UNSIGNED_BYTE, imageSrcGL);
}
_glBindTexture(0x0DE1 /* GL_TEXTURE_2D */, prev);
Runtime.dynCall('viii', frame_gl_cb, [RWC.contexts[data].glTexId, 0x0DE1 /* GL_TEXTURE_2D */, 0]);
{{{ makeDynCall('viip', 'frame_gl_cb') }}}(RWC.contexts[c].glTexId, 0x0DE1 /* GL_TEXTURE_2D */, 0);
ret = 1;
}
if (RWC.contexts[data].rawFbCanvas && frame_raw_cb !== 0)
if (RWC.contexts[c].rawFb && frame_raw_cb !== 0)
{
if (!RWC.contexts[data].rawFbCanvasCtx) {
RWC.contexts[data].rawFbCanvas.width = RWC.contexts[data].videoElement.videoWidth;
RWC.contexts[data].rawFbCanvas.height = RWC.contexts[data].videoElement.videoHeight;
RWC.contexts[data].rawFbCanvasCtx = RWC.contexts[data].rawFbCanvas.getContext("2d");
RWC.contexts[data].rawBuffer = _malloc(RWC.contexts[data].videoElement.videoWidth * RWC.contexts[data].videoElement.videoHeight * 4);
}
RWC.contexts[data].rawFbCanvasCtx.drawImage(RWC.contexts[data].videoElement, 0, 0, RWC.contexts[data].rawFbCanvas.width, RWC.contexts[data].rawFbCanvas.height);
var image = RWC.contexts[data].rawFbCanvasCtx.getImageData(0, 0, RWC.contexts[data].videoElement.videoWidth, RWC.contexts[data].videoElement.videoHeight);
Module.HEAPU8.set(image.data, RWC.contexts[data].rawBuffer);
Runtime.dynCall('viiii', frame_raw_cb, [RWC.contexts[data].rawBuffer, RWC.contexts[data].videoElement.videoWidth, RWC.contexts[data].videoElement.videoHeight, RWC.contexts[data].videoElement.videoWidth * 4]);
{{{ makeDynCall('vpiii', 'frame_raw_cb') }}}(RWC.contexts[c].rawBuffer, RWC.contexts[c].width, RWC.contexts[c].height, RWC.contexts[c].width * 4);
ret = 1;
}
@ -140,4 +282,4 @@ var LibraryRWebCam = {
};
autoAddDeps(LibraryRWebCam, '$RWC');
mergeInto(LibraryManager.library, LibraryRWebCam);
addToLibrary(LibraryRWebCam);

View File

@ -1,134 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>RetroArch Web Player</title>
<script type="text/javascript" src="browserfs.js"></script>
<style>
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
textarea.emscripten { border: 0px; font-family: 'Share Tech Mono'; font-size: 12px; width: 100%; overflow:hide; resize:none; color:black; }
div.emscripten, h1 { text-align: left; }
div.canvas_border { background-color:gray; width:800px; height:600px; margin-left: auto; margin-right: auto; }
canvas.emscripten { border: 0px none; }
</style>
</head>
<body>
<hr/>
<div class="emscripten_border" id="canvas_div" style="display: none">
<canvas class="emscripten" id="canvas" tabindex="1" oncontextmenu="event.preventDefault()"></canvas>
</div>
<div class="emscripten emscripten_border" id="openrom">
<button id="btnLoad" onclick="document.getElementById('rom').click()">Upload Content</button>
<input style="display: none" type="file" id="rom" name="upload" onclick="document.getElementById('btnLoad').click();" onchange="runEmulator(event.target.files);" multiple />
<button id="btnStart" onclick="startRetroArch()">Start RetroArch</button>
</div>
<hr/>
<div class="emscripten">
<input type="checkbox" id="resize"><label for="resize">Resize canvas</label>
<input type="checkbox" id="pointerLock" checked><label for="pointerLock">Lock/hide mouse pointer</label>
<input type="button" value="Fullscreen" onclick="Module.requestFullScreen(document.getElementById('pointerLock').checked, document.getElementById('resize').checked)"><br>
<input type="checkbox" id="vsync"><label for="vsync" id="vsync-label">Enable V-sync (can only be done before loading game)</label><br>
<input type="checkbox" id="sdl2"><label for="sdl2" id="sdl2-label">Enable SDL2</label><br>
<input type="textbox" id="latency" size="3" maxlength="3" value="96"> <label for="latency" id="latency-label">Audio latency (ms) (increase if you hear pops at fullspeed, can only be done before loading game)</label>
</div>
<div class="emscripten" id="controls">
Controls:<br>
<br>
A button (OK in menu): X<br>
B button (Back in menu): Z<br>
X Button: S<br>
Y Button: A<br>
L Button: Q<br>
R Button: W<br>
D-pad: Arrow Keys<br>
Start Button: Enter<br>
Select Button: Shift<br>
Toggle Menu: F1<br>
Fast forward: Spacebar (toggle)<br>
Slow motion: E (hold)</br>
Save state: F2<br>
Load state: F4
</div>
</body>
</html>
<script type='text/javascript'>
var count = 0;
function runEmulator(files)
{
count = files.length;
document.getElementById("openrom").innerHTML = '';
document.getElementById("openrom").style.display = 'none';
for (var i = 0; i < files.length; i++)
{
filereader = new FileReader();
filereader.file_name = files[i].name;
filereader.onload = function(){uploadData(this.result, this.file_name)};
filereader.readAsArrayBuffer(files[i]);
}
}
function startRetroArch()
{
Module.FS_createFolder('/', 'etc', true, true);
Module.FS_createFolder('/', 'config', true, true);
Module.FS_createFolder('/', 'content', true, true);
Module.FS_createFolder('/', 'savefiles', true, true);
Module.FS_createFolder('/', 'savestates', true, true);
var config = 'input_player1_select = shift\n';
var latency = parseInt(document.getElementById('latency').value, 10);
if (isNaN(latency)) latency = 96;
config += 'audio_latency = ' + latency + '\n'
if (document.getElementById('vsync').checked)
config += 'video_vsync = true\n';
else
config += 'video_vsync = false\n';
Module.FS_createDataFile('/etc', 'retroarch.cfg', config, true, true);
document.getElementById('canvas_div').style.display = 'block';
document.getElementById('vsync').disabled = true;
document.getElementById('vsync-label').style.color = 'gray';
document.getElementById('latency').disabled = true;
document.getElementById('latency-label').style.color = 'gray';
Module['callMain'](Module['arguments']);
}
function uploadData(data, name)
{
var dataView = new Uint8Array(data);
Module.FS_createDataFile('/', name, dataView, true, false);
}
var Module =
{
noInitialRun: true,
arguments: ["-v", "--menu"],
preRun: [],
postRun: [],
print: (function()
{
return function(text)
{
text = Array.prototype.slice.call(arguments).join(' ');
console.log(text);
};
})(),
printErr: function(text)
{
var errArgs = Array.prototype.slice.call(arguments).join(' ');
console.error(errArgs);
},
canvas: document.getElementById('canvas'),
totalDependencies: 0,
monitorRunDependencies: function(left)
{
this.totalDependencies = Math.max(this.totalDependencies, left);
}
};
</script>
<script type="text/javascript" src="browserfs.js"></script>
<script type="text/javascript" src="gambatte.js"></script>

View File

@ -17,6 +17,7 @@
#include <emscripten/emscripten.h>
#include <emscripten/html5.h>
#include <emscripten/threading.h>
#include <string.h>
#include <malloc.h>
#include <stdio.h>
@ -60,7 +61,6 @@
#ifdef PROXY_TO_PTHREAD
#include <emscripten/wasm_worker.h>
#include <emscripten/threading.h>
#include <emscripten/proxying.h>
#include <emscripten/atomic.h>
#define PLATFORM_SETVAL(type, addr, val) emscripten_atomic_store_##type(addr, val)
@ -73,10 +73,17 @@
#include "platform_emscripten.h"
void emscripten_mainloop(void);
/* javascript library functions */
void PlatformEmscriptenWatchCanvasSizeAndDpr(double *dpr);
void PlatformEmscriptenWatchWindowVisibility(void);
void PlatformEmscriptenPowerStateInit(void);
void PlatformEmscriptenMemoryUsageInit(void);
void PlatformEmscriptenWatchFullscreen(void);
void PlatformEmscriptenGLContextEventInit(void);
void PlatformEmscriptenSetCanvasSize(int width, int height);
void PlatformEmscriptenSetWakeLock(bool state);
uint32_t PlatformEmscriptenGetSystemInfo(void);
typedef struct
{
@ -88,12 +95,18 @@ typedef struct
uint64_t memory_used;
uint64_t memory_limit;
double device_pixel_ratio;
enum platform_emscripten_browser browser;
enum platform_emscripten_os os;
int raf_interval;
int canvas_width;
int canvas_height;
int power_state_discharge_time;
float power_state_level;
bool has_async_atomics;
bool enable_set_canvas_size;
bool disable_detect_enter_fullscreen;
bool fullscreen;
bool gl_context_lost;
volatile bool power_state_charging;
volatile bool power_state_supported;
volatile bool window_hidden;
@ -102,6 +115,22 @@ typedef struct
static emscripten_platform_data_t *emscripten_platform_data = NULL;
/* defined here since implementation is frontend dependent */
void retro_sleep(unsigned msec)
{
#if defined(EMSCRIPTEN_ASYNCIFY) && !defined(HAVE_THREADS)
emscripten_sleep(msec);
#elif defined(EMSCRIPTEN_ASYNCIFY)
if (emscripten_is_main_browser_thread())
emscripten_sleep(msec);
else
emscripten_thread_sleep(msec);
#else
/* also works on main thread, but uses busy wait */
emscripten_thread_sleep(msec);
#endif
}
/* begin exported functions */
/* saves and states */
@ -133,6 +162,11 @@ void cmd_undo_load_state(void)
/* misc */
void cmd_toggle_fullscreen(void)
{
command_event(CMD_EVENT_FULLSCREEN_TOGGLE, NULL);
}
void cmd_take_screenshot(void)
{
command_event(CMD_EVENT_TAKE_SCREENSHOT, NULL);
@ -232,7 +266,7 @@ void cmd_cheat_apply_cheats(void)
/* javascript callbacks */
void platform_emscripten_update_canvas_dimensions(int width, int height, double *dpr)
void platform_emscripten_update_canvas_dimensions_cb(int width, int height, double *dpr)
{
printf("[INFO] Setting real canvas size: %d x %d\n", width, height);
emscripten_set_canvas_element_size("#canvas", width, height);
@ -243,14 +277,14 @@ void platform_emscripten_update_canvas_dimensions(int width, int height, double
PLATFORM_SETVAL(f64, &emscripten_platform_data->device_pixel_ratio, *dpr);
}
void platform_emscripten_update_window_hidden(bool hidden)
void platform_emscripten_update_window_hidden_cb(bool hidden)
{
if (!emscripten_platform_data)
return;
emscripten_platform_data->window_hidden = hidden;
}
void platform_emscripten_update_power_state(bool supported, int discharge_time, float level, bool charging)
void platform_emscripten_update_power_state_cb(bool supported, int discharge_time, float level, bool charging)
{
if (!emscripten_platform_data)
return;
@ -260,12 +294,40 @@ void platform_emscripten_update_power_state(bool supported, int discharge_time,
PLATFORM_SETVAL(f32, &emscripten_platform_data->power_state_level, level);
}
void platform_emscripten_update_memory_usage(uint32_t used1, uint32_t used2, uint32_t limit1, uint32_t limit2)
void platform_emscripten_update_memory_usage_cb(uint64_t used, uint64_t limit)
{
if (!emscripten_platform_data)
return;
PLATFORM_SETVAL(u64, &emscripten_platform_data->memory_used, used1 | ((uint64_t)used2 << 32));
PLATFORM_SETVAL(u64, &emscripten_platform_data->memory_limit, limit1 | ((uint64_t)limit2 << 32));
PLATFORM_SETVAL(u64, &emscripten_platform_data->memory_used, used);
PLATFORM_SETVAL(u64, &emscripten_platform_data->memory_limit, limit);
}
static void fullscreen_update(void *data)
{
command_event(CMD_EVENT_FULLSCREEN_TOGGLE, NULL);
}
void platform_emscripten_update_fullscreen_state_cb(bool state)
{
if (state == emscripten_platform_data->fullscreen || (state && emscripten_platform_data->disable_detect_enter_fullscreen))
return;
emscripten_platform_data->fullscreen = state;
platform_emscripten_run_on_program_thread_async(fullscreen_update, NULL);
}
void platform_emscripten_gl_context_lost_cb(void)
{
printf("[WARN] WebGL context lost!\n");
emscripten_platform_data->gl_context_lost = true;
}
void platform_emscripten_gl_context_restored_cb(void)
{
printf("[INFO] WebGL context restored.\n");
emscripten_platform_data->gl_context_lost = false;
/* there might be a better way to do this, but for now this works */
command_event(CMD_EVENT_REINIT, NULL);
}
void platform_emscripten_command_raise_flag()
@ -315,7 +377,7 @@ void platform_emscripten_command_reply(const char *msg, size_t len)
size_t platform_emscripten_command_read(char **into, size_t max_len)
{
if (!emscripten_platform_data || !emscripten_platform_data->command_flag)
if (!emscripten_platform_data->command_flag)
return 0;
return MAIN_THREAD_EM_ASM_INT({
var next_command = RPE.command_queue.shift();
@ -334,16 +396,12 @@ size_t platform_emscripten_command_read(char **into, size_t max_len)
void platform_emscripten_get_canvas_size(int *width, int *height)
{
if (!emscripten_platform_data)
goto error;
*width = PLATFORM_GETVAL(u32, &emscripten_platform_data->canvas_width);
*height = PLATFORM_GETVAL(u32, &emscripten_platform_data->canvas_height);
if (*width != 0 || *height != 0)
return;
error:
*width = 800;
*height = 600;
RARCH_ERR("[EMSCRIPTEN]: Could not get screen dimensions!\n");
@ -354,6 +412,15 @@ double platform_emscripten_get_dpr(void)
return PLATFORM_GETVAL(f64, &emscripten_platform_data->device_pixel_ratio);
}
unsigned platform_emscripten_get_min_sleep_ms(void)
{
if (emscripten_platform_data->browser == PLATFORM_EMSCRIPTEN_BROWSER_FIREFOX &&
emscripten_platform_data->os == PLATFORM_EMSCRIPTEN_OS_WINDOWS)
return 16;
return 5;
}
bool platform_emscripten_has_async_atomics(void)
{
return emscripten_platform_data->has_async_atomics;
@ -366,7 +433,7 @@ bool platform_emscripten_is_window_hidden(void)
bool platform_emscripten_should_drop_iter(void)
{
return (emscripten_platform_data->window_hidden && emscripten_platform_data->raf_interval);
return (emscripten_platform_data->gl_context_lost || (emscripten_platform_data->window_hidden && emscripten_platform_data->raf_interval));
}
#ifdef PROXY_TO_PTHREAD
@ -413,6 +480,68 @@ void platform_emscripten_set_main_loop_interval(int interval)
#endif
}
void platform_emscripten_set_pointer_visibility(bool state)
{
MAIN_THREAD_EM_ASM({
if ($0) {
Module.canvas.style.removeProperty("cursor");
} else {
Module.canvas.style.setProperty("cursor", "none");
}
}, state);
}
static void platform_emscripten_do_set_fullscreen_state(void *data)
{
bool ran_user_cb = EM_ASM_INT({
var func = $0 ? "fullscreenEnter" : "fullscreenExit";
if (Module[func]) {
Module[func]();
return 1;
}
return 0;
}, emscripten_platform_data->fullscreen);
if (ran_user_cb)
return;
if (emscripten_platform_data->fullscreen)
emscripten_request_fullscreen("#canvas", false);
else
emscripten_exit_fullscreen();
}
void platform_emscripten_set_fullscreen_state(bool state)
{
if (state == emscripten_platform_data->fullscreen)
return;
emscripten_platform_data->fullscreen = state;
platform_emscripten_run_on_browser_thread_sync(platform_emscripten_do_set_fullscreen_state, NULL);
}
void platform_emscripten_set_wake_lock(bool state)
{
PlatformEmscriptenSetWakeLock(state);
}
void platform_emscripten_set_canvas_size(int width, int height)
{
if (!emscripten_platform_data->enable_set_canvas_size)
return;
PlatformEmscriptenSetCanvasSize(width, height);
}
enum platform_emscripten_browser platform_emscripten_get_browser(void)
{
return emscripten_platform_data->browser;
}
enum platform_emscripten_os platform_emscripten_get_os(void)
{
return emscripten_platform_data->os;
}
/* frontend driver impl */
static void frontend_emscripten_get_env(int *argc, char *argv[],
@ -575,7 +704,7 @@ static void platform_emscripten_mount_filesystems(void)
path_parent_dir(parent, strlen(parent));
if (!path_mkdir(parent))
{
printf("mkdir error %d\n", errno);
printf("[OPFS] mkdir error %d\n", errno);
abort();
}
free(parent);
@ -628,7 +757,7 @@ static void platform_emscripten_mount_filesystems(void)
char *base_url = strdup(line);
base_url[strcspn(base_url, "\r\n")] = '\0'; // drop newline
base_url[len-1] = '\0'; // drop newline
backend_t fetch;
backend_t fetch = NULL;
len = max_line_len;
// Don't create fetch backend unless manifest actually has entries
while (getline(&line, &len, file) != -1)
@ -636,6 +765,10 @@ static void platform_emscripten_mount_filesystems(void)
if (!fetch)
{
fetch = wasmfs_create_fetch_backend(base_url, 16*1024*1024);
if(!fetch) {
printf("[FetchFS] couldn't create fetch backend for %s\n", base_url);
abort();
}
wasmfs_create_directory(fetch_base_dir, 0777, fetch);
}
char *realfs_path = strstr(line, " "), *url = line;
@ -700,6 +833,7 @@ static int thread_main(int argc, char *argv[])
platform_emscripten_mount_filesystems();
#endif
PlatformEmscriptenGLContextEventInit();
emscripten_set_main_loop(emscripten_mainloop, 0, 0);
#ifdef PROXY_TO_PTHREAD
emscripten_set_main_loop_timing(EM_TIMING_SETIMMEDIATE, 0);
@ -733,6 +867,7 @@ static void raf_signaler(void)
int main(int argc, char *argv[])
{
int ret = 0;
uint32_t system_info;
#ifdef PROXY_TO_PTHREAD
pthread_attr_t attr;
pthread_t thread;
@ -740,14 +875,58 @@ int main(int argc, char *argv[])
/* this never gets freed */
emscripten_platform_data = (emscripten_platform_data_t *)calloc(1, sizeof(emscripten_platform_data_t));
system_info = PlatformEmscriptenGetSystemInfo();
emscripten_platform_data->browser = system_info & 0xFFFF;
emscripten_platform_data->os = system_info >> 16;
emscripten_platform_data->enable_set_canvas_size = !!getenv("ENABLE_SET_CANVAS_SIZE");
emscripten_platform_data->disable_detect_enter_fullscreen = !!getenv("DISABLE_DETECT_ENTER_FULLSCREEN");
emscripten_platform_data->has_async_atomics = EM_ASM_INT({
return Atomics?.waitAsync?.toString().includes("[native code]");
});
EM_ASM({
/* keyboard events won't work without the canvas being focused */
if (!Module.canvas.getAttribute("tabindex"))
Module.canvas.setAttribute("tabindex", "-1");
Module.canvas.focus();
Module.canvas.addEventListener("pointerdown", function() {
Module.canvas.focus();
}, false);
/* disable browser right click menu */
Module.canvas.addEventListener("contextmenu", function(e) {
e.preventDefault();
}, false);
/* background should be black */
Module.canvas.style.backgroundColor = "#000000";
/* border and padding may interfere with pointer event coordinates */
Module.canvas.style.setProperty("padding", "0px", "important");
Module.canvas.style.setProperty("border", "none", "important");
/* ensure canvas size is constrained by CSS, otherwise infinite resizing may occur */
if (window.getComputedStyle(Module.canvas).display == "inline") {
console.warn("[WARN] Canvas should not use display: inline!");
Module.canvas.style.display = "inline-block";
}
var oldWidth = Module.canvas.clientWidth;
var oldHeight = Module.canvas.clientHeight;
Module.canvas.width = 64;
Module.canvas.height = 64;
if (oldWidth != Module.canvas.clientWidth || oldHeight != Module.canvas.clientHeight) {
console.warn("[WARN] Canvas size should be set using CSS properties!");
Module.canvas.style.width = oldWidth + "px";
Module.canvas.style.height = oldHeight + "px";
}
});
PlatformEmscriptenWatchCanvasSizeAndDpr(malloc(sizeof(double)));
PlatformEmscriptenWatchWindowVisibility();
PlatformEmscriptenPowerStateInit();
PlatformEmscriptenMemoryUsageInit();
PlatformEmscriptenWatchFullscreen();
emscripten_platform_data->raf_interval = 1;
#ifdef PROXY_TO_PTHREAD

View File

@ -20,6 +20,23 @@
#include <boolean.h>
enum platform_emscripten_browser
{
PLATFORM_EMSCRIPTEN_BROWSER_OTHER = 0,
PLATFORM_EMSCRIPTEN_BROWSER_CHROMIUM,
PLATFORM_EMSCRIPTEN_BROWSER_FIREFOX,
PLATFORM_EMSCRIPTEN_BROWSER_SAFARI
};
enum platform_emscripten_os
{
PLATFORM_EMSCRIPTEN_OS_OTHER = 0,
PLATFORM_EMSCRIPTEN_OS_WINDOWS,
PLATFORM_EMSCRIPTEN_OS_LINUX,
PLATFORM_EMSCRIPTEN_OS_IOS,
PLATFORM_EMSCRIPTEN_OS_MACOS
};
/**
* Synchronously run a function on the browser thread.
*
@ -77,6 +94,15 @@ void platform_emscripten_get_canvas_size(int *width, int *height);
*/
double platform_emscripten_get_dpr(void);
/**
* Get the minimum amount of time that setTimeout (retro_sleep on browser thread) can
* sleep for in a loop.
* This may vary between browsers: usually 5 ms, but much higher for firefox on windows.
*
* @return Minimum sleep in milliseconds.
*/
unsigned platform_emscripten_get_min_sleep_ms(void);
/**
* Check if the browser supports Atomics.waitAsync.
*
@ -128,4 +154,48 @@ void platform_emscripten_exit_fake_block(void);
*/
void platform_emscripten_set_main_loop_interval(int interval);
/**
* Hide or show the cursor.
*
* @param state True to show, false to hide.
*/
void platform_emscripten_set_pointer_visibility(bool state);
/**
* Try to enter or exit fullscreen.
*
* @param state True to enter, false to exit.
*/
void platform_emscripten_set_fullscreen_state(bool state);
/**
* Try to prevent the screen from dimming.
*
* @param state True to request, false to release.
*/
void platform_emscripten_set_wake_lock(bool state);
/**
* Try to set the real screen dimensions of the canvas.
* Will only work if explicitly enabled by the embedder.
*
* @param width New width
* @param height New height
*/
void platform_emscripten_set_canvas_size(int width, int height);
/**
* Get the browser that the program is running in.
*
* @return enum platform_emscripten_browser
*/
enum platform_emscripten_browser platform_emscripten_get_browser(void);
/**
* Get the OS that the program is running in.
*
* @return enum platform_emscripten_os
*/
enum platform_emscripten_os platform_emscripten_get_os(void);
#endif

View File

@ -73,6 +73,7 @@ typedef struct gl3
const gfx_ctx_driver_t *ctx_driver;
void *ctx_data;
gl3_filter_chain_t *filter_chain;
gl3_filter_chain_t *filter_chain_default;
GLuint *overlay_tex;
float *overlay_vertex_coord;
float *overlay_tex_coord;

View File

@ -551,6 +551,7 @@ struct vk_draw_triangles
typedef struct vk
{
vulkan_filter_chain_t *filter_chain;
vulkan_filter_chain_t *filter_chain_default;
vulkan_context_t *context;
void *ctx_data;
const gfx_ctx_driver_t *ctx_driver;

View File

@ -73,3 +73,4 @@ generate_source 'staging/cursor-shape' 'cursor-shape-v1'
generate_source 'unstable/tablet' 'tablet-unstable-v2'
generate_source 'staging/content-type' 'content-type-v1'
generate_source 'staging/single-pixel-buffer' 'single-pixel-buffer-v1'
generate_source 'staging/xdg-toplevel-icon' 'xdg-toplevel-icon-v1'

View File

@ -295,6 +295,10 @@ void gfx_ctx_wl_destroy_resources_common(gfx_ctx_wayland_data_t *wl)
zxdg_toplevel_decoration_v1_destroy(wl->deco);
if (wl->xdg_toplevel)
xdg_toplevel_destroy(wl->xdg_toplevel);
if (wl->xdg_toplevel_icon_manager)
xdg_toplevel_icon_manager_v1_destroy(wl->xdg_toplevel_icon_manager);
if (wl->xdg_toplevel_icon)
xdg_toplevel_icon_v1_destroy(wl->xdg_toplevel_icon);
if (wl->xdg_surface)
xdg_surface_destroy(wl->xdg_surface);
if (wl->surface)
@ -363,30 +367,32 @@ void gfx_ctx_wl_destroy_resources_common(gfx_ctx_wayland_data_t *wl)
wl_display_disconnect(wl->input.dpy);
}
wl->input.dpy = NULL;
wl->registry = NULL;
wl->compositor = NULL;
wl->shm = NULL;
wl->data_device_manager = NULL;
wl->xdg_shell = NULL;
wl->seat = NULL;
wl->relative_pointer_manager = NULL;
wl->pointer_constraints = NULL;
wl->content_type = NULL;
wl->content_type_manager = NULL;
wl->cursor_shape_manager = NULL;
wl->cursor_shape_device = NULL;
wl->idle_inhibit_manager = NULL;
wl->deco_manager = NULL;
wl->single_pixel_manager = NULL;
wl->surface = NULL;
wl->xdg_surface = NULL;
wl->xdg_toplevel = NULL;
wl->deco = NULL;
wl->idle_inhibitor = NULL;
wl->wl_touch = NULL;
wl->wl_pointer = NULL;
wl->wl_keyboard = NULL;
wl->input.dpy = NULL;
wl->registry = NULL;
wl->compositor = NULL;
wl->shm = NULL;
wl->data_device_manager = NULL;
wl->xdg_shell = NULL;
wl->seat = NULL;
wl->relative_pointer_manager = NULL;
wl->pointer_constraints = NULL;
wl->content_type = NULL;
wl->content_type_manager = NULL;
wl->cursor_shape_manager = NULL;
wl->cursor_shape_device = NULL;
wl->idle_inhibit_manager = NULL;
wl->deco_manager = NULL;
wl->single_pixel_manager = NULL;
wl->surface = NULL;
wl->xdg_surface = NULL;
wl->xdg_toplevel = NULL;
wl->xdg_toplevel_icon = NULL;
wl->xdg_toplevel_icon_manager = NULL;
wl->deco = NULL;
wl->idle_inhibitor = NULL;
wl->wl_touch = NULL;
wl->wl_pointer = NULL;
wl->wl_keyboard = NULL;
wl->width = 0;
wl->height = 0;
@ -586,6 +592,45 @@ static void shm_buffer_paint_icon(
}
}
static bool wl_create_toplevel_icon(gfx_ctx_wayland_data_t *wl, struct xdg_toplevel *toplevel)
{
struct xdg_toplevel_icon_v1 *icon = xdg_toplevel_icon_manager_v1_create_icon(
wl->xdg_toplevel_icon_manager);
xdg_toplevel_icon_v1_set_name(icon, WAYLAND_APP_ID);
const int icon_size = wl->buffer_scale > 1 ? 128 : 64;
shm_buffer_t *icon_buffer = create_shm_buffer(wl,
icon_size, icon_size, WL_SHM_FORMAT_ARGB8888);
if (icon_buffer)
{
shm_buffer_paint_icon(icon_buffer, icon_size, icon_size, 1, icon_size / 16);
xdg_toplevel_icon_v1_add_buffer(
icon, icon_buffer->wl_buffer, 1);
}
else
{
RARCH_ERR("[Wayland] Failed to create toplevel icon buffer\n");
return false;
}
xdg_toplevel_icon_manager_v1_set_icon(
wl->xdg_toplevel_icon_manager, toplevel, icon);
#ifdef HAVE_LIBDECOR_H
if (wl->libdecor_frame)
{
wl->libdecor_icon = icon;
}
else
#endif
{
wl->xdg_toplevel_icon = icon;
}
return true;
}
static void shm_buffer_paint_checkerboard(
shm_buffer_t *buffer,
int width, int height, int scale,
@ -778,6 +823,11 @@ bool gfx_ctx_wl_init_common(
RARCH_LOG("[Wayland]: Compositor doesn't support the %s protocol!\n", wp_single_pixel_buffer_manager_v1_interface.name);
}
if (!wl->xdg_toplevel_icon_manager)
{
RARCH_LOG("[Wayland]: Compositor doesn't support the %s protocol!\n", xdg_toplevel_icon_manager_v1_interface.name);
}
wl->surface = wl_compositor_create_surface(wl->compositor);
if (wl->viewporter)
wl->viewport = wp_viewporter_get_viewport(wl->viewporter, wl->surface);
@ -809,6 +859,12 @@ bool gfx_ctx_wl_init_common(
goto error;
}
if (wl->xdg_toplevel_icon_manager)
{
struct xdg_toplevel *xdg_toplevel = wl->libdecor_frame_get_xdg_toplevel(wl->libdecor_frame);
wl_create_toplevel_icon(wl, xdg_toplevel);
}
wl->libdecor_frame_set_app_id(wl->libdecor_frame, WAYLAND_APP_ID);
wl->libdecor_frame_set_title(wl->libdecor_frame, WINDOW_TITLE);
wl->libdecor_frame_map(wl->libdecor_frame);
@ -842,6 +898,9 @@ bool gfx_ctx_wl_init_common(
wl->deco = zxdg_decoration_manager_v1_get_toplevel_decoration(
wl->deco_manager, wl->xdg_toplevel);
if (wl->xdg_toplevel_icon_manager)
wl_create_toplevel_icon(wl, wl->xdg_toplevel);
/* Waiting for xdg_toplevel to be configured before starting to draw */
wl_surface_commit(wl->surface);
wl->configured = true;

View File

@ -1190,16 +1190,18 @@ static LRESULT CALLBACK wnd_proc_common_internal(HWND hwnd,
g_win32_flags |= WIN32_CMN_FLAG_TASKBAR_CREATED;
#endif
break;
#ifdef HAVE_CLIP_WINDOW
case WM_SETFOCUS:
#ifdef HAVE_CLIP_WINDOW
if (input_state_get_ptr()->flags & INP_FLAG_GRAB_MOUSE_STATE)
win32_clip_window(true);
#endif
break;
case WM_KILLFOCUS:
#ifdef HAVE_CLIP_WINDOW
if (input_state_get_ptr()->flags & INP_FLAG_GRAB_MOUSE_STATE)
win32_clip_window(false);
break;
#endif
break;
case WM_DISPLAYCHANGE: /* Fix size after display mode switch when using SR */
{
HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
@ -1463,16 +1465,34 @@ static LRESULT CALLBACK wnd_proc_common_dinput_internal(HWND hwnd,
g_win32_flags |= WIN32_CMN_FLAG_TASKBAR_CREATED;
#endif
break;
#ifdef HAVE_CLIP_WINDOW
case WM_SETFOCUS:
#ifdef HAVE_CLIP_WINDOW
if (input_state_get_ptr()->flags & INP_FLAG_GRAB_MOUSE_STATE)
win32_clip_window(true);
#endif
#if !defined(_XBOX)
{
void* input_data = (void*)(LONG_PTR)GetWindowLongPtr(main_window.hwnd, GWLP_USERDATA);
if (input_data && dinput_handle_message(input_data,
message, wparam, lparam))
return 0;
}
#endif
break;
case WM_KILLFOCUS:
#ifdef HAVE_CLIP_WINDOW
if (input_state_get_ptr()->flags & INP_FLAG_GRAB_MOUSE_STATE)
win32_clip_window(false);
break;
#endif
#if !defined(_XBOX)
{
void* input_data = (void*)(LONG_PTR)GetWindowLongPtr(main_window.hwnd, GWLP_USERDATA);
if (input_data && dinput_handle_message(input_data,
message, wparam, lparam))
return 0;
}
#endif
break;
case WM_DISPLAYCHANGE: /* Fix size after display mode switch when using SR */
{
HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);

View File

@ -1004,6 +1004,7 @@ static uint32_t d3d11_get_flags(void *data)
#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG);
BIT32_SET(flags, GFX_CTX_FLAGS_SUBFRAME_SHADERS);
BIT32_SET(flags, GFX_CTX_FLAGS_FAST_TOGGLE_SHADERS);
#endif
return flags;
@ -2735,7 +2736,7 @@ static void d3d11_init_render_targets(d3d11_video_t* d3d11, unsigned width, unsi
height = d3d11->vp.height;
}
RARCH_LOG("[D3D11]: Updating framebuffer size %ux%u.\n", width, height);
RARCH_DBG("[D3D11]: Updating framebuffer size %ux%u.\n", width, height);
if ( (i != (d3d11->shader_preset->passes - 1))
|| (width != d3d11->vp.width)
@ -3056,7 +3057,7 @@ static bool d3d11_gfx_frame(
texture = d3d11->frame.texture;
if (d3d11->shader_preset)
if (d3d11->shader_preset && video_info->shader_active)
{
for (i = 0; i < d3d11->shader_preset->passes; i++)
{
@ -3081,10 +3082,9 @@ static bool d3d11_gfx_frame(
}
if (d3d11->shader_preset->pass[i].frame_count_mod)
d3d11->pass[i].frame_count =
frame_count % d3d11->shader_preset->pass[i].frame_count_mod;
d3d11->pass[i].frame_count = frame_count % d3d11->shader_preset->pass[i].frame_count_mod;
else
d3d11->pass[i].frame_count = frame_count;
d3d11->pass[i].frame_count = frame_count;
#ifdef HAVE_REWIND
d3d11->pass[i].frame_direction = state_manager_frame_is_reversed() ? -1 : 1;
@ -3096,10 +3096,10 @@ static bool d3d11_gfx_frame(
d3d11->pass[i].rotation = retroarch_get_rotation();
d3d11->pass[i].core_aspect = video_driver_get_core_aspect();
/* OriginalAspectRotated: return 1 / aspect for 90 and 270 rotated content */
d3d11->pass[i].core_aspect_rot = video_driver_get_core_aspect();
uint32_t rot = retroarch_get_rotation();
if (rot == 1 || rot == 3)
d3d11->pass[i].core_aspect_rot = 1/d3d11->pass[i].core_aspect_rot;
d3d11->pass[i].core_aspect_rot = d3d11->pass[i].core_aspect;
if ( d3d11->pass[i].rotation == VIDEO_ROTATION_90_DEG
|| d3d11->pass[i].rotation == VIDEO_ROTATION_270_DEG)
d3d11->pass[i].core_aspect_rot = 1 / d3d11->pass[i].core_aspect_rot;
/* Sub-frame info for multiframe shaders (per real content frame).
Should always be 1 for non-use of subframes */

View File

@ -1152,6 +1152,7 @@ static uint32_t d3d12_get_flags(void *data)
#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG);
BIT32_SET(flags, GFX_CTX_FLAGS_SUBFRAME_SHADERS);
BIT32_SET(flags, GFX_CTX_FLAGS_FAST_TOGGLE_SHADERS);
#endif
return flags;
@ -3134,7 +3135,7 @@ static void d3d12_init_render_targets(d3d12_video_t* d3d12, unsigned width, unsi
height = d3d12->vp.height;
}
RARCH_LOG("[D3D12]: Updating framebuffer size %ux%u.\n", width, height);
RARCH_DBG("[D3D12]: Updating framebuffer size %ux%u.\n", width, height);
if (i == (d3d12->shader_preset->passes - 1))
{
@ -3565,7 +3566,7 @@ static bool d3d12_gfx_frame(
texture = d3d12->frame.texture;
if (d3d12->shader_preset)
if (d3d12->shader_preset && video_info->shader_active)
{
cmd->lpVtbl->SetGraphicsRootSignature(cmd,
d3d12->desc.sl_rootSignature);
@ -3587,25 +3588,23 @@ static bool d3d12_gfx_frame(
cmd->lpVtbl->SetPipelineState(cmd, d3d12->pass[i].pipe);
if (d3d12->shader_preset->pass[i].frame_count_mod)
d3d12->pass[i].frame_count = frame_count
% d3d12->shader_preset->pass[i].frame_count_mod;
d3d12->pass[i].frame_count = frame_count % d3d12->shader_preset->pass[i].frame_count_mod;
else
d3d12->pass[i].frame_count = frame_count;
#ifdef HAVE_REWIND
if (state_manager_frame_is_reversed())
d3d12->pass[i].frame_direction = -1;
else
d3d12->pass[i].frame_direction = state_manager_frame_is_reversed() ? -1 : 1;
#else
d3d12->pass[i].frame_direction = 1;
#endif
d3d12->pass[i].frame_direction = 1;
d3d12->pass[i].frame_time_delta = (uint32_t)video_driver_get_frame_time_delta_usec();
d3d12->pass[i].original_fps = video_driver_get_original_fps();
d3d12->pass[i].rotation = retroarch_get_rotation();
d3d12->pass[i].core_aspect = video_driver_get_core_aspect();
d3d12->pass[i].frame_time_delta = (uint32_t)video_driver_get_frame_time_delta_usec();
d3d12->pass[i].original_fps = video_driver_get_original_fps();
d3d12->pass[i].rotation = retroarch_get_rotation();
d3d12->pass[i].core_aspect = video_driver_get_core_aspect();
/* OriginalAspectRotated: return 1 / aspect for 90 and 270 rotated content */
d3d12->pass[i].core_aspect_rot = video_driver_get_core_aspect();
uint32_t rot = retroarch_get_rotation();
if (rot == 1 || rot == 3)
d3d12->pass[i].core_aspect_rot = d3d12->pass[i].core_aspect;
if ( d3d12->pass[i].rotation == VIDEO_ROTATION_90_DEG
|| d3d12->pass[i].rotation == VIDEO_ROTATION_270_DEG)
d3d12->pass[i].core_aspect_rot = 1 / d3d12->pass[i].core_aspect_rot;
/* Sub-frame info for multiframe shaders (per real content frame).

View File

@ -1332,6 +1332,10 @@ static void gl3_destroy_resources(gl3_t *gl)
gl3_filter_chain_free(gl->filter_chain);
gl->filter_chain = NULL;
if (gl->filter_chain_default)
gl3_filter_chain_free(gl->filter_chain_default);
gl->filter_chain_default = NULL;
glBindVertexArray(0);
if (gl->vao != 0)
glDeleteVertexArrays(1, &gl->vao);
@ -1710,14 +1714,17 @@ static bool gl3_init_default_filter_chain(gl3_t *gl)
if (!gl->ctx_driver)
return false;
gl->filter_chain = gl3_filter_chain_create_default(
if (gl->filter_chain_default)
return true;
gl->filter_chain_default = gl3_filter_chain_create_default(
gl->video_info.smooth
? GLSLANG_FILTER_CHAIN_LINEAR
: GLSLANG_FILTER_CHAIN_NEAREST);
if (!gl->filter_chain)
if (!gl->filter_chain_default)
{
RARCH_ERR("Failed to create filter chain.\n");
RARCH_ERR("[GLCore]: Failed to create default filter chain.\n");
return false;
}
@ -1726,6 +1733,9 @@ static bool gl3_init_default_filter_chain(gl3_t *gl)
static bool gl3_init_filter_chain_preset(gl3_t *gl, const char *shader_path)
{
if (!gl->ctx_driver)
return false;
gl->filter_chain = gl3_filter_chain_create_from_preset(
shader_path,
gl->video_info.smooth
@ -2107,7 +2117,7 @@ static void *gl3_init(const video_info_t *video,
if (!gl_check_error(&error_string))
{
RARCH_ERR("%s\n", error_string);
RARCH_ERR("[GLCore]: %s\n", error_string);
free(error_string);
goto error;
}
@ -2650,6 +2660,7 @@ static bool gl3_frame(void *data, const void *frame,
{
struct gl3_filter_chain_texture texture;
struct gl3_streamed_texture *streamed = NULL;
gl3_filter_chain_t *filter_chain = NULL;
gl3_t *gl = (gl3_t*)data;
unsigned width = video_info->width;
unsigned height = video_info->height;
@ -2734,26 +2745,44 @@ static bool gl3_frame(void *data, const void *frame,
texture.padded_width = streamed->width;
texture.padded_height = streamed->height;
}
gl3_filter_chain_set_frame_count(gl->filter_chain, frame_count);
/* Fast toggle shader filter chain logic */
filter_chain = gl->filter_chain;
if (!video_info->shader_active && gl->filter_chain != gl->filter_chain_default)
{
if (!gl->filter_chain_default)
gl3_init_default_filter_chain(gl);
if (gl->filter_chain_default)
filter_chain = gl->filter_chain_default;
else
return false;
}
if (!filter_chain && gl->filter_chain_default)
filter_chain = gl->filter_chain_default;
gl3_filter_chain_set_frame_count(filter_chain, frame_count);
#ifdef HAVE_REWIND
gl3_filter_chain_set_frame_direction(gl->filter_chain, state_manager_frame_is_reversed() ? -1 : 1);
gl3_filter_chain_set_frame_direction(filter_chain, state_manager_frame_is_reversed() ? -1 : 1);
#else
gl3_filter_chain_set_frame_direction(gl->filter_chain, 1);
gl3_filter_chain_set_frame_direction(filter_chain, 1);
#endif
gl3_filter_chain_set_frame_time_delta(gl->filter_chain, (uint32_t)video_driver_get_frame_time_delta_usec());
gl3_filter_chain_set_frame_time_delta(filter_chain, (uint32_t)video_driver_get_frame_time_delta_usec());
gl3_filter_chain_set_original_fps(gl->filter_chain, video_driver_get_original_fps());
gl3_filter_chain_set_original_fps(filter_chain, video_driver_get_original_fps());
gl3_filter_chain_set_rotation(gl->filter_chain, retroarch_get_rotation());
gl3_filter_chain_set_rotation(filter_chain, retroarch_get_rotation());
gl3_filter_chain_set_core_aspect(gl->filter_chain, video_driver_get_core_aspect());
gl3_filter_chain_set_core_aspect(filter_chain, video_driver_get_core_aspect());
/* OriginalAspectRotated: return 1/aspect for 90 and 270 rotated content */
uint32_t rot = retroarch_get_rotation();
float core_aspect_rot = video_driver_get_core_aspect();
if (rot == 1 || rot == 3)
core_aspect_rot = 1/core_aspect_rot;
gl3_filter_chain_set_core_aspect_rot(gl->filter_chain, core_aspect_rot);
gl3_filter_chain_set_core_aspect_rot(filter_chain, core_aspect_rot);
/* Sub-frame info for multiframe shaders (per real content frame).
Should always be 1 for non-use of subframes*/
@ -2765,13 +2794,13 @@ static bool gl3_frame(void *data, const void *frame,
|| video_info->runloop_is_paused
|| (gl->flags & GL3_FLAG_MENU_TEXTURE_ENABLE))
gl3_filter_chain_set_shader_subframes(
gl->filter_chain, 1);
filter_chain, 1);
else
gl3_filter_chain_set_shader_subframes(
gl->filter_chain, video_info->shader_subframes);
filter_chain, video_info->shader_subframes);
gl3_filter_chain_set_current_shader_subframe(
gl->filter_chain, 1);
filter_chain, 1);
}
#ifdef GL3_ROLLING_SCANLINE_SIMULATION
@ -2783,25 +2812,25 @@ static bool gl3_frame(void *data, const void *frame,
&& !video_info->runloop_is_paused
&& (!(gl->flags & GL3_FLAG_MENU_TEXTURE_ENABLE)))
gl3_filter_chain_set_simulate_scanline(
gl->filter_chain, true);
filter_chain, true);
else
gl3_filter_chain_set_simulate_scanline(
gl->filter_chain, false);
filter_chain, false);
#endif /* GL3_ROLLING_SCANLINE_SIMULATION */
gl3_filter_chain_set_input_texture(gl->filter_chain, &texture);
gl3_filter_chain_build_offscreen_passes(gl->filter_chain,
gl3_filter_chain_set_input_texture(filter_chain, &texture);
gl3_filter_chain_build_offscreen_passes(filter_chain,
&gl->filter_chain_vp);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
gl3_filter_chain_build_viewport_pass(gl->filter_chain,
gl3_filter_chain_build_viewport_pass(filter_chain,
&gl->filter_chain_vp,
(gl->flags & GL3_FLAG_HW_RENDER_BOTTOM_LEFT)
? gl->mvp.data
: gl->mvp_yflip.data);
gl3_filter_chain_end_frame(gl->filter_chain);
gl3_filter_chain_end_frame(filter_chain);
#ifdef HAVE_OVERLAY
if ((gl->flags & GL3_FLAG_OVERLAY_ENABLE) && overlay_behind_menu)
@ -2940,9 +2969,9 @@ static bool gl3_frame(void *data, const void *frame,
for (i = 1; i < (int) video_info->shader_subframes; i++)
{
gl3_filter_chain_set_shader_subframes(
gl->filter_chain, video_info->shader_subframes);
filter_chain, video_info->shader_subframes);
gl3_filter_chain_set_current_shader_subframe(
gl->filter_chain, i+1);
filter_chain, i+1);
if (!gl3_frame(gl, NULL, 0, 0, frame_count, 0, msg,
video_info))
@ -2975,6 +3004,7 @@ static uint32_t gl3_get_flags(void *data)
BIT32_SET(flags, GFX_CTX_FLAGS_SCREENSHOTS_SUPPORTED);
BIT32_SET(flags, GFX_CTX_FLAGS_OVERLAY_BEHIND_MENU_SUPPORTED);
BIT32_SET(flags, GFX_CTX_FLAGS_SUBFRAME_SHADERS);
BIT32_SET(flags, GFX_CTX_FLAGS_FAST_TOGGLE_SHADERS);
return flags;
}

View File

@ -2829,6 +2829,9 @@ static bool vulkan_init_default_filter_chain(vk_t *vk)
if (!vk->context)
return false;
if (vk->filter_chain_default)
return true;
info.device = vk->context->device;
info.gpu = vk->context->gpu;
info.memory_properties = &vk->context->memory_properties;
@ -2844,15 +2847,15 @@ static bool vulkan_init_default_filter_chain(vk_t *vk)
info.swapchain.render_pass = vk->render_pass;
info.swapchain.num_indices = vk->context->num_swapchain_images;
vk->filter_chain = vulkan_filter_chain_create_default(
vk->filter_chain_default = vulkan_filter_chain_create_default(
&info,
vk->video.smooth
? GLSLANG_FILTER_CHAIN_LINEAR
: GLSLANG_FILTER_CHAIN_NEAREST);
if (!vk->filter_chain)
if (!vk->filter_chain_default)
{
RARCH_ERR("Failed to create filter chain.\n");
RARCH_ERR("[Vulkan]: Failed to create default filter chain.\n");
return false;
}
@ -2860,11 +2863,11 @@ static bool vulkan_init_default_filter_chain(vk_t *vk)
if (vk->context->flags & VK_CTX_FLAG_HDR_ENABLE)
{
struct video_shader* shader_preset = vulkan_filter_chain_get_preset(
vk->filter_chain);
vk->filter_chain_default);
VkFormat rt_format = (shader_preset && shader_preset->passes)
? vulkan_filter_chain_get_pass_rt_format(vk->filter_chain, shader_preset->passes - 1)
? vulkan_filter_chain_get_pass_rt_format(vk->filter_chain_default, shader_preset->passes - 1)
: VK_FORMAT_UNDEFINED;
bool emits_hdr10 = shader_preset && shader_preset->passes && vulkan_filter_chain_emits_hdr10(vk->filter_chain);
bool emits_hdr10 = shader_preset && shader_preset->passes && vulkan_filter_chain_emits_hdr10(vk->filter_chain_default);
if (vulkan_is_hdr10_format(rt_format))
{
@ -2901,6 +2904,9 @@ static bool vulkan_init_filter_chain_preset(vk_t *vk, const char *shader_path)
{
struct vulkan_filter_chain_create_info info;
if (!vk->context)
return false;
info.device = vk->context->device;
info.gpu = vk->context->gpu;
info.memory_properties = &vk->context->memory_properties;
@ -3106,6 +3112,9 @@ static void vulkan_free(void *data)
if (vk->filter_chain)
vulkan_filter_chain_free((vulkan_filter_chain_t*)vk->filter_chain);
if (vk->filter_chain_default)
vulkan_filter_chain_free((vulkan_filter_chain_t*)vk->filter_chain_default);
#ifdef VULKAN_HDR_SWAPCHAIN
if (vk->context->flags & VK_CTX_FLAG_HDR_SUPPORT)
{
@ -3647,10 +3656,10 @@ static void vulkan_check_swapchain(vk_t *vk)
filter_info.num_indices = vk->context->num_swapchain_images;
if (
!vulkan_filter_chain_update_swapchain_info(
(vulkan_filter_chain_t*)vk->filter_chain,
(vk->filter_chain) ? vk->filter_chain : vk->filter_chain_default,
&filter_info)
)
RARCH_ERR("Failed to update filter chain info. This will probably lead to a crash ...\n");
RARCH_ERR("[Vulkan]: Failed to update filter chain info.\n");
}
static void vulkan_set_nonblock_state(void *data, bool state,
@ -4365,6 +4374,7 @@ static bool vulkan_frame(void *data, const void *frame,
VkCommandBufferBeginInfo begin_info;
VkSemaphore signal_semaphores[2];
vk_t *vk = (vk_t*)data;
vulkan_filter_chain_t *filter_chain = NULL;
bool waits_for_semaphores = false;
unsigned width = video_info->width;
unsigned height = video_info->height;
@ -4391,11 +4401,29 @@ static bool vulkan_frame(void *data, const void *frame,
unsigned swapchain_index =
vk->context->current_swapchain_index;
bool overlay_behind_menu = video_info->overlay_behind_menu;
bool use_main_buffer = true;
/* Fast toggle shader filter chain logic */
filter_chain = vk->filter_chain;
if (!video_info->shader_active && vk->filter_chain != vk->filter_chain_default)
{
if (!vk->filter_chain_default)
vulkan_init_default_filter_chain(vk);
if (vk->filter_chain_default)
filter_chain = vk->filter_chain_default;
else
return false;
}
if (!filter_chain && vk->filter_chain_default)
filter_chain = vk->filter_chain_default;
#ifdef VULKAN_HDR_SWAPCHAIN
bool use_main_buffer =
use_main_buffer =
( vk->context->flags & VK_CTX_FLAG_HDR_ENABLE)
&& (!vk->filter_chain || !vulkan_filter_chain_emits_hdr10(vk->filter_chain));
&& (!filter_chain || !vulkan_filter_chain_emits_hdr10(filter_chain));
#endif /* VULKAN_HDR_SWAPCHAIN */
/* Bookkeeping on start of frame. */
@ -4516,9 +4544,9 @@ static bool vulkan_frame(void *data, const void *frame,
/* Notify filter chain about the new sync index. */
vulkan_filter_chain_notify_sync_index(
(vulkan_filter_chain_t*)vk->filter_chain, frame_index);
(vulkan_filter_chain_t*)filter_chain, frame_index);
vulkan_filter_chain_set_frame_count(
(vulkan_filter_chain_t*)vk->filter_chain, frame_count);
(vulkan_filter_chain_t*)filter_chain, frame_count);
/* Sub-frame info for multiframe shaders (per real content frame).
Should always be 1 for non-use of subframes*/
@ -4531,13 +4559,13 @@ static bool vulkan_frame(void *data, const void *frame,
|| (vk->context->swap_interval > 1)
|| (vk->flags & VK_FLAG_MENU_ENABLE))
vulkan_filter_chain_set_shader_subframes(
(vulkan_filter_chain_t*)vk->filter_chain, 1);
(vulkan_filter_chain_t*)filter_chain, 1);
else
vulkan_filter_chain_set_shader_subframes(
(vulkan_filter_chain_t*)vk->filter_chain, video_info->shader_subframes);
(vulkan_filter_chain_t*)filter_chain, video_info->shader_subframes);
vulkan_filter_chain_set_current_shader_subframe(
(vulkan_filter_chain_t*)vk->filter_chain, 1);
(vulkan_filter_chain_t*)filter_chain, 1);
}
#ifdef VULKAN_ROLLING_SCANLINE_SIMULATION
@ -4551,32 +4579,32 @@ static bool vulkan_frame(void *data, const void *frame,
&& (!(vk->flags & VK_FLAG_MENU_ENABLE))
&& !(vk->context->swap_interval > 1))
vulkan_filter_chain_set_simulate_scanline(
(vulkan_filter_chain_t*)vk->filter_chain, true);
(vulkan_filter_chain_t*)filter_chain, true);
else
vulkan_filter_chain_set_simulate_scanline(
(vulkan_filter_chain_t*)vk->filter_chain, false);
(vulkan_filter_chain_t*)filter_chain, false);
#endif /* VULKAN_ROLLING_SCANLINE_SIMULATION */
#ifdef HAVE_REWIND
vulkan_filter_chain_set_frame_direction(
(vulkan_filter_chain_t*)vk->filter_chain,
(vulkan_filter_chain_t*)filter_chain,
state_manager_frame_is_reversed() ? -1 : 1);
#else
vulkan_filter_chain_set_frame_direction(
(vulkan_filter_chain_t*)vk->filter_chain,
(vulkan_filter_chain_t*)filter_chain,
1);
#endif
vulkan_filter_chain_set_frame_time_delta(
(vulkan_filter_chain_t*)vk->filter_chain, (uint32_t)video_driver_get_frame_time_delta_usec());
(vulkan_filter_chain_t*)filter_chain, (uint32_t)video_driver_get_frame_time_delta_usec());
vulkan_filter_chain_set_original_fps(
(vulkan_filter_chain_t*)vk->filter_chain, video_driver_get_original_fps());
(vulkan_filter_chain_t*)filter_chain, video_driver_get_original_fps());
vulkan_filter_chain_set_rotation(
(vulkan_filter_chain_t*)vk->filter_chain, retroarch_get_rotation());
(vulkan_filter_chain_t*)filter_chain, retroarch_get_rotation());
vulkan_filter_chain_set_core_aspect(
(vulkan_filter_chain_t*)vk->filter_chain, video_driver_get_core_aspect());
(vulkan_filter_chain_t*)filter_chain, video_driver_get_core_aspect());
/* OriginalAspectRotated: return 1/aspect for 90 and 270 rotated content */
uint32_t rot = retroarch_get_rotation();
@ -4584,7 +4612,7 @@ static bool vulkan_frame(void *data, const void *frame,
if (rot == 1 || rot == 3)
core_aspect_rot = 1/core_aspect_rot;
vulkan_filter_chain_set_core_aspect_rot(
(vulkan_filter_chain_t*)vk->filter_chain, core_aspect_rot);
(vulkan_filter_chain_t*)filter_chain, core_aspect_rot);
/* Render offscreen filter chain passes. */
{
@ -4648,13 +4676,13 @@ static bool vulkan_frame(void *data, const void *frame,
}
vulkan_filter_chain_set_input_texture((vulkan_filter_chain_t*)
vk->filter_chain, &input);
filter_chain, &input);
}
vulkan_set_viewport(vk, width, height, false, true);
vulkan_filter_chain_build_offscreen_passes(
(vulkan_filter_chain_t*)vk->filter_chain,
(vulkan_filter_chain_t*)filter_chain,
vk->cmd, &vk->vk_vp);
#if defined(HAVE_MENU)
@ -4712,7 +4740,7 @@ static bool vulkan_frame(void *data, const void *frame,
vkCmdBeginRenderPass(vk->cmd, &rp_info, VK_SUBPASS_CONTENTS_INLINE);
vulkan_filter_chain_build_viewport_pass(
(vulkan_filter_chain_t*)vk->filter_chain, vk->cmd,
(vulkan_filter_chain_t*)filter_chain, vk->cmd,
&vk->vk_vp, vk->mvp.data);
#ifdef HAVE_OVERLAY
@ -4802,7 +4830,7 @@ static bool vulkan_frame(void *data, const void *frame,
/* End the filter chain frame.
* This must happen outside a render pass.
*/
vulkan_filter_chain_end_frame((vulkan_filter_chain_t*)vk->filter_chain, vk->cmd);
vulkan_filter_chain_end_frame((vulkan_filter_chain_t*)filter_chain, vk->cmd);
if (
(backbuffer->image != VK_NULL_HANDLE)
@ -5115,9 +5143,9 @@ static bool vulkan_frame(void *data, const void *frame,
for (j = 1; j < (int) video_info->shader_subframes; j++)
{
vulkan_filter_chain_set_shader_subframes(
(vulkan_filter_chain_t*)vk->filter_chain, video_info->shader_subframes);
(vulkan_filter_chain_t*)filter_chain, video_info->shader_subframes);
vulkan_filter_chain_set_current_shader_subframe(
(vulkan_filter_chain_t*)vk->filter_chain, j+1);
(vulkan_filter_chain_t*)filter_chain, j+1);
if (!vulkan_frame(vk, NULL, 0, 0, frame_count, 0, msg,
video_info))
{
@ -5466,6 +5494,7 @@ static uint32_t vulkan_get_flags(void *data)
BIT32_SET(flags, GFX_CTX_FLAGS_SCREENSHOTS_SUPPORTED);
BIT32_SET(flags, GFX_CTX_FLAGS_OVERLAY_BEHIND_MENU_SUPPORTED);
BIT32_SET(flags, GFX_CTX_FLAGS_SUBFRAME_SHADERS);
BIT32_SET(flags, GFX_CTX_FLAGS_FAST_TOGGLE_SHADERS);
return flags;
}

View File

@ -63,16 +63,6 @@ static void gfx_ctx_emscripten_check_window(void *data, bool *quit,
*quit = false;
}
static void gfx_ctx_emscripten_swap_buffers(void *data)
{
#ifdef HAVE_EGL
/* Doesn't really do anything in WebGL, but it might
* if we use WebGL workers in the future */
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
egl_swap_buffers(&emscripten->egl);
#endif
}
static void gfx_ctx_emscripten_get_video_size(void *data,
unsigned *width, unsigned *height)
{
@ -181,11 +171,11 @@ error:
}
static bool gfx_ctx_emscripten_set_video_mode(void *data,
unsigned width, unsigned height,
bool fullscreen)
unsigned width, unsigned height, bool fullscreen)
{
if (g_egl_inited)
return false;
platform_emscripten_set_fullscreen_state(fullscreen);
if (!fullscreen)
platform_emscripten_set_canvas_size(width, height);
g_egl_inited = true;
return true;
@ -212,9 +202,20 @@ static void gfx_ctx_emscripten_input_driver(void *data,
*input_data = rwebinput;
}
static bool gfx_ctx_emscripten_has_focus(void *data) { return g_egl_inited; }
static bool gfx_ctx_emscripten_has_focus(void *data) {
return g_egl_inited && !platform_emscripten_is_window_hidden();
}
static bool gfx_ctx_emscripten_suppress_screensaver(void *data, bool enable) { return false; }
static bool gfx_ctx_emscripten_suppress_screensaver(void *data, bool enable)
{
platform_emscripten_set_wake_lock(enable);
return true;
}
static void gfx_ctx_emscripten_show_mouse(void *data, bool state)
{
platform_emscripten_set_pointer_visibility(state);
}
static float gfx_ctx_emscripten_translate_aspect(void *data,
unsigned width, unsigned height) { return (float)width / height; }
@ -259,11 +260,11 @@ const gfx_ctx_driver_t gfx_ctx_emscripten = {
gfx_ctx_emscripten_translate_aspect,
NULL, /* update_title */
gfx_ctx_emscripten_check_window,
NULL, /* set_resize */
NULL, /* set_resize: no-op */
gfx_ctx_emscripten_has_focus,
gfx_ctx_emscripten_suppress_screensaver,
false,
gfx_ctx_emscripten_swap_buffers,
true, /* has_windowed */
NULL, /* swap_buffers: no-op */
gfx_ctx_emscripten_input_driver,
#ifdef HAVE_EGL
egl_get_proc_address,
@ -272,11 +273,11 @@ const gfx_ctx_driver_t gfx_ctx_emscripten = {
#endif
gfx_ctx_emscripten_init_egl_image_buffer,
gfx_ctx_emscripten_write_egl_image,
NULL,
gfx_ctx_emscripten_show_mouse,
"egl_emscripten",
gfx_ctx_emscripten_get_flags,
gfx_ctx_emscripten_set_flags,
gfx_ctx_emscripten_bind_hw_render,
NULL,
NULL
NULL, /* get_context_data */
NULL /* make_current */
};

View File

@ -57,12 +57,6 @@ static void gfx_ctx_emscripten_webgl_check_window(void *data, bool *quit,
*quit = false;
}
/* https://github.com/emscripten-core/emscripten/issues/17816#issuecomment-1249719343 */
static void gfx_ctx_emscripten_webgl_swap_buffers(void *data)
{
(void)data;
}
static void gfx_ctx_emscripten_webgl_get_video_size(void *data,
unsigned *width, unsigned *height)
{
@ -114,7 +108,7 @@ static void *gfx_ctx_emscripten_webgl_init(void *video_driver)
EmscriptenWebGLContextAttributes attrs = {0};
emscripten_webgl_init_context_attributes(&attrs);
attrs.alpha = false;
attrs.alpha = true;
attrs.depth = true;
attrs.stencil = true;
attrs.antialias = false;
@ -152,57 +146,24 @@ error:
return NULL;
}
static bool gfx_ctx_emscripten_webgl_set_canvas_size(int width, int height)
{
#ifdef NO_CANVAS_RESIZE
return false;
#endif
double dpr = platform_emscripten_get_dpr();
EMSCRIPTEN_RESULT r = emscripten_set_element_css_size("#canvas", (double)width / dpr, (double)height / dpr);
RARCH_LOG("[EMSCRIPTEN/WebGL]: set canvas size to %d, %d\n", width, height);
if (r != EMSCRIPTEN_RESULT_SUCCESS)
{
RARCH_ERR("[EMSCRIPTEN/WebGL]: error resizing canvas: %d\n", r);
return false;
}
return true;
}
static bool gfx_ctx_emscripten_webgl_set_video_mode(void *data,
unsigned width, unsigned height,
bool fullscreen)
unsigned width, unsigned height, bool fullscreen)
{
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
if (!emscripten || !emscripten->ctx)
return false;
if (width != 0 && height != 0)
{
if (!gfx_ctx_emscripten_webgl_set_canvas_size(width, height))
return false;
}
emscripten->fb_width = width;
emscripten->fb_height = height;
platform_emscripten_set_fullscreen_state(fullscreen);
if (!fullscreen)
platform_emscripten_set_canvas_size(width, height);
return true;
}
bool gfx_ctx_emscripten_webgl_set_resize(void *data, unsigned width, unsigned height)
{
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
if (!emscripten || !emscripten->ctx)
return false;
return gfx_ctx_emscripten_webgl_set_canvas_size(width, height);
}
static enum gfx_ctx_api gfx_ctx_emscripten_webgl_get_api(void *data) { return GFX_CTX_OPENGL_ES_API; }
static bool gfx_ctx_emscripten_webgl_bind_api(void *data,
enum gfx_ctx_api api, unsigned major, unsigned minor)
{
return true;
}
enum gfx_ctx_api api, unsigned major, unsigned minor) { return true; }
static void gfx_ctx_emscripten_webgl_input_driver(void *data,
const char *name,
@ -216,10 +177,19 @@ static void gfx_ctx_emscripten_webgl_input_driver(void *data,
static bool gfx_ctx_emscripten_webgl_has_focus(void *data)
{
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
return emscripten && emscripten->ctx;
return emscripten && emscripten->ctx && !platform_emscripten_is_window_hidden();
}
static bool gfx_ctx_emscripten_webgl_suppress_screensaver(void *data, bool enable) { return false; }
static bool gfx_ctx_emscripten_webgl_suppress_screensaver(void *data, bool enable)
{
platform_emscripten_set_wake_lock(enable);
return true;
}
static void gfx_ctx_emscripten_webgl_show_mouse(void *data, bool state)
{
platform_emscripten_set_pointer_visibility(state);
}
static float gfx_ctx_emscripten_webgl_translate_aspect(void *data,
unsigned width, unsigned height) { return (float)width / height; }
@ -239,6 +209,11 @@ static uint32_t gfx_ctx_emscripten_webgl_get_flags(void *data)
static void gfx_ctx_emscripten_webgl_set_flags(void *data, uint32_t flags) { }
static gfx_ctx_proc_t gfx_ctx_emscripten_webgl_get_proc_address(const char *symbol)
{
return emscripten_webgl_get_proc_address(symbol);
}
const gfx_ctx_driver_t gfx_ctx_emscripten_webgl = {
gfx_ctx_emscripten_webgl_init,
gfx_ctx_emscripten_webgl_destroy,
@ -255,16 +230,16 @@ const gfx_ctx_driver_t gfx_ctx_emscripten_webgl = {
gfx_ctx_emscripten_webgl_translate_aspect,
NULL, /* update_title */
gfx_ctx_emscripten_webgl_check_window,
gfx_ctx_emscripten_webgl_set_resize,
NULL, /* set_resize: no-op */
gfx_ctx_emscripten_webgl_has_focus,
gfx_ctx_emscripten_webgl_suppress_screensaver,
false,
gfx_ctx_emscripten_webgl_swap_buffers,
true, /* has_windowed */
NULL, /* swap_buffers: no-op: https://github.com/emscripten-core/emscripten/issues/17816#issuecomment-1249719343 */
gfx_ctx_emscripten_webgl_input_driver,
gfx_ctx_emscripten_webgl_get_proc_address,
NULL,
NULL,
NULL,
NULL,
gfx_ctx_emscripten_webgl_show_mouse,
"webgl_emscripten",
gfx_ctx_emscripten_webgl_get_flags,
gfx_ctx_emscripten_webgl_set_flags,

View File

@ -109,7 +109,7 @@ static void *sdl_ctx_init(void *video_driver)
#ifdef WEBOS
SDL_SetHint(SDL_HINT_WEBOS_ACCESS_POLICY_KEYS_BACK, "true");
SDL_SetHint(SDL_HINT_WEBOS_ACCESS_POLICY_KEYS_EXIT, "true");
SDL_SetHint(SDL_HINT_WEBOS_CURSOR_SLEEP_TIME, "1000");
SDL_SetHint(SDL_HINT_WEBOS_CURSOR_SLEEP_TIME, "5000");
#endif
/* Initialise graphics subsystem, if required */

View File

@ -403,6 +403,10 @@ static const char *font_paths[] = {
"/Library/Fonts/Microsoft/Candara.ttf",
"/Library/Fonts/Verdana.ttf",
"/Library/Fonts/Tahoma.ttf",
#elif defined(WEBOS)
"/usr/share/fonts/MuseoSans-Medium.ttf",
"/usr/share/fonts/LG_Smart_UI-Regular.ttf",
"/usr/share/fonts/DroidSans.ttf",
#else
"/usr/share/fonts/TTF/DejaVuSansMono.ttf",
"/usr/share/fonts/TTF/DejaVuSans.ttf",

View File

@ -248,7 +248,8 @@ enum display_flags
GFX_CTX_FLAGS_SCREENSHOTS_SUPPORTED,
GFX_CTX_FLAGS_OVERLAY_BEHIND_MENU_SUPPORTED,
GFX_CTX_FLAGS_CRT_SWITCHRES,
GFX_CTX_FLAGS_SUBFRAME_SHADERS
GFX_CTX_FLAGS_SUBFRAME_SHADERS,
GFX_CTX_FLAGS_FAST_TOGGLE_SHADERS
};
enum shader_uniform_type

View File

@ -2548,9 +2548,12 @@ void video_viewport_get_scaled_integer(struct video_viewport *vp,
float underscale_ratio = 0;
float overscale_ratio = 0;
uint16_t content_width_ar = content_width;
uint16_t height_threshold = height * 1.12f;
uint8_t overscale_w = 0;
uint8_t overscale_h = 0;
uint8_t i = 0;
bool hires_w = false;
bool hires_h = false;
/* Reset width to exact width */
content_width = (rotation % 2)
@ -2573,7 +2576,7 @@ void video_viewport_get_scaled_integer(struct video_viewport *vp,
}
if (scaling == VIDEO_SCALE_INTEGER_SCALING_SMART)
max_scale_h = ((int)(height - content_height * overscale_h) < -(int)(height * 0.20f))
max_scale_h = ((content_height * overscale_h) > height_threshold)
? overscale_h - 1
: overscale_h;
else if (scaling == VIDEO_SCALE_INTEGER_SCALING_OVERSCALE)
@ -2601,46 +2604,82 @@ void video_viewport_get_scaled_integer(struct video_viewport *vp,
else if (i > 1)
max_scale_w = i - 1;
/* Special half width scale for hi-res */
if ( axis == VIDEO_SCALE_INTEGER_AXIS_Y_XHALF
|| axis == VIDEO_SCALE_INTEGER_AXIS_YHALF_XHALF
|| axis == VIDEO_SCALE_INTEGER_AXIS_XHALF)
{
float scale_w_ratio = (float)(content_width * max_scale_w) / (float)(content_height * max_scale_h);
uint8_t hires_w = content_width / 512;
int content_width_diff = content_width_ar - (content_width / (hires_w + 1));
if ( content_width_ar - content_width_diff == (int)content_width / 2
&& content_width_diff < 20
&& scale_w_ratio - target_ratio > 0.25f)
half_w = -1;
}
/* Decide hi-res source */
hires_w = content_width / 512;
hires_h = content_height / ((rotation % 2) ? 288 : 300);
/* Special half height scale for hi-res */
if ( axis == VIDEO_SCALE_INTEGER_AXIS_YHALF_XHALF
|| axis == VIDEO_SCALE_INTEGER_AXIS_XHALF)
if ( (hires_h)
&& (axis == VIDEO_SCALE_INTEGER_AXIS_YHALF_XHALF)
&& (scaling != VIDEO_SCALE_INTEGER_SCALING_UNDERSCALE))
{
if ( max_scale_h == (height / content_height)
&& content_height / ((rotation % 2) ? 288 : 300)
&& content_height * max_scale_h < height * 0.90f
&& content_height * (max_scale_h + 0.5f) < height_threshold
)
half_h = 1;
}
/* Special half width scale for hi-res */
if ( (hires_w || hires_h)
&& ( axis == VIDEO_SCALE_INTEGER_AXIS_Y_XHALF
|| axis == VIDEO_SCALE_INTEGER_AXIS_YHALF_XHALF
|| axis == VIDEO_SCALE_INTEGER_AXIS_XHALF))
{
float diff = 1.0f;
uint8_t mode = 0;
/* Reset current target ratio for stable width matching */
if (!hires_w && half_h)
target_ratio = (float)(content_width * max_scale_w) / (float)(content_height * max_scale_h);
/* Find the nearest ratio */
for (i = 0; i < 4; i++)
{
float halfstep_prev_ratio = (float)(content_width * max_scale_w) / (float)(content_height * max_scale_h);
float halfstep_next_ratio = (float)(content_width * max_scale_w) / (float)(content_height * (max_scale_h + 0.5f));
float diff_mode = content_width;
if (content_height * (max_scale_h + 0.5f) < height * 1.12f)
/* Skip half scales with lo-res width */
if (!hires_w && !(i % 2))
continue;
switch (i)
{
half_h = 1;
if (halfstep_next_ratio - target_ratio <= target_ratio - halfstep_prev_ratio)
half_w = 1;
case 0: diff_mode *= (max_scale_w - 0.5f); break;
case 1: diff_mode *= (max_scale_w); break;
case 2: diff_mode *= (max_scale_w + 0.5f); break;
case 3: diff_mode *= (max_scale_w + 1.0f); break;
}
diff_mode /= (content_height * (max_scale_h + (half_h * 0.5f)));
diff_mode = fabsf(diff_mode - target_ratio);
if (diff_mode <= diff)
{
diff = diff_mode;
mode = i;
}
}
switch (mode)
{
case 0: half_w = -1; break;
case 1: half_w = 0; break;
case 2: half_w = 1; break;
case 3: half_w = 2; break;
}
}
}
padding_x = width - content_width * (max_scale_w + (half_w * 0.5f));
padding_y = height - content_height * (max_scale_h + (half_h * 0.5f));
/* Use regular scaling if overscale is unreasonable */
if ( padding_x <= (int)-video_st->av_info.geometry.base_width
|| padding_y <= (int)-video_st->av_info.geometry.base_height)
{
video_viewport_get_scaled_aspect(vp, width, height, y_down);
return;
}
}
else
{
@ -2817,7 +2856,15 @@ void video_driver_build_info(video_frame_info_t *video_info)
input_driver_state_t *input_st = input_state_get_ptr();
#ifdef HAVE_MENU
struct menu_state *menu_st = menu_state_get_ptr();
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
struct video_shader *menu_shader = menu_shader_get();
#else
struct video_shader *menu_shader = NULL;
#endif
#else
struct video_shader *menu_shader = NULL;
#endif /* HAVE_MENU */
uint8_t menu_shdr_flags = (menu_shader) ? menu_shader->flags : 0;
#ifdef HAVE_GFX_WIDGETS
dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
#endif
@ -2827,6 +2874,7 @@ void video_driver_build_info(video_frame_info_t *video_info)
VIDEO_DRIVER_THREADED_LOCK(video_st, is_threaded);
#endif
custom_vp = &settings->video_vp_custom;
#ifdef HAVE_GFX_WIDGETS
video_info->widgets_active = p_dispwidget->active;
@ -2896,6 +2944,7 @@ void video_driver_build_info(video_frame_info_t *video_info)
video_info->scale_width = video_st->scale_width;
video_info->scale_height = video_st->scale_height;
video_info->shader_active = !(menu_shdr_flags & SHDR_FLAG_DISABLED) ? true : false;
video_info->hdr_enable = settings->bools.video_hdr_enable;
video_info->libretro_running = false;
@ -4573,9 +4622,6 @@ void video_frame_delay(video_driver_state_t *video_st,
|| (runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION)
|| (runloop_st->flags & RUNLOOP_FLAG_FASTMOTION);
/* Black frame insertion + swap interval multiplier */
refresh_rate = (refresh_rate / (video_bfi + 1.0f) / video_swap_interval / shader_subframes);
/* Treat values 20+ as frame time percentage */
if (video_frame_delay >= 20)
video_frame_delay = 1 / refresh_rate * 1000 * (video_frame_delay / 100.0f);
@ -4583,6 +4629,9 @@ void video_frame_delay(video_driver_state_t *video_st,
else if (video_frame_delay == 0 && settings->bools.video_frame_delay_auto)
video_frame_delay = 1 / refresh_rate * 1000 * 0.75f;
/* Black frame insertion + swap interval multiplier */
refresh_rate = (refresh_rate / (video_bfi + 1.0f) / video_swap_interval / shader_subframes);
if (settings->bools.video_frame_delay_auto)
{
static uint8_t skip_update = 0;

View File

@ -494,6 +494,7 @@ typedef struct video_frame_info
bool hdr_enable;
bool overlay_behind_menu;
bool scan_subframes;
bool shader_active;
} video_frame_info_t;
typedef void (*update_window_title_cb)(void*);

View File

@ -3140,27 +3140,41 @@ const char *video_shader_get_current_shader_preset(void)
return NULL;
}
void video_shader_toggle(settings_t *settings)
void video_shader_toggle(settings_t *settings, bool write)
{
bool toggle = !settings->bools.video_shader_enable;
bool enabled = settings->bools.video_shader_enable;
#ifdef HAVE_MENU
struct video_shader *menu_shdr = menu_shader_get();
struct menu_state *menu_st = menu_state_get_ptr();
menu_shdr->flags |= SHDR_FLAG_MODIFIED;
if (toggle)
menu_shdr->flags &= ~SHDR_FLAG_MODIFIED;
menu_shdr->flags &= ~SHDR_FLAG_TEMPORARY;
#endif
/* Cold start from hotkey requires enabling shaders initially */
if (!write && !enabled)
{
write = true;
enabled = true;
configuration_set_bool(settings, settings->bools.video_shader_enable, true);
}
#ifdef HAVE_MENU
else if (!write)
enabled = (menu_shdr->flags & SHDR_FLAG_DISABLED);
#endif
#ifdef HAVE_MENU
if (enabled)
menu_shdr->flags &= ~SHDR_FLAG_DISABLED;
else
menu_shdr->flags |= SHDR_FLAG_DISABLED;
if (!write && video_driver_test_all_flags(GFX_CTX_FLAGS_FAST_TOGGLE_SHADERS))
menu_shdr->flags |= SHDR_FLAG_TEMPORARY;
menu_st->flags |= MENU_ST_FLAG_ENTRIES_NEED_REFRESH
| MENU_ST_FLAG_PREVENT_POPULATE;
#endif
command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL);
/* TODO/FIXME: Due to general_write_handler being called twice,
* this has be done in this order in order to truly disable */
if (!toggle)
configuration_set_bool(settings, settings->bools.video_shader_enable, toggle);
}

View File

@ -88,7 +88,8 @@ enum video_shader_flags
/* Indicative of whether shader was modified -
* for instance from the menus */
SHDR_FLAG_MODIFIED = (1 << 1),
SHDR_FLAG_DISABLED = (1 << 2)
SHDR_FLAG_DISABLED = (1 << 2),
SHDR_FLAG_TEMPORARY = (1 << 3)
};
enum gfx_wrap_type
@ -289,7 +290,7 @@ bool video_shader_apply_shader(
const char *video_shader_get_preset_extension(enum rarch_shader_type type);
void video_shader_toggle(settings_t *settings);
void video_shader_toggle(settings_t *settings, bool write);
RETRO_END_DECLS

View File

@ -857,9 +857,6 @@ RSOUND
AUDIO
============================================================ */
#include "../audio/audio_driver.c"
#ifdef HAVE_MICROPHONE
#include "../audio/microphone_driver.c"
#endif
#if defined(__PS3__) || defined (__PSL1GHT__)
#include "../audio/drivers/ps3_audio.c"
#elif defined(XENON)

View File

@ -85,3 +85,7 @@
#if defined(HAVE_CLOUDSYNC) && defined(HAVE_ICLOUD)
#include "../network/cloud_sync/icloud.m"
#endif
#if defined(HAVE_CLOUDSYNC) && defined(HAVE_ICLOUD_DRIVE)
#include "../network/cloud_sync/icloud_drive.m"
#endif

View File

@ -806,6 +806,10 @@ static void wl_registry_handle_global(void *data, struct wl_registry *reg,
wl->single_pixel_manager = (struct wp_single_pixel_buffer_manager_v1*)
wl_registry_bind(
reg, id, &wp_single_pixel_buffer_manager_v1_interface, MIN(version, 1));
else if (string_is_equal(interface, xdg_toplevel_icon_manager_v1_interface.name) && found++)
wl->xdg_toplevel_icon_manager = (struct xdg_toplevel_icon_manager_v1*)
wl_registry_bind(
reg, id, &xdg_toplevel_icon_manager_v1_interface, MIN(version, 1));
if (found > 1)
RARCH_LOG("[Wayland]: Registered interface %s at version %u\n",

View File

@ -44,6 +44,7 @@
#include "../../gfx/common/wayland/viewporter.h"
#include "../../gfx/common/wayland/xdg-decoration-unstable-v1.h"
#include "../../gfx/common/wayland/xdg-shell.h"
#include "../../gfx/common/wayland/xdg-toplevel-icon-v1.h"
#define FRACTIONAL_SCALE_V1_DEN 120
#define FRACTIONAL_SCALE_MULT(v, scale_num) \
@ -159,6 +160,8 @@ typedef struct gfx_ctx_wayland_data
struct wp_fractional_scale_v1 *fractional_scale;
struct xdg_wm_base *xdg_shell;
struct xdg_toplevel *xdg_toplevel;
struct xdg_toplevel_icon_v1 *xdg_toplevel_icon;
struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager;
struct wl_keyboard *wl_keyboard;
struct wl_pointer *wl_pointer;
struct zwp_relative_pointer_v1 *wl_relative_pointer;
@ -172,6 +175,7 @@ typedef struct gfx_ctx_wayland_data
#ifdef HAVE_LIBDECOR_H
struct libdecor *libdecor_context;
struct libdecor_frame *libdecor_frame;
struct xdg_toplevel_icon_v1 *libdecor_icon;
#ifdef HAVE_DYLIB
dylib_t libdecor;
#define RA_WAYLAND_SYM(rc,fn,params) rc (*fn) params;

View File

@ -83,7 +83,8 @@ enum dinput_input_flags
DINP_FLAG_MOUSE_WU_BTN = (1 << 10),
DINP_FLAG_MOUSE_WD_BTN = (1 << 11),
DINP_FLAG_MOUSE_HWU_BTN = (1 << 12),
DINP_FLAG_MOUSE_HWD_BTN = (1 << 13)
DINP_FLAG_MOUSE_HWD_BTN = (1 << 13),
DINP_FLAG_MOUSE_IGNORE = (1 << 14)
};
struct dinput_input
@ -390,6 +391,15 @@ static void dinput_poll(void *data)
ScreenToClient((HWND)video_driver_window_get(), &point);
di->mouse_x = point.x;
di->mouse_y = point.y;
/* Ignore application focusing mouse clicks */
if (di->flags & DINP_FLAG_MOUSE_IGNORE)
{
if (mouse_state.rgbButtons[0] || mouse_state.rgbButtons[1])
di->flags &= ~(DINP_FLAG_MOUSE_L_BTN | DINP_FLAG_MOUSE_R_BTN);
else if (!mouse_state.rgbButtons[0] && !mouse_state.rgbButtons[1])
di->flags &= ~DINP_FLAG_MOUSE_IGNORE;
}
}
}
@ -893,6 +903,10 @@ bool dinput_handle_message(void *data,
switch (message)
{
case WM_SETFOCUS:
case WM_KILLFOCUS:
di->flags |= DINP_FLAG_MOUSE_IGNORE;
break;
case WM_NCLBUTTONDBLCLK:
di->flags |= DINP_FLAG_DBCLK_ON_TITLEBAR;
break;

View File

@ -87,11 +87,21 @@ typedef struct rwebinput_mouse_states
uint8_t buttons;
} rwebinput_mouse_state_t;
typedef struct rwebinput_motion_states
{
float x;
float y;
float z;
bool enabled;
} rwebinput_motion_state_t;
typedef struct rwebinput_input
{
rwebinput_mouse_state_t mouse; /* double alignment */
rwebinput_keyboard_event_queue_t keyboard; /* ptr alignment */
rwebinput_pointer_state_t pointer[MAX_TOUCH]; /* int alignment */
rwebinput_motion_state_t accelerometer; /* float alignment */
rwebinput_motion_state_t gyroscope; /* float alignment */
unsigned pointer_count;
bool keys[RETROK_LAST];
bool pointerlock_active;
@ -406,6 +416,26 @@ static EM_BOOL rwebinput_pointerlockchange_cb(int event_type,
return EM_TRUE;
}
static EM_BOOL rwebinput_devicemotion_cb(int event_type,
const EmscriptenDeviceMotionEvent *device_motion_event, void *user_data)
{
rwebinput_input_t *rwebinput = (rwebinput_input_t*)user_data;
/* TODO: what units does mGBA want? does something need to be changed on the core side? */
/* given in m/s^2 (inverted) */
rwebinput->accelerometer.x = device_motion_event->accelerationIncludingGravityX / -9.8;
rwebinput->accelerometer.y = device_motion_event->accelerationIncludingGravityY / -9.8;
rwebinput->accelerometer.z = device_motion_event->accelerationIncludingGravityZ / -9.8;
/* XYZ == BetaGammaAlpha according to W3C? in my testing it is AlphaBetaGamma... */
/* libretro wants radians/s but it is too fast in mGBA, see above comment */
/* given in degrees/s */
rwebinput->gyroscope.x = device_motion_event->rotationRateAlpha / 180;
rwebinput->gyroscope.y = device_motion_event->rotationRateBeta / 180;
rwebinput->gyroscope.z = device_motion_event->rotationRateGamma / 180;
return EM_TRUE;
}
static void *rwebinput_input_init(const char *joypad_driver)
{
EMSCRIPTEN_RESULT r;
@ -746,6 +776,84 @@ static void rwebinput_input_free(void *data)
free(data);
}
static bool rwebinput_set_sensor_state(void *data, unsigned port,
enum retro_sensor_action action, unsigned rate)
{
rwebinput_input_t *rwebinput = (rwebinput_input_t*)data;
EMSCRIPTEN_RESULT r;
bool old_state = rwebinput->accelerometer.enabled || rwebinput->gyroscope.enabled;
bool new_state;
switch (action)
{
case RETRO_SENSOR_ACCELEROMETER_ENABLE:
rwebinput->accelerometer.enabled = true;
break;
case RETRO_SENSOR_ACCELEROMETER_DISABLE:
rwebinput->accelerometer.enabled = false;
break;
case RETRO_SENSOR_GYROSCOPE_ENABLE:
rwebinput->gyroscope.enabled = true;
break;
case RETRO_SENSOR_GYROSCOPE_DISABLE:
rwebinput->gyroscope.enabled = false;
break;
case RETRO_SENSOR_ILLUMINANCE_ENABLE:
case RETRO_SENSOR_ILLUMINANCE_DISABLE:
return false; /* not supported (browsers removed support for now) */
default:
return false;
}
new_state = rwebinput->accelerometer.enabled || rwebinput->gyroscope.enabled;
if (!old_state && new_state)
{
r = emscripten_set_devicemotion_callback(rwebinput, false, rwebinput_devicemotion_cb);
if (r != EMSCRIPTEN_RESULT_SUCCESS)
{
RARCH_ERR(
"[EMSCRIPTEN/INPUT] failed to create devicemotion callback: %d\n", r);
return false;
}
}
else if (old_state && !new_state)
{
r = emscripten_set_devicemotion_callback(rwebinput, false, NULL);
if (r != EMSCRIPTEN_RESULT_SUCCESS)
{
RARCH_ERR(
"[EMSCRIPTEN/INPUT] failed to remove devicemotion callback: %d\n", r);
return false;
}
}
return true;
}
static float rwebinput_get_sensor_input(void *data, unsigned port, unsigned id)
{
rwebinput_input_t *rwebinput = (rwebinput_input_t*)data;
switch (id)
{
case RETRO_SENSOR_ACCELEROMETER_X:
return rwebinput->accelerometer.x;
case RETRO_SENSOR_ACCELEROMETER_Y:
return rwebinput->accelerometer.y;
case RETRO_SENSOR_ACCELEROMETER_Z:
return rwebinput->accelerometer.z;
case RETRO_SENSOR_GYROSCOPE_X:
return rwebinput->gyroscope.x;
case RETRO_SENSOR_GYROSCOPE_Y:
return rwebinput->gyroscope.y;
case RETRO_SENSOR_GYROSCOPE_Z:
return rwebinput->gyroscope.z;
}
return 0.0f;
}
static void rwebinput_process_keyboard_events(
rwebinput_input_t *rwebinput,
rwebinput_keyboard_event_t *event)
@ -840,8 +948,8 @@ input_driver_t input_rwebinput = {
rwebinput_input_poll,
rwebinput_input_state,
rwebinput_input_free,
NULL,
NULL,
rwebinput_set_sensor_state,
rwebinput_get_sensor_input,
rwebinput_get_capabilities,
"rwebinput",
rwebinput_grab_mouse,

View File

@ -639,6 +639,10 @@ static void winraw_poll(void *data)
for (i = 0; i < wr->mouse_cnt; ++i)
{
/* Clear buttons when not focused */
if (!winraw_focus)
g_mice[i].flags = 0;
wr->mice[i].x = g_mice[i].x;
wr->mice[i].y = g_mice[i].y;
wr->mice[i].dlt_x = InterlockedExchange(&g_mice[i].dlt_x, 0);

Some files were not shown because too many files have changed in this diff Show More