Merge pull request #21838 from hrydgard/sdl3-port

Port from SDL2 to SDL3
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-06-16 12:50:14 +02:00
commit dffde6e189
27 files changed
+758 -1151

No files matched your search

+35
View File
@@ -0,0 +1,35 @@
---
name: ppsspp-core
description: "Use for PPSSPP emulator core C++ tasks: HLE, CPU, GPU, timing, regression analysis, and minimal-risk fixes."
---
# PPSSPP Core Agent
You are a specialized agent for PPSSPP emulator-core engineering work.
## Focus
1. C++ changes in core emulator paths, including HLE, CPU, GPU, timing, and synchronization.
2. Bug-risk-first analysis for behavior regressions and compatibility issues.
3. Minimal, targeted diffs that preserve existing architecture.
## Workflow
1. Locate affected code paths and nearby call flow before editing.
2. Identify behavior and compatibility risks first, especially for savestates and timing.
3. Implement the smallest safe patch that addresses the issue.
4. Validate with targeted build or test commands relevant to touched code.
5. Report outcomes with explicit notes on what was and was not validated.
## Guardrails
1. Do not perform broad refactors unless required to fix correctness.
2. Avoid changing unrelated platform paths while fixing a focused issue.
3. Keep threading and shared-state handling aligned with existing patterns.
4. Preserve serialization assumptions unless migration handling is included.
## Output Style
1. Lead with findings and risks for review requests.
2. Include concrete file and symbol references.
3. Keep recommendations specific, testable, and scoped.
+38
View File
@@ -0,0 +1,38 @@
# PPSSPP Copilot Instructions
These rules apply to this repository by default.
Ignore the folder ai_instructions, it's old stuff from contributors.
## Priorities
1. Preserve emulator behavior and compatibility first.
2. Prefer correctness over speed when they conflict.
3. Improve performance only when behavior is unchanged and measurable.
4. Keep style changes minimal unless requested. Follow existing code patterns and conventions.
## Change Strategy
1. Make the smallest safe change that solves the issue.
2. Avoid broad refactors unless they are required for correctness.
3. Preserve platform-specific code paths and build logic.
4. Keep cross-platform parity in mind when changing shared code.
## Core Safety Checks
1. For HLE, CPU, GPU, timing, threading, and memory changes, call out regression risks explicitly.
2. Consider savestate compatibility when changing serialized state.
3. Keep lock usage and shared-state access consistent with existing patterns.
4. Prefer existing architecture and helper paths over introducing new abstractions.
## Build and Validation
To verify that things build on Linux/Mac, use ./b.sh --debug. For Windows, use the Visual Studio solution in the Windows subdirectory.
Do not run unit test (I will add instructions for how to run them later).
## Review Expectations
1. In reviews, list concrete risks and behavior changes before summaries.
2. Include file and symbol references for important findings.
3. Propose focused follow-up checks when full validation is not possible.
+3 -63
View File
@@ -113,7 +113,8 @@ jobs:
libc++ \
libc++abi \
glew \
sdl2_ttf \
sdl3 \
sdl3_ttf \
python \
patchelf \
pipewire \
@@ -160,72 +161,11 @@ jobs:
#clang --version
#clang++ --version
# Install SDL2 from AUR
sed -i 's|EUID == 0|EUID == 69|g' /usr/bin/makepkg # because the docker image is ran as root this is needed lol
#git clone https://aur.archlinux.org/sdl2.git
mkdir sdl2
pushd sdl2
cat <<-'EOF' > ./PKGBUILD
# Maintainer: HurricanePootis <[email protected]>
# Contributor: Sven-Hendrik Haase <[email protected]>
pkgname=sdl2
pkgver=2.32.8
pkgrel=1
pkgdesc="A library for portable low-level access to a video framebuffer, audio output, mouse, and keyboard (Version 2)"
arch=('x86_64')
url="https://www.libsdl.org"
license=('Zlib')
provides=('sdl2-compat')
conflicts=('sdl2-compat')
depends=('glibc' 'libxext' 'libxrender' 'libx11' 'libgl' 'libxcursor' 'hidapi' 'libusb')
makedepends=('alsa-lib' 'dbus' 'mesa' 'libpulse' 'libxrandr' 'libxinerama' 'wayland' 'libxkbcommon'
'wayland-protocols' 'ibus' 'libxss' 'cmake' 'jack' 'ninja' 'pipewire'
'libdecor' 'vulkan-driver' 'vulkan-headers' 'libsamplerate')
optdepends=('alsa-lib: ALSA audio driver'
'libpulse: PulseAudio audio driver'
'jack: JACK audio driver'
'pipewire: PipeWire audio driver'
'libdecor: Wayland client decorations')
source=("https://github.com/libsdl-org/SDL/releases/download/release-${pkgver}/SDL2-${pkgver}.tar.gz")
sha512sums=('484c33638e7bd1002815bb1f6a47a292d1eaf0b963598dde65f4a3e077dfe75ee35b9ea4b3b767365b3ef4f613c4d69ce55b5e96675de562994344e83a978272')
prepare(){
cd "$srcdir/SDL2-$pkgver"
}
build() {
CFLAGS+=" -ffat-lto-objects"
cmake -S SDL2-${pkgver} -B build -G Ninja \
-D CMAKE_INSTALL_PREFIX=/usr \
-D SDL_STATIC=OFF \
-D SDL_RPATH=OFF
cmake --build build
}
package() {
DESTDIR="${pkgdir}" cmake --install build
# For some reason, this isn't named correctly and we have to fix it to reflect the actual staticlib name.
sed -i "s/libSDL2\.a/libSDL2main.a/g" "$pkgdir"/usr/lib/cmake/SDL2/SDL2Targets-noconfig.cmake
install -Dm644 SDL2-${pkgver}/LICENSE.txt "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
}
# vim:set ts=2 sw=2 et:
EOF
sed -i "s|x86_64|$ARCH|" ./PKGBUILD
makepkg -f
pacman --noconfirm -Rsndd sdl2-compat
pacman --noconfirm -U *.pkg.tar.*
popd
# End of Install SDL2 from AUR
# Build ppsspp
#./b.sh --release --no-sdl2 --no-png
mkdir -p build
pushd build
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSE_SYSTEM_LIBSDL2=OFF -DUSE_SYSTEM_LIBPNG=OFF -DUSE_SYSTEM_FFMPEG=OFF -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ..
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSE_SYSTEM_LIBPNG=OFF -DUSE_SYSTEM_FFMPEG=OFF -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ..
make -j$(nproc)
popd
ccache -s -v
+22 -20
View File
@@ -170,69 +170,69 @@ jobs:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
- os: ubuntu-26.04
extra: test
cc: clang
cxx: clang++
args: ./b.sh --headless --unittest
id: clang-normal
- os: ubuntu-latest
- os: ubuntu-26.04
cc: gcc
cxx: g++
args: ./b.sh --headless --unittest
id: gcc-normal
- os: ubuntu-latest
- os: ubuntu-26.04
extra: android
cc: clang
cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=arm64-v8a UNITTEST=1 HEADLESS=1
id: android-arm64
- os: ubuntu-latest
- os: ubuntu-26.04
extra: android
cc: clang
cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=armeabi-v7a UNITTEST=1 HEADLESS=1
id: android-arm32
- os: ubuntu-latest
- os: ubuntu-26.04
extra: android
cc: clang
cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=x86_64 UNITTEST=1 HEADLESS=1
id: android-x86_64
- os: ubuntu-latest
- os: ubuntu-26.04
extra: android
cc: clang
cxx: clang++
args: cd android && ./ab.sh -j2 APP_ABI=arm64-v8a OPENXR=1
id: android-vr
- os: ubuntu-latest
- os: ubuntu-26.04
extra: android
cc: clang
cxx: clang++
args: ./b.sh --libretro_android ppsspp_libretro
id: android-libretro
- os: ubuntu-latest
- os: ubuntu-26.04
extra: qt
cc: gcc
cxx: g++
args: ./b.sh --qt
id: qt
- os: ubuntu-latest
- os: ubuntu-26.04
extra: libretro
cc: gcc
cxx: g++
args: make -C libretro -f Makefile -j2
id: gcc-libretro
- os: ubuntu-latest
- os: ubuntu-26.04
extra: libretro
cc: clang
cxx: clang++
args: make -C libretro -f Makefile -j2
id: clang-libretro
- os: ubuntu-latest
- os: ubuntu-26.04
extra: loongarch64
cc: gcc
cxx: g++
@@ -243,7 +243,7 @@ jobs:
extra: test
cc: clang
cxx: clang++
args: ./b.sh --headless --unittest --fat --no-png --no-sdl2
args: ./b.sh --headless --unittest --no-png
id: macos
- os: macos-latest
extra: libretro_mac
@@ -276,6 +276,7 @@ jobs:
with:
cache: true
version: 5.15.2
setup-python: false
- name: Setup Android NDK
uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779 # v1.6.0
@@ -287,9 +288,12 @@ jobs:
- name: Install Linux dependencies
if: runner.os == 'Linux' && matrix.extra != 'android' && matrix.extra != 'loongarch64'
run: |
sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu $(lsb_release -sc) main universe restricted multiverse"
sudo apt-get update -y -qq
sudo apt-get install libsdl2-dev libgl1-mesa-dev libglu1-mesa-dev libsdl2-ttf-dev libfontconfig1-dev
sudo apt-get install libsdl3-dev libgl1-mesa-dev libglu1-mesa-dev libsdl3-ttf-dev libfontconfig1-dev
- name: Install macOS SDL3 dependencies
if: runner.os == 'macOS' && matrix.id == 'macos'
run: brew install sdl3 sdl3_ttf
- name: Install loongarch64 cross-compilation dependencies
if: matrix.extra == 'loongarch64'
@@ -423,7 +427,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
os: [ubuntu-26.04, macos-latest]
runs-on: ${{ matrix.os }}
needs: build
@@ -441,14 +445,12 @@ jobs:
- name: Install Linux dependencies
if: runner.os == 'Linux'
run: |
sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu $(lsb_release -sc) main universe restricted multiverse"
sudo apt-get update -y -qq
sudo apt-get install libsdl2-dev libgl1-mesa-dev libglu1-mesa-dev libsdl2-ttf-dev libfontconfig1-dev
sudo apt-get install libsdl3-dev libgl1-mesa-dev libglu1-mesa-dev libsdl3-ttf-dev libfontconfig1-dev
- name: Install macOS dependencies
if: runner.os == 'macOS'
run: |
git submodule update --init SDL/macOS
run: brew install sdl3 sdl3_ttf
- name: Download build
uses: actions/download-artifact@v8
@@ -481,7 +483,7 @@ jobs:
steps:
- name: Install Linux dependencies (Alpine)
run: |
apk add build-base wget git bash cmake python3 glu-dev sdl2-dev
apk add build-base wget git bash cmake python3 glu-dev sdl3-dev sdl3_ttf-dev
- uses: actions/checkout@v6
with:
+66 -93
View File
@@ -109,12 +109,6 @@ if(ANDROID OR WIN32 OR (UNIX AND NOT ARM_NO_VULKAN))
set(VULKAN ON)
endif()
# Default to bundled SDL2 on macOS, system SDL2 elsewhere.
if(APPLE AND NOT IOS)
set(DEFAULT_USE_SYSTEM_LIBSDL2 OFF)
else()
set(DEFAULT_USE_SYSTEM_LIBSDL2 ON)
endif()
if(CMAKE_CXX_COMPILER MATCHES "webos" OR
CMAKE_CXX_COMPILER MATCHES "starfish")
@@ -206,7 +200,7 @@ option(BUILD_BUNDLED_FFMPEG "Build bundled ffmpeg module" ${BUILD_BUNDLED_FFMPEG
option(USE_SYSTEM_FREETYPE "Dynamically link against system freetype" ${USE_SYSTEM_FREETYPE})
option(USE_SYSTEM_LIBCHDR "Dynamically link against system libchdr" ${USE_SYSTEM_LIBCHDR})
option(USE_SYSTEM_LIBZIP "Dynamically link against system libzip" ${USE_SYSTEM_LIBZIP})
option(USE_SYSTEM_LIBSDL2 "Dynamically link against system SDL2" ${DEFAULT_USE_SYSTEM_LIBSDL2})
option(USE_SYSTEM_LIBPNG "Dynamically link against system libpng" ON)
option(USE_SYSTEM_RAPIDJSON "Build using system rapidjson" ${USE_SYSTEM_RAPIDJSON})
option(USE_SYSTEM_ZSTD "Dynamically link against system zstd" ${USE_SYSTEM_ZSTD})
@@ -325,31 +319,28 @@ if(USING_EGL)
list(APPEND OPENGL_LIBRARIES ${EGL_LIBRARIES})
endif()
if(NOT LIBRETRO AND NOT IOS AND NOT MACOSX)
find_package(SDL2)
find_package(SDL2_ttf)
find_package(Fontconfig)
set(SDL_LIB_TARGET "")
set(SDL_TTF_LIB_TARGET "")
set(FONTCONFIG_LIB_TARGET "")
# TODO: this can be removed once CI supports newer SDL2_ttf
if (NOT SDL2_ttf_FOUND)
find_package(PkgConfig)
if(PkgConfig_FOUND)
pkg_check_modules(SDL2_ttf_PKGCONFIG IMPORTED_TARGET SDL2_ttf)
endif()
endif()
endif()
if(MACOSX AND NOT IOS)
if(USE_SYSTEM_LIBSDL2)
find_package(SDL2)
find_package(SDL2_ttf)
if(NOT LIBRETRO AND NOT IOS AND NOT LOONGARCH64_DEVICE)
find_package(SDL3 REQUIRED)
set(SDL_LIB_TARGET SDL3::SDL3)
find_package(SDL3_ttf REQUIRED)
set(SDL_TTF_LIB_TARGET SDL3_ttf::SDL3_ttf)
message(STATUS "Using SDL3 and SDL3_ttf")
add_compile_definitions(USE_SDL3_TTF)
find_package(Fontconfig QUIET)
if(Fontconfig_FOUND)
set(FONTCONFIG_LIB_TARGET Fontconfig::Fontconfig)
add_compile_definitions(USE_SDL3_TTF_FONTCONFIG)
message(STATUS "Using Fontconfig for SDL3_ttf fallback fonts")
else()
find_library(SDL2Fwk SDL2 REQUIRED PATHS SDL/macOS)
message(STATUS "found SDL2Fwk=${SDL2Fwk}")
add_compile_definitions(HAVE_SYSCTLBYNAME)
message(WARNING "Fontconfig not found - SDL3_ttf fallback fonts for missing glyphs will be limited")
endif()
endif()
include(FindThreads)
if(APPLE)
@@ -966,6 +957,10 @@ endif()
target_link_libraries(Common Ext::Snappy cpu_features)
if(SDL_LIB_TARGET AND TARGET SDL3::Headers)
target_link_libraries(Common SDL3::Headers)
endif()
if(NOT LIBRETRO)
target_link_libraries(Common imgui)
endif()
@@ -1495,58 +1490,49 @@ elseif(USING_QT_UI)
set(TargetBin PPSSPPQt)
# Enable SDL joystick if SDL is found
if(SDL2_FOUND)
if(SDL_LIB_TARGET)
add_compile_definitions(SDL)
list(APPEND nativeExtra
SDL/SDLJoystick.h
SDL/SDLJoystick.cpp
)
list(APPEND nativeExtraLibs SDL2::SDL2)
list(APPEND nativeExtraLibs ${SDL_LIB_TARGET})
endif()
elseif(WIN32)
# Don't care about SDL.
set(TargetBin PPSSPPWindows)
elseif(LIBRETRO)
elseif(LOONGARCH64_DEVICE)
# Cross-compiling for loongarch64 currently only builds PPSSPPHeadless, and there's
# no SDL3 available for that target, so skip the desktop SDL frontend entirely.
else()
if(SDL2_FOUND OR SDL2Fwk)
if(GOLD)
set(TargetBin PPSSPPGold)
else()
set(TargetBin PPSSPPSDL)
endif()
add_compile_definitions(SDL)
if(GOLD)
set(TargetBin PPSSPPGold)
else()
set(TargetBin PPSSPPSDL)
endif()
# Require SDL
add_compile_definitions(SDL)
list(APPEND nativeExtra
SDL/SDLJoystick.h
SDL/SDLJoystick.cpp
SDL/SDLMain.cpp
SDL/SDLGLGraphicsContext.cpp
)
if(NOT USE_LIBNX)
list(APPEND nativeExtra
SDL/SDLJoystick.h
SDL/SDLJoystick.cpp
SDL/SDLMain.cpp
SDL/SDLGLGraphicsContext.cpp
SDL/SDLVulkanGraphicsContext.cpp
)
if(NOT USE_LIBNX)
list(APPEND nativeExtra
SDL/SDLVulkanGraphicsContext.cpp
)
endif()
if(SDL2_ttf_FOUND OR
(SDL2_ttf_PKGCONFIG_FOUND AND
SDL2_ttf_PKGCONFIG_VERSION VERSION_GREATER_EQUAL "2.0.18"))
add_compile_definitions(USE_SDL2_TTF)
if(FONTCONFIG_FOUND)
add_compile_definitions(USE_SDL2_TTF_FONTCONFIG)
list(APPEND nativeExtraLibs Fontconfig::Fontconfig)
endif()
elseif(SDL2_ttf_PKGCONFIG_FOUND)
message(WARNING "Found SDL2_ttf <2.0.18 - this is too old, falling back to atlas")
endif()
if(SDL2_ttf_FOUND)
list(APPEND nativeExtraLibs SDL2_ttf::SDL2_ttf)
elseif(SDL2_ttf_PKGCONFIG_FOUND)
list(APPEND nativeExtraLibs PkgConfig::SDL2_ttf_PKGCONFIG)
endif()
if(APPLE)
list(APPEND nativeExtra
SDL/SDLMain.h
SDL/SDLMain.mm
endif()
if(SDL_TTF_LIB_TARGET)
list(APPEND nativeExtraLibs ${SDL_TTF_LIB_TARGET})
endif()
if(FONTCONFIG_LIB_TARGET)
list(APPEND nativeExtraLibs ${FONTCONFIG_LIB_TARGET})
endif()
if(APPLE)
list(APPEND nativeExtra
SDL/SDLCocoaMetalLayer.h
SDL/SDLCocoaMetalLayer.mm
SDL/CocoaBarItems.mm
@@ -1558,32 +1544,21 @@ else()
UI/PSPNSApplicationDelegate.mm
UI/PSPNSApplicationDelegate.h)
# TODO
# set_source_files_properties(SDL/SDLMain.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(Core/Util/DarwinFileSystemServices.mm
UI/PSPNSApplicationDelegate.mm
SDL/CocoaMetalLayer.mm
SDL/CocoaBarItems.mm
SDL/PPSSPPAboutViewController.m
SDL/MacCameraHelper.mm
Common/Battery/AppleBatteryClient.m
Common/Render/Text/draw_text_cocoa.mm
PROPERTIES COMPILE_FLAGS -fobjc-arc)
list(APPEND nativeExtraLibs ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY} "-framework AVFoundation" "-framework CoreMedia" "-framework CoreVideo")
if(USE_SYSTEM_LIBSDL2)
list(APPEND nativeExtraLibs SDL2::SDL2)
else()
list(APPEND nativeExtraLibs ${SDL2Fwk})
endif()
elseif(USING_EGL)
list(APPEND nativeExtraLibs pthread SDL2::SDL2)
else()
list(APPEND nativeExtraLibs SDL2::SDL2)
endif()
elseif(NOT CMAKE_CROSSCOMPILING)
message(FATAL_ERROR "SDL2 not found - required for this platform")
set_source_files_properties(Core/Util/DarwinFileSystemServices.mm
UI/PSPNSApplicationDelegate.mm
SDL/CocoaMetalLayer.mm
SDL/CocoaBarItems.mm
SDL/PPSSPPAboutViewController.m
SDL/MacCameraHelper.mm
Common/Battery/AppleBatteryClient.m
Common/Render/Text/draw_text_cocoa.mm
PROPERTIES COMPILE_FLAGS -fobjc-arc)
list(APPEND nativeExtraLibs ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY} "-framework AVFoundation" "-framework CoreMedia" "-framework CoreVideo")
list(APPEND nativeExtraLibs ${SDL_LIB_TARGET})
elseif(USING_EGL)
list(APPEND nativeExtraLibs pthread ${SDL_LIB_TARGET})
else()
list(APPEND nativeExtraLibs ${SDL_LIB_TARGET})
endif()
endif()
@@ -3104,8 +3079,6 @@ if(TargetBin)
file(INSTALL "${CMAKE_SOURCE_DIR}/ext/vulkan/macOS/Frameworks/libMoltenVK.dylib" DESTINATION "${CMAKE_BINARY_DIR}/${TargetBin}.app/Contents/Frameworks/")
if(USING_QT_UI)
add_custom_command(TARGET ${TargetBin} POST_BUILD COMMAND /bin/bash "${CMAKE_SOURCE_DIR}/Qt/macbundle.sh" "${CMAKE_BINARY_DIR}/PPSSPPQt.app")
elseif(NOT USE_SYSTEM_LIBSDL2)
add_custom_command(TARGET ${TargetBin} POST_BUILD COMMAND /bin/bash "${CMAKE_SOURCE_DIR}/SDL/macbundle.sh" "${CMAKE_BINARY_DIR}/${TargetBin}.app" "${TargetBin}")
endif()
endif()
elseif(WIN32)
+1 -1
View File
@@ -316,7 +316,7 @@ TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) {
drawer = new TextDrawerQt(draw);
#elif PPSSPP_PLATFORM(ANDROID)
drawer = new TextDrawerAndroid(draw);
#elif USE_SDL2_TTF
#elif USE_SDL3_TTF
drawer = new TextDrawerSDL(draw);
#endif
if (drawer && !drawer->IsReady()) {
+46 -39
View File
@@ -13,10 +13,24 @@
#include "Common/Render/Text/draw_text.h"
#include "Common/Render/Text/draw_text_sdl.h"
#if defined(USE_SDL2_TTF)
#if defined(USE_SDL3_TTF)
#include "SDL2/SDL.h"
#include "SDL2/SDL_ttf.h"
#include <SDL3/SDL.h>
#include <SDL3/SDL_properties.h>
#include <SDL3_ttf/SDL_ttf.h>
static TTF_Font *OpenFontFace(const std::string &fontPath, float ptSize, int faceIndex) {
SDL_PropertiesID props = SDL_CreateProperties();
if (!props) {
return nullptr;
}
SDL_SetStringProperty(props, TTF_PROP_FONT_CREATE_FILENAME_STRING, fontPath.c_str());
SDL_SetFloatProperty(props, TTF_PROP_FONT_CREATE_SIZE_FLOAT, ptSize);
SDL_SetNumberProperty(props, TTF_PROP_FONT_CREATE_FACE_NUMBER, faceIndex);
TTF_Font *font = TTF_OpenFontWithProperties(props);
SDL_DestroyProperties(props);
return font;
}
static std::string getlocale() {
// setlocale is not an intuitive function...
@@ -31,13 +45,13 @@ static std::string getlocale() {
}
TextDrawerSDL::TextDrawerSDL(Draw::DrawContext *draw): TextDrawer(draw) {
if (TTF_Init() < 0) {
ERROR_LOG(Log::G3D, "Unable to initialize SDL2_ttf");
if (!TTF_Init()) {
ERROR_LOG(Log::G3D, "Unable to initialize SDL3_ttf");
}
dpiScale_ = CalculateDPIScale();
#if defined(USE_SDL2_TTF_FONTCONFIG)
#if defined(USE_SDL3_TTF_FONTCONFIG)
config = FcInitLoadConfigAndFonts();
#endif
@@ -50,7 +64,7 @@ TextDrawerSDL::~TextDrawerSDL() {
TTF_Quit();
#if defined(USE_SDL2_TTF_FONTCONFIG)
#if defined(USE_SDL3_TTF_FONTCONFIG)
FcConfigDestroy(config);
// Don't call this - it crashes, see https://github.com/openframeworks/openFrameworks/issues/5061.
//FcFini();
@@ -59,7 +73,7 @@ TextDrawerSDL::~TextDrawerSDL() {
// If a user complains about missing characters on SDL, re-visit this!
void TextDrawerSDL::PrepareFallbackFonts(std::string_view locale) {
#if defined(USE_SDL2_TTF_FONTCONFIG)
#if defined(USE_SDL3_TTF_FONTCONFIG)
FcObjectSet *os = FcObjectSetBuild (FC_FILE, FC_INDEX, (char *) 0);
// To install the recommended Droid Sans fallback font in Ubuntu:
@@ -189,22 +203,11 @@ void TextDrawerSDL::PrepareFallbackFonts(std::string_view locale) {
Path fontPath = Path(fontDirs[i]) / fallbackFonts[j];
if (File::Exists(fontPath)) {
TTF_Font *openedFont = TTF_OpenFont(fontPath.ToString().c_str(), 24);
int64_t numFaces = TTF_FontFaces(openedFont);
TTF_Font *openedFont = TTF_OpenFont(fontPath.ToString().c_str(), 24.0f);
int numFaces = TTF_GetNumFontFaces(openedFont);
for (int k = 0; k < numFaces; k++) {
TTF_Font *fontFace = TTF_OpenFontIndex(fontPath.ToString().c_str(), 24, k);
std::string fontFaceName(TTF_FontFaceStyleName(fontFace));
TTF_CloseFont(fontFace);
if (strstr(fontFaceName.c_str(), "Medium") ||
strstr(fontFaceName.c_str(), "Regular"))
{
fallbackFontPaths_.push_back(std::make_pair(fontPath.ToString(), k));
break;
}
fallbackFontPaths_.push_back(std::make_pair(fontPath.ToString(), k));
}
TTF_CloseFont(openedFont);
}
}
@@ -230,7 +233,7 @@ uint32_t TextDrawerSDL::CheckMissingGlyph(std::string_view text) {
uint32_t missingGlyph = 0;
while (!utf8Decoded.end()) {
uint32_t glyph = utf8Decoded.next();
if (!TTF_GlyphIsProvided32(font, glyph)) {
if (!TTF_FontHasGlyph(font, glyph)) {
missingGlyph = glyph;
break;
}
@@ -249,7 +252,7 @@ int TextDrawerSDL::FindFallbackFonts(uint32_t missingGlyph, int ptSize) {
// If we encounter a missing glyph, try to use one of already loaded fallback fonts.
for (int i = 0; i < fallbackFonts_.size(); i++) {
TTF_Font *fallbackFont = fallbackFonts_[i];
if (TTF_GlyphIsProvided32(fallbackFont, missingGlyph)) {
if (TTF_FontHasGlyph(fallbackFont, missingGlyph)) {
glyphFallbackFontIndex_[missingGlyph] = i;
return i;
}
@@ -261,13 +264,15 @@ int TextDrawerSDL::FindFallbackFonts(uint32_t missingGlyph, int ptSize) {
std::string& fontPath = fallbackFontPaths_[i].first;
int faceIndex = fallbackFontPaths_[i].second;
TTF_Font *font = TTF_OpenFontIndex(fontPath.c_str(), ptSize, faceIndex);
TTF_Font *font = OpenFontFace(fontPath, static_cast<float>(ptSize), faceIndex);
if (TTF_GlyphIsProvided32(font, missingGlyph)) {
if (font && TTF_FontHasGlyph(font, missingGlyph)) {
fallbackFonts_.push_back(font);
return fallbackFonts_.size() - 1;
} else {
TTF_CloseFont(font);
if (font) {
TTF_CloseFont(font);
}
}
}
@@ -292,9 +297,9 @@ void TextDrawerSDL::SetOrCreateFont(const FontStyle &style) {
size_t fileSz;
fileData = g_VFS.ReadFile(useFont.c_str(), &fileSz);
if (fileData) {
SDL_RWops *rw = SDL_RWFromMem(fileData, static_cast<int>(fileSz));
SDL_IOStream *rw = SDL_IOFromConstMem(fileData, fileSz);
INFO_LOG(Log::G3D, "Opened font from RW: '%p' '%d'", fileData, (int)fileSz);
font = TTF_OpenFontRW(rw, 1, ptSize);
font = TTF_OpenFontIO(rw, true, static_cast<float>(ptSize));
if (!font) {
ERROR_LOG(Log::G3D, "Failed to load font from asset file: '%s'", useFont.c_str());
}
@@ -343,7 +348,7 @@ void TextDrawerSDL::MeasureStringInternal(std::string_view str, float *w, float
line = " ";
}
temp = line; // zero-terminate, ugh.
TTF_SizeUTF8(font, temp.c_str(), &width, &height);
TTF_GetStringSize(font, temp.c_str(), temp.size(), &width, &height);
if (width > extW)
extW = width;
@@ -375,7 +380,7 @@ bool TextDrawerSDL::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
std::string processedStr(str);
// If a string includes only newlines, SDL2_ttf will refuse to render it
// If a string includes only newlines, SDL3_ttf will refuse to render it
// thinking it is empty. Add a space to avoid that.
bool isAllNewline = processedStr.find_first_not_of('\n') == std::string::npos;
@@ -386,24 +391,26 @@ bool TextDrawerSDL::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
uint32_t missingGlyph = CheckMissingGlyph(processedStr);
if (missingGlyph) {
const int ptSize = TTF_FontHeight(font) / 1.25;
const int ptSize = static_cast<int>(TTF_GetFontSize(font));
int fallbackFont = FindFallbackFonts(missingGlyph, ptSize);
if (fallbackFont >= 0 && fallbackFont < (int)fallbackFonts_.size()) {
font = fallbackFonts_[fallbackFont];
}
}
#if SDL_TTF_VERSION_ATLEAST(2, 20, 0)
if (align & ALIGN_HCENTER)
TTF_SetFontWrappedAlign(font, TTF_WRAPPED_ALIGN_CENTER);
TTF_SetFontWrapAlignment(font, TTF_HORIZONTAL_ALIGN_CENTER);
else if (align & ALIGN_RIGHT)
TTF_SetFontWrappedAlign(font, TTF_WRAPPED_ALIGN_RIGHT);
TTF_SetFontWrapAlignment(font, TTF_HORIZONTAL_ALIGN_RIGHT);
else
TTF_SetFontWrappedAlign(font, TTF_WRAPPED_ALIGN_LEFT);
#endif
TTF_SetFontWrapAlignment(font, TTF_HORIZONTAL_ALIGN_LEFT);
SDL_Color fgColor = { 0xFF, 0xFF, 0xFF, 0xFF };
SDL_Surface *text = TTF_RenderUTF8_Blended_Wrapped(font, processedStr.c_str(), fgColor, 0);
SDL_Surface *text = TTF_RenderText_Blended_Wrapped(font, processedStr.c_str(), processedStr.size(), fgColor, 0);
if (!text) {
ERROR_LOG(Log::G3D, "Failed to render SDL3_ttf text: %s", SDL_GetError());
return false;
}
SDL_LockSurface(text);
entry.texture = nullptr;
@@ -440,7 +447,7 @@ bool TextDrawerSDL::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
}
SDL_UnlockSurface(text);
SDL_FreeSurface(text);
SDL_DestroySurface(text);
return true;
}
+6 -6
View File
@@ -5,12 +5,12 @@
#include <map>
#include "Common/Render/Text/draw_text.h"
#if defined(USE_SDL2_TTF)
#if defined(USE_SDL3_TTF)
#include "SDL2/SDL.h"
#include "SDL2/SDL_ttf.h"
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#if defined(USE_SDL2_TTF_FONTCONFIG)
#if defined(USE_SDL3_TTF_FONTCONFIG)
#include <fontconfig/fontconfig.h>
#endif
@@ -40,9 +40,9 @@ private:
std::map<int, int> glyphFallbackFontIndex_;
#if defined(USE_SDL2_TTF_FONTCONFIG)
#if defined(USE_SDL3_TTF_FONTCONFIG)
FcConfig *config;
#endif
#endif
};
#endif
+78 -45
View File
@@ -29,8 +29,8 @@
#ifdef SDL
#include "SDL/SDLJoystick.h"
#include "SDL_audio.h"
#include "SDL_keyboard.h"
#include <SDL3/SDL_audio.h>
#include <SDL3/SDL_keyboard.h>
#endif
#include "Common/Audio/AudioBackend.h"
@@ -75,55 +75,86 @@ MainWindow *g_mainWindow;
#ifdef SDL
SDL_AudioSpec g_retFmt;
static int g_audioFramesPerBuffer = 0;
static SDL_AudioDeviceID audioDev = 0;
static SDL_AudioStream *audioStream = nullptr;
extern void mixaudio(void *userdata, Uint8 *stream, int len) {
NativeMix((short *)stream, len / 4, AUDIO_FREQ, userdata);
static void sdl_mixaudio_callback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount) {
if (additional_amount <= 0) {
return;
}
const int frames = additional_amount / (int)(sizeof(int16_t) * 2);
if (frames <= 0) {
return;
}
std::vector<int16_t> mixBuf(frames * 2);
NativeMix(mixBuf.data(), frames, AUDIO_FREQ, userdata);
SDL_PutAudioStreamData(stream, mixBuf.data(), (int)(mixBuf.size() * sizeof(int16_t)));
}
static void InitSDLAudioDevice() {
SDL_AudioSpec fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.freq = 44100;
fmt.format = AUDIO_S16;
fmt.channels = 2;
fmt.samples = std::max(g_Config.iSDLAudioBufferSize, 128);
fmt.callback = &mixaudio;
fmt.userdata = nullptr;
SDL_AudioSpec fmt{};
fmt.freq = AUDIO_FREQ;
fmt.format = SDL_AUDIO_S16;
fmt.channels = AUDIO_CHANNELS;
g_audioFramesPerBuffer = std::max(g_Config.iSDLAudioBufferSize, 128);
audioDev = 0;
SDL_AudioDeviceID chosenDevice = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
if (!g_Config.sAudioDevice.empty()) {
audioDev = SDL_OpenAudioDevice(g_Config.sAudioDevice.c_str(), 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if (audioDev <= 0) {
WARN_LOG(Log::Audio, "Failed to open preferred audio device %s", g_Config.sAudioDevice.c_str());
int deviceCount = 0;
SDL_AudioDeviceID *devices = SDL_GetAudioPlaybackDevices(&deviceCount);
for (int i = 0; devices && i < deviceCount; ++i) {
const char *deviceName = SDL_GetAudioDeviceName(devices[i]);
if (deviceName && g_Config.sAudioDevice == deviceName) {
chosenDevice = devices[i];
break;
}
}
if (devices) {
SDL_free(devices);
}
if (chosenDevice == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) {
WARN_LOG(Log::Audio, "Failed to find preferred audio device %s", g_Config.sAudioDevice.c_str());
}
}
if (audioDev <= 0) {
audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
audioStream = SDL_OpenAudioDeviceStream(chosenDevice, &fmt, sdl_mixaudio_callback, nullptr);
if (!audioStream && chosenDevice != SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) {
audioStream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, sdl_mixaudio_callback, nullptr);
}
if (audioDev <= 0) {
if (!audioStream) {
ERROR_LOG(Log::Audio, "Failed to open audio: %s", SDL_GetError());
} else {
if (g_retFmt.samples != fmt.samples) // Notify, but still use it
ERROR_LOG(Log::Audio, "Output audio samples: %d (requested: %d)", g_retFmt.samples, fmt.samples);
if (g_retFmt.format != fmt.format || g_retFmt.channels != fmt.channels) {
ERROR_LOG(Log::Audio, "Sound buffer format does not match requested format.");
ERROR_LOG(Log::Audio, "Output audio freq: %d (requested: %d)", g_retFmt.freq, fmt.freq);
ERROR_LOG(Log::Audio, "Output audio format: %d (requested: %d)", g_retFmt.format, fmt.format);
ERROR_LOG(Log::Audio, "Output audio channels: %d (requested: %d)", g_retFmt.channels, fmt.channels);
ERROR_LOG(Log::Audio, "Provided output format does not match requirement, turning audio off");
SDL_CloseAudioDevice(audioDev);
}
SDL_PauseAudioDevice(audioDev, 0);
audioDev = 0;
return;
}
audioDev = SDL_GetAudioStreamDevice(audioStream);
if (!SDL_GetAudioDeviceFormat(audioDev, &g_retFmt, &g_audioFramesPerBuffer)) {
g_retFmt = fmt;
}
if (g_retFmt.format != fmt.format || g_retFmt.channels != fmt.channels) {
ERROR_LOG(Log::Audio, "Sound buffer format does not match requested format.");
ERROR_LOG(Log::Audio, "Output audio freq: %d (requested: %d)", g_retFmt.freq, fmt.freq);
ERROR_LOG(Log::Audio, "Output audio format: %d (requested: %d)", g_retFmt.format, fmt.format);
ERROR_LOG(Log::Audio, "Output audio channels: %d (requested: %d)", g_retFmt.channels, fmt.channels);
}
if (!SDL_ResumeAudioStreamDevice(audioStream)) {
ERROR_LOG(Log::Audio, "Failed to start audio stream: %s", SDL_GetError());
SDL_DestroyAudioStream(audioStream);
audioStream = nullptr;
audioDev = 0;
}
}
static void StopSDLAudioDevice() {
if (audioDev > 0) {
SDL_PauseAudioDevice(audioDev, 1);
SDL_CloseAudioDevice(audioDev);
if (audioStream) {
SDL_DestroyAudioStream(audioStream);
audioStream = nullptr;
}
audioDev = 0;
}
#endif
@@ -149,19 +180,24 @@ std::string System_GetProperty(SystemProperty prop) {
case SYSPROP_AUDIO_DEVICE_LIST:
{
std::string result;
for (int i = 0; i < SDL_GetNumAudioDevices(0); ++i) {
const char *name = SDL_GetAudioDeviceName(i, 0);
int count = 0;
SDL_AudioDeviceID *devices = SDL_GetAudioPlaybackDevices(&count);
for (int i = 0; devices && i < count; ++i) {
const char *name = SDL_GetAudioDeviceName(devices[i]);
if (!name) {
continue;
}
if (i == 0) {
if (result.empty()) {
result = name;
} else {
result.append(1, '\0');
result.append(name);
}
}
if (devices) {
SDL_free(devices);
}
return result;
}
#endif
@@ -195,14 +231,14 @@ int64_t System_GetPropertyInt(SystemProperty prop) {
case SYSPROP_AUDIO_SAMPLE_RATE:
return g_retFmt.freq;
case SYSPROP_AUDIO_FRAMES_PER_BUFFER:
return g_retFmt.samples;
return g_audioFramesPerBuffer;
case SYSPROP_KEYBOARD_LAYOUT:
{
// TODO: Use Qt APIs for detecting this
char q, w, y;
q = SDL_GetKeyFromScancode(SDL_SCANCODE_Q);
w = SDL_GetKeyFromScancode(SDL_SCANCODE_W);
y = SDL_GetKeyFromScancode(SDL_SCANCODE_Y);
q = SDL_GetKeyFromScancode(SDL_SCANCODE_Q, SDL_KMOD_NONE, false);
w = SDL_GetKeyFromScancode(SDL_SCANCODE_W, SDL_KMOD_NONE, false);
y = SDL_GetKeyFromScancode(SDL_SCANCODE_Y, SDL_KMOD_NONE, false);
if (q == 'a' && w == 'z' && y == 'y')
return KEYBOARD_LAYOUT_AZERTY;
else if (q == 'q' && w == 'w' && y == 'z')
@@ -883,10 +919,7 @@ int main(int argc, char *argv[])
INFO_LOG(Log::System, "Left mainInternal here.");
#ifdef SDL
if (audioDev > 0) {
SDL_PauseAudioDevice(audioDev, 1);
SDL_CloseAudioDevice(audioDev);
}
StopSDLAudioDevice();
#endif
NativeShutdown();
glslang::FinalizeProcess();
+28 -28
View File
@@ -7,32 +7,32 @@
// TODO: Add any missing keys
static const std::map<int, InputKeyCode> KeyMapRawSDLtoNative = InitConstMap<int, InputKeyCode>
(SDLK_UNKNOWN, NKCODE_UNKNOWN)
(SDLK_p, NKCODE_P)
(SDLK_o, NKCODE_O)
(SDLK_i, NKCODE_I)
(SDLK_u, NKCODE_U)
(SDLK_y, NKCODE_Y)
(SDLK_t, NKCODE_T)
(SDLK_r, NKCODE_R)
(SDLK_e, NKCODE_E)
(SDLK_w, NKCODE_W)
(SDLK_q, NKCODE_Q)
(SDLK_l, NKCODE_L)
(SDLK_k, NKCODE_K)
(SDLK_j, NKCODE_J)
(SDLK_h, NKCODE_H)
(SDLK_g, NKCODE_G)
(SDLK_f, NKCODE_F)
(SDLK_d, NKCODE_D)
(SDLK_s, NKCODE_S)
(SDLK_a, NKCODE_A)
(SDLK_m, NKCODE_M)
(SDLK_n, NKCODE_N)
(SDLK_b, NKCODE_B)
(SDLK_v, NKCODE_V)
(SDLK_c, NKCODE_C)
(SDLK_x, NKCODE_X)
(SDLK_z, NKCODE_Z)
(SDLK_P, NKCODE_P)
(SDLK_O, NKCODE_O)
(SDLK_I, NKCODE_I)
(SDLK_U, NKCODE_U)
(SDLK_Y, NKCODE_Y)
(SDLK_T, NKCODE_T)
(SDLK_R, NKCODE_R)
(SDLK_E, NKCODE_E)
(SDLK_W, NKCODE_W)
(SDLK_Q, NKCODE_Q)
(SDLK_L, NKCODE_L)
(SDLK_K, NKCODE_K)
(SDLK_J, NKCODE_J)
(SDLK_H, NKCODE_H)
(SDLK_G, NKCODE_G)
(SDLK_F, NKCODE_F)
(SDLK_D, NKCODE_D)
(SDLK_S, NKCODE_S)
(SDLK_A, NKCODE_A)
(SDLK_M, NKCODE_M)
(SDLK_N, NKCODE_N)
(SDLK_B, NKCODE_B)
(SDLK_V, NKCODE_V)
(SDLK_C, NKCODE_C)
(SDLK_X, NKCODE_X)
(SDLK_Z, NKCODE_Z)
(SDLK_COMMA, NKCODE_COMMA)
(SDLK_PERIOD, NKCODE_PERIOD)
(SDLK_LALT, NKCODE_ALT_LEFT)
@@ -48,7 +48,7 @@ static const std::map<int, InputKeyCode> KeyMapRawSDLtoNative = InitConstMap<int
(SDLK_RIGHTBRACKET, NKCODE_RIGHT_BRACKET)
(SDLK_BACKSLASH, NKCODE_BACKSLASH)
(SDLK_SEMICOLON, NKCODE_SEMICOLON)
(SDLK_QUOTE, NKCODE_APOSTROPHE)
(SDLK_APOSTROPHE, NKCODE_APOSTROPHE)
(SDLK_SLASH, NKCODE_SLASH)
(SDLK_AT, NKCODE_AT)
(SDLK_PLUS, NKCODE_PLUS)
@@ -80,7 +80,7 @@ static const std::map<int, InputKeyCode> KeyMapRawSDLtoNative = InitConstMap<int
(SDLK_KP_PERIOD, NKCODE_NUMPAD_DOT)
(SDLK_KP_ENTER, NKCODE_NUMPAD_ENTER)
(SDLK_KP_EQUALS, NKCODE_NUMPAD_EQUALS)
(SDLK_BACKQUOTE, NKCODE_GRAVE)
(SDLK_GRAVE, NKCODE_GRAVE)
(SDLK_1, NKCODE_1)
(SDLK_2, NKCODE_2)
(SDLK_3, NKCODE_3)
+31 -39
View File
@@ -88,43 +88,19 @@ static int8_t EGL_Open(SDL_Window *window) {
g_Window = (EGLNativeWindowType)nullptr;
#else
// Get the SDL window native handle
SDL_SysWMinfo sysInfo{};
SDL_VERSION(&sysInfo.version);
if (!SDL_GetWindowWMInfo(window, &sysInfo)) {
fprintf(stderr, "ERROR: Unable to retrieve native window handle\n");
g_Display = (EGLNativeDisplayType)XOpenDisplay(nullptr);
g_XDisplayOpen = g_Display != nullptr;
if (!g_XDisplayOpen)
EGL_ERROR("Unable to get display!", false);
g_Window = (EGLNativeWindowType)nullptr;
SDL_PropertiesID windowProps = SDL_GetWindowProperties(window);
void *x11Display = SDL_GetPointerProperty(windowProps, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, nullptr);
if (x11Display != nullptr) {
g_Display = (EGLNativeDisplayType)x11Display;
g_Window = (EGLNativeWindowType)(uintptr_t)SDL_GetNumberProperty(windowProps, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
} else {
switch (sysInfo.subsystem) {
case SDL_SYSWM_X11:
g_Display = (EGLNativeDisplayType)sysInfo.info.x11.display;
g_Window = (EGLNativeWindowType)sysInfo.info.x11.window;
break;
#if defined(SDL_VIDEO_DRIVER_DIRECTFB)
case SDL_SYSWM_DIRECTFB:
g_Display = (EGLNativeDisplayType)EGL_DEFAULT_DISPLAY;
g_Window = (EGLNativeWindowType)sysInfo.info.dfb.surface;
break;
#endif
#if SDL_VERSION_ATLEAST(2, 0, 2) && defined(SDL_VIDEO_DRIVER_WAYLAND)
case SDL_SYSWM_WAYLAND:
g_Display = (EGLNativeDisplayType)sysInfo.info.wl.display;
g_Window = (EGLNativeWindowType)sysInfo.info.wl.egl_window;
break;
#endif
#if SDL_VERSION_ATLEAST(2, 0, 5) && defined(SDL_VIDEO_DRIVER_VIVANTE)
case SDL_SYSWM_VIVANTE:
g_Display = (EGLNativeDisplayType)sysInfo.info.vivante.display;
g_Window = (EGLNativeWindowType)sysInfo.info.vivante.window;
break;
#endif
}
if (!EGL_OpenInit()) {
// Let's try again with X11.
void *waylandDisplay = SDL_GetPointerProperty(windowProps, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, nullptr);
void *waylandEGLWindow = SDL_GetPointerProperty(windowProps, SDL_PROP_WINDOW_WAYLAND_EGL_WINDOW_POINTER, nullptr);
if (waylandDisplay != nullptr && waylandEGLWindow != nullptr) {
g_Display = (EGLNativeDisplayType)waylandDisplay;
g_Window = (EGLNativeWindowType)waylandEGLWindow;
} else {
fprintf(stderr, "ERROR: Unable to retrieve native window properties\n");
g_Display = (EGLNativeDisplayType)XOpenDisplay(nullptr);
g_XDisplayOpen = g_Display != nullptr;
if (!g_XDisplayOpen)
@@ -133,6 +109,14 @@ static int8_t EGL_Open(SDL_Window *window) {
}
}
if (!EGL_OpenInit()) {
g_Display = (EGLNativeDisplayType)XOpenDisplay(nullptr);
g_XDisplayOpen = g_Display != nullptr;
if (!g_XDisplayOpen)
EGL_ERROR("Unable to get display!", false);
g_Window = (EGLNativeWindowType)nullptr;
}
#endif
if (g_eglDisplay == EGL_NO_DISPLAY)
EGL_OpenInit();
@@ -346,7 +330,7 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h,
SetGLCoreContext(true);
#endif
window = SDL_CreateWindow("PPSSPP", x, y, w, h, mode);
window = SDL_CreateWindow("PPSSPP", w, h, (SDL_WindowFlags)mode);
if (!window) {
// Definitely don't shutdown here: we'll keep trying more GL versions.
fprintf(stderr, "SDL_CreateWindow failed for GL %d.%d: %s\n", ver.major, ver.minor, SDL_GetError());
@@ -354,6 +338,10 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h,
continue;
}
if (x != SDL_WINDOWPOS_UNDEFINED && y != SDL_WINDOWPOS_UNDEFINED) {
SDL_SetWindowPosition(window, x, y);
}
glContext = SDL_GL_CreateContext(window);
if (glContext != nullptr) {
// Victory, got one.
@@ -371,7 +359,7 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h,
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SetGLCoreContext(false);
window = SDL_CreateWindow("PPSSPP", x, y, w, h, mode);
window = SDL_CreateWindow("PPSSPP", w, h, (SDL_WindowFlags)mode);
if (window == nullptr) {
NativeShutdown();
fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError());
@@ -379,6 +367,10 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h,
return 2;
}
if (x != SDL_WINDOWPOS_UNDEFINED && y != SDL_WINDOWPOS_UNDEFINED) {
SDL_SetWindowPosition(window, x, y);
}
glContext = SDL_GL_CreateContext(window);
if (glContext == nullptr) {
// OK, now we really have tried everything.
@@ -462,7 +454,7 @@ void SDLGLGraphicsContext::ShutdownFromRenderThread() {
#ifdef USING_EGL
EGL_Close();
#endif
SDL_GL_DeleteContext(glContext);
SDL_GL_DestroyContext(glContext);
glContext = nullptr;
window_ = nullptr;
}
+1 -7
View File
@@ -1,11 +1,5 @@
#include "ppsspp_config.h"
#if PPSSPP_PLATFORM(MAC)
#include "SDL2/SDL.h"
#include "SDL2/SDL_syswm.h"
#else
#include "SDL.h"
#include "SDL_syswm.h"
#endif
#include <SDL3/SDL.h>
#include <string>
+96 -101
View File
@@ -13,15 +13,15 @@
using namespace std;
static int SDLJoystickEventHandlerWrapper(void* userdata, SDL_Event* event) {
static bool SDLJoystickEventHandlerWrapper(void* userdata, SDL_Event* event) {
static_cast<SDLJoystick *>(userdata)->ProcessInput(*event);
return 0;
return true;
}
SDLJoystick::SDLJoystick(bool init_SDL ) : registeredAsEventHandler(false) {
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
if (init_SDL) {
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER);
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_GAMEPAD);
}
const char *dbPath = "gamecontrollerdb.txt";
@@ -30,9 +30,8 @@ SDLJoystick::SDLJoystick(bool init_SDL ) : registeredAsEventHandler(false) {
size_t size;
u8 *mappingData = g_VFS.ReadFile(dbPath, &size);
if (mappingData) {
SDL_RWops *rw = SDL_RWFromConstMem(mappingData, size);
// 1 to free the rw after use
if (SDL_GameControllerAddMappingsFromRW(rw, 1) == -1) {
SDL_IOStream *io = SDL_IOFromConstMem(mappingData, size);
if (SDL_AddGamepadMappingsFromIO(io, true) == -1) {
ERROR_LOG(Log::System, "Failed to read mapping data - corrupt?");
}
delete[] mappingData;
@@ -43,69 +42,74 @@ SDLJoystick::SDLJoystick(bool init_SDL ) : registeredAsEventHandler(false) {
}
void SDLJoystick::setUpControllers() {
int numjoys = SDL_NumJoysticks();
for (int i = 0; i < numjoys; i++) {
setUpController(i);
int numjoys = 0;
SDL_JoystickID *joysticks = SDL_GetJoysticks(&numjoys);
for (int i = 0; joysticks && i < numjoys; i++) {
setUpController(joysticks[i]);
}
if (joysticks) {
SDL_free(joysticks);
}
if (controllers.size() > 0) {
INFO_LOG(Log::System, "pad 1 has been assigned to control pad: %s", SDL_GameControllerName(controllers.front()));
INFO_LOG(Log::System, "pad 1 has been assigned to control pad: %s", SDL_GetGamepadName(controllers.front()));
}
}
void SDLJoystick::setUpController(int deviceIndex) {
void SDLJoystick::setUpController(SDL_JoystickID deviceID) {
static constexpr int cbGUID = 33;
char pszGUID[cbGUID];
if (!SDL_IsGameController(deviceIndex)) {
WARN_LOG(Log::System, "Control pad device %d not supported by SDL game controller database, attempting to create default mapping...", deviceIndex);
SDL_Joystick *joystick = SDL_JoystickOpen(deviceIndex);
if (!SDL_IsGamepad(deviceID)) {
WARN_LOG(Log::System, "Control pad device %d not supported by SDL game controller database, attempting to create default mapping...", deviceID);
SDL_Joystick *joystick = SDL_OpenJoystick(deviceID);
if (joystick) {
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), pszGUID, cbGUID);
SDL_GUIDToString(SDL_GetJoystickGUID(joystick), pszGUID, cbGUID);
// create default mapping - this is the PS3 dual shock mapping
const std::string safeName = ReplaceAll(SDL_JoystickName(joystick), ",", "");
const std::string safeName = ReplaceAll(SDL_GetJoystickName(joystick), ",", "");
std::string mapping = std::string(pszGUID) + "," + safeName + ",x:b3,a:b0,b:b1,y:b2,back:b8,guide:b10,start:b9,dpleft:b15,dpdown:b14,dpright:b16,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:a5,leftstick:b7,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4";
if (SDL_GameControllerAddMapping(mapping.c_str()) == 1){
if (SDL_AddGamepadMapping(mapping.c_str()) == 1){
INFO_LOG(Log::System, "Added default mapping ok");
} else {
ERROR_LOG(Log::System, "Failed to add default mapping");
}
SDL_JoystickClose(joystick);
SDL_CloseJoystick(joystick);
} else {
ERROR_LOG(Log::System, "Failed to get joystick identifier. Read-only device? Control pad device %d", deviceIndex);
ERROR_LOG(Log::System, "Failed to get joystick identifier. Read-only device? Control pad device %d", deviceID);
}
} else {
SDL_Joystick *joystick = SDL_JoystickOpen(deviceIndex);
SDL_Joystick *joystick = SDL_OpenJoystick(deviceID);
if (joystick) {
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), pszGUID, cbGUID);
SDL_JoystickClose(joystick);
SDL_GUIDToString(SDL_GetJoystickGUID(joystick), pszGUID, cbGUID);
SDL_CloseJoystick(joystick);
}
}
SDL_GameController *controller = SDL_GameControllerOpen(deviceIndex);
SDL_Gamepad *controller = SDL_OpenGamepad(deviceID);
if (controller) {
if (SDL_GameControllerGetAttached(controller)) {
if (SDL_GamepadConnected(controller)) {
controllers.push_back(controller);
controllerDeviceMap[SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller))] = deviceIndex;
INFO_LOG(Log::System, "found control pad: %s, loading mapping", SDL_GameControllerName(controller));
controllerDeviceMap[SDL_GetJoystickID(SDL_GetGamepadJoystick(controller))] = deviceID;
INFO_LOG(Log::System, "found control pad: %s, loading mapping", SDL_GetGamepadName(controller));
// NOTE: The case to InputDeviceID here is wrong, we should do some kind of lookup.
KeyMap::NotifyPadConnected((InputDeviceID)deviceIndex, std::string(pszGUID) + ": " + SDL_GameControllerName(controller));
const char *mapping = SDL_GameControllerMapping(controller);
KeyMap::NotifyPadConnected((InputDeviceID)deviceID, std::string(pszGUID) + ": " + SDL_GetGamepadName(controller));
char *mapping = SDL_GetGamepadMapping(controller);
if (!mapping) {
WARN_LOG(Log::System, "Could not find mapping in SDL2 controller database");
} else {
INFO_LOG(Log::System, "SUCCESS, mapping is: %s", mapping);
SDL_free(mapping);
}
} else {
SDL_GameControllerClose(controller);
SDL_CloseGamepad(controller);
}
}
}
SDLJoystick::~SDLJoystick() {
if (registeredAsEventHandler) {
SDL_DelEventWatch(SDLJoystickEventHandlerWrapper, this);
SDL_RemoveEventWatch(SDLJoystickEventHandlerWrapper, this);
}
for (auto & controller : controllers) {
SDL_GameControllerClose(controller);
SDL_CloseGamepad(controller);
}
}
@@ -114,59 +118,51 @@ void SDLJoystick::registerEventHandler() {
registeredAsEventHandler = true;
}
InputKeyCode SDLJoystick::getKeycodeForButton(SDL_GameControllerButton button) {
InputKeyCode SDLJoystick::getKeycodeForButton(SDL_GamepadButton button) {
switch (button) {
case SDL_CONTROLLER_BUTTON_DPAD_UP:
case SDL_GAMEPAD_BUTTON_DPAD_UP:
return NKCODE_DPAD_UP;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
return NKCODE_DPAD_DOWN;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
return NKCODE_DPAD_LEFT;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
return NKCODE_DPAD_RIGHT;
case SDL_CONTROLLER_BUTTON_A:
case SDL_GAMEPAD_BUTTON_SOUTH:
return NKCODE_BUTTON_2;
case SDL_CONTROLLER_BUTTON_B:
case SDL_GAMEPAD_BUTTON_EAST:
return NKCODE_BUTTON_3;
case SDL_CONTROLLER_BUTTON_X:
case SDL_GAMEPAD_BUTTON_WEST:
return NKCODE_BUTTON_4;
case SDL_CONTROLLER_BUTTON_Y:
case SDL_GAMEPAD_BUTTON_NORTH:
return NKCODE_BUTTON_1;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
return NKCODE_BUTTON_5;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
return NKCODE_BUTTON_6;
case SDL_CONTROLLER_BUTTON_START:
case SDL_GAMEPAD_BUTTON_START:
return NKCODE_BUTTON_10;
case SDL_CONTROLLER_BUTTON_BACK:
case SDL_GAMEPAD_BUTTON_BACK:
return NKCODE_BUTTON_9; // select button
case SDL_CONTROLLER_BUTTON_GUIDE:
case SDL_GAMEPAD_BUTTON_GUIDE:
return NKCODE_BACK; // pause menu
case SDL_CONTROLLER_BUTTON_LEFTSTICK:
case SDL_GAMEPAD_BUTTON_LEFT_STICK:
return NKCODE_BUTTON_THUMBL;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
case SDL_GAMEPAD_BUTTON_RIGHT_STICK:
return NKCODE_BUTTON_THUMBR;
// Found these limits by checking out the SDL2 branch of the SDL repo, doing git blame, then `git tag --contains (commit)` etc.
#if SDL_VERSION_ATLEAST(2, 0, 16)
case SDL_CONTROLLER_BUTTON_MISC1:
case SDL_GAMEPAD_BUTTON_MISC1:
return NKCODE_BUTTON_11;
#endif
#if SDL_VERSION_ATLEAST(2, 0, 28)
case SDL_CONTROLLER_BUTTON_PADDLE1:
case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1:
return NKCODE_BUTTON_12;
case SDL_CONTROLLER_BUTTON_PADDLE2:
case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2:
return NKCODE_BUTTON_13;
case SDL_CONTROLLER_BUTTON_PADDLE3:
case SDL_GAMEPAD_BUTTON_LEFT_PADDLE1:
return NKCODE_BUTTON_14;
case SDL_CONTROLLER_BUTTON_PADDLE4:
case SDL_GAMEPAD_BUTTON_LEFT_PADDLE2:
return NKCODE_BUTTON_15;
#endif
#if SDL_VERSION_ATLEAST(2, 0, 14)
case SDL_CONTROLLER_BUTTON_TOUCHPAD:
case SDL_GAMEPAD_BUTTON_TOUCHPAD:
return NKCODE_BUTTON_16;
#endif
case SDL_CONTROLLER_BUTTON_INVALID:
case SDL_GAMEPAD_BUTTON_INVALID:
default:
return NKCODE_UNKNOWN;
}
@@ -174,39 +170,37 @@ InputKeyCode SDLJoystick::getKeycodeForButton(SDL_GameControllerButton button) {
void SDLJoystick::ProcessInput(const SDL_Event &event){
switch (event.type) {
case SDL_CONTROLLERBUTTONDOWN:
if (event.cbutton.state == SDL_PRESSED) {
auto code = getKeycodeForButton((SDL_GameControllerButton)event.cbutton.button);
if (code != NKCODE_UNKNOWN) {
KeyInput key;
key.flags = KeyInputFlags::DOWN;
key.keyCode = code;
key.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.cbutton.which);
NativeKey(key);
}
}
break;
case SDL_CONTROLLERBUTTONUP:
if (event.cbutton.state == SDL_RELEASED) {
auto code = getKeycodeForButton((SDL_GameControllerButton)event.cbutton.button);
if (code != NKCODE_UNKNOWN) {
KeyInput key;
key.flags = KeyInputFlags::UP;
key.keyCode = code;
key.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.cbutton.which);
NativeKey(key);
}
}
break;
case SDL_CONTROLLERAXISMOTION:
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
{
InputDeviceID deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.caxis.which);
// TODO: Can we really cast axis IDs like that? Do they match?
InputAxis axisId = (InputAxis)event.caxis.axis;
float value = event.caxis.value * (1.f / 32767.f);
auto code = getKeycodeForButton((SDL_GamepadButton)event.gbutton.button);
if (code != NKCODE_UNKNOWN) {
KeyInput key;
key.flags = KeyInputFlags::DOWN;
key.keyCode = code;
key.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.gbutton.which);
NativeKey(key);
}
break;
}
case SDL_EVENT_GAMEPAD_BUTTON_UP:
{
auto code = getKeycodeForButton((SDL_GamepadButton)event.gbutton.button);
if (code != NKCODE_UNKNOWN) {
KeyInput key;
key.flags = KeyInputFlags::UP;
key.keyCode = code;
key.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.gbutton.which);
NativeKey(key);
}
break;
}
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
{
InputDeviceID deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.gaxis.which);
InputAxis axisId = (InputAxis)event.gaxis.axis;
float value = event.gaxis.value * (1.f / 32767.f);
if (value > 1.0f) value = 1.0f;
if (value < -1.0f) value = -1.0f;
// Filter duplicate axis values.
auto key = std::pair<InputDeviceID, InputAxis>(deviceId, axisId);
auto iter = prevAxisValue_.find(key);
if (iter == prevAxisValue_.end()) {
@@ -218,28 +212,29 @@ void SDLJoystick::ProcessInput(const SDL_Event &event){
axis.value = value;
axis.deviceId = deviceId;
NativeAxis(&axis, 1);
} // else ignore event.
}
break;
}
case SDL_CONTROLLERDEVICEREMOVED:
// for removal events, "which" is the instance ID for SDL_CONTROLLERDEVICEREMOVED
case SDL_EVENT_GAMEPAD_REMOVED:
for (auto it = controllers.begin(); it != controllers.end(); ++it) {
if (SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(*it)) == event.cdevice.which) {
SDL_GameControllerClose(*it);
if (SDL_GetJoystickID(SDL_GetGamepadJoystick(*it)) == event.gdevice.which) {
SDL_CloseGamepad(*it);
controllerDeviceMap.erase(event.gdevice.which);
controllers.erase(it);
break;
}
}
break;
case SDL_CONTROLLERDEVICEADDED:
// for add events, "which" is the device index!
case SDL_EVENT_GAMEPAD_ADDED:
{
int prevNumControllers = controllers.size();
setUpController(event.cdevice.which);
setUpController(event.gdevice.which);
if (prevNumControllers == 0 && controllers.size() > 0) {
INFO_LOG(Log::System, "pad 1 has been assigned to control pad: %s", SDL_GameControllerName(controllers.front()));
INFO_LOG(Log::System, "pad 1 has been assigned to control pad: %s", SDL_GetGamepadName(controllers.front()));
}
break;
}
}
}
int SDLJoystick::getDeviceIndex(int instanceId) {
+5 -12
View File
@@ -1,14 +1,7 @@
#pragma once
#ifdef _MSC_VER
#include "SDL/SDL.h"
#else
#if PPSSPP_PLATFORM(MAC)
#include "SDL2/SDL.h"
#else
#include "SDL.h"
#endif
#endif
#include <SDL3/SDL.h>
#include <map>
#include <vector>
#include "Common/Input/InputState.h"
#include "Common/Input/KeyCodes.h"
@@ -23,13 +16,13 @@ public:
void ProcessInput(const SDL_Event &event);
private:
void setUpController(int deviceIndex);
void setUpController(SDL_JoystickID deviceID);
void setUpControllers();
InputKeyCode getKeycodeForButton(SDL_GameControllerButton button);
InputKeyCode getKeycodeForButton(SDL_GamepadButton button);
int getDeviceIndex(int instanceId);
bool registeredAsEventHandler;
std::vector<SDL_GameController *> controllers;
std::vector<SDL_Gamepad *> controllers;
std::map<int, int> controllerDeviceMap;
// Deduplicate axis events. Pair is device, axis.
+250 -205
View File
@@ -3,15 +3,8 @@
#include <pwd.h>
#include "ppsspp_config.h"
#if PPSSPP_PLATFORM(MAC)
#include "SDL2/SDL.h"
#include "SDL2/SDL_syswm.h"
#include "SDL2/SDL_mouse.h"
#else
#include "SDL.h"
#include "SDL_syswm.h"
#include "SDL_mouse.h"
#endif
#include <SDL3/SDL.h>
#include <SDL3/SDL_mouse.h>
#include "SDL/SDLJoystick.h"
SDLJoystick *joystick = NULL;
@@ -69,11 +62,7 @@ SDLJoystick *joystick = NULL;
#include "SDLGLGraphicsContext.h"
#include "SDLVulkanGraphicsContext.h"
#if PPSSPP_PLATFORM(MAC)
#include "SDL2/SDL_vulkan.h"
#else
#include "SDL_vulkan.h"
#endif
#include <SDL3/SDL_vulkan.h>
#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
#include "Core/Util/DarwinFileSystemServices.h"
@@ -104,6 +93,7 @@ static int g_sampleRate = 44100;
static bool g_rebootEmuThread = false;
static SDL_AudioSpec g_retFmt;
static int g_audioFramesPerBuffer = 0;
static bool g_textFocusChanged;
static bool g_textFocus;
@@ -134,78 +124,114 @@ int getDisplayNumber(void) {
return displayNumber;
}
void sdl_mixaudio_callback(void *userdata, Uint8 *stream, int len) {
NativeMix((short *)stream, len / (2 * 2), g_sampleRate, userdata);
void sdl_mixaudio_callback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount) {
(void)total_amount;
if (additional_amount <= 0) {
return;
}
const int frames = additional_amount / (int)(sizeof(int16_t) * 2);
if (frames <= 0) {
return;
}
std::vector<int16_t> mixBuf(frames * 2);
NativeMix(mixBuf.data(), frames, g_sampleRate, userdata);
SDL_PutAudioStreamData(stream, mixBuf.data(), (int)(mixBuf.size() * sizeof(int16_t)));
}
static SDL_AudioDeviceID audioDev = 0;
static SDL_AudioStream *audioStream = nullptr;
// Must be called after NativeInit().
static void InitSDLAudioDevice(const std::string &name = "") {
SDL_AudioSpec fmt;
memset(&fmt, 0, sizeof(fmt));
SDL_AudioSpec fmt{};
fmt.freq = g_sampleRate;
fmt.format = AUDIO_S16;
fmt.format = SDL_AUDIO_S16;
fmt.channels = 2;
fmt.samples = std::max(g_Config.iSDLAudioBufferSize, 128);
fmt.callback = &sdl_mixaudio_callback;
fmt.userdata = nullptr;
g_audioFramesPerBuffer = std::max(g_Config.iSDLAudioBufferSize, 128);
std::string startDevice = name;
if (startDevice.empty()) {
startDevice = g_Config.sAudioDevice;
}
int deviceCount = 0;
SDL_AudioDeviceID *devices = SDL_GetAudioPlaybackDevices(&deviceCount);
SDL_AudioDeviceID chosenDevice = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
// List available audio devices before trying to open, for debugging purposes.
const int deviceCount = SDL_GetNumAudioDevices(0);
if (deviceCount > 0) {
if (deviceCount > 0 && devices) {
INFO_LOG(Log::Audio, "Available audio devices:");
for (int i = 0; i < deviceCount; i++) {
const char *deviceName = SDL_GetAudioDeviceName(i, 0);
const char *deviceName = SDL_GetAudioDeviceName(devices[i]);
if (!deviceName) {
deviceName = "(unknown)";
}
INFO_LOG(Log::Audio, " * '%s'", deviceName);
if (!startDevice.empty() && startDevice == deviceName) {
chosenDevice = devices[i];
}
}
} else {
INFO_LOG(Log::Audio, "Failed to list audio devices: retval=%d", deviceCount);
}
if (!startDevice.empty() && chosenDevice == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) {
WARN_LOG(Log::Audio, "Audio device '%s' not found, using default", startDevice.c_str());
}
if (audioStream) {
SDL_DestroyAudioStream(audioStream);
audioStream = nullptr;
}
audioDev = 0;
if (!startDevice.empty()) {
if (chosenDevice == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) {
INFO_LOG(Log::Audio, "Opening default audio device");
} else {
INFO_LOG(Log::Audio, "Opening audio device: '%s'", startDevice.c_str());
audioDev = SDL_OpenAudioDevice(startDevice.c_str(), 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if (audioDev <= 0) {
WARN_LOG(Log::Audio, "Failed to open audio device '%s'", startDevice.c_str());
}
}
if (audioDev <= 0) {
if (audioDev < 0) {
WARN_LOG(Log::Audio, "SDL: Error: '%s'. Trying the default audio device", SDL_GetError());
} else {
INFO_LOG(Log::Audio, "Opening default audio device");
}
audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
audioStream = SDL_OpenAudioDeviceStream(chosenDevice, &fmt, sdl_mixaudio_callback, nullptr);
if (!audioStream && chosenDevice != SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) {
WARN_LOG(Log::Audio, "SDL: Error opening '%s': '%s'. Trying default.", startDevice.c_str(), SDL_GetError());
audioStream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, sdl_mixaudio_callback, nullptr);
}
if (audioDev <= 0) {
if (!audioStream) {
ERROR_LOG(Log::Audio, "Failed to open audio device '%s', second try. Giving up.", SDL_GetError());
} else {
if (g_retFmt.samples != fmt.samples) // Notify, but still use it
ERROR_LOG(Log::Audio, "Output audio samples: %d (requested: %d)", g_retFmt.samples, fmt.samples);
if (g_retFmt.format != fmt.format || g_retFmt.channels != fmt.channels) {
ERROR_LOG(Log::Audio, "Sound buffer format does not match requested format.");
ERROR_LOG(Log::Audio, "Output audio freq: %d (requested: %d)", g_retFmt.freq, fmt.freq);
ERROR_LOG(Log::Audio, "Output audio format: %d (requested: %d)", g_retFmt.format, fmt.format);
ERROR_LOG(Log::Audio, "Output audio channels: %d (requested: %d)", g_retFmt.channels, fmt.channels);
ERROR_LOG(Log::Audio, "Provided output format does not match requirement, turning audio off");
SDL_CloseAudioDevice(audioDev);
audioDev = SDL_GetAudioStreamDevice(audioStream);
if (!SDL_GetAudioDeviceFormat(audioDev, &g_retFmt, &g_audioFramesPerBuffer)) {
WARN_LOG(Log::Audio, "Could not query active audio format: %s", SDL_GetError());
g_retFmt = fmt;
g_audioFramesPerBuffer = std::max(g_Config.iSDLAudioBufferSize, 128);
}
SDL_PauseAudioDevice(audioDev, 0);
if (g_retFmt.freq != fmt.freq || g_retFmt.format != fmt.format || g_retFmt.channels != fmt.channels) {
WARN_LOG(Log::Audio, "Audio output format differs from requested (freq=%d/%d format=%u/%u ch=%d/%d)", g_retFmt.freq, fmt.freq, (unsigned)g_retFmt.format, (unsigned)fmt.format, g_retFmt.channels, fmt.channels);
}
if (!SDL_ResumeAudioStreamDevice(audioStream)) {
ERROR_LOG(Log::Audio, "Failed to start audio stream: %s", SDL_GetError());
SDL_DestroyAudioStream(audioStream);
audioStream = nullptr;
audioDev = 0;
}
}
if (devices) {
SDL_free(devices);
}
}
static void StopSDLAudioDevice() {
if (audioDev > 0) {
SDL_PauseAudioDevice(audioDev, 1);
SDL_CloseAudioDevice(audioDev);
if (audioStream) {
SDL_DestroyAudioStream(audioStream);
audioStream = nullptr;
}
audioDev = 0;
}
static void UpdateScreenDPI(SDL_Window *window) {
@@ -213,9 +239,9 @@ static void UpdateScreenDPI(SDL_Window *window) {
SDL_GetWindowSize(window, &window_width, &window_height);
if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL)
SDL_GL_GetDrawableSize(window, &drawable_width, NULL);
SDL_GetWindowSizeInPixels(window, &drawable_width, NULL);
else if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN)
SDL_Vulkan_GetDrawableSize(window, &drawable_width, NULL);
SDL_GetWindowSizeInPixels(window, &drawable_width, NULL);
else {
// If we add SDL support for more platforms, we'll end up here.
g_DesktopDPI = 1.0f;
@@ -605,8 +631,10 @@ std::string System_GetProperty(SystemProperty prop) {
case SYSPROP_AUDIO_DEVICE_LIST:
{
std::string result;
for (int i = 0; i < SDL_GetNumAudioDevices(0); ++i) {
const char *name = SDL_GetAudioDeviceName(i, 0);
int count = 0;
SDL_AudioDeviceID *devices = SDL_GetAudioPlaybackDevices(&count);
for (int i = 0; devices && i < count; ++i) {
const char *name = SDL_GetAudioDeviceName(devices[i]);
if (!name) {
continue;
}
@@ -618,6 +646,9 @@ std::string System_GetProperty(SystemProperty prop) {
result.append(name);
}
}
if (devices) {
SDL_free(devices);
}
return result;
}
case SYSPROP_BUILD_VERSION:
@@ -661,7 +692,7 @@ int64_t System_GetPropertyInt(SystemProperty prop) {
case SYSPROP_AUDIO_SAMPLE_RATE:
return g_retFmt.freq;
case SYSPROP_AUDIO_FRAMES_PER_BUFFER:
return g_retFmt.samples;
return g_audioFramesPerBuffer;
case SYSPROP_DEVICE_TYPE:
#if defined(MOBILE_DEVICE)
return DEVICE_TYPE_MOBILE;
@@ -669,13 +700,20 @@ int64_t System_GetPropertyInt(SystemProperty prop) {
return DEVICE_TYPE_DESKTOP;
#endif
case SYSPROP_DISPLAY_COUNT:
return SDL_GetNumVideoDisplays();
{
int displayCount = 0;
SDL_DisplayID *displays = SDL_GetDisplays(&displayCount);
if (displays) {
SDL_free(displays);
}
return displayCount;
}
case SYSPROP_KEYBOARD_LAYOUT:
{
char q, w, y;
q = SDL_GetKeyFromScancode(SDL_SCANCODE_Q);
w = SDL_GetKeyFromScancode(SDL_SCANCODE_W);
y = SDL_GetKeyFromScancode(SDL_SCANCODE_Y);
q = SDL_GetKeyFromScancode(SDL_SCANCODE_Q, SDL_KMOD_NONE, false);
w = SDL_GetKeyFromScancode(SDL_SCANCODE_W, SDL_KMOD_NONE, false);
y = SDL_GetKeyFromScancode(SDL_SCANCODE_Y, SDL_KMOD_NONE, false);
if (q == 'a' && w == 'z' && y == 'y')
return KEYBOARD_LAYOUT_AZERTY;
else if (q == 'q' && w == 'w' && y == 'z')
@@ -824,14 +862,7 @@ void UpdateWindowState(SDL_Window *window) {
SDL_SetWindowTitle(window, g_windowState.title.c_str());
if (g_windowState.applyFullScreenNextFrame) {
g_windowState.applyFullScreenNextFrame = false;
Uint32 window_flags = SDL_GetWindowFlags(window);
if (g_Config.bFullScreen) {
window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
} else {
window_flags &= ~SDL_WINDOW_FULLSCREEN_DESKTOP;
}
SDL_SetWindowFullscreen(window, window_flags);
SDL_SetWindowFullscreen(window, g_Config.bFullScreen);
}
if (g_windowState.clipboardDataAvailable) {
SDL_SetClipboardText(g_windowState.clipboardString.c_str());
@@ -884,14 +915,11 @@ static void EmuThreadJoin() {
}
struct InputStateTracker {
void MouseCaptureControl() {
void MouseCaptureControl(SDL_Window *window) {
bool captureMouseCondition = g_Config.bMouseControl && ((GetUIState() == UISTATE_INGAME && g_Config.bMouseConfine) || g_IsMappingMouseInput);
if (mouseCaptured != captureMouseCondition) {
mouseCaptured = captureMouseCondition;
if (captureMouseCondition)
SDL_SetRelativeMouseMode(SDL_TRUE);
else
SDL_SetRelativeMouseMode(SDL_FALSE);
SDL_SetWindowRelativeMouseMode(window, captureMouseCondition);
}
}
@@ -899,25 +927,25 @@ struct InputStateTracker {
bool mouseCaptured;
};
SDL_Cursor *g_builtinCursors[SDL_NUM_SYSTEM_CURSORS];
SDL_Cursor *g_builtinCursors[SDL_SYSTEM_CURSOR_COUNT];
static SDL_SystemCursor GetSDLCursorFromImgui(ImGuiMouseCursor cursor) {
switch (cursor) {
case ImGuiMouseCursor_Arrow: return SDL_SYSTEM_CURSOR_ARROW; break;
case ImGuiMouseCursor_TextInput: return SDL_SYSTEM_CURSOR_IBEAM; break;
case ImGuiMouseCursor_ResizeAll: return SDL_SYSTEM_CURSOR_SIZEALL; break;
case ImGuiMouseCursor_ResizeEW: return SDL_SYSTEM_CURSOR_SIZEWE; break;
case ImGuiMouseCursor_ResizeNS: return SDL_SYSTEM_CURSOR_SIZENS; break;
case ImGuiMouseCursor_ResizeNESW: return SDL_SYSTEM_CURSOR_SIZENESW; break;
case ImGuiMouseCursor_ResizeNWSE: return SDL_SYSTEM_CURSOR_SIZENWSE; break;
case ImGuiMouseCursor_Hand: return SDL_SYSTEM_CURSOR_HAND; break;
case ImGuiMouseCursor_NotAllowed: return SDL_SYSTEM_CURSOR_NO; break;
default: return SDL_SYSTEM_CURSOR_ARROW; break;
case ImGuiMouseCursor_Arrow: return SDL_SYSTEM_CURSOR_DEFAULT; break;
case ImGuiMouseCursor_TextInput: return SDL_SYSTEM_CURSOR_TEXT; break;
case ImGuiMouseCursor_ResizeAll: return SDL_SYSTEM_CURSOR_MOVE; break;
case ImGuiMouseCursor_ResizeEW: return SDL_SYSTEM_CURSOR_EW_RESIZE; break;
case ImGuiMouseCursor_ResizeNS: return SDL_SYSTEM_CURSOR_NS_RESIZE; break;
case ImGuiMouseCursor_ResizeNESW: return SDL_SYSTEM_CURSOR_NESW_RESIZE; break;
case ImGuiMouseCursor_ResizeNWSE: return SDL_SYSTEM_CURSOR_NWSE_RESIZE; break;
case ImGuiMouseCursor_Hand: return SDL_SYSTEM_CURSOR_POINTER; break;
case ImGuiMouseCursor_NotAllowed: return SDL_SYSTEM_CURSOR_NOT_ALLOWED; break;
default: return SDL_SYSTEM_CURSOR_DEFAULT; break;
}
}
void UpdateCursor() {
static SDL_SystemCursor curCursor = SDL_SYSTEM_CURSOR_ARROW;
static SDL_SystemCursor curCursor = SDL_SYSTEM_CURSOR_DEFAULT;
auto cursor = ImGui_ImplPlatform_GetCursor();
SDL_SystemCursor sysCursor = GetSDLCursorFromImgui(cursor);
if (sysCursor != curCursor) {
@@ -930,39 +958,24 @@ void UpdateCursor() {
}
static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputStateTracker *inputTracker) {
// We have to juggle around 3 kinds of "DPI spaces" if a logical DPI is
// provided (through --dpi, it is equal to system DPI if unspecified):
// - SDL gives us motion events in "system DPI" points
// - Native_UpdateScreenScale expects pixels, so in a way "96 DPI" points
// - The UI code expects motion events in "logical DPI" points
float mx = event.motion.x * g_DesktopDPI * g_display.dpi_scale_x;
float my = event.motion.y * g_DesktopDPI * g_display.dpi_scale_x;
switch (event.type) {
case SDL_QUIT:
case SDL_EVENT_QUIT:
g_QuitRequested = 1;
break;
#if !defined(MOBILE_DEVICE)
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_SIZE_CHANGED: // better than RESIZED, more general
#if !defined(MOBILE_DEVICE)
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
{
int new_width = event.window.data1;
int new_height = event.window.data2;
// The size given by SDL is in point-units, convert these to
// pixels before passing to Native_UpdateScreenScale()
int new_width_px = new_width * g_DesktopDPI;
int new_height_px = new_height * g_DesktopDPI;
Native_NotifyWindowHidden(false);
Uint32 window_flags = SDL_GetWindowFlags(window);
bool fullscreen = (window_flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP));
Uint64 window_flags = SDL_GetWindowFlags(window);
bool fullscreen = (window_flags & SDL_WINDOW_FULLSCREEN) != 0;
// This one calls NativeResized if the size changed.
Native_UpdateScreenScale(new_width_px, new_height_px, UIScaleFactorToMultiplier(g_Config.iUIScaleFactor));
Native_UpdateScreenScale(new_width, new_height, UIScaleFactorToMultiplier(g_Config.iUIScaleFactor));
// Set variable here in case fullscreen was toggled by hotkey
if (g_Config.bFullScreen != fullscreen) {
@@ -974,22 +987,24 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
}
if (!g_Config.bFullScreen) {
g_Config.iWindowWidth = new_width;
g_Config.iWindowHeight = new_height;
int windowWidth = 0;
int windowHeight = 0;
SDL_GetWindowSize(window, &windowWidth, &windowHeight);
g_Config.iWindowWidth = windowWidth;
g_Config.iWindowHeight = windowHeight;
}
// Hide/Show cursor correctly toggling fullscreen
if (lastUIState == UISTATE_INGAME && fullscreen && !g_Config.bShowTouchControls) {
SDL_ShowCursor(SDL_DISABLE);
SDL_HideCursor();
} else if (lastUIState != UISTATE_INGAME || !fullscreen) {
SDL_ShowCursor(SDL_ENABLE);
SDL_ShowCursor();
}
break;
}
case SDL_WINDOWEVENT_MOVED:
case SDL_EVENT_WINDOW_MOVED:
{
Uint32 window_flags = SDL_GetWindowFlags(window);
bool fullscreen = (window_flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP));
Uint64 window_flags = SDL_GetWindowFlags(window);
bool fullscreen = (window_flags & SDL_WINDOW_FULLSCREEN) != 0;
if (!fullscreen) {
g_Config.iWindowX = (int)event.window.data1;
g_Config.iWindowY = (int)event.window.data2;
@@ -997,7 +1012,7 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
break;
}
case SDL_WINDOWEVENT_FOCUS_LOST:
case SDL_EVENT_WINDOW_FOCUS_LOST:
{
if (g_Config.bPauseOnLostFocus && GetUIState() == UISTATE_INGAME) {
Core_Break(BreakReason::UIFocus, 0);
@@ -1005,7 +1020,7 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
}
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
case SDL_EVENT_WINDOW_FOCUS_GAINED:
{
if (Core_BreakReason() == BreakReason::UIFocus) {
Core_Resume();
@@ -1013,23 +1028,20 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
}
break;
case SDL_WINDOWEVENT_MINIMIZED:
case SDL_WINDOWEVENT_HIDDEN:
case SDL_EVENT_WINDOW_MINIMIZED:
case SDL_EVENT_WINDOW_HIDDEN:
Native_NotifyWindowHidden(true);
break;
case SDL_WINDOWEVENT_EXPOSED:
case SDL_WINDOWEVENT_SHOWN:
case SDL_EVENT_WINDOW_EXPOSED:
case SDL_EVENT_WINDOW_SHOWN:
Native_NotifyWindowHidden(false);
break;
default:
break;
}
break;
#endif
case SDL_KEYDOWN:
case SDL_EVENT_KEY_DOWN:
{
if (event.key.repeat > 0) { break;}
int k = event.key.keysym.sym;
int k = event.key.key;
KeyInput key;
key.flags = KeyInputFlags::DOWN;
auto mapped = KeyMapRawSDLtoNative.find(k);
@@ -1050,8 +1062,8 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
// "Enable standard shortcut keys"
// does on Windows.
if (g_Config.bSystemControls) {
bool ctrl = bool(event.key.keysym.mod & KMOD_CTRL);
if (ctrl && (k == SDLK_w))
bool ctrl = bool(event.key.mod & SDL_KMOD_CTRL);
if (ctrl && (k == SDLK_W))
{
if (Core_IsStepping())
Core_Resume();
@@ -1075,10 +1087,10 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
}
break;
}
case SDL_KEYUP:
case SDL_EVENT_KEY_UP:
{
if (event.key.repeat > 0) { break;}
int k = event.key.keysym.sym;
int k = event.key.key;
KeyInput key;
key.flags = KeyInputFlags::UP;
auto mapped = KeyMapRawSDLtoNative.find(k);
@@ -1090,7 +1102,7 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
NativeKey(key);
break;
}
case SDL_TEXTINPUT:
case SDL_EVENT_TEXT_INPUT:
{
int pos = 0;
int c = u8_nextchar(event.text.text, &pos, strlen(event.text.text));
@@ -1103,12 +1115,12 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
}
// This behavior doesn't feel right on a macbook with a touchpad.
#if !PPSSPP_PLATFORM(MAC)
case SDL_FINGERMOTION:
case SDL_EVENT_FINGER_MOTION:
{
int w, h;
SDL_GetWindowSize(window, &w, &h);
TouchInput input{};
input.id = event.tfinger.fingerId;
input.id = event.tfinger.fingerID;
input.x = event.tfinger.x * w * g_DesktopDPI * g_display.dpi_scale_x;
input.y = event.tfinger.y * h * g_DesktopDPI * g_display.dpi_scale_x;
input.flags = TouchInputFlags::MOVE;
@@ -1116,12 +1128,12 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
NativeTouch(input);
break;
}
case SDL_FINGERDOWN:
case SDL_EVENT_FINGER_DOWN:
{
int w, h;
SDL_GetWindowSize(window, &w, &h);
TouchInput input{};
input.id = event.tfinger.fingerId;
input.id = event.tfinger.fingerID;
input.x = event.tfinger.x * w * g_DesktopDPI * g_display.dpi_scale_x;
input.y = event.tfinger.y * h * g_DesktopDPI * g_display.dpi_scale_x;
input.flags = TouchInputFlags::DOWN;
@@ -1135,12 +1147,12 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
NativeKey(key);
break;
}
case SDL_FINGERUP:
case SDL_EVENT_FINGER_UP:
{
int w, h;
SDL_GetWindowSize(window, &w, &h);
TouchInput input{};
input.id = event.tfinger.fingerId;
input.id = event.tfinger.fingerID;
input.x = event.tfinger.x * w * g_DesktopDPI * g_display.dpi_scale_x;
input.y = event.tfinger.y * h * g_DesktopDPI * g_display.dpi_scale_x;
input.flags = TouchInputFlags::UP;
@@ -1155,10 +1167,17 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
break;
}
#endif
case SDL_MOUSEBUTTONDOWN:
case SDL_EVENT_MOUSE_BUTTON_DOWN:
switch (event.button.button) {
case SDL_BUTTON_LEFT:
{
// We have to juggle around 3 kinds of "DPI spaces" if a logical DPI is
// provided (through --dpi, it is equal to system DPI if unspecified):
// - SDL gives us motion events in "system DPI" points
// - Native_UpdateScreenScale expects pixels, so in a way "96 DPI" points
// - The UI code expects motion events in "logical DPI" points
float mx = event.button.x * g_DesktopDPI * g_display.dpi_scale_x;
float my = event.button.y * g_DesktopDPI * g_display.dpi_scale_x;
inputTracker->mouseDown |= 1;
TouchInput input{};
input.x = mx;
@@ -1173,6 +1192,8 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
break;
case SDL_BUTTON_RIGHT:
{
float mx = event.button.x * g_DesktopDPI * g_display.dpi_scale_x;
float my = event.button.y * g_DesktopDPI * g_display.dpi_scale_x;
inputTracker->mouseDown |= 2;
TouchInput input{};
input.x = mx;
@@ -1205,37 +1226,37 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
break;
}
break;
case SDL_MOUSEWHEEL:
case SDL_EVENT_MOUSE_WHEEL:
{
KeyInput key{};
key.deviceId = DEVICE_ID_MOUSE;
key.flags = KeyInputFlags::DOWN;
#if SDL_VERSION_ATLEAST(2, 0, 18)
if (event.wheel.preciseY != 0.0f) {
// Should the scale be DPI-driven?
float wheelY = event.wheel.y;
if (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) {
wheelY = -wheelY;
}
if (wheelY != 0.0f) {
const float scale = 30.0f;
key.keyCode = event.wheel.preciseY > 0 ? NKCODE_EXT_MOUSEWHEEL_UP : NKCODE_EXT_MOUSEWHEEL_DOWN;
key.keyCode = wheelY > 0 ? NKCODE_EXT_MOUSEWHEEL_UP : NKCODE_EXT_MOUSEWHEEL_DOWN;
key.flags |= KeyInputFlags::HAS_WHEEL_DELTA;
int wheelDelta = event.wheel.preciseY * scale;
if (event.wheel.preciseY < 0) {
wheelDelta = -wheelDelta;
}
int wheelDelta = (int)(fabsf(wheelY) * scale);
key.flags = (KeyInputFlags)((u32)key.flags | (wheelDelta << 16));
NativeKey(key);
break;
}
#endif
if (event.wheel.y > 0) {
if (event.wheel.integer_y > 0) {
key.keyCode = NKCODE_EXT_MOUSEWHEEL_UP;
NativeKey(key);
} else if (event.wheel.y < 0) {
} else if (event.wheel.integer_y < 0) {
key.keyCode = NKCODE_EXT_MOUSEWHEEL_DOWN;
NativeKey(key);
}
break;
}
case SDL_MOUSEMOTION:
case SDL_EVENT_MOUSE_MOTION:
{
float mx = event.motion.x * g_DesktopDPI * g_display.dpi_scale_x;
float my = event.motion.y * g_DesktopDPI * g_display.dpi_scale_x;
TouchInput input{};
input.x = mx;
input.y = my;
@@ -1248,10 +1269,12 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
UpdateCursor();
break;
}
case SDL_MOUSEBUTTONUP:
case SDL_EVENT_MOUSE_BUTTON_UP:
switch (event.button.button) {
case SDL_BUTTON_LEFT:
{
float mx = event.button.x * g_DesktopDPI * g_display.dpi_scale_x;
float my = event.button.y * g_DesktopDPI * g_display.dpi_scale_x;
inputTracker->mouseDown &= ~1;
TouchInput input{};
input.x = mx;
@@ -1265,6 +1288,8 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
break;
case SDL_BUTTON_RIGHT:
{
float mx = event.button.x * g_DesktopDPI * g_display.dpi_scale_x;
float my = event.button.y * g_DesktopDPI * g_display.dpi_scale_x;
inputTracker->mouseDown &= ~2;
// Right button only emits mouse move events. This is weird,
// but consistent with Windows. Needs cleanup.
@@ -1299,11 +1324,10 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
}
break;
#if SDL_VERSION_ATLEAST(2, 0, 4)
case SDL_AUDIODEVICEADDED:
case SDL_EVENT_AUDIO_DEVICE_ADDED:
// Automatically switch to the new device.
if (event.adevice.iscapture == 0) {
const char *name = SDL_GetAudioDeviceName(event.adevice.which, 0);
if (!event.adevice.recording) {
const char *name = SDL_GetAudioDeviceName(event.adevice.which);
if (!name) {
INFO_LOG(Log::Audio, "Got bogus new audio device notification");
break;
@@ -1323,14 +1347,13 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
}
}
break;
case SDL_AUDIODEVICEREMOVED:
if (event.adevice.iscapture == 0 && event.adevice.which == audioDev) {
case SDL_EVENT_AUDIO_DEVICE_REMOVED:
if (!event.adevice.recording && event.adevice.which == audioDev) {
StopSDLAudioDevice();
INFO_LOG(Log::Audio, "Audio device removed, reselecting");
InitSDLAudioDevice();
}
break;
#endif
default:
if (joystick) {
@@ -1340,13 +1363,13 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
}
}
void UpdateTextFocus() {
void UpdateTextFocus(SDL_Window *window) {
if (g_textFocusChanged) {
DEBUG_LOG(Log::System, "Updating text focus: %d", g_textFocus);
if (g_textFocus) {
SDL_StartTextInput();
SDL_StartTextInput(window);
} else {
SDL_StopTextInput();
SDL_StopTextInput(window);
}
g_textFocusChanged = false;
}
@@ -1357,9 +1380,9 @@ void UpdateSDLCursor() {
if (lastUIState != GetUIState()) {
lastUIState = GetUIState();
if (lastUIState == UISTATE_INGAME && g_Config.bFullScreen && !g_Config.bShowTouchControls)
SDL_ShowCursor(SDL_DISABLE);
SDL_HideCursor();
if (lastUIState != UISTATE_INGAME || !g_Config.bFullScreen)
SDL_ShowCursor(SDL_ENABLE);
SDL_ShowCursor();
}
#endif
}
@@ -1464,8 +1487,8 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "DEBUG: Vulkan is not available, not using Vulkan.\n");
}
SDL_version compiled;
SDL_version linked;
const int compiled = SDL_VERSION;
const int linked = SDL_GetVersion();
int set_xres = -1;
int set_yres = -1;
bool portrait = false;
@@ -1476,6 +1499,7 @@ int main(int argc, char *argv[]) {
// Produce a new set of arguments with the ones we skip.
int remain_argc = 1;
const char *remain_argv[256] = { argv[0] };
constexpr int remain_argv_cap = (int)(sizeof(remain_argv) / sizeof(remain_argv[0]));
// Option to force a specific OpenGL version (42="4.2",
// etc.; -1 means "try them all").
@@ -1488,7 +1512,7 @@ int main(int argc, char *argv[]) {
Uint32 mode = 0;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--fullscreen")) {
mode |= SDL_WINDOW_FULLSCREEN_DESKTOP;
mode |= SDL_WINDOW_FULLSCREEN;
g_Config.DoNotSaveSetting(&g_Config.bFullScreen);
} else if (set_xres == -2)
set_xres = parseInt(argv[i]);
@@ -1531,9 +1555,14 @@ int main(int argc, char *argv[]) {
force_gl_version = int(10.0 * val + 0.5);
}
} else {
remain_argv[remain_argc++] = argv[i];
if (remain_argc < remain_argv_cap - 1) {
remain_argv[remain_argc++] = argv[i];
} else {
fprintf(stderr, "Too many command-line arguments, ignoring: %s\n", argv[i]);
}
}
}
remain_argv[remain_argc] = nullptr;
std::string app_name;
std::string app_name_nice;
@@ -1542,35 +1571,40 @@ int main(int argc, char *argv[]) {
NativeGetAppInfo(&app_name, &app_name_nice, &landscape, &version);
bool joystick_enabled = true;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO) < 0) {
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_AUDIO)) {
fprintf(stderr, "Failed to initialize SDL with joystick support. Retrying without.\n");
joystick_enabled = false;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
return 1;
}
}
SDL_VERSION(&compiled);
SDL_GetVersion(&linked);
fprintf(stderr, "Info: We compiled against SDL version %d.%d.%d", compiled.major, compiled.minor, compiled.patch);
if (compiled.minor != linked.minor || compiled.patch != linked.patch) {
fprintf(stderr, ", but we are linking against SDL version %d.%d.%d., be aware that this can lead to unexpected behaviors\n", linked.major, linked.minor, linked.patch);
fprintf(stderr, "Info: We compiled against SDL version %d.%d.%d", SDL_VERSIONNUM_MAJOR(compiled), SDL_VERSIONNUM_MINOR(compiled), SDL_VERSIONNUM_MICRO(compiled));
if (compiled != linked) {
fprintf(stderr, ", but we are linking against SDL version %d.%d.%d., be aware that this can lead to unexpected behaviors\n", SDL_VERSIONNUM_MAJOR(linked), SDL_VERSIONNUM_MINOR(linked), SDL_VERSIONNUM_MICRO(linked));
} else {
fprintf(stderr, " and we are linking against SDL version %d.%d.%d. :)\n", linked.major, linked.minor, linked.patch);
fprintf(stderr, " and we are linking against SDL version %d.%d.%d. :)\n", SDL_VERSIONNUM_MAJOR(linked), SDL_VERSIONNUM_MINOR(linked), SDL_VERSIONNUM_MICRO(linked));
}
// Get the video info before doing anything else, so we don't get skewed resolution results.
// TODO: support multiple displays correctly
SDL_DisplayMode displayMode;
int should_be_zero = SDL_GetCurrentDisplayMode(0, &displayMode);
if (should_be_zero != 0) {
fprintf(stderr, "Could not get display mode: %s\n", SDL_GetError());
int displayCount = 0;
SDL_DisplayID *displayIDs = SDL_GetDisplays(&displayCount);
if (!displayIDs || displayCount == 0) {
fprintf(stderr, "Could not enumerate displays: %s\n", SDL_GetError());
return 1;
}
g_DesktopWidth = displayMode.w;
g_DesktopHeight = displayMode.h;
g_RefreshRate = displayMode.refresh_rate;
const SDL_DisplayMode *displayMode = SDL_GetCurrentDisplayMode(displayIDs[0]);
if (!displayMode) {
fprintf(stderr, "Could not get display mode: %s\n", SDL_GetError());
SDL_free(displayIDs);
return 1;
}
g_DesktopWidth = displayMode->w;
g_DesktopHeight = displayMode->h;
g_RefreshRate = displayMode->refresh_rate;
SDL_free(displayIDs);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
@@ -1581,19 +1615,19 @@ int main(int argc, char *argv[]) {
// Force fullscreen if the resolution is too low to run windowed.
if (g_DesktopWidth < 480 * 2 && g_DesktopHeight < 272 * 2) {
mode |= SDL_WINDOW_FULLSCREEN_DESKTOP;
mode |= SDL_WINDOW_FULLSCREEN;
}
// If we're on mobile, don't try for windowed either.
#if defined(MOBILE_DEVICE) && !PPSSPP_PLATFORM(SWITCH)
mode |= SDL_WINDOW_FULLSCREEN;
#elif defined(USING_FBDEV) || PPSSPP_PLATFORM(SWITCH)
mode |= SDL_WINDOW_FULLSCREEN_DESKTOP;
mode |= SDL_WINDOW_FULLSCREEN;
#else
mode |= SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
mode |= SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY;
#endif
if (mode & SDL_WINDOW_FULLSCREEN_DESKTOP) {
if (mode & SDL_WINDOW_FULLSCREEN) {
g_display.pixel_xres = g_DesktopWidth;
g_display.pixel_yres = g_DesktopHeight;
g_Config.bFullScreen = true;
@@ -1626,7 +1660,7 @@ int main(int argc, char *argv[]) {
}
// Mac / Linux
char path[2048];
char path[2048] = {};
#if PPSSPP_PLATFORM(SWITCH)
strcpy(path, "/switch/ppsspp/");
#else
@@ -1637,10 +1671,10 @@ int main(int argc, char *argv[]) {
the_path = pwd->pw_dir;
}
if (the_path)
strcpy(path, the_path);
snprintf(path, sizeof(path), "%s", the_path);
#endif
if (strlen(path) > 0 && path[strlen(path) - 1] != '/')
strcat(path, "/");
if (path[0] != '\0' && path[strlen(path) - 1] != '/')
strncat(path, "/", sizeof(path) - strlen(path) - 1);
#if PPSSPP_PLATFORM(MAC)
std::string external_dir_str;
@@ -1656,7 +1690,7 @@ int main(int argc, char *argv[]) {
// Use the setting from the config when initing the window.
if (g_Config.bFullScreen) {
mode |= SDL_WINDOW_FULLSCREEN_DESKTOP;
mode |= SDL_WINDOW_FULLSCREEN;
g_display.pixel_xres = g_DesktopWidth;
g_display.pixel_yres = g_DesktopHeight;
}
@@ -1750,11 +1784,18 @@ int main(int argc, char *argv[]) {
int width = 0, height = 0;
unsigned char *imageData;
if (pngLoad(iconPath, &width, &height, &imageData) == 1) {
SDL_Surface *surface = SDL_CreateRGBSurface(0, width, height, 32,
0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
memcpy(surface->pixels, imageData, width*height*4);
SDL_SetWindowIcon(window, surface);
SDL_FreeSurface(surface);
SDL_Surface *surface = SDL_CreateSurface(width, height, SDL_PIXELFORMAT_RGBA32);
if (surface) {
if (surface->pitch == width * 4) {
memcpy(surface->pixels, imageData, width * height * 4);
} else {
for (int y = 0; y < height; ++y) {
memcpy((uint8_t *)surface->pixels + y * surface->pitch, imageData + y * width * 4, width * 4);
}
}
SDL_SetWindowIcon(window, surface);
SDL_DestroySurface(surface);
}
free(imageData);
imageData = NULL;
}
@@ -1769,13 +1810,13 @@ int main(int argc, char *argv[]) {
g_Config.sFailedGPUBackends.clear();
#ifdef MOBILE_DEVICE
SDL_ShowCursor(SDL_DISABLE);
SDL_HideCursor();
#endif
// Avoid the IME popup when holding keys. This doesn't affect all versions of SDL.
// Note: We re-enable it in text input fields! This is necessary otherwise we don't receive
// KeyInputFlags::CHAR events.
SDL_StopTextInput();
SDL_StopTextInput(window);
InitSDLAudioDevice();
g_audioStartTime = time_now_d();
@@ -1827,10 +1868,10 @@ int main(int argc, char *argv[]) {
if (g_QuitRequested || g_RestartRequested)
break;
UpdateTextFocus();
UpdateTextFocus(window);
UpdateSDLCursor();
inputTracker.MouseCaptureControl();
inputTracker.MouseCaptureControl(window);
{
std::lock_guard<std::mutex> guard(g_mutexWindow);
@@ -1854,10 +1895,10 @@ int main(int argc, char *argv[]) {
if (g_QuitRequested || g_RestartRequested)
break;
UpdateTextFocus();
UpdateTextFocus(window);
UpdateSDLCursor();
inputTracker.MouseCaptureControl();
inputTracker.MouseCaptureControl(window);
bool renderThreadPaused = Native_IsWindowHidden() && g_Config.bPauseWhenMinimized && emuThreadState != (int)EmuThreadState::DISABLED;
if (emuThreadState != (int)EmuThreadState::DISABLED && !renderThreadPaused) {
@@ -1923,10 +1964,14 @@ int main(int argc, char *argv[]) {
graphicsContext->Shutdown();
delete graphicsContext;
if (audioDev > 0) {
SDL_PauseAudioDevice(audioDev, 1);
SDL_CloseAudioDevice(audioDev);
for (int i = 0; i < SDL_SYSTEM_CURSOR_COUNT; ++i) {
if (g_builtinCursors[i]) {
SDL_DestroyCursor(g_builtinCursors[i]);
g_builtinCursors[i] = nullptr;
}
}
StopSDLAudioDevice();
SDL_Quit();
#if PPSSPP_PLATFORM(RPI)
bcm_host_deinit();
-16
View File
@@ -1,16 +0,0 @@
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
Feel free to customize this file to suit your needs
*/
#ifndef _SDLMain_h_
#define _SDLMain_h_
#import <Cocoa/Cocoa.h>
@interface SDLMain : NSObject
@end
#endif /* _SDLMain_h_ */
-394
View File
@@ -1,394 +0,0 @@
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
Initial Version: Darrell Walisser <[email protected]>
Non-NIB-Code & other changes: Max Horn <[email protected]>
Feel free to customize this file to suit your needs
*/
#include "ppsspp_config.h"
#if PPSSPP_PLATFORM(MAC)
#include "SDL2/SDL.h"
#else
#include "SDL.h"
#endif
#include "SDLMain.h"
#include "Common/Profiler/Profiler.h"
#include <signal.h>
#include <sys/param.h> /* for MAXPATHLEN */
#include <unistd.h>
/* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
but the method still is there and works. To avoid warnings, we declare
it ourselves here. */
@interface NSApplication(SDL_Missing_Methods)
- (void)setAppleMenu:(NSMenu *)menu;
@end
/* Use this flag to determine whether we use SDLMain.nib or not */
#define SDL_USE_NIB_FILE 0
/* Use this flag to determine whether we use CPS (docking) or not */
#define SDL_USE_CPS 1
#ifdef SDL_USE_CPS
/* Portions of CPS.h */
typedef struct CPSProcessSerNum
{
UInt32 lo;
UInt32 hi;
} CPSProcessSerNum;
extern "C" OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
extern "C" OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
extern "C" OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
#endif /* SDL_USE_CPS */
static int gArgc;
static char **gArgv;
static BOOL gFinderLaunch;
static BOOL gCalledAppMainline = FALSE;
static NSString *getApplicationName(void)
{
const NSDictionary *dict;
NSString *appName = 0;
/* Determine the application name */
dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
if (dict)
appName = [dict objectForKey: @"CFBundleName"];
if (![appName length])
appName = [[NSProcessInfo processInfo] processName];
return appName;
}
#if SDL_USE_NIB_FILE
/* A helper category for NSString */
@interface NSString (ReplaceSubString)
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
@end
#endif
@interface NSApplication (SDLApplication)
@end
@implementation NSApplication (SDLApplication)
/* Invoked from the Quit menu item */
- (void)terminate:(id)sender
{
/* Post a SDL_QUIT event */
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
}
@end
/* The main class of the application, the application's delegate */
@implementation SDLMain
/* Set the working directory to the .app's parent directory */
- (void) setupWorkingDirectory:(BOOL)shouldChdir
{
if (shouldChdir)
{
char parentdir[MAXPATHLEN];
CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) {
chdir(parentdir); /* chdir to the binary app's parent */
}
CFRelease(url);
CFRelease(url2);
}
}
#if SDL_USE_NIB_FILE
/* Fix menu to contain the real app name instead of "SDL App" */
- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
{
NSRange aRange;
NSEnumerator *enumerator;
NSMenuItem *menuItem;
aRange = [[aMenu title] rangeOfString:@"SDL App"];
if (aRange.length != 0)
[aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
enumerator = [[aMenu itemArray] objectEnumerator];
while ((menuItem = [enumerator nextObject]))
{
aRange = [[menuItem title] rangeOfString:@"SDL App"];
if (aRange.length != 0)
[menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
if ([menuItem hasSubmenu])
[self fixMenu:[menuItem submenu] withAppName:appName];
}
}
#else
static void setApplicationMenu(void)
{
/* warning: this code is very odd */
NSMenu *appleMenu;
NSMenuItem *menuItem;
NSString *title;
NSString *appName;
appName = getApplicationName();
appleMenu = [[NSMenu alloc] initWithTitle:@""];
/* Add menu items */
title = [@"About " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
[appleMenu addItem:[NSMenuItem separatorItem]];
title = [@"Hide " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
[appleMenu addItem:[NSMenuItem separatorItem]];
title = [@"Quit " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
/* Put menu into the menubar */
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
[menuItem setSubmenu:appleMenu];
[[NSApp mainMenu] addItem:menuItem];
/* Tell the application object that this is now the application menu */
[NSApp setAppleMenu:appleMenu];
/* Finally give up our references to the objects */
[appleMenu release];
[menuItem release];
}
/* Create a window menu */
static void setupWindowMenu(void)
{
NSMenu *windowMenu;
NSMenuItem *windowMenuItem;
NSMenuItem *menuItem;
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
/* "Minimize" item */
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
[windowMenu addItem:menuItem];
[menuItem release];
/* Put menu into the menubar */
windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
[windowMenuItem setSubmenu:windowMenu];
[[NSApp mainMenu] addItem:windowMenuItem];
/* Tell the application object that this is now the window menu */
[NSApp setWindowsMenu:windowMenu];
/* Finally give up our references to the objects */
[windowMenu release];
[windowMenuItem release];
}
/* Replacement for NSApplicationMain */
static void CustomApplicationMain (int argc, char **argv)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
SDLMain *sdlMain;
/* Ensure the application object is initialised */
[NSApplication sharedApplication];
#ifdef SDL_USE_CPS
{
CPSProcessSerNum PSN;
/* Tell the dock about us */
if (!CPSGetCurrentProcess(&PSN))
if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
if (!CPSSetFrontProcess(&PSN))
[NSApplication sharedApplication];
}
#endif /* SDL_USE_CPS */
/* Set up the menubar */
[NSApp setMainMenu:[[NSMenu alloc] init]];
setApplicationMenu();
setupWindowMenu();
/* Create SDLMain and make it the app delegate */
sdlMain = [[SDLMain alloc] init];
[NSApp setDelegate:sdlMain];
/* Start the main event loop */
[NSApp run];
[sdlMain release];
[pool release];
}
#endif
/*
* Catch document open requests...this lets us notice files when the app
* was launched by double-clicking a document, or when a document was
* dragged/dropped on the app's icon. You need to have a
* CFBundleDocumentsType section in your Info.plist to get this message,
* apparently.
*
* Files are added to gArgv, so to the app, they'll look like command line
* arguments. Previously, apps launched from the finder had nothing but
* an argv[0].
*
* This message may be received multiple times to open several docs on launch.
*
* This message is ignored once the app's mainline has been called.
*/
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
const char *temparg;
size_t arglen;
char *arg;
char **newargv;
if (!gFinderLaunch) /* MacOS is passing command line args. */
return FALSE;
if (gCalledAppMainline) /* app has started, ignore this document. */
return FALSE;
temparg = [filename UTF8String];
arglen = SDL_strlen(temparg) + 1;
arg = (char *) SDL_malloc(arglen);
if (arg == NULL)
return FALSE;
newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
if (newargv == NULL)
{
SDL_free(arg);
return FALSE;
}
gArgv = newargv;
SDL_strlcpy(arg, temparg, arglen);
gArgv[gArgc++] = arg;
gArgv[gArgc] = NULL;
return TRUE;
}
/* Called when the internal event loop has just started running */
- (void) applicationDidFinishLaunching: (NSNotification *) note
{
int status;
/* Set the working directory to the .app's parent directory */
[self setupWorkingDirectory:gFinderLaunch];
#if SDL_USE_NIB_FILE
/* Set the main menu to contain the real app name instead of "SDL App" */
[self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
#endif
/* Hand off to main application code */
gCalledAppMainline = TRUE;
status = SDL_main (gArgc, gArgv);
/* We're done, thank you for playing */
exit(status);
}
@end
@implementation NSString (ReplaceSubString)
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
{
unsigned int bufferSize;
unsigned int selfLen = [self length];
unsigned int aStringLen = [aString length];
unichar *buffer;
NSRange localRange;
NSString *result;
bufferSize = selfLen + aStringLen - aRange.length;
buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar));
/* Get first part into buffer */
localRange.location = 0;
localRange.length = aRange.location;
[self getCharacters:buffer range:localRange];
/* Get middle part into buffer */
localRange.location = 0;
localRange.length = aStringLen;
[aString getCharacters:(buffer+aRange.location) range:localRange];
/* Get last part into buffer */
localRange.location = aRange.location + aRange.length;
localRange.length = selfLen - localRange.location;
[self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
/* Build output string */
result = [NSString stringWithCharacters:buffer length:bufferSize];
NSDeallocateMemoryPages(buffer, bufferSize);
return result;
}
@end
#ifdef main
# undef main
#endif
extern "C" {
/* Main entry point to executable - should *not* be SDL_main! */
int main (int argc, char **argv)
{
/* Copy the arguments into a global variable */
/* This is passed if we are launched by double-clicking */
if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
gArgv[0] = argv[0];
gArgv[1] = NULL;
gArgc = 1;
gFinderLaunch = YES;
} else {
int i;
gArgc = argc;
gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
for (i = 0; i <= argc; i++)
gArgv[i] = argv[i];
gFinderLaunch = NO;
}
PROFILE_INIT();
// Ignore sigpipe.
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
perror("Unable to ignore SIGPIPE");
}
#if SDL_USE_NIB_FILE
NSApplicationMain (argc, argv);
#else
CustomApplicationMain (argc, argv);
#endif
return 0;
}
} // extern "C"
+40 -41
View File
@@ -11,11 +11,7 @@
#include "GPU/Vulkan/VulkanUtil.h"
#include "Core/System.h"
#if PPSSPP_PLATFORM(MAC)
#include "SDL2/SDL_vulkan.h"
#else
#include "SDL_vulkan.h"
#endif
#include <SDL3/SDL_vulkan.h>
#include "SDLVulkanGraphicsContext.h"
#if defined(VK_USE_PLATFORM_METAL_EXT)
@@ -29,11 +25,14 @@ static const bool g_Validate = false;
#endif
bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h, int mode, std::string *error_message) {
window = SDL_CreateWindow("Initializing Vulkan...", x, y, w, h, mode);
window = SDL_CreateWindow("Initializing Vulkan...", w, h, (SDL_WindowFlags)mode);
if (!window) {
fprintf(stderr, "Error creating SDL window: %s\n", SDL_GetError());
exit(1);
}
if (x != SDL_WINDOWPOS_UNDEFINED && y != SDL_WINDOWPOS_UNDEFINED) {
SDL_SetWindowPosition(window, x, y);
}
init_glslang();
@@ -77,55 +76,55 @@ bool SDLVulkanGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, in
vulkan_->SetCbGetDrawSize([window]() {
int w=1,h=1;
SDL_Vulkan_GetDrawableSize(window, &w, &h);
SDL_GetWindowSizeInPixels(window, &w, &h);
return VkExtent2D {(uint32_t)w, (uint32_t)h};
});
SDL_SysWMinfo sys_info{};
SDL_VERSION(&sys_info.version); //Set SDL version
if (!SDL_GetWindowWMInfo(window, &sys_info)) {
fprintf(stderr, "Error getting SDL window wm info: %s\n", SDL_GetError());
exit(1);
}
switch (sys_info.subsystem) {
case SDL_SYSWM_X11:
SDL_PropertiesID windowProps = SDL_GetWindowProperties(window);
bool surfaceInitialized = false;
void *x11Display = SDL_GetPointerProperty(windowProps, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, nullptr);
if (x11Display != nullptr) {
intptr_t x11Window = (intptr_t)SDL_GetNumberProperty(windowProps, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
#if defined(VK_USE_PLATFORM_XLIB_KHR)
vulkan_->InitSurface(WINDOWSYSTEM_XLIB, (void*)sys_info.info.x11.display,
(void *)(intptr_t)sys_info.info.x11.window);
vulkan_->InitSurface(WINDOWSYSTEM_XLIB, x11Display, (void *)x11Window);
surfaceInitialized = true;
#elif defined(VK_USE_PLATFORM_XCB_KHR)
vulkan_->InitSurface(WINDOWSYSTEM_XCB, (void*)XGetXCBConnection(sys_info.info.x11.display),
(void *)(intptr_t)sys_info.info.x11.window);
vulkan_->InitSurface(WINDOWSYSTEM_XCB, (void *)XGetXCBConnection((Display *)x11Display), (void *)x11Window);
surfaceInitialized = true;
#endif
break;
}
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
case SDL_SYSWM_WAYLAND:
vulkan_->InitSurface(WINDOWSYSTEM_WAYLAND, (void*)sys_info.info.wl.display, (void *)sys_info.info.wl.surface);
break;
if (!surfaceInitialized) {
void *waylandDisplay = SDL_GetPointerProperty(windowProps, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, nullptr);
void *waylandSurface = SDL_GetPointerProperty(windowProps, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr);
if (waylandDisplay != nullptr && waylandSurface != nullptr) {
vulkan_->InitSurface(WINDOWSYSTEM_WAYLAND, waylandDisplay, waylandSurface);
surfaceInitialized = true;
}
}
#endif
#if defined(VK_USE_PLATFORM_METAL_EXT)
#if PPSSPP_PLATFORM(MAC)
case SDL_SYSWM_COCOA:
vulkan_->InitSurface(WINDOWSYSTEM_METAL_EXT, makeWindowMetalCompatible(sys_info.info.cocoa.window), nullptr);
break;
if (!surfaceInitialized) {
void *cocoaWindow = SDL_GetPointerProperty(windowProps, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
if (cocoaWindow != nullptr) {
vulkan_->InitSurface(WINDOWSYSTEM_METAL_EXT, makeWindowMetalCompatible(cocoaWindow), nullptr);
surfaceInitialized = true;
}
}
#else
case SDL_SYSWM_UIKIT:
vulkan_->InitSurface(WINDOWSYSTEM_METAL_EXT, makeWindowMetalCompatible(sys_info.info.uikit.window), nullptr);
break;
if (!surfaceInitialized) {
void *uikitWindow = SDL_GetPointerProperty(windowProps, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, nullptr);
if (uikitWindow != nullptr) {
vulkan_->InitSurface(WINDOWSYSTEM_METAL_EXT, makeWindowMetalCompatible(uikitWindow), nullptr);
surfaceInitialized = true;
}
}
#endif
#endif
#if defined(VK_USE_PLATFORM_DISPLAY_KHR)
case SDL_SYSWM_KMSDRM:
/*
There is no problem passing null for the next two arguments, and reinit will be called later
huangzihan china
*/
vulkan_->InitSurface(WINDOWSYSTEM_DISPLAY, nullptr, nullptr);
break;
#endif
default:
fprintf(stderr, "Vulkan subsystem %d not supported\n", sys_info.subsystem);
if (!surfaceInitialized) {
fprintf(stderr, "Unable to determine Vulkan window system from SDL3 window properties\n");
exit(1);
break;
}
bool useMultiThreading = g_Config.bRenderMultiThreading;
+1 -7
View File
@@ -1,11 +1,5 @@
#include "ppsspp_config.h"
#if PPSSPP_PLATFORM(MAC)
#include "SDL2/SDL.h"
#include "SDL2/SDL_syswm.h"
#else
#include "SDL.h"
#include "SDL_syswm.h"
#endif
#include <SDL3/SDL.h>
#include "Common/GraphicsContext.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
+2 -2
View File
@@ -192,7 +192,7 @@ void DrawTexturesWindow(ImConfig &cfg, TextureCacheCommon *textureCache) {
ImGui::Text("Addr: %08x: %dx%d, %d mips, %s", (uint32_t)(cfg.selectedTexId & 0xFFFFFFFF), w, h, entry->maxLevel + 1, GeTextureFormatToString((GETextureFormat)entry->format));
}
ImGui::Text("Stride: %d", entry->bufw);
ImGui::Text("Status: %08x: %s", entry->status, TexStatusToString(entry->status).c_str());
ImGui::Text("Status: %08x: %s", (u32)entry->status, TexStatusToString(entry->status).c_str());
ImGui::Text("Hash: %08x", entry->fullhash);
ImGui::Text("CLUT Hash: %08x", entry->cluthash);
ImGui::Text("MaxSeenV: %08x", entry->maxSeenV);
@@ -211,7 +211,7 @@ void DrawTexturesWindow(ImConfig &cfg, TextureCacheCommon *textureCache) {
ImGui::Text("Key: %08x_%08x", (u32)(desc.cachekey >> 32), (u32)desc.cachekey);
ImGui::Text("Hashfiles: %s", desc.hashfiles.c_str());
ImGui::Text("Base: %s", desc.basePath.c_str());
ImGui::Text("Alpha status: %02x", entry->replacedTexture->AlphaStatus());
ImGui::Text("Alpha status: %d", (int)entry->replacedTexture->AlphaStatus());
}
} else {
ImGui::Text("Not replaced");
+2
View File
@@ -324,6 +324,8 @@ void RemoteISOScreen::sendMessage(UIMessage message, const char *value) {
ERROR_LOG(Log::UI, "Unknown permission granted: %s", value);
}
break;
default:
break;
}
}
-16
View File
@@ -1030,14 +1030,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="..\SDL\SDLMain.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="..\SDL\SDLVulkanGraphicsContext.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -1373,14 +1365,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</None>
<None Include="..\SDL\SDLMain.mm">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</None>
<None Include="..\settings.gradle" />
<None Include="..\Core\Util\DarwinFileSystemServices.mm" />
<None Include="..\UI\PSPNSApplicationDelegate.mm">
+1 -7
View File
@@ -496,9 +496,6 @@
<ClInclude Include="..\SDL\SDLJoystick.h">
<Filter>Other Platforms\SDL</Filter>
</ClInclude>
<ClInclude Include="..\SDL\SDLMain.h">
<Filter>Other Platforms\SDL</Filter>
</ClInclude>
<ClInclude Include="..\SDL\SDLVulkanGraphicsContext.h">
<Filter>Other Platforms\SDL</Filter>
</ClInclude>
@@ -719,9 +716,6 @@
<None Include="..\SDL\SDLCocoaMetalLayer.mm">
<Filter>Other Platforms\SDL</Filter>
</None>
<None Include="..\SDL\SDLMain.mm">
<Filter>Other Platforms\SDL</Filter>
</None>
<None Include="..\assets\compatvr.ini">
<Filter>Resource Files</Filter>
</None>
@@ -886,4 +880,4 @@
<Filter>assets</Filter>
</Text>
</ItemGroup>
</Project>
</Project>
+1
View File
@@ -2,3 +2,4 @@ Win32
x64
ARM64
ARM
SDL3
+1
View File
@@ -40,6 +40,7 @@
#define HAVE_FTELLO
#endif
#define HAVE_SNPRINTF
#define HAVE_STDBOOL_H 1
#define HAVE_STRDUP
#if !defined(__MINGW32__) && defined(_WIN32)
#define HAVE_STRICMP
+3 -7
View File
@@ -20,11 +20,7 @@
#include <cstdio>
#include "ppsspp_config.h"
#if PPSSPP_PLATFORM(MAC)
#include "SDL2/SDL.h"
#else
#include "SDL.h"
#endif
#include <SDL3/SDL.h>
#include "headless/SDLHeadlessHost.h"
#include "Common/GPU/OpenGL/GLCommon.h"
@@ -49,7 +45,7 @@ SDL_Window *CreateHiddenWindow() {
if (!WINDOW_VISIBLE) {
flags |= SDL_WINDOW_HIDDEN;
}
return SDL_CreateWindow("PPSSPPHeadless", 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, flags);
return SDL_CreateWindow("PPSSPPHeadless", WINDOW_WIDTH, WINDOW_HEIGHT, flags);
}
class GLDummyGraphicsContext : public GraphicsContext {
@@ -64,7 +60,7 @@ public:
delete draw_;
draw_ = nullptr;
SDL_GL_DeleteContext(glContext_);
SDL_GL_DestroyContext(glContext_);
glContext_ = nullptr;
SDL_DestroyWindow(screen_);
screen_ = nullptr;
+1 -2
View File
@@ -24,8 +24,7 @@
#undef HEADLESSHOST_CLASS
#define HEADLESSHOST_CLASS SDLHeadlessHost
struct SDL_Window;
typedef void *SDL_GLContext;
#include <SDL3/SDL.h>
class SDLHeadlessHost : public HeadlessHost
{