mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Merge pull request #21838 from hrydgard/sdl3-port
Port from SDL2 to SDL3
This commit is contained in:
27 files changed
+758
-1151
No files matched your search
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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,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
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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
@@ -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"
|
||||
@@ -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,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"
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -324,6 +324,8 @@ void RemoteISOScreen::sendMessage(UIMessage message, const char *value) {
|
||||
ERROR_LOG(Log::UI, "Unknown permission granted: %s", value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
@@ -2,3 +2,4 @@ Win32
|
||||
x64
|
||||
ARM64
|
||||
ARM
|
||||
SDL3
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user