mirror of
https://github.com/kichikuou/xsystem35-sdl2.git
synced 2026-10-03 12:18:13 +03:00
Compare commits
27
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48458ac034 | ||
|
|
ffdd362f35 | ||
|
|
78519bb5ef | ||
|
|
5989fe72e2 | ||
|
|
66d0636197 | ||
|
|
6c8590f8d4 | ||
|
|
3cbe4c73d8 | ||
|
|
80da6cd782 | ||
|
|
2f73abd486 | ||
|
|
5844226b2f | ||
|
|
87596c58c5 | ||
|
|
212a7bf138 | ||
|
|
652724a9ab | ||
|
|
658debdcf3 | ||
|
|
ed4fbc1f19 | ||
|
|
c1910382f8 | ||
|
|
495d6146f3 | ||
|
|
6f01734215 | ||
|
|
b55574c162 | ||
|
|
9f92186905 | ||
|
|
d80d82912a | ||
|
|
eeb4953213 | ||
|
|
5abc9e8177 | ||
|
|
5d7ec418b1 | ||
|
|
e488123345 | ||
|
|
5eea90c9a8 | ||
|
|
677ecb5a0f |
@@ -24,5 +24,6 @@ jobs:
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: xsystem35-android-sdl3
|
||||
path: android/app/build/outputs/apk/release/app-release.apk
|
||||
archive: false
|
||||
|
||||
@@ -2,12 +2,17 @@ name: Emscripten Build
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
EM_VERSION: latest
|
||||
EM_VERSION: 6.0.10
|
||||
EM_CACHE_FOLDER: 'emsdk-cache'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
sdl-version: [2, 3]
|
||||
name: Emscripten SDL${{ matrix.sdl-version }}
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
@@ -17,7 +22,7 @@ jobs:
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{env.EM_CACHE_FOLDER}}
|
||||
key: ${{env.EM_VERSION}}-${{ runner.os }}
|
||||
key: ${{env.EM_VERSION}}-${{ runner.os }}-sdl${{ matrix.sdl-version }}
|
||||
|
||||
- name: Setup Emscripten toolchain
|
||||
uses: emscripten-core/setup-emsdk@v16
|
||||
@@ -30,15 +35,16 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir -p out/wasm
|
||||
cd out/wasm
|
||||
emcmake cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_COMPILE_WARNING_AS_ERROR=YES ../../
|
||||
make -j4
|
||||
mkdir xsystem35
|
||||
mv src/xsystem35.* xsystem35/
|
||||
emcmake cmake -S . -B out/wasm \
|
||||
-DCMAKE_BUILD_TYPE=MinSizeRel \
|
||||
-DCMAKE_COMPILE_WARNING_AS_ERROR=YES \
|
||||
-DXSYSTEM35_SDL_VERSION=${{ matrix.sdl-version }}
|
||||
cmake --build out/wasm -j4
|
||||
mkdir out/wasm/xsystem35
|
||||
mv out/wasm/src/xsystem35.* out/wasm/xsystem35/
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: xsystem35-wasm
|
||||
name: xsystem35-wasm-sdl${{ matrix.sdl-version }}
|
||||
path: out/wasm/xsystem35
|
||||
|
||||
+15
-10
@@ -3,33 +3,38 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-26.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build-type: ["Debug", "Release"]
|
||||
sdl-version: [2, 3]
|
||||
|
||||
name: Linux ${{ matrix.build-type }}
|
||||
name: Linux SDL${{ matrix.sdl-version }} ${{ matrix.build-type }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install Deps
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libsdl2-dev libfreetype-dev libsdl2-mixer-dev libwebp-dev libportmidi-dev libcjson-dev asciidoctor gettext
|
||||
sudo apt install libfreetype-dev libwebp-dev libportmidi-dev libcjson-dev asciidoctor gettext
|
||||
if [ "${{ matrix.sdl-version }}" = 2 ]; then
|
||||
sudo apt install libsdl2-dev libsdl2-mixer-dev
|
||||
else
|
||||
sudo apt install libsdl3-dev
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir -p out/${{ matrix.build-type }}
|
||||
cd out/${{ matrix.build-type }}
|
||||
cmake -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} ../../
|
||||
make -j4
|
||||
cmake -S . -B out/build -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \
|
||||
-DXSYSTEM35_SDL_VERSION=${{ matrix.sdl-version }}
|
||||
cmake --build out/build -j4
|
||||
|
||||
- name: Test
|
||||
run: ctest --output-on-failure
|
||||
working-directory: out/${{ matrix.build-type }}
|
||||
run: ctest --test-dir out/build --output-on-failure
|
||||
|
||||
- name: Verify xsystem35.pot is up to date
|
||||
if: matrix.build-type == 'Debug'
|
||||
if: matrix.build-type == 'Debug' && matrix.sdl-version == 2
|
||||
run: |
|
||||
xgettext --default-domain=xsystem35 --directory=. \
|
||||
--keyword=_ --keyword=N_ \
|
||||
|
||||
@@ -9,11 +9,23 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- sys: mingw32
|
||||
package: "xsystem35-32bit"
|
||||
package: "xsystem35-sdl2-32bit"
|
||||
sdl-version: 2
|
||||
sdl-deps: "SDL2:p SDL2_mixer:p"
|
||||
cmake_args: "-DENABLE_DEBUGGER=no"
|
||||
deps: ""
|
||||
- sys: ucrt64
|
||||
package: "xsystem35-64bit"
|
||||
package: "xsystem35-sdl2-64bit"
|
||||
sdl-version: 2
|
||||
sdl-deps: "SDL2:p SDL2_mixer:p"
|
||||
cmake_args: ""
|
||||
deps: >-
|
||||
cjson:p
|
||||
portmidi:p
|
||||
- sys: ucrt64
|
||||
package: "xsystem35-sdl3-64bit"
|
||||
sdl-version: 3
|
||||
sdl-deps: "sdl3:p sdl3-mixer:p"
|
||||
cmake_args: ""
|
||||
deps: >-
|
||||
cjson:p
|
||||
@@ -21,7 +33,7 @@ jobs:
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
name: MSYS2 ${{ matrix.sys }}
|
||||
name: MSYS2 SDL${{ matrix.sdl-version }} ${{ matrix.sys }}
|
||||
steps:
|
||||
|
||||
- name: Set up MSYS2
|
||||
@@ -29,9 +41,8 @@ jobs:
|
||||
with:
|
||||
msystem: ${{ matrix.sys }}
|
||||
pacboy: >-
|
||||
SDL2:p
|
||||
${{ matrix.sdl-deps }}
|
||||
freetype:p
|
||||
SDL2_mixer:p
|
||||
libwebp:p
|
||||
${{ matrix.deps }}
|
||||
|
||||
@@ -50,10 +61,11 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir -p out/release
|
||||
cd out/release
|
||||
cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_COMPILE_WARNING_AS_ERROR=YES ${{ matrix.cmake_args }} ../../
|
||||
ninja
|
||||
cmake -S . -B out/release -GNinja -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_COMPILE_WARNING_AS_ERROR=YES \
|
||||
-DXSYSTEM35_SDL_VERSION=${{ matrix.sdl-version }} \
|
||||
${{ matrix.cmake_args }}
|
||||
cmake --build out/release
|
||||
|
||||
- name: Test
|
||||
run: ctest --output-on-failure
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
out
|
||||
android/.gradle
|
||||
android/.idea
|
||||
android/app/aars
|
||||
android/app/.cxx
|
||||
android/app/.externalNativeBuild
|
||||
android/app/build
|
||||
|
||||
+63
-9
@@ -7,6 +7,23 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||
|
||||
set(XSYSTEM35_VERSION "2.20.0")
|
||||
|
||||
if (ANDROID)
|
||||
set(XSYSTEM35_DEFAULT_SDL_VERSION 3)
|
||||
else()
|
||||
set(XSYSTEM35_DEFAULT_SDL_VERSION 2)
|
||||
endif()
|
||||
set(XSYSTEM35_SDL_VERSION "${XSYSTEM35_DEFAULT_SDL_VERSION}" CACHE STRING
|
||||
"SDL major version (2 or 3)")
|
||||
set_property(CACHE XSYSTEM35_SDL_VERSION PROPERTY STRINGS 2 3)
|
||||
if (NOT XSYSTEM35_SDL_VERSION STREQUAL "2" AND
|
||||
NOT XSYSTEM35_SDL_VERSION STREQUAL "3")
|
||||
message(FATAL_ERROR "XSYSTEM35_SDL_VERSION must be 2 or 3")
|
||||
endif()
|
||||
add_library(xsystem35_sdl INTERFACE)
|
||||
target_compile_definitions(xsystem35_sdl INTERFACE
|
||||
XSYSTEM35_SDL_VERSION=${XSYSTEM35_SDL_VERSION})
|
||||
add_library(xsystem35_sdl_mixer INTERFACE)
|
||||
|
||||
include(CheckSymbolExists)
|
||||
include(FetchContent)
|
||||
|
||||
@@ -64,16 +81,23 @@ if (EMSCRIPTEN)
|
||||
INTERFACE_LINK_OPTIONS ${option})
|
||||
endfunction()
|
||||
add_emscripten_library(zlib -sUSE_ZLIB=1)
|
||||
add_emscripten_library(sdl2 -sUSE_SDL=2)
|
||||
target_compile_options(xsystem35_sdl INTERFACE
|
||||
-sUSE_SDL=${XSYSTEM35_SDL_VERSION})
|
||||
target_link_options(xsystem35_sdl INTERFACE
|
||||
-sUSE_SDL=${XSYSTEM35_SDL_VERSION})
|
||||
add_emscripten_library(freetype2 -sUSE_FREETYPE=1)
|
||||
set(DEFAULT_FONT_PATH /fonts/)
|
||||
fetch_webp()
|
||||
set(HAVE_WEBP 1)
|
||||
|
||||
elseif (ANDROID)
|
||||
add_library(sdl2 ALIAS SDL2)
|
||||
if (NOT XSYSTEM35_SDL_VERSION STREQUAL "3")
|
||||
message(FATAL_ERROR "Android supports SDL3 only")
|
||||
endif()
|
||||
target_link_libraries(xsystem35_sdl INTERFACE SDL3::SDL3)
|
||||
target_link_libraries(xsystem35_sdl_mixer INTERFACE
|
||||
SDL3_mixer::SDL3_mixer)
|
||||
add_library(freetype2 ALIAS freetype)
|
||||
add_library(sdl2_mixer ALIAS SDL2_mixer)
|
||||
find_library(ndk_log log)
|
||||
find_library(ndk_zlib z)
|
||||
add_library(zlib INTERFACE)
|
||||
@@ -89,19 +113,48 @@ else() # non-emscripten, non-android
|
||||
add_library(zlib ALIAS ZLIB::ZLIB)
|
||||
|
||||
include(FindPkgConfig)
|
||||
pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2>=2.18.0)
|
||||
pkg_check_modules(FREETYPE REQUIRED IMPORTED_TARGET freetype2)
|
||||
pkg_check_modules(SDL2MIXER REQUIRED IMPORTED_TARGET SDL2_mixer)
|
||||
if (XSYSTEM35_SDL_VERSION STREQUAL "2")
|
||||
pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2>=2.18.0)
|
||||
pkg_check_modules(SDL2MIXER REQUIRED IMPORTED_TARGET SDL2_mixer)
|
||||
if (WIN32)
|
||||
add_static_library(sdl2 SDL2)
|
||||
add_static_library(sdl2_mixer SDL2MIXER)
|
||||
target_link_libraries(xsystem35_sdl INTERFACE sdl2)
|
||||
target_link_libraries(xsystem35_sdl_mixer INTERFACE sdl2_mixer)
|
||||
else()
|
||||
target_link_libraries(xsystem35_sdl INTERFACE PkgConfig::SDL2)
|
||||
target_link_libraries(xsystem35_sdl_mixer INTERFACE
|
||||
PkgConfig::SDL2MIXER)
|
||||
endif()
|
||||
else()
|
||||
find_package(SDL3 3.4 REQUIRED CONFIG)
|
||||
find_package(SDL3_mixer 3.2 QUIET CONFIG)
|
||||
if (NOT SDL3_mixer_FOUND)
|
||||
message(STATUS "SDL3_mixer not found; building SDL3_mixer 3.2.4 from source")
|
||||
FetchContent_Declare(
|
||||
SDL3_mixer
|
||||
URL https://github.com/libsdl-org/SDL_mixer/archive/refs/tags/release-3.2.4.tar.gz
|
||||
URL_HASH SHA256=f2ea848ccdf2f394cd4973ee0f6c482e04511044695cccfd46bab6dcd7f780aa
|
||||
)
|
||||
FetchContent_MakeAvailable(SDL3_mixer)
|
||||
endif()
|
||||
if (WIN32)
|
||||
target_link_libraries(xsystem35_sdl INTERFACE SDL3::SDL3-static)
|
||||
target_link_libraries(xsystem35_sdl_mixer INTERFACE
|
||||
SDL3_mixer::SDL3_mixer-static)
|
||||
else()
|
||||
target_link_libraries(xsystem35_sdl INTERFACE SDL3::SDL3)
|
||||
target_link_libraries(xsystem35_sdl_mixer INTERFACE
|
||||
SDL3_mixer::SDL3_mixer)
|
||||
endif()
|
||||
endif()
|
||||
if (WIN32)
|
||||
add_static_library(sdl2 SDL2)
|
||||
add_static_library(freetype2 FREETYPE)
|
||||
# harfbuzz and graphite2, which the static freetype depends on, are C++ libraries.
|
||||
target_link_libraries(freetype2 INTERFACE -lstdc++)
|
||||
add_static_library(sdl2_mixer SDL2MIXER)
|
||||
else()
|
||||
add_library(sdl2 ALIAS PkgConfig::SDL2)
|
||||
add_library(freetype2 ALIAS PkgConfig::FREETYPE)
|
||||
add_library(sdl2_mixer ALIAS PkgConfig::SDL2MIXER)
|
||||
set(DEFAULT_FONT_PATH ${CMAKE_INSTALL_PREFIX}/share/xsystem35/fonts/)
|
||||
endif()
|
||||
|
||||
@@ -212,6 +265,7 @@ endfunction()
|
||||
|
||||
message("xsystem35 summary:")
|
||||
message("------------------")
|
||||
print_summary(SDL ${XSYSTEM35_SDL_VERSION})
|
||||
print_summary(audio ${SUMMARY_AUDIO})
|
||||
print_summary(cdrom ${SUMMARY_CDROM})
|
||||
print_summary(midi ${SUMMARY_MIDI})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# xsytem35-sdl2
|
||||
# xsystem35 for SDL2/SDL3
|
||||
|
||||
This is a multi-platform port of `xsystem35`, a free implementation of
|
||||
AliceSoft's System 3.x game engine.
|
||||
@@ -102,53 +102,97 @@ $ xsystem35
|
||||
See [xsystem35 command manual](doc/xsystem35.6.adoc) for detailed usage.
|
||||
|
||||
## Building
|
||||
|
||||
SDL2 is used by default on desktop platforms and Emscripten. Pass
|
||||
`-DXSYSTEM35_SDL_VERSION=3` to CMake to select SDL3. Use a separate build
|
||||
directory for each SDL version.
|
||||
|
||||
The supported configurations are:
|
||||
|
||||
| Platform | SDL2 | SDL3 |
|
||||
| --- | --- | --- |
|
||||
| Linux and macOS | Yes | Yes |
|
||||
| Windows 64-bit | Yes | Yes |
|
||||
| Windows 32-bit | Yes | No |
|
||||
| Emscripten | Yes | Yes |
|
||||
| Android | No | Yes |
|
||||
|
||||
SDL2 builds require SDL 2.18 or later and SDL2_mixer. SDL3 builds require
|
||||
SDL 3.4 or later and SDL3_mixer 3.2 or later. The remaining common
|
||||
dependencies are FreeType, zlib, and optionally libwebp, PortMidi, and cJSON.
|
||||
|
||||
### Linux (Debian / Ubuntu)
|
||||
|
||||
```bash
|
||||
$ sudo apt install build-essential cmake libsdl2-dev libsdl2-mixer-dev libfreetype-dev libwebp-dev libportmidi-dev libcjson-dev asciidoctor
|
||||
$ mkdir -p out/debug
|
||||
$ cd out/debug
|
||||
$ cmake -DCMAKE_BUILD_TYPE=Debug ../../
|
||||
$ make && make install
|
||||
$ sudo apt install build-essential cmake libsdl2-dev libsdl2-mixer-dev \
|
||||
libfreetype-dev libwebp-dev libportmidi-dev libcjson-dev asciidoctor
|
||||
$ cmake -S . -B out/sdl2 -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DXSYSTEM35_SDL_VERSION=2
|
||||
$ cmake --build out/sdl2
|
||||
$ sudo cmake --install out/sdl2
|
||||
```
|
||||
|
||||
For SDL3 on a distribution that provides SDL 3.4 or later (for example,
|
||||
Ubuntu 26.04), install `libsdl3-dev` instead of the SDL2 packages and
|
||||
configure `out/sdl3` with `-DXSYSTEM35_SDL_VERSION=3`. If SDL3_mixer 3.2 or
|
||||
later is installed, CMake uses it; otherwise, CMake downloads and builds
|
||||
SDL3_mixer 3.2.4 during configuration.
|
||||
|
||||
### MacOS
|
||||
|
||||
[Homebrew](https://brew.sh/) is required.
|
||||
|
||||
```bash
|
||||
$ brew install cmake pkg-config sdl2 sdl2_mixer freetype webp portmidi cjson asciidoctor
|
||||
$ mkdir -p out/debug
|
||||
$ cd out/debug
|
||||
$ cmake -DCMAKE_BUILD_TYPE=Debug ../../
|
||||
$ make && make install
|
||||
$ brew install cmake pkg-config sdl2 sdl2_mixer freetype webp portmidi \
|
||||
cjson asciidoctor
|
||||
$ cmake -S . -B out/sdl2 -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DXSYSTEM35_SDL_VERSION=2
|
||||
$ cmake --build out/sdl2
|
||||
$ sudo cmake --install out/sdl2
|
||||
```
|
||||
|
||||
For SDL3, install `sdl3 sdl3_mixer` instead of `sdl2 sdl2_mixer` and
|
||||
configure `out/sdl3` with `-DXSYSTEM35_SDL_VERSION=3`.
|
||||
|
||||
### Windows
|
||||
|
||||
[MSYS2](https://www.msys2.org) is required.
|
||||
|
||||
```bash
|
||||
$ pacman -S cmake mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-SDL2 mingw-w64-ucrt-x86_64-freetype mingw-w64-ucrt-x86_64-SDL2_mixer mingw-w64-ucrt-x86_64-libwebp mingw-w64-ucrt-x86_64-portmidi mingw-w64-ucrt-x86_64-cjson
|
||||
$ mkdir -p out/debug
|
||||
$ cd out/debug
|
||||
$ cmake -G"MSYS Makefiles" -DCMAKE_BUILD_TYPE=Debug ../../
|
||||
$ make
|
||||
$ pacman -S mingw-w64-ucrt-x86_64-gcc \
|
||||
mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-SDL2 \
|
||||
mingw-w64-ucrt-x86_64-SDL2_mixer mingw-w64-ucrt-x86_64-freetype \
|
||||
mingw-w64-ucrt-x86_64-libwebp mingw-w64-ucrt-x86_64-portmidi \
|
||||
mingw-w64-ucrt-x86_64-cjson
|
||||
$ cmake -S . -B out/sdl2 -G"MSYS Makefiles" \
|
||||
-DCMAKE_BUILD_TYPE=Debug -DXSYSTEM35_SDL_VERSION=2
|
||||
$ cmake --build out/sdl2
|
||||
```
|
||||
|
||||
For a 64-bit SDL3 build, install `mingw-w64-ucrt-x86_64-sdl3` and
|
||||
`mingw-w64-ucrt-x86_64-sdl3-mixer` instead of the SDL2 packages, then
|
||||
configure `out/sdl3` with `-DXSYSTEM35_SDL_VERSION=3`. The MSYS2 mingw32
|
||||
repository does not provide SDL3, so 32-bit Windows builds remain on SDL2.
|
||||
|
||||
### Emscripten
|
||||
|
||||
```bash
|
||||
$ mkdir -p out/wasm
|
||||
$ cd out/wasm
|
||||
$ emcmake cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../../
|
||||
$ make
|
||||
$ emcmake cmake -S . -B out/wasm-sdl2 -DCMAKE_BUILD_TYPE=MinSizeRel \
|
||||
-DXSYSTEM35_SDL_VERSION=2
|
||||
$ cmake --build out/wasm-sdl2
|
||||
```
|
||||
|
||||
For SDL3, use a different build directory and
|
||||
`-DXSYSTEM35_SDL_VERSION=3`. Both versions use Emscripten's bundled SDL port;
|
||||
SDL_mixer is not required.
|
||||
|
||||
To use the generated binary, check out
|
||||
[Kichikuou on Web](https://github.com/kichikuou/web) and copy `out/xsystem35.*`
|
||||
into its `docs` directory.
|
||||
[Kichikuou on Web](https://github.com/kichikuou/web) and copy
|
||||
`out/wasm-sdl2/src/xsystem35.*` into its `docs` directory.
|
||||
|
||||
### Android
|
||||
|
||||
See [android/README.md](android/).
|
||||
|
||||
Android uses SDL3 exclusively. The Gradle build downloads fixed official SDL3
|
||||
3.4.16 and SDL3_mixer 3.2.4 AARs and exposes them to CMake through Prefab.
|
||||
|
||||
@@ -73,6 +73,11 @@ Note: This ZIP format is also compatible with
|
||||
|
||||
## Building from Source
|
||||
|
||||
Android builds use SDL3 exclusively. Gradle downloads the fixed official SDL3
|
||||
3.4.16 and SDL3_mixer 3.2.4 AARs, verifies their checksums, and supplies their
|
||||
native libraries to CMake through Prefab. No separate SDL installation is
|
||||
required.
|
||||
|
||||
### Using Android Studio
|
||||
Open this directory as an Android Studio project.
|
||||
|
||||
|
||||
@@ -8,10 +8,80 @@ else {
|
||||
}
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
def aarsDir = file('aars')
|
||||
def assetsLicensesDir = file('src/main/assets/licenses')
|
||||
|
||||
def sha256 = { File file ->
|
||||
def digest = java.security.MessageDigest.getInstance('SHA-256')
|
||||
file.withInputStream { input ->
|
||||
byte[] buffer = new byte[8192]
|
||||
for (int n = input.read(buffer); n >= 0; n = input.read(buffer)) {
|
||||
digest.update(buffer, 0, n)
|
||||
}
|
||||
}
|
||||
digest.digest().encodeHex().toString()
|
||||
}
|
||||
|
||||
def installAar = { String url, String archiveSha256, String aarName,
|
||||
String aarSha256, String licenseName ->
|
||||
def aarFile = new File(aarsDir, aarName)
|
||||
def licenseFile = new File(assetsLicensesDir, licenseName)
|
||||
if (!aarFile.exists() || sha256(aarFile) != aarSha256 || !licenseFile.exists()) {
|
||||
logger.lifecycle("Downloading ${aarName} ...")
|
||||
def archive = File.createTempFile('sdl-android', '.zip')
|
||||
try {
|
||||
new URL(url).withInputStream { input ->
|
||||
archive.withOutputStream { it << input }
|
||||
}
|
||||
if (sha256(archive) != archiveSha256) {
|
||||
throw new GradleException("Checksum mismatch for ${url}")
|
||||
}
|
||||
aarsDir.mkdirs()
|
||||
assetsLicensesDir.mkdirs()
|
||||
copy {
|
||||
from zipTree(archive)
|
||||
include aarName
|
||||
into aarsDir
|
||||
}
|
||||
copy {
|
||||
from zipTree(archive)
|
||||
include 'LICENSE.txt'
|
||||
into assetsLicensesDir
|
||||
rename 'LICENSE.txt', licenseName
|
||||
}
|
||||
} finally {
|
||||
archive.delete()
|
||||
}
|
||||
}
|
||||
if (!aarFile.exists() || sha256(aarFile) != aarSha256) {
|
||||
throw new GradleException("Checksum mismatch for ${aarFile}")
|
||||
}
|
||||
aarFile
|
||||
}
|
||||
|
||||
def sdlVersion = '3.4.16'
|
||||
def sdlAarFile = installAar(
|
||||
"https://github.com/libsdl-org/SDL/releases/download/release-${sdlVersion}/SDL3-devel-${sdlVersion}-android.zip",
|
||||
'e1da8d9298471ea4ab8c6d48a804c0ac5415f35f30fcf7eba61aa7357e165765',
|
||||
"SDL3-${sdlVersion}.aar",
|
||||
'03710fc7b49cc070551446841a843840fabf2aaaaa3043cd5739292a55e4e61c',
|
||||
'SDL')
|
||||
|
||||
def sdlMixerVersion = '3.2.4'
|
||||
def sdlMixerAarFile = installAar(
|
||||
"https://github.com/libsdl-org/SDL_mixer/releases/download/release-${sdlMixerVersion}/SDL3_mixer-devel-${sdlMixerVersion}-android.zip",
|
||||
'b7f9f1ee5162a0492e71941f2cbdc50ec256ef15c0e995ca72c7e519eebd810d',
|
||||
"SDL3_mixer-${sdlMixerVersion}.aar",
|
||||
'd13dc45d4a4d6a2db4132400e77629c466ed533641fc9e211dfe85fab025b792',
|
||||
'SDL_mixer')
|
||||
|
||||
android {
|
||||
if (buildAsApplication) {
|
||||
namespace "io.github.kichikuou.xsystem35"
|
||||
}
|
||||
buildFeatures {
|
||||
prefab true
|
||||
}
|
||||
compileSdkVersion 34
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
@@ -20,7 +90,7 @@ android {
|
||||
versionName "2.20.0" + gitRevision()
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_APP_PLATFORM=android-19", "-DANDROID_STL=c++_static"
|
||||
arguments "-DANDROID_STL=c++_static", "-DXSYSTEM35_SDL_VERSION=3"
|
||||
// abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||
abiFilters 'armeabi-v7a', 'arm64-v8a'
|
||||
}
|
||||
@@ -83,6 +153,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation files(sdlAarFile, sdlMixerAarFile)
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
include $(call all-subdir-makefiles)
|
||||
@@ -1,10 +0,0 @@
|
||||
|
||||
# Uncomment this if you're using STL in your project
|
||||
# You can find more information here:
|
||||
# https://developer.android.com/ndk/guides/cpp-support
|
||||
# APP_STL := c++_shared
|
||||
|
||||
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
|
||||
|
||||
# Min runtime API level
|
||||
APP_PLATFORM=android-16
|
||||
@@ -3,25 +3,17 @@ cmake_minimum_required(VERSION 3.14)
|
||||
project(GAME)
|
||||
set(PROJECT_ROOT_DIR ../../..)
|
||||
|
||||
# Compilation of SDL and companion libraries
|
||||
# SDL3 and SDL3_mixer are provided by their official Android AARs through Prefab.
|
||||
find_package(SDL3 3.4 REQUIRED CONFIG)
|
||||
find_package(SDL3_mixer 3.2.4 REQUIRED CONFIG)
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
SDL
|
||||
URL https://github.com/libsdl-org/SDL/releases/download/release-2.32.10/SDL2-2.32.10.tar.gz
|
||||
URL_HASH SHA1=ce98fa93e31836a751feca374ab28a0770b63c16
|
||||
)
|
||||
FetchContent_Declare(
|
||||
freetype
|
||||
URL https://downloads.sourceforge.net/project/freetype/freetype2/2.14.3/freetype-2.14.3.tar.gz
|
||||
https://download.savannah.gnu.org/releases/freetype/freetype-2.14.3.tar.gz
|
||||
URL_HASH SHA1=a911f65c6355ddcf7aaba46e250dbc00f67e1678
|
||||
)
|
||||
FetchContent_Declare(
|
||||
SDL_mixer
|
||||
URL https://github.com/libsdl-org/SDL_mixer/releases/download/release-2.8.0/SDL2_mixer-2.8.0.tar.gz
|
||||
URL_HASH SHA1=a58c69f9d00e44833b9e00e1adb58d85759ca499
|
||||
)
|
||||
|
||||
# Only FreeType's own rasterizer is needed, none of its optional dependencies.
|
||||
set(FT_DISABLE_ZLIB ON CACHE BOOL "Disable use of system zlib" FORCE)
|
||||
set(FT_DISABLE_BZIP2 ON CACHE BOOL "Disable use of system libbz2" FORCE)
|
||||
@@ -29,15 +21,7 @@ set(FT_DISABLE_PNG ON CACHE BOOL "Disable use of system libpng" FORCE)
|
||||
set(FT_DISABLE_HARFBUZZ ON CACHE BOOL "Disable use of harfbuzz" FORCE)
|
||||
set(FT_DISABLE_BROTLI ON CACHE BOOL "Disable use of brotli" FORCE)
|
||||
|
||||
set(SDL2MIXER_OPUS OFF CACHE BOOL "Enable Opus music" FORCE)
|
||||
set(SDL2MIXER_FLAC OFF CACHE BOOL "Enable FLAC music" FORCE)
|
||||
set(SDL2MIXER_MOD OFF CACHE BOOL "Support loading MOD music" FORCE)
|
||||
set(SDL2MIXER_MIDI OFF CACHE BOOL "Enable MIDI music" FORCE)
|
||||
set(SDL2MIXER_WAVPACK OFF CACHE BOOL "Enable WavPack music" FORCE)
|
||||
set(SDL2MIXER_SAMPLES OFF CACHE BOOL "Build the SDL2_mixer sample program(s)" FORCE)
|
||||
set(SDL2MIXER_INSTALL OFF CACHE BOOL "Enable SDL2_mixer install target" FORCE)
|
||||
|
||||
FetchContent_MakeAvailable(SDL freetype SDL_mixer)
|
||||
FetchContent_MakeAvailable(freetype)
|
||||
|
||||
# The main CMakeLists.txt of xsystem35
|
||||
add_subdirectory(${PROJECT_ROOT_DIR} xsystem35)
|
||||
@@ -52,6 +36,4 @@ file(COPY_FILE ${PROJECT_ROOT_DIR}/licenses/MTLc3m.txt ${ASSETS_DIR}/licenses/MT
|
||||
file(COPY_FILE ${PROJECT_ROOT_DIR}/licenses/mincho.txt ${ASSETS_DIR}/licenses/mincho)
|
||||
file(COPY_FILE ${PROJECT_ROOT_DIR}/licenses/nanojpeg.txt ${ASSETS_DIR}/licenses/nanojpeg)
|
||||
file(COPY_FILE ${PROJECT_ROOT_DIR}/licenses/microui.txt ${ASSETS_DIR}/licenses/microui)
|
||||
file(COPY_FILE ${sdl_SOURCE_DIR}/LICENSE.txt ${ASSETS_DIR}/licenses/SDL)
|
||||
file(COPY_FILE ${freetype_SOURCE_DIR}/docs/GPLv2.TXT ${ASSETS_DIR}/licenses/freetype)
|
||||
file(COPY_FILE ${sdl_mixer_SOURCE_DIR}/LICENSE.txt ${ASSETS_DIR}/licenses/SDL_mixer)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := main
|
||||
|
||||
SDL_PATH := ../SDL
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include
|
||||
|
||||
# Add your application source files here...
|
||||
LOCAL_SRC_FILES := YourSourceHere.c
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := SDL2
|
||||
|
||||
LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -lOpenSLES -llog -landroid
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
@@ -1,13 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
|
||||
project(MY_APP)
|
||||
|
||||
find_library(SDL2 SDL2)
|
||||
|
||||
add_library(main SHARED)
|
||||
|
||||
target_sources(main PRIVATE YourSourceHere.c)
|
||||
|
||||
target_link_libraries(main SDL2)
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ class GameActivity : SDLActivity() {
|
||||
}
|
||||
|
||||
override fun getLibraries(): Array<String> {
|
||||
return arrayOf("SDL2", "xsystem35")
|
||||
return arrayOf("SDL3", "xsystem35")
|
||||
}
|
||||
|
||||
override fun getArguments(): Array<String> {
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.hardware.usb.UsbDevice;
|
||||
|
||||
interface HIDDevice
|
||||
{
|
||||
public int getId();
|
||||
public int getVendorId();
|
||||
public int getProductId();
|
||||
public String getSerialNumber();
|
||||
public int getVersion();
|
||||
public String getManufacturerName();
|
||||
public String getProductName();
|
||||
public UsbDevice getDevice();
|
||||
public boolean open();
|
||||
public int sendFeatureReport(byte[] report);
|
||||
public int sendOutputReport(byte[] report);
|
||||
public boolean getFeatureReport(byte[] report);
|
||||
public void setFrozen(boolean frozen);
|
||||
public void close();
|
||||
public void shutdown();
|
||||
}
|
||||
@@ -1,650 +0,0 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.os.*;
|
||||
|
||||
//import com.android.internal.util.HexDump;
|
||||
|
||||
import java.lang.Runnable;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.UUID;
|
||||
|
||||
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
|
||||
|
||||
private static final String TAG = "hidapi";
|
||||
private HIDDeviceManager mManager;
|
||||
private BluetoothDevice mDevice;
|
||||
private int mDeviceId;
|
||||
private BluetoothGatt mGatt;
|
||||
private boolean mIsRegistered = false;
|
||||
private boolean mIsConnected = false;
|
||||
private boolean mIsChromebook = false;
|
||||
private boolean mIsReconnecting = false;
|
||||
private boolean mFrozen = false;
|
||||
private LinkedList<GattOperation> mOperations;
|
||||
GattOperation mCurrentOperation = null;
|
||||
private Handler mHandler;
|
||||
|
||||
private static final int TRANSPORT_AUTO = 0;
|
||||
private static final int TRANSPORT_BREDR = 1;
|
||||
private static final int TRANSPORT_LE = 2;
|
||||
|
||||
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
|
||||
|
||||
static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
|
||||
static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
|
||||
static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
|
||||
static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
|
||||
|
||||
static class GattOperation {
|
||||
private enum Operation {
|
||||
CHR_READ,
|
||||
CHR_WRITE,
|
||||
ENABLE_NOTIFICATION
|
||||
}
|
||||
|
||||
Operation mOp;
|
||||
UUID mUuid;
|
||||
byte[] mValue;
|
||||
BluetoothGatt mGatt;
|
||||
boolean mResult = true;
|
||||
|
||||
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
|
||||
mGatt = gatt;
|
||||
mOp = operation;
|
||||
mUuid = uuid;
|
||||
}
|
||||
|
||||
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
|
||||
mGatt = gatt;
|
||||
mOp = operation;
|
||||
mUuid = uuid;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
// This is executed in main thread
|
||||
BluetoothGattCharacteristic chr;
|
||||
|
||||
switch (mOp) {
|
||||
case CHR_READ:
|
||||
chr = getCharacteristic(mUuid);
|
||||
//Log.v(TAG, "Reading characteristic " + chr.getUuid());
|
||||
if (!mGatt.readCharacteristic(chr)) {
|
||||
Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
|
||||
mResult = false;
|
||||
break;
|
||||
}
|
||||
mResult = true;
|
||||
break;
|
||||
case CHR_WRITE:
|
||||
chr = getCharacteristic(mUuid);
|
||||
//Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
|
||||
chr.setValue(mValue);
|
||||
if (!mGatt.writeCharacteristic(chr)) {
|
||||
Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
|
||||
mResult = false;
|
||||
break;
|
||||
}
|
||||
mResult = true;
|
||||
break;
|
||||
case ENABLE_NOTIFICATION:
|
||||
chr = getCharacteristic(mUuid);
|
||||
//Log.v(TAG, "Writing descriptor of " + chr.getUuid());
|
||||
if (chr != null) {
|
||||
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
||||
if (cccd != null) {
|
||||
int properties = chr.getProperties();
|
||||
byte[] value;
|
||||
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
|
||||
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
|
||||
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
|
||||
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
|
||||
} else {
|
||||
Log.e(TAG, "Unable to start notifications on input characteristic");
|
||||
mResult = false;
|
||||
return;
|
||||
}
|
||||
|
||||
mGatt.setCharacteristicNotification(chr, true);
|
||||
cccd.setValue(value);
|
||||
if (!mGatt.writeDescriptor(cccd)) {
|
||||
Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
|
||||
mResult = false;
|
||||
return;
|
||||
}
|
||||
mResult = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean finish() {
|
||||
return mResult;
|
||||
}
|
||||
|
||||
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
|
||||
BluetoothGattService valveService = mGatt.getService(steamControllerService);
|
||||
if (valveService == null)
|
||||
return null;
|
||||
return valveService.getCharacteristic(uuid);
|
||||
}
|
||||
|
||||
static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
|
||||
return new GattOperation(gatt, Operation.CHR_READ, uuid);
|
||||
}
|
||||
|
||||
static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
|
||||
return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
|
||||
}
|
||||
|
||||
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
|
||||
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
|
||||
}
|
||||
}
|
||||
|
||||
public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
|
||||
mManager = manager;
|
||||
mDevice = device;
|
||||
mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
|
||||
mIsRegistered = false;
|
||||
mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
|
||||
mOperations = new LinkedList<GattOperation>();
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
mGatt = connectGatt();
|
||||
// final HIDDeviceBLESteamController finalThis = this;
|
||||
// mHandler.postDelayed(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// finalThis.checkConnectionForChromebookIssue();
|
||||
// }
|
||||
// }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
|
||||
}
|
||||
|
||||
public String getIdentifier() {
|
||||
return String.format("SteamController.%s", mDevice.getAddress());
|
||||
}
|
||||
|
||||
public BluetoothGatt getGatt() {
|
||||
return mGatt;
|
||||
}
|
||||
|
||||
// Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
|
||||
// of TRANSPORT_LE. Let's force ourselves to connect low energy.
|
||||
private BluetoothGatt connectGatt(boolean managed) {
|
||||
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
|
||||
try {
|
||||
return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
|
||||
} catch (Exception e) {
|
||||
return mDevice.connectGatt(mManager.getContext(), managed, this);
|
||||
}
|
||||
} else {
|
||||
return mDevice.connectGatt(mManager.getContext(), managed, this);
|
||||
}
|
||||
}
|
||||
|
||||
private BluetoothGatt connectGatt() {
|
||||
return connectGatt(false);
|
||||
}
|
||||
|
||||
protected int getConnectionState() {
|
||||
|
||||
Context context = mManager.getContext();
|
||||
if (context == null) {
|
||||
// We are lacking any context to get our Bluetooth information. We'll just assume disconnected.
|
||||
return BluetoothProfile.STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||
if (btManager == null) {
|
||||
// This device doesn't support Bluetooth. We should never be here, because how did
|
||||
// we instantiate a device to start with?
|
||||
return BluetoothProfile.STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
|
||||
}
|
||||
|
||||
public void reconnect() {
|
||||
|
||||
if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void checkConnectionForChromebookIssue() {
|
||||
if (!mIsChromebook) {
|
||||
// We only do this on Chromebooks, because otherwise it's really annoying to just attempt
|
||||
// over and over.
|
||||
return;
|
||||
}
|
||||
|
||||
int connectionState = getConnectionState();
|
||||
|
||||
switch (connectionState) {
|
||||
case BluetoothProfile.STATE_CONNECTED:
|
||||
if (!mIsConnected) {
|
||||
// We are in the Bad Chromebook Place. We can force a disconnect
|
||||
// to try to recover.
|
||||
Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect.");
|
||||
mIsReconnecting = true;
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
break;
|
||||
}
|
||||
else if (!isRegistered()) {
|
||||
if (mGatt.getServices().size() > 0) {
|
||||
Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover.");
|
||||
probeService(this);
|
||||
}
|
||||
else {
|
||||
Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover.");
|
||||
mIsReconnecting = true;
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case BluetoothProfile.STATE_DISCONNECTED:
|
||||
Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover.");
|
||||
|
||||
mIsReconnecting = true;
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
break;
|
||||
|
||||
case BluetoothProfile.STATE_CONNECTING:
|
||||
Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer.");
|
||||
break;
|
||||
}
|
||||
|
||||
final HIDDeviceBLESteamController finalThis = this;
|
||||
mHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finalThis.checkConnectionForChromebookIssue();
|
||||
}
|
||||
}, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
|
||||
}
|
||||
|
||||
private boolean isRegistered() {
|
||||
return mIsRegistered;
|
||||
}
|
||||
|
||||
private void setRegistered() {
|
||||
mIsRegistered = true;
|
||||
}
|
||||
|
||||
private boolean probeService(HIDDeviceBLESteamController controller) {
|
||||
|
||||
if (isRegistered()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mIsConnected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.v(TAG, "probeService controller=" + controller);
|
||||
|
||||
for (BluetoothGattService service : mGatt.getServices()) {
|
||||
if (service.getUuid().equals(steamControllerService)) {
|
||||
Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
|
||||
|
||||
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
|
||||
if (chr.getUuid().equals(inputCharacteristic)) {
|
||||
Log.v(TAG, "Found input characteristic");
|
||||
// Start notifications
|
||||
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
||||
if (cccd != null) {
|
||||
enableNotification(chr.getUuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
|
||||
Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
|
||||
mIsConnected = false;
|
||||
mIsReconnecting = true;
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void finishCurrentGattOperation() {
|
||||
GattOperation op = null;
|
||||
synchronized (mOperations) {
|
||||
if (mCurrentOperation != null) {
|
||||
op = mCurrentOperation;
|
||||
mCurrentOperation = null;
|
||||
}
|
||||
}
|
||||
if (op != null) {
|
||||
boolean result = op.finish(); // TODO: Maybe in main thread as well?
|
||||
|
||||
// Our operation failed, let's add it back to the beginning of our queue.
|
||||
if (!result) {
|
||||
mOperations.addFirst(op);
|
||||
}
|
||||
}
|
||||
executeNextGattOperation();
|
||||
}
|
||||
|
||||
private void executeNextGattOperation() {
|
||||
synchronized (mOperations) {
|
||||
if (mCurrentOperation != null)
|
||||
return;
|
||||
|
||||
if (mOperations.isEmpty())
|
||||
return;
|
||||
|
||||
mCurrentOperation = mOperations.removeFirst();
|
||||
}
|
||||
|
||||
// Run in main thread
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (mOperations) {
|
||||
if (mCurrentOperation == null) {
|
||||
Log.e(TAG, "Current operation null in executor?");
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentOperation.run();
|
||||
// now wait for the GATT callback and when it comes, finish this operation
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void queueGattOperation(GattOperation op) {
|
||||
synchronized (mOperations) {
|
||||
mOperations.add(op);
|
||||
}
|
||||
executeNextGattOperation();
|
||||
}
|
||||
|
||||
private void enableNotification(UUID chrUuid) {
|
||||
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
|
||||
queueGattOperation(op);
|
||||
}
|
||||
|
||||
public void writeCharacteristic(UUID uuid, byte[] value) {
|
||||
GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
|
||||
queueGattOperation(op);
|
||||
}
|
||||
|
||||
public void readCharacteristic(UUID uuid) {
|
||||
GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
|
||||
queueGattOperation(op);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////// BluetoothGattCallback overridden methods
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
|
||||
//Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
|
||||
mIsReconnecting = false;
|
||||
if (newState == 2) {
|
||||
mIsConnected = true;
|
||||
// Run directly, without GattOperation
|
||||
if (!isRegistered()) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mGatt.discoverServices();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (newState == 0) {
|
||||
mIsConnected = false;
|
||||
}
|
||||
|
||||
// Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
|
||||
}
|
||||
|
||||
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||
//Log.v(TAG, "onServicesDiscovered status=" + status);
|
||||
if (status == 0) {
|
||||
if (gatt.getServices().size() == 0) {
|
||||
Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
|
||||
mIsReconnecting = true;
|
||||
mIsConnected = false;
|
||||
gatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
}
|
||||
else {
|
||||
probeService(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||
//Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
|
||||
|
||||
if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
|
||||
mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
|
||||
}
|
||||
|
||||
finishCurrentGattOperation();
|
||||
}
|
||||
|
||||
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||
//Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
|
||||
|
||||
if (characteristic.getUuid().equals(reportCharacteristic)) {
|
||||
// Only register controller with the native side once it has been fully configured
|
||||
if (!isRegistered()) {
|
||||
Log.v(TAG, "Registering Steam Controller with ID: " + getId());
|
||||
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0);
|
||||
setRegistered();
|
||||
}
|
||||
}
|
||||
|
||||
finishCurrentGattOperation();
|
||||
}
|
||||
|
||||
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||
// Enable this for verbose logging of controller input reports
|
||||
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
|
||||
|
||||
if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
|
||||
mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||
//Log.v(TAG, "onDescriptorRead status=" + status);
|
||||
}
|
||||
|
||||
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||
BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
|
||||
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
|
||||
|
||||
if (chr.getUuid().equals(inputCharacteristic)) {
|
||||
boolean hasWrittenInputDescriptor = true;
|
||||
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
|
||||
if (reportChr != null) {
|
||||
Log.v(TAG, "Writing report characteristic to enter valve mode");
|
||||
reportChr.setValue(enterValveMode);
|
||||
gatt.writeCharacteristic(reportChr);
|
||||
}
|
||||
}
|
||||
|
||||
finishCurrentGattOperation();
|
||||
}
|
||||
|
||||
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||
//Log.v(TAG, "onReliableWriteCompleted status=" + status);
|
||||
}
|
||||
|
||||
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
|
||||
//Log.v(TAG, "onReadRemoteRssi status=" + status);
|
||||
}
|
||||
|
||||
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
|
||||
//Log.v(TAG, "onMtuChanged status=" + status);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////// Public API
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return mDeviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVendorId() {
|
||||
// Valve Corporation
|
||||
final int VALVE_USB_VID = 0x28DE;
|
||||
return VALVE_USB_VID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProductId() {
|
||||
// We don't have an easy way to query from the Bluetooth device, but we know what it is
|
||||
final int D0G_BLE2_PID = 0x1106;
|
||||
return D0G_BLE2_PID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerialNumber() {
|
||||
// This will be read later via feature report by Steam
|
||||
return "12345";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturerName() {
|
||||
return "Valve Corporation";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProductName() {
|
||||
return "Steam Controller";
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbDevice getDevice() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sendFeatureReport(byte[] report) {
|
||||
if (!isRegistered()) {
|
||||
Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
|
||||
if (mIsConnected) {
|
||||
probeService(this);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We need to skip the first byte, as that doesn't go over the air
|
||||
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
|
||||
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
|
||||
writeCharacteristic(reportCharacteristic, actual_report);
|
||||
return report.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sendOutputReport(byte[] report) {
|
||||
if (!isRegistered()) {
|
||||
Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
|
||||
if (mIsConnected) {
|
||||
probeService(this);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
|
||||
writeCharacteristic(reportCharacteristic, report);
|
||||
return report.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getFeatureReport(byte[] report) {
|
||||
if (!isRegistered()) {
|
||||
Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
|
||||
if (mIsConnected) {
|
||||
probeService(this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//Log.v(TAG, "getFeatureReport");
|
||||
readCharacteristic(reportCharacteristic);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrozen(boolean frozen) {
|
||||
mFrozen = frozen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
close();
|
||||
|
||||
BluetoothGatt g = mGatt;
|
||||
if (g != null) {
|
||||
g.disconnect();
|
||||
g.close();
|
||||
mGatt = null;
|
||||
}
|
||||
mManager = null;
|
||||
mIsRegistered = false;
|
||||
mIsConnected = false;
|
||||
mOperations.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,698 +0,0 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.usb.*;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class HIDDeviceManager {
|
||||
private static final String TAG = "hidapi";
|
||||
private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
|
||||
|
||||
private static HIDDeviceManager sManager;
|
||||
private static int sManagerRefCount = 0;
|
||||
|
||||
public static HIDDeviceManager acquire(Context context) {
|
||||
if (sManagerRefCount == 0) {
|
||||
sManager = new HIDDeviceManager(context);
|
||||
}
|
||||
++sManagerRefCount;
|
||||
return sManager;
|
||||
}
|
||||
|
||||
public static void release(HIDDeviceManager manager) {
|
||||
if (manager == sManager) {
|
||||
--sManagerRefCount;
|
||||
if (sManagerRefCount == 0) {
|
||||
sManager.close();
|
||||
sManager = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
|
||||
private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
|
||||
private int mNextDeviceId = 0;
|
||||
private SharedPreferences mSharedPreferences = null;
|
||||
private boolean mIsChromebook = false;
|
||||
private UsbManager mUsbManager;
|
||||
private Handler mHandler;
|
||||
private BluetoothManager mBluetoothManager;
|
||||
private List<BluetoothDevice> mLastBluetoothDevices;
|
||||
|
||||
private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
|
||||
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
handleUsbDeviceAttached(usbDevice);
|
||||
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
|
||||
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
handleUsbDeviceDetached(usbDevice);
|
||||
} else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
|
||||
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
// Bluetooth device was connected. If it was a Steam Controller, handle it
|
||||
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
|
||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
Log.d(TAG, "Bluetooth device connected: " + device);
|
||||
|
||||
if (isSteamController(device)) {
|
||||
connectBluetoothDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
// Bluetooth device was disconnected, remove from controller manager (if any)
|
||||
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
|
||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
Log.d(TAG, "Bluetooth device disconnected: " + device);
|
||||
|
||||
disconnectBluetoothDevice(device);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private HIDDeviceManager(final Context context) {
|
||||
mContext = context;
|
||||
|
||||
HIDDeviceRegisterCallback();
|
||||
|
||||
mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
|
||||
mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
|
||||
|
||||
// if (shouldClear) {
|
||||
// SharedPreferences.Editor spedit = mSharedPreferences.edit();
|
||||
// spedit.clear();
|
||||
// spedit.commit();
|
||||
// }
|
||||
// else
|
||||
{
|
||||
mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
|
||||
}
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public int getDeviceIDForIdentifier(String identifier) {
|
||||
SharedPreferences.Editor spedit = mSharedPreferences.edit();
|
||||
|
||||
int result = mSharedPreferences.getInt(identifier, 0);
|
||||
if (result == 0) {
|
||||
result = mNextDeviceId++;
|
||||
spedit.putInt("next_device_id", mNextDeviceId);
|
||||
}
|
||||
|
||||
spedit.putInt(identifier, result);
|
||||
spedit.commit();
|
||||
return result;
|
||||
}
|
||||
|
||||
private void initializeUSB() {
|
||||
mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
|
||||
if (mUsbManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
// Logging
|
||||
for (UsbDevice device : mUsbManager.getDeviceList().values()) {
|
||||
Log.i(TAG,"Path: " + device.getDeviceName());
|
||||
Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
|
||||
Log.i(TAG,"Product: " + device.getProductName());
|
||||
Log.i(TAG,"ID: " + device.getDeviceId());
|
||||
Log.i(TAG,"Class: " + device.getDeviceClass());
|
||||
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
|
||||
Log.i(TAG,"Vendor ID " + device.getVendorId());
|
||||
Log.i(TAG,"Product ID: " + device.getProductId());
|
||||
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
|
||||
Log.i(TAG,"---------------------------------------");
|
||||
|
||||
// Get interface details
|
||||
for (int index = 0; index < device.getInterfaceCount(); index++) {
|
||||
UsbInterface mUsbInterface = device.getInterface(index);
|
||||
Log.i(TAG," ***** *****");
|
||||
Log.i(TAG," Interface index: " + index);
|
||||
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
|
||||
Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
|
||||
Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
|
||||
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
|
||||
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
|
||||
|
||||
// Get endpoint details
|
||||
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
|
||||
{
|
||||
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
|
||||
Log.i(TAG," ++++ ++++ ++++");
|
||||
Log.i(TAG," Endpoint index: " + epi);
|
||||
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
|
||||
Log.i(TAG," Direction: " + mEndpoint.getDirection());
|
||||
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
|
||||
Log.i(TAG," Interval: " + mEndpoint.getInterval());
|
||||
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
|
||||
Log.i(TAG," Type: " + mEndpoint.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.i(TAG," No more devices connected.");
|
||||
*/
|
||||
|
||||
// Register for USB broadcasts and permission completions
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
|
||||
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
|
||||
filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
|
||||
mContext.registerReceiver(mUsbBroadcast, filter);
|
||||
|
||||
for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
|
||||
handleUsbDeviceAttached(usbDevice);
|
||||
}
|
||||
}
|
||||
|
||||
UsbManager getUSBManager() {
|
||||
return mUsbManager;
|
||||
}
|
||||
|
||||
private void shutdownUSB() {
|
||||
try {
|
||||
mContext.unregisterReceiver(mUsbBroadcast);
|
||||
} catch (Exception e) {
|
||||
// We may not have registered, that's okay
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
|
||||
return true;
|
||||
}
|
||||
if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||
final int XB360_IFACE_SUBCLASS = 93;
|
||||
final int XB360_IFACE_PROTOCOL = 1; // Wired
|
||||
final int XB360W_IFACE_PROTOCOL = 129; // Wireless
|
||||
final int[] SUPPORTED_VENDORS = {
|
||||
0x0079, // GPD Win 2
|
||||
0x044f, // Thrustmaster
|
||||
0x045e, // Microsoft
|
||||
0x046d, // Logitech
|
||||
0x056e, // Elecom
|
||||
0x06a3, // Saitek
|
||||
0x0738, // Mad Catz
|
||||
0x07ff, // Mad Catz
|
||||
0x0e6f, // PDP
|
||||
0x0f0d, // Hori
|
||||
0x1038, // SteelSeries
|
||||
0x11c9, // Nacon
|
||||
0x12ab, // Unknown
|
||||
0x1430, // RedOctane
|
||||
0x146b, // BigBen
|
||||
0x1532, // Razer Sabertooth
|
||||
0x15e4, // Numark
|
||||
0x162e, // Joytech
|
||||
0x1689, // Razer Onza
|
||||
0x1949, // Lab126, Inc.
|
||||
0x1bad, // Harmonix
|
||||
0x20d6, // PowerA
|
||||
0x24c6, // PowerA
|
||||
0x2c22, // Qanba
|
||||
0x2dc8, // 8BitDo
|
||||
0x9886, // ASTRO Gaming
|
||||
};
|
||||
|
||||
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||
usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
|
||||
(usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL ||
|
||||
usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) {
|
||||
int vendor_id = usbDevice.getVendorId();
|
||||
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||
if (vendor_id == supportedVid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||
final int XB1_IFACE_SUBCLASS = 71;
|
||||
final int XB1_IFACE_PROTOCOL = 208;
|
||||
final int[] SUPPORTED_VENDORS = {
|
||||
0x03f0, // HP
|
||||
0x044f, // Thrustmaster
|
||||
0x045e, // Microsoft
|
||||
0x0738, // Mad Catz
|
||||
0x0b05, // ASUS
|
||||
0x0e6f, // PDP
|
||||
0x0f0d, // Hori
|
||||
0x10f5, // Turtle Beach
|
||||
0x1532, // Razer Wildcat
|
||||
0x20d6, // PowerA
|
||||
0x24c6, // PowerA
|
||||
0x2dc8, // 8BitDo
|
||||
0x2e24, // Hyperkin
|
||||
0x3537, // GameSir
|
||||
};
|
||||
|
||||
if (usbInterface.getId() == 0 &&
|
||||
usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||
usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
|
||||
usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
|
||||
int vendor_id = usbDevice.getVendorId();
|
||||
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||
if (vendor_id == supportedVid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleUsbDeviceAttached(UsbDevice usbDevice) {
|
||||
connectHIDDeviceUSB(usbDevice);
|
||||
}
|
||||
|
||||
private void handleUsbDeviceDetached(UsbDevice usbDevice) {
|
||||
List<Integer> devices = new ArrayList<Integer>();
|
||||
for (HIDDevice device : mDevicesById.values()) {
|
||||
if (usbDevice.equals(device.getDevice())) {
|
||||
devices.add(device.getId());
|
||||
}
|
||||
}
|
||||
for (int id : devices) {
|
||||
HIDDevice device = mDevicesById.get(id);
|
||||
mDevicesById.remove(id);
|
||||
device.shutdown();
|
||||
HIDDeviceDisconnected(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
|
||||
for (HIDDevice device : mDevicesById.values()) {
|
||||
if (usbDevice.equals(device.getDevice())) {
|
||||
boolean opened = false;
|
||||
if (permission_granted) {
|
||||
opened = device.open();
|
||||
}
|
||||
HIDDeviceOpenResult(device.getId(), opened);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void connectHIDDeviceUSB(UsbDevice usbDevice) {
|
||||
synchronized (this) {
|
||||
int interface_mask = 0;
|
||||
for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) {
|
||||
UsbInterface usbInterface = usbDevice.getInterface(interface_index);
|
||||
if (isHIDDeviceInterface(usbDevice, usbInterface)) {
|
||||
// Check to see if we've already added this interface
|
||||
// This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive
|
||||
int interface_id = usbInterface.getId();
|
||||
if ((interface_mask & (1 << interface_id)) != 0) {
|
||||
continue;
|
||||
}
|
||||
interface_mask |= (1 << interface_id);
|
||||
|
||||
HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
|
||||
int id = device.getId();
|
||||
mDevicesById.put(id, device);
|
||||
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeBluetooth() {
|
||||
Log.d(TAG, "Initializing Bluetooth");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 31 /* Android 12 */ &&
|
||||
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH_CONNECT, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH_CONNECT");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ &&
|
||||
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18 /* Android 4.3 (JELLY_BEAN_MR2) */)) {
|
||||
Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find bonded bluetooth controllers and create SteamControllers for them
|
||||
mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||
if (mBluetoothManager == null) {
|
||||
// This device doesn't support Bluetooth.
|
||||
return;
|
||||
}
|
||||
|
||||
BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
|
||||
if (btAdapter == null) {
|
||||
// This device has Bluetooth support in the codebase, but has no available adapters.
|
||||
return;
|
||||
}
|
||||
|
||||
// Get our bonded devices.
|
||||
for (BluetoothDevice device : btAdapter.getBondedDevices()) {
|
||||
|
||||
Log.d(TAG, "Bluetooth device available: " + device);
|
||||
if (isSteamController(device)) {
|
||||
connectBluetoothDevice(device);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// NOTE: These don't work on Chromebooks, to my undying dismay.
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
|
||||
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
||||
mContext.registerReceiver(mBluetoothBroadcast, filter);
|
||||
|
||||
if (mIsChromebook) {
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
mLastBluetoothDevices = new ArrayList<BluetoothDevice>();
|
||||
|
||||
// final HIDDeviceManager finalThis = this;
|
||||
// mHandler.postDelayed(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// finalThis.chromebookConnectionHandler();
|
||||
// }
|
||||
// }, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownBluetooth() {
|
||||
try {
|
||||
mContext.unregisterReceiver(mBluetoothBroadcast);
|
||||
} catch (Exception e) {
|
||||
// We may not have registered, that's okay
|
||||
}
|
||||
}
|
||||
|
||||
// Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
|
||||
// This function provides a sort of dummy version of that, watching for changes in the
|
||||
// connected devices and attempting to add controllers as things change.
|
||||
public void chromebookConnectionHandler() {
|
||||
if (!mIsChromebook) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<BluetoothDevice> disconnected = new ArrayList<BluetoothDevice>();
|
||||
ArrayList<BluetoothDevice> connected = new ArrayList<BluetoothDevice>();
|
||||
|
||||
List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
|
||||
|
||||
for (BluetoothDevice bluetoothDevice : currentConnected) {
|
||||
if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
|
||||
connected.add(bluetoothDevice);
|
||||
}
|
||||
}
|
||||
for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
|
||||
if (!currentConnected.contains(bluetoothDevice)) {
|
||||
disconnected.add(bluetoothDevice);
|
||||
}
|
||||
}
|
||||
|
||||
mLastBluetoothDevices = currentConnected;
|
||||
|
||||
for (BluetoothDevice bluetoothDevice : disconnected) {
|
||||
disconnectBluetoothDevice(bluetoothDevice);
|
||||
}
|
||||
for (BluetoothDevice bluetoothDevice : connected) {
|
||||
connectBluetoothDevice(bluetoothDevice);
|
||||
}
|
||||
|
||||
final HIDDeviceManager finalThis = this;
|
||||
mHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finalThis.chromebookConnectionHandler();
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
|
||||
Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
|
||||
synchronized (this) {
|
||||
if (mBluetoothDevices.containsKey(bluetoothDevice)) {
|
||||
Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
|
||||
|
||||
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
|
||||
device.reconnect();
|
||||
|
||||
return false;
|
||||
}
|
||||
HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
|
||||
int id = device.getId();
|
||||
mBluetoothDevices.put(bluetoothDevice, device);
|
||||
mDevicesById.put(id, device);
|
||||
|
||||
// The Steam Controller will mark itself connected once initialization is complete
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
|
||||
synchronized (this) {
|
||||
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
|
||||
if (device == null)
|
||||
return;
|
||||
|
||||
int id = device.getId();
|
||||
mBluetoothDevices.remove(bluetoothDevice);
|
||||
mDevicesById.remove(id);
|
||||
device.shutdown();
|
||||
HIDDeviceDisconnected(id);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSteamController(BluetoothDevice bluetoothDevice) {
|
||||
// Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
|
||||
if (bluetoothDevice == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the device has no local name, we really don't want to try an equality check against it.
|
||||
if (bluetoothDevice.getName() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
|
||||
}
|
||||
|
||||
private void close() {
|
||||
shutdownUSB();
|
||||
shutdownBluetooth();
|
||||
synchronized (this) {
|
||||
for (HIDDevice device : mDevicesById.values()) {
|
||||
device.shutdown();
|
||||
}
|
||||
mDevicesById.clear();
|
||||
mBluetoothDevices.clear();
|
||||
HIDDeviceReleaseCallback();
|
||||
}
|
||||
}
|
||||
|
||||
public void setFrozen(boolean frozen) {
|
||||
synchronized (this) {
|
||||
for (HIDDevice device : mDevicesById.values()) {
|
||||
device.setFrozen(frozen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private HIDDevice getDevice(int id) {
|
||||
synchronized (this) {
|
||||
HIDDevice result = mDevicesById.get(id);
|
||||
if (result == null) {
|
||||
Log.v(TAG, "No device for id: " + id);
|
||||
Log.v(TAG, "Available devices: " + mDevicesById.keySet());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////// JNI interface functions
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public boolean initialize(boolean usb, boolean bluetooth) {
|
||||
Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")");
|
||||
|
||||
if (usb) {
|
||||
initializeUSB();
|
||||
}
|
||||
if (bluetooth) {
|
||||
initializeBluetooth();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean openDevice(int deviceID) {
|
||||
Log.v(TAG, "openDevice deviceID=" + deviceID);
|
||||
HIDDevice device = getDevice(deviceID);
|
||||
if (device == null) {
|
||||
HIDDeviceDisconnected(deviceID);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look to see if this is a USB device and we have permission to access it
|
||||
UsbDevice usbDevice = device.getDevice();
|
||||
if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) {
|
||||
HIDDeviceOpenPending(deviceID);
|
||||
try {
|
||||
final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31
|
||||
int flags;
|
||||
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
|
||||
flags = FLAG_MUTABLE;
|
||||
} else {
|
||||
flags = 0;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 33 /* Android 14.0 (U) */) {
|
||||
Intent intent = new Intent(HIDDeviceManager.ACTION_USB_PERMISSION);
|
||||
intent.setPackage(mContext.getPackageName());
|
||||
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, intent, flags));
|
||||
} else {
|
||||
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), flags));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
|
||||
HIDDeviceOpenResult(deviceID, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return device.open();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int sendOutputReport(int deviceID, byte[] report) {
|
||||
try {
|
||||
//Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
|
||||
HIDDevice device;
|
||||
device = getDevice(deviceID);
|
||||
if (device == null) {
|
||||
HIDDeviceDisconnected(deviceID);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return device.sendOutputReport(report);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int sendFeatureReport(int deviceID, byte[] report) {
|
||||
try {
|
||||
//Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
|
||||
HIDDevice device;
|
||||
device = getDevice(deviceID);
|
||||
if (device == null) {
|
||||
HIDDeviceDisconnected(deviceID);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return device.sendFeatureReport(report);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean getFeatureReport(int deviceID, byte[] report) {
|
||||
try {
|
||||
//Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
|
||||
HIDDevice device;
|
||||
device = getDevice(deviceID);
|
||||
if (device == null) {
|
||||
HIDDeviceDisconnected(deviceID);
|
||||
return false;
|
||||
}
|
||||
|
||||
return device.getFeatureReport(report);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void closeDevice(int deviceID) {
|
||||
try {
|
||||
Log.v(TAG, "closeDevice deviceID=" + deviceID);
|
||||
HIDDevice device;
|
||||
device = getDevice(deviceID);
|
||||
if (device == null) {
|
||||
HIDDeviceDisconnected(deviceID);
|
||||
return;
|
||||
}
|
||||
|
||||
device.close();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////// Native methods
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private native void HIDDeviceRegisterCallback();
|
||||
private native void HIDDeviceReleaseCallback();
|
||||
|
||||
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
|
||||
native void HIDDeviceOpenPending(int deviceID);
|
||||
native void HIDDeviceOpenResult(int deviceID, boolean opened);
|
||||
native void HIDDeviceDisconnected(int deviceID);
|
||||
|
||||
native void HIDDeviceInputReport(int deviceID, byte[] report);
|
||||
native void HIDDeviceFeatureReport(int deviceID, byte[] report);
|
||||
}
|
||||
@@ -1,309 +0,0 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.hardware.usb.*;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import java.util.Arrays;
|
||||
|
||||
class HIDDeviceUSB implements HIDDevice {
|
||||
|
||||
private static final String TAG = "hidapi";
|
||||
|
||||
protected HIDDeviceManager mManager;
|
||||
protected UsbDevice mDevice;
|
||||
protected int mInterfaceIndex;
|
||||
protected int mInterface;
|
||||
protected int mDeviceId;
|
||||
protected UsbDeviceConnection mConnection;
|
||||
protected UsbEndpoint mInputEndpoint;
|
||||
protected UsbEndpoint mOutputEndpoint;
|
||||
protected InputThread mInputThread;
|
||||
protected boolean mRunning;
|
||||
protected boolean mFrozen;
|
||||
|
||||
public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
|
||||
mManager = manager;
|
||||
mDevice = usbDevice;
|
||||
mInterfaceIndex = interface_index;
|
||||
mInterface = mDevice.getInterface(mInterfaceIndex).getId();
|
||||
mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
|
||||
mRunning = false;
|
||||
}
|
||||
|
||||
public String getIdentifier() {
|
||||
return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return mDeviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVendorId() {
|
||||
return mDevice.getVendorId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProductId() {
|
||||
return mDevice.getProductId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerialNumber() {
|
||||
String result = null;
|
||||
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||
try {
|
||||
result = mDevice.getSerialNumber();
|
||||
}
|
||||
catch (SecurityException exception) {
|
||||
//Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = "";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturerName() {
|
||||
String result = null;
|
||||
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||
result = mDevice.getManufacturerName();
|
||||
}
|
||||
if (result == null) {
|
||||
result = String.format("%x", getVendorId());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProductName() {
|
||||
String result = null;
|
||||
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||
result = mDevice.getProductName();
|
||||
}
|
||||
if (result == null) {
|
||||
result = String.format("%x", getProductId());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbDevice getDevice() {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
public String getDeviceName() {
|
||||
return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open() {
|
||||
mConnection = mManager.getUSBManager().openDevice(mDevice);
|
||||
if (mConnection == null) {
|
||||
Log.w(TAG, "Unable to open USB device " + getDeviceName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Force claim our interface
|
||||
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
|
||||
if (!mConnection.claimInterface(iface, true)) {
|
||||
Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the endpoints
|
||||
for (int j = 0; j < iface.getEndpointCount(); j++) {
|
||||
UsbEndpoint endpt = iface.getEndpoint(j);
|
||||
switch (endpt.getDirection()) {
|
||||
case UsbConstants.USB_DIR_IN:
|
||||
if (mInputEndpoint == null) {
|
||||
mInputEndpoint = endpt;
|
||||
}
|
||||
break;
|
||||
case UsbConstants.USB_DIR_OUT:
|
||||
if (mOutputEndpoint == null) {
|
||||
mOutputEndpoint = endpt;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the required endpoints were present
|
||||
if (mInputEndpoint == null || mOutputEndpoint == null) {
|
||||
Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start listening for input
|
||||
mRunning = true;
|
||||
mInputThread = new InputThread();
|
||||
mInputThread.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sendFeatureReport(byte[] report) {
|
||||
int res = -1;
|
||||
int offset = 0;
|
||||
int length = report.length;
|
||||
boolean skipped_report_id = false;
|
||||
byte report_number = report[0];
|
||||
|
||||
if (report_number == 0x0) {
|
||||
++offset;
|
||||
--length;
|
||||
skipped_report_id = true;
|
||||
}
|
||||
|
||||
res = mConnection.controlTransfer(
|
||||
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
|
||||
0x09/*HID set_report*/,
|
||||
(3/*HID feature*/ << 8) | report_number,
|
||||
mInterface,
|
||||
report, offset, length,
|
||||
1000/*timeout millis*/);
|
||||
|
||||
if (res < 0) {
|
||||
Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (skipped_report_id) {
|
||||
++length;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sendOutputReport(byte[] report) {
|
||||
int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
|
||||
if (r != report.length) {
|
||||
Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getFeatureReport(byte[] report) {
|
||||
int res = -1;
|
||||
int offset = 0;
|
||||
int length = report.length;
|
||||
boolean skipped_report_id = false;
|
||||
byte report_number = report[0];
|
||||
|
||||
if (report_number == 0x0) {
|
||||
/* Offset the return buffer by 1, so that the report ID
|
||||
will remain in byte 0. */
|
||||
++offset;
|
||||
--length;
|
||||
skipped_report_id = true;
|
||||
}
|
||||
|
||||
res = mConnection.controlTransfer(
|
||||
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
|
||||
0x01/*HID get_report*/,
|
||||
(3/*HID feature*/ << 8) | report_number,
|
||||
mInterface,
|
||||
report, offset, length,
|
||||
1000/*timeout millis*/);
|
||||
|
||||
if (res < 0) {
|
||||
Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (skipped_report_id) {
|
||||
++res;
|
||||
++length;
|
||||
}
|
||||
|
||||
byte[] data;
|
||||
if (res == length) {
|
||||
data = report;
|
||||
} else {
|
||||
data = Arrays.copyOfRange(report, 0, res);
|
||||
}
|
||||
mManager.HIDDeviceFeatureReport(mDeviceId, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mRunning = false;
|
||||
if (mInputThread != null) {
|
||||
while (mInputThread.isAlive()) {
|
||||
mInputThread.interrupt();
|
||||
try {
|
||||
mInputThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
// Keep trying until we're done
|
||||
}
|
||||
}
|
||||
mInputThread = null;
|
||||
}
|
||||
if (mConnection != null) {
|
||||
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
|
||||
mConnection.releaseInterface(iface);
|
||||
mConnection.close();
|
||||
mConnection = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
close();
|
||||
mManager = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrozen(boolean frozen) {
|
||||
mFrozen = frozen;
|
||||
}
|
||||
|
||||
protected class InputThread extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
int packetSize = mInputEndpoint.getMaxPacketSize();
|
||||
byte[] packet = new byte[packetSize];
|
||||
while (mRunning) {
|
||||
int r;
|
||||
try
|
||||
{
|
||||
r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
|
||||
break;
|
||||
}
|
||||
if (r < 0) {
|
||||
// Could be a timeout or an I/O error
|
||||
}
|
||||
if (r > 0) {
|
||||
byte[] data;
|
||||
if (r == packetSize) {
|
||||
data = packet;
|
||||
} else {
|
||||
data = Arrays.copyOfRange(packet, 0, r);
|
||||
}
|
||||
|
||||
if (!mFrozen) {
|
||||
mManager.HIDDeviceInputReport(mDeviceId, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.lang.Class;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
SDL library initialization
|
||||
*/
|
||||
public class SDL {
|
||||
|
||||
// This function should be called first and sets up the native code
|
||||
// so it can call into the Java classes
|
||||
public static void setupJNI() {
|
||||
SDLActivity.nativeSetupJNI();
|
||||
SDLAudioManager.nativeSetupJNI();
|
||||
SDLControllerManager.nativeSetupJNI();
|
||||
}
|
||||
|
||||
// This function should be called each time the activity is started
|
||||
public static void initialize() {
|
||||
setContext(null);
|
||||
|
||||
SDLActivity.initialize();
|
||||
SDLAudioManager.initialize();
|
||||
SDLControllerManager.initialize();
|
||||
}
|
||||
|
||||
// This function stores the current activity (SDL or not)
|
||||
public static void setContext(Context context) {
|
||||
SDLAudioManager.setContext(context);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public static Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
|
||||
loadLibrary(libraryName, mContext);
|
||||
}
|
||||
|
||||
public static void loadLibrary(String libraryName, Context context) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
|
||||
|
||||
if (libraryName == null) {
|
||||
throw new NullPointerException("No library name provided.");
|
||||
}
|
||||
|
||||
try {
|
||||
// Let's see if we have ReLinker available in the project. This is necessary for
|
||||
// some projects that have huge numbers of local libraries bundled, and thus may
|
||||
// trip a bug in Android's native library loader which ReLinker works around. (If
|
||||
// loadLibrary works properly, ReLinker will simply use the normal Android method
|
||||
// internally.)
|
||||
//
|
||||
// To use ReLinker, just add it as a dependency. For more information, see
|
||||
// https://github.com/KeepSafe/ReLinker for ReLinker's repository.
|
||||
//
|
||||
Class<?> relinkClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
|
||||
Class<?> relinkListenerClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
|
||||
Class<?> contextClass = context.getClassLoader().loadClass("android.content.Context");
|
||||
Class<?> stringClass = context.getClassLoader().loadClass("java.lang.String");
|
||||
|
||||
// Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
|
||||
// they've changed during updates.
|
||||
Method forceMethod = relinkClass.getDeclaredMethod("force");
|
||||
Object relinkInstance = forceMethod.invoke(null);
|
||||
Class<?> relinkInstanceClass = relinkInstance.getClass();
|
||||
|
||||
// Actually load the library!
|
||||
Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
|
||||
loadMethod.invoke(relinkInstance, context, libraryName, null, null);
|
||||
}
|
||||
catch (final Throwable e) {
|
||||
// Fall back
|
||||
try {
|
||||
System.loadLibrary(libraryName);
|
||||
}
|
||||
catch (final UnsatisfiedLinkError ule) {
|
||||
throw ule;
|
||||
}
|
||||
catch (final SecurityException se) {
|
||||
throw se;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static Context mContext;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,514 +0,0 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioDeviceCallback;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.AudioTrack;
|
||||
import android.media.MediaRecorder;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SDLAudioManager {
|
||||
protected static final String TAG = "SDLAudio";
|
||||
|
||||
protected static AudioTrack mAudioTrack;
|
||||
protected static AudioRecord mAudioRecord;
|
||||
protected static Context mContext;
|
||||
|
||||
private static final int[] NO_DEVICES = {};
|
||||
|
||||
private static AudioDeviceCallback mAudioDeviceCallback;
|
||||
|
||||
public static void initialize() {
|
||||
mAudioTrack = null;
|
||||
mAudioRecord = null;
|
||||
mAudioDeviceCallback = null;
|
||||
|
||||
if(Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */)
|
||||
{
|
||||
mAudioDeviceCallback = new AudioDeviceCallback() {
|
||||
@Override
|
||||
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
|
||||
Arrays.stream(addedDevices).forEach(deviceInfo -> addAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
|
||||
Arrays.stream(removedDevices).forEach(deviceInfo -> removeAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static void setContext(Context context) {
|
||||
mContext = context;
|
||||
if (context != null) {
|
||||
registerAudioDeviceCallback();
|
||||
}
|
||||
}
|
||||
|
||||
public static void release(Context context) {
|
||||
unregisterAudioDeviceCallback(context);
|
||||
}
|
||||
|
||||
// Audio
|
||||
|
||||
protected static String getAudioFormatString(int audioFormat) {
|
||||
switch (audioFormat) {
|
||||
case AudioFormat.ENCODING_PCM_8BIT:
|
||||
return "8-bit";
|
||||
case AudioFormat.ENCODING_PCM_16BIT:
|
||||
return "16-bit";
|
||||
case AudioFormat.ENCODING_PCM_FLOAT:
|
||||
return "float";
|
||||
default:
|
||||
return Integer.toString(audioFormat);
|
||||
}
|
||||
}
|
||||
|
||||
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||
int channelConfig;
|
||||
int sampleSize;
|
||||
int frameSize;
|
||||
|
||||
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
|
||||
|
||||
/* On older devices let's use known good settings */
|
||||
if (Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||
if (desiredChannels > 2) {
|
||||
desiredChannels = 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* AudioTrack has sample rate limitation of 48000 (fixed in 5.0.2) */
|
||||
if (Build.VERSION.SDK_INT < 22 /* Android 5.1 (LOLLIPOP_MR1) */) {
|
||||
if (sampleRate < 8000) {
|
||||
sampleRate = 8000;
|
||||
} else if (sampleRate > 48000) {
|
||||
sampleRate = 48000;
|
||||
}
|
||||
}
|
||||
|
||||
if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
|
||||
int minSDKVersion = (isCapture ? 23 /* Android 6.0 (M) */ : 21 /* Android 5.0 (LOLLIPOP) */);
|
||||
if (Build.VERSION.SDK_INT < minSDKVersion) {
|
||||
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
|
||||
}
|
||||
}
|
||||
switch (audioFormat)
|
||||
{
|
||||
case AudioFormat.ENCODING_PCM_8BIT:
|
||||
sampleSize = 1;
|
||||
break;
|
||||
case AudioFormat.ENCODING_PCM_16BIT:
|
||||
sampleSize = 2;
|
||||
break;
|
||||
case AudioFormat.ENCODING_PCM_FLOAT:
|
||||
sampleSize = 4;
|
||||
break;
|
||||
default:
|
||||
Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT");
|
||||
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
|
||||
sampleSize = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isCapture) {
|
||||
switch (desiredChannels) {
|
||||
case 1:
|
||||
channelConfig = AudioFormat.CHANNEL_IN_MONO;
|
||||
break;
|
||||
case 2:
|
||||
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
|
||||
break;
|
||||
default:
|
||||
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
|
||||
desiredChannels = 2;
|
||||
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (desiredChannels) {
|
||||
case 1:
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
|
||||
break;
|
||||
case 2:
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||
break;
|
||||
case 3:
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
|
||||
break;
|
||||
case 4:
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
|
||||
break;
|
||||
case 5:
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
|
||||
break;
|
||||
case 6:
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||
break;
|
||||
case 7:
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
|
||||
break;
|
||||
case 8:
|
||||
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
|
||||
} else {
|
||||
Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
|
||||
desiredChannels = 6;
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
|
||||
desiredChannels = 2;
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
Log.v(TAG, "Speaker configuration (and order of channels):");
|
||||
|
||||
if ((channelConfig & 0x00000004) != 0) {
|
||||
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT");
|
||||
}
|
||||
if ((channelConfig & 0x00000008) != 0) {
|
||||
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT");
|
||||
}
|
||||
if ((channelConfig & 0x00000010) != 0) {
|
||||
Log.v(TAG, " CHANNEL_OUT_FRONT_CENTER");
|
||||
}
|
||||
if ((channelConfig & 0x00000020) != 0) {
|
||||
Log.v(TAG, " CHANNEL_OUT_LOW_FREQUENCY");
|
||||
}
|
||||
if ((channelConfig & 0x00000040) != 0) {
|
||||
Log.v(TAG, " CHANNEL_OUT_BACK_LEFT");
|
||||
}
|
||||
if ((channelConfig & 0x00000080) != 0) {
|
||||
Log.v(TAG, " CHANNEL_OUT_BACK_RIGHT");
|
||||
}
|
||||
if ((channelConfig & 0x00000100) != 0) {
|
||||
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT_OF_CENTER");
|
||||
}
|
||||
if ((channelConfig & 0x00000200) != 0) {
|
||||
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT_OF_CENTER");
|
||||
}
|
||||
if ((channelConfig & 0x00000400) != 0) {
|
||||
Log.v(TAG, " CHANNEL_OUT_BACK_CENTER");
|
||||
}
|
||||
if ((channelConfig & 0x00000800) != 0) {
|
||||
Log.v(TAG, " CHANNEL_OUT_SIDE_LEFT");
|
||||
}
|
||||
if ((channelConfig & 0x00001000) != 0) {
|
||||
Log.v(TAG, " CHANNEL_OUT_SIDE_RIGHT");
|
||||
}
|
||||
*/
|
||||
}
|
||||
frameSize = (sampleSize * desiredChannels);
|
||||
|
||||
// Let the user pick a larger buffer if they really want -- but ye
|
||||
// gods they probably shouldn't, the minimums are horrifyingly high
|
||||
// latency already
|
||||
int minBufferSize;
|
||||
if (isCapture) {
|
||||
minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
|
||||
} else {
|
||||
minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
|
||||
}
|
||||
desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize);
|
||||
|
||||
int[] results = new int[4];
|
||||
|
||||
if (isCapture) {
|
||||
if (mAudioRecord == null) {
|
||||
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
|
||||
channelConfig, audioFormat, desiredFrames * frameSize);
|
||||
|
||||
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
|
||||
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
|
||||
Log.e(TAG, "Failed during initialization of AudioRecord");
|
||||
mAudioRecord.release();
|
||||
mAudioRecord = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
|
||||
mAudioRecord.setPreferredDevice(getOutputAudioDeviceInfo(deviceId));
|
||||
}
|
||||
|
||||
mAudioRecord.startRecording();
|
||||
}
|
||||
|
||||
results[0] = mAudioRecord.getSampleRate();
|
||||
results[1] = mAudioRecord.getAudioFormat();
|
||||
results[2] = mAudioRecord.getChannelCount();
|
||||
|
||||
} else {
|
||||
if (mAudioTrack == null) {
|
||||
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
|
||||
|
||||
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
|
||||
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
|
||||
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
|
||||
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
|
||||
/* Try again, with safer values */
|
||||
|
||||
Log.e(TAG, "Failed during initialization of Audio Track");
|
||||
mAudioTrack.release();
|
||||
mAudioTrack = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
|
||||
mAudioTrack.setPreferredDevice(getInputAudioDeviceInfo(deviceId));
|
||||
}
|
||||
|
||||
mAudioTrack.play();
|
||||
}
|
||||
|
||||
results[0] = mAudioTrack.getSampleRate();
|
||||
results[1] = mAudioTrack.getAudioFormat();
|
||||
results[2] = mAudioTrack.getChannelCount();
|
||||
}
|
||||
results[3] = desiredFrames;
|
||||
|
||||
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz");
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS))
|
||||
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static AudioDeviceInfo getOutputAudioDeviceInfo(int deviceId) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
|
||||
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerAudioDeviceCallback() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void unregisterAudioDeviceCallback(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static int[] getAudioOutputDevices() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
|
||||
} else {
|
||||
return NO_DEVICES;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static int[] getAudioInputDevices() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
|
||||
} else {
|
||||
return NO_DEVICES;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static void audioWriteFloatBuffer(float[] buffer) {
|
||||
if (mAudioTrack == null) {
|
||||
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||
Log.e(TAG, "Attempted to make an incompatible audio call with uninitialized audio! (floating-point output is supported since Android 5.0 Lollipop)");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < buffer.length;) {
|
||||
int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
|
||||
if (result > 0) {
|
||||
i += result;
|
||||
} else if (result == 0) {
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch(InterruptedException e) {
|
||||
// Nom nom
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "SDL audio: error return from write(float)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static void audioWriteShortBuffer(short[] buffer) {
|
||||
if (mAudioTrack == null) {
|
||||
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < buffer.length;) {
|
||||
int result = mAudioTrack.write(buffer, i, buffer.length - i);
|
||||
if (result > 0) {
|
||||
i += result;
|
||||
} else if (result == 0) {
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch(InterruptedException e) {
|
||||
// Nom nom
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "SDL audio: error return from write(short)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static void audioWriteByteBuffer(byte[] buffer) {
|
||||
if (mAudioTrack == null) {
|
||||
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < buffer.length; ) {
|
||||
int result = mAudioTrack.write(buffer, i, buffer.length - i);
|
||||
if (result > 0) {
|
||||
i += result;
|
||||
} else if (result == 0) {
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch(InterruptedException e) {
|
||||
// Nom nom
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "SDL audio: error return from write(byte)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
|
||||
}
|
||||
|
||||
/** This method is called by SDL using JNI. */
|
||||
public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
|
||||
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||
return 0;
|
||||
} else {
|
||||
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||
}
|
||||
}
|
||||
|
||||
/** This method is called by SDL using JNI. */
|
||||
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
|
||||
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||
return mAudioRecord.read(buffer, 0, buffer.length);
|
||||
} else {
|
||||
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||
}
|
||||
}
|
||||
|
||||
/** This method is called by SDL using JNI. */
|
||||
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
|
||||
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||
return mAudioRecord.read(buffer, 0, buffer.length);
|
||||
} else {
|
||||
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||
}
|
||||
}
|
||||
|
||||
/** This method is called by SDL using JNI. */
|
||||
public static void audioClose() {
|
||||
if (mAudioTrack != null) {
|
||||
mAudioTrack.stop();
|
||||
mAudioTrack.release();
|
||||
mAudioTrack = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** This method is called by SDL using JNI. */
|
||||
public static void captureClose() {
|
||||
if (mAudioRecord != null) {
|
||||
mAudioRecord.stop();
|
||||
mAudioRecord.release();
|
||||
mAudioRecord = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** This method is called by SDL using JNI. */
|
||||
public static void audioSetThreadPriority(boolean iscapture, int device_id) {
|
||||
try {
|
||||
|
||||
/* Set thread name */
|
||||
if (iscapture) {
|
||||
Thread.currentThread().setName("SDLAudioC" + device_id);
|
||||
} else {
|
||||
Thread.currentThread().setName("SDLAudioP" + device_id);
|
||||
}
|
||||
|
||||
/* Set thread priority */
|
||||
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.v(TAG, "modify thread properties failed " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static native int nativeSetupJNI();
|
||||
|
||||
public static native void removeAudioDevice(boolean isCapture, int deviceId);
|
||||
|
||||
public static native void addAudioDevice(boolean isCapture, int deviceId);
|
||||
|
||||
}
|
||||
@@ -1,856 +0,0 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
|
||||
public class SDLControllerManager
|
||||
{
|
||||
|
||||
public static native int nativeSetupJNI();
|
||||
|
||||
public static native int nativeAddJoystick(int device_id, String name, String desc,
|
||||
int vendor_id, int product_id,
|
||||
boolean is_accelerometer, int button_mask,
|
||||
int naxes, int axis_mask, int nhats, int nballs);
|
||||
public static native int nativeRemoveJoystick(int device_id);
|
||||
public static native int nativeAddHaptic(int device_id, String name);
|
||||
public static native int nativeRemoveHaptic(int device_id);
|
||||
public static native int onNativePadDown(int device_id, int keycode);
|
||||
public static native int onNativePadUp(int device_id, int keycode);
|
||||
public static native void onNativeJoy(int device_id, int axis,
|
||||
float value);
|
||||
public static native void onNativeHat(int device_id, int hat_id,
|
||||
int x, int y);
|
||||
|
||||
protected static SDLJoystickHandler mJoystickHandler;
|
||||
protected static SDLHapticHandler mHapticHandler;
|
||||
|
||||
private static final String TAG = "SDLControllerManager";
|
||||
|
||||
public static void initialize() {
|
||||
if (mJoystickHandler == null) {
|
||||
if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
|
||||
mJoystickHandler = new SDLJoystickHandler_API19();
|
||||
} else {
|
||||
mJoystickHandler = new SDLJoystickHandler_API16();
|
||||
}
|
||||
}
|
||||
|
||||
if (mHapticHandler == null) {
|
||||
if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
|
||||
mHapticHandler = new SDLHapticHandler_API26();
|
||||
} else {
|
||||
mHapticHandler = new SDLHapticHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
|
||||
public static boolean handleJoystickMotionEvent(MotionEvent event) {
|
||||
return mJoystickHandler.handleMotionEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static void pollInputDevices() {
|
||||
mJoystickHandler.pollInputDevices();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static void pollHapticDevices() {
|
||||
mHapticHandler.pollHapticDevices();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static void hapticRun(int device_id, float intensity, int length) {
|
||||
mHapticHandler.run(device_id, intensity, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static void hapticStop(int device_id)
|
||||
{
|
||||
mHapticHandler.stop(device_id);
|
||||
}
|
||||
|
||||
// Check if a given device is considered a possible SDL joystick
|
||||
public static boolean isDeviceSDLJoystick(int deviceId) {
|
||||
InputDevice device = InputDevice.getDevice(deviceId);
|
||||
// We cannot use InputDevice.isVirtual before API 16, so let's accept
|
||||
// only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
|
||||
if ((device == null) || (deviceId < 0)) {
|
||||
return false;
|
||||
}
|
||||
int sources = device.getSources();
|
||||
|
||||
/* This is called for every button press, so let's not spam the logs */
|
||||
/*
|
||||
if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
||||
Log.v(TAG, "Input device " + device.getName() + " has class joystick.");
|
||||
}
|
||||
if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
|
||||
Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
|
||||
}
|
||||
if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
|
||||
Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
|
||||
}
|
||||
*/
|
||||
|
||||
return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ||
|
||||
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
|
||||
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SDLJoystickHandler {
|
||||
|
||||
/**
|
||||
* Handles given MotionEvent.
|
||||
* @param event the event to be handled.
|
||||
* @return if given event was processed.
|
||||
*/
|
||||
public boolean handleMotionEvent(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles adding and removing of input devices.
|
||||
*/
|
||||
public void pollInputDevices() {
|
||||
}
|
||||
}
|
||||
|
||||
/* Actual joystick functionality available for API >= 12 devices */
|
||||
class SDLJoystickHandler_API16 extends SDLJoystickHandler {
|
||||
|
||||
static class SDLJoystick {
|
||||
public int device_id;
|
||||
public String name;
|
||||
public String desc;
|
||||
public ArrayList<InputDevice.MotionRange> axes;
|
||||
public ArrayList<InputDevice.MotionRange> hats;
|
||||
}
|
||||
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
|
||||
@Override
|
||||
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
|
||||
// Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL
|
||||
int arg0Axis = arg0.getAxis();
|
||||
int arg1Axis = arg1.getAxis();
|
||||
if (arg0Axis == MotionEvent.AXIS_GAS) {
|
||||
arg0Axis = MotionEvent.AXIS_BRAKE;
|
||||
} else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
|
||||
arg0Axis = MotionEvent.AXIS_GAS;
|
||||
}
|
||||
if (arg1Axis == MotionEvent.AXIS_GAS) {
|
||||
arg1Axis = MotionEvent.AXIS_BRAKE;
|
||||
} else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
|
||||
arg1Axis = MotionEvent.AXIS_GAS;
|
||||
}
|
||||
|
||||
// Make sure the AXIS_Z is sorted between AXIS_RY and AXIS_RZ.
|
||||
// This is because the usual pairing are:
|
||||
// - AXIS_X + AXIS_Y (left stick).
|
||||
// - AXIS_RX, AXIS_RY (sometimes the right stick, sometimes triggers).
|
||||
// - AXIS_Z, AXIS_RZ (sometimes the right stick, sometimes triggers).
|
||||
// This sorts the axes in the above order, which tends to be correct
|
||||
// for Xbox-ish game pads that have the right stick on RX/RY and the
|
||||
// triggers on Z/RZ.
|
||||
//
|
||||
// Gamepads that don't have AXIS_Z/AXIS_RZ but use
|
||||
// AXIS_LTRIGGER/AXIS_RTRIGGER are unaffected by this.
|
||||
//
|
||||
// References:
|
||||
// - https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input
|
||||
// - https://www.kernel.org/doc/html/latest/input/gamepad.html
|
||||
if (arg0Axis == MotionEvent.AXIS_Z) {
|
||||
arg0Axis = MotionEvent.AXIS_RZ - 1;
|
||||
} else if (arg0Axis > MotionEvent.AXIS_Z && arg0Axis < MotionEvent.AXIS_RZ) {
|
||||
--arg0Axis;
|
||||
}
|
||||
if (arg1Axis == MotionEvent.AXIS_Z) {
|
||||
arg1Axis = MotionEvent.AXIS_RZ - 1;
|
||||
} else if (arg1Axis > MotionEvent.AXIS_Z && arg1Axis < MotionEvent.AXIS_RZ) {
|
||||
--arg1Axis;
|
||||
}
|
||||
|
||||
return arg0Axis - arg1Axis;
|
||||
}
|
||||
}
|
||||
|
||||
private final ArrayList<SDLJoystick> mJoysticks;
|
||||
|
||||
public SDLJoystickHandler_API16() {
|
||||
|
||||
mJoysticks = new ArrayList<SDLJoystick>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pollInputDevices() {
|
||||
int[] deviceIds = InputDevice.getDeviceIds();
|
||||
|
||||
for (int device_id : deviceIds) {
|
||||
if (SDLControllerManager.isDeviceSDLJoystick(device_id)) {
|
||||
SDLJoystick joystick = getJoystick(device_id);
|
||||
if (joystick == null) {
|
||||
InputDevice joystickDevice = InputDevice.getDevice(device_id);
|
||||
joystick = new SDLJoystick();
|
||||
joystick.device_id = device_id;
|
||||
joystick.name = joystickDevice.getName();
|
||||
joystick.desc = getJoystickDescriptor(joystickDevice);
|
||||
joystick.axes = new ArrayList<InputDevice.MotionRange>();
|
||||
joystick.hats = new ArrayList<InputDevice.MotionRange>();
|
||||
|
||||
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
|
||||
Collections.sort(ranges, new RangeComparator());
|
||||
for (InputDevice.MotionRange range : ranges) {
|
||||
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
||||
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
|
||||
joystick.hats.add(range);
|
||||
} else {
|
||||
joystick.axes.add(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mJoysticks.add(joystick);
|
||||
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
|
||||
getVendorId(joystickDevice), getProductId(joystickDevice), false,
|
||||
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check removed devices */
|
||||
ArrayList<Integer> removedDevices = null;
|
||||
for (SDLJoystick joystick : mJoysticks) {
|
||||
int device_id = joystick.device_id;
|
||||
int i;
|
||||
for (i = 0; i < deviceIds.length; i++) {
|
||||
if (device_id == deviceIds[i]) break;
|
||||
}
|
||||
if (i == deviceIds.length) {
|
||||
if (removedDevices == null) {
|
||||
removedDevices = new ArrayList<Integer>();
|
||||
}
|
||||
removedDevices.add(device_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (removedDevices != null) {
|
||||
for (int device_id : removedDevices) {
|
||||
SDLControllerManager.nativeRemoveJoystick(device_id);
|
||||
for (int i = 0; i < mJoysticks.size(); i++) {
|
||||
if (mJoysticks.get(i).device_id == device_id) {
|
||||
mJoysticks.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected SDLJoystick getJoystick(int device_id) {
|
||||
for (SDLJoystick joystick : mJoysticks) {
|
||||
if (joystick.device_id == device_id) {
|
||||
return joystick;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMotionEvent(MotionEvent event) {
|
||||
int actionPointerIndex = event.getActionIndex();
|
||||
int action = event.getActionMasked();
|
||||
if (action == MotionEvent.ACTION_MOVE) {
|
||||
SDLJoystick joystick = getJoystick(event.getDeviceId());
|
||||
if (joystick != null) {
|
||||
for (int i = 0; i < joystick.axes.size(); i++) {
|
||||
InputDevice.MotionRange range = joystick.axes.get(i);
|
||||
/* Normalize the value to -1...1 */
|
||||
float value = (event.getAxisValue(range.getAxis(), actionPointerIndex) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
|
||||
SDLControllerManager.onNativeJoy(joystick.device_id, i, value);
|
||||
}
|
||||
for (int i = 0; i < joystick.hats.size() / 2; i++) {
|
||||
int hatX = Math.round(event.getAxisValue(joystick.hats.get(2 * i).getAxis(), actionPointerIndex));
|
||||
int hatY = Math.round(event.getAxisValue(joystick.hats.get(2 * i + 1).getAxis(), actionPointerIndex));
|
||||
SDLControllerManager.onNativeHat(joystick.device_id, i, hatX, hatY);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getJoystickDescriptor(InputDevice joystickDevice) {
|
||||
String desc = joystickDevice.getDescriptor();
|
||||
|
||||
if (desc != null && !desc.isEmpty()) {
|
||||
return desc;
|
||||
}
|
||||
|
||||
return joystickDevice.getName();
|
||||
}
|
||||
public int getProductId(InputDevice joystickDevice) {
|
||||
return 0;
|
||||
}
|
||||
public int getVendorId(InputDevice joystickDevice) {
|
||||
return 0;
|
||||
}
|
||||
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
|
||||
return -1;
|
||||
}
|
||||
public int getButtonMask(InputDevice joystickDevice) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
|
||||
|
||||
@Override
|
||||
public int getProductId(InputDevice joystickDevice) {
|
||||
return joystickDevice.getProductId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVendorId(InputDevice joystickDevice) {
|
||||
return joystickDevice.getVendorId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
|
||||
// For compatibility, keep computing the axis mask like before,
|
||||
// only really distinguishing 2, 4 and 6 axes.
|
||||
int axis_mask = 0;
|
||||
if (ranges.size() >= 2) {
|
||||
// ((1 << SDL_GAMEPAD_AXIS_LEFTX) | (1 << SDL_GAMEPAD_AXIS_LEFTY))
|
||||
axis_mask |= 0x0003;
|
||||
}
|
||||
if (ranges.size() >= 4) {
|
||||
// ((1 << SDL_GAMEPAD_AXIS_RIGHTX) | (1 << SDL_GAMEPAD_AXIS_RIGHTY))
|
||||
axis_mask |= 0x000c;
|
||||
}
|
||||
if (ranges.size() >= 6) {
|
||||
// ((1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER))
|
||||
axis_mask |= 0x0030;
|
||||
}
|
||||
// Also add an indicator bit for whether the sorting order has changed.
|
||||
// This serves to disable outdated gamecontrollerdb.txt mappings.
|
||||
boolean have_z = false;
|
||||
boolean have_past_z_before_rz = false;
|
||||
for (InputDevice.MotionRange range : ranges) {
|
||||
int axis = range.getAxis();
|
||||
if (axis == MotionEvent.AXIS_Z) {
|
||||
have_z = true;
|
||||
} else if (axis > MotionEvent.AXIS_Z && axis < MotionEvent.AXIS_RZ) {
|
||||
have_past_z_before_rz = true;
|
||||
}
|
||||
}
|
||||
if (have_z && have_past_z_before_rz) {
|
||||
// If both these exist, the compare() function changed sorting order.
|
||||
// Set a bit to indicate this fact.
|
||||
axis_mask |= 0x8000;
|
||||
}
|
||||
return axis_mask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getButtonMask(InputDevice joystickDevice) {
|
||||
int button_mask = 0;
|
||||
int[] keys = new int[] {
|
||||
KeyEvent.KEYCODE_BUTTON_A,
|
||||
KeyEvent.KEYCODE_BUTTON_B,
|
||||
KeyEvent.KEYCODE_BUTTON_X,
|
||||
KeyEvent.KEYCODE_BUTTON_Y,
|
||||
KeyEvent.KEYCODE_BACK,
|
||||
KeyEvent.KEYCODE_MENU,
|
||||
KeyEvent.KEYCODE_BUTTON_MODE,
|
||||
KeyEvent.KEYCODE_BUTTON_START,
|
||||
KeyEvent.KEYCODE_BUTTON_THUMBL,
|
||||
KeyEvent.KEYCODE_BUTTON_THUMBR,
|
||||
KeyEvent.KEYCODE_BUTTON_L1,
|
||||
KeyEvent.KEYCODE_BUTTON_R1,
|
||||
KeyEvent.KEYCODE_DPAD_UP,
|
||||
KeyEvent.KEYCODE_DPAD_DOWN,
|
||||
KeyEvent.KEYCODE_DPAD_LEFT,
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||
KeyEvent.KEYCODE_BUTTON_SELECT,
|
||||
KeyEvent.KEYCODE_DPAD_CENTER,
|
||||
|
||||
// These don't map into any SDL controller buttons directly
|
||||
KeyEvent.KEYCODE_BUTTON_L2,
|
||||
KeyEvent.KEYCODE_BUTTON_R2,
|
||||
KeyEvent.KEYCODE_BUTTON_C,
|
||||
KeyEvent.KEYCODE_BUTTON_Z,
|
||||
KeyEvent.KEYCODE_BUTTON_1,
|
||||
KeyEvent.KEYCODE_BUTTON_2,
|
||||
KeyEvent.KEYCODE_BUTTON_3,
|
||||
KeyEvent.KEYCODE_BUTTON_4,
|
||||
KeyEvent.KEYCODE_BUTTON_5,
|
||||
KeyEvent.KEYCODE_BUTTON_6,
|
||||
KeyEvent.KEYCODE_BUTTON_7,
|
||||
KeyEvent.KEYCODE_BUTTON_8,
|
||||
KeyEvent.KEYCODE_BUTTON_9,
|
||||
KeyEvent.KEYCODE_BUTTON_10,
|
||||
KeyEvent.KEYCODE_BUTTON_11,
|
||||
KeyEvent.KEYCODE_BUTTON_12,
|
||||
KeyEvent.KEYCODE_BUTTON_13,
|
||||
KeyEvent.KEYCODE_BUTTON_14,
|
||||
KeyEvent.KEYCODE_BUTTON_15,
|
||||
KeyEvent.KEYCODE_BUTTON_16,
|
||||
};
|
||||
int[] masks = new int[] {
|
||||
(1 << 0), // A -> A
|
||||
(1 << 1), // B -> B
|
||||
(1 << 2), // X -> X
|
||||
(1 << 3), // Y -> Y
|
||||
(1 << 4), // BACK -> BACK
|
||||
(1 << 6), // MENU -> START
|
||||
(1 << 5), // MODE -> GUIDE
|
||||
(1 << 6), // START -> START
|
||||
(1 << 7), // THUMBL -> LEFTSTICK
|
||||
(1 << 8), // THUMBR -> RIGHTSTICK
|
||||
(1 << 9), // L1 -> LEFTSHOULDER
|
||||
(1 << 10), // R1 -> RIGHTSHOULDER
|
||||
(1 << 11), // DPAD_UP -> DPAD_UP
|
||||
(1 << 12), // DPAD_DOWN -> DPAD_DOWN
|
||||
(1 << 13), // DPAD_LEFT -> DPAD_LEFT
|
||||
(1 << 14), // DPAD_RIGHT -> DPAD_RIGHT
|
||||
(1 << 4), // SELECT -> BACK
|
||||
(1 << 0), // DPAD_CENTER -> A
|
||||
(1 << 15), // L2 -> ??
|
||||
(1 << 16), // R2 -> ??
|
||||
(1 << 17), // C -> ??
|
||||
(1 << 18), // Z -> ??
|
||||
(1 << 20), // 1 -> ??
|
||||
(1 << 21), // 2 -> ??
|
||||
(1 << 22), // 3 -> ??
|
||||
(1 << 23), // 4 -> ??
|
||||
(1 << 24), // 5 -> ??
|
||||
(1 << 25), // 6 -> ??
|
||||
(1 << 26), // 7 -> ??
|
||||
(1 << 27), // 8 -> ??
|
||||
(1 << 28), // 9 -> ??
|
||||
(1 << 29), // 10 -> ??
|
||||
(1 << 30), // 11 -> ??
|
||||
(1 << 31), // 12 -> ??
|
||||
// We're out of room...
|
||||
0xFFFFFFFF, // 13 -> ??
|
||||
0xFFFFFFFF, // 14 -> ??
|
||||
0xFFFFFFFF, // 15 -> ??
|
||||
0xFFFFFFFF, // 16 -> ??
|
||||
};
|
||||
boolean[] has_keys = joystickDevice.hasKeys(keys);
|
||||
for (int i = 0; i < keys.length; ++i) {
|
||||
if (has_keys[i]) {
|
||||
button_mask |= masks[i];
|
||||
}
|
||||
}
|
||||
return button_mask;
|
||||
}
|
||||
}
|
||||
|
||||
class SDLHapticHandler_API26 extends SDLHapticHandler {
|
||||
@Override
|
||||
public void run(int device_id, float intensity, int length) {
|
||||
SDLHaptic haptic = getHaptic(device_id);
|
||||
if (haptic != null) {
|
||||
Log.d("SDL", "Rtest: Vibe with intensity " + intensity + " for " + length);
|
||||
if (intensity == 0.0f) {
|
||||
stop(device_id);
|
||||
return;
|
||||
}
|
||||
|
||||
int vibeValue = Math.round(intensity * 255);
|
||||
|
||||
if (vibeValue > 255) {
|
||||
vibeValue = 255;
|
||||
}
|
||||
if (vibeValue < 1) {
|
||||
stop(device_id);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue));
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
|
||||
// something went horribly wrong with the Android 8.0 APIs.
|
||||
haptic.vib.vibrate(length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SDLHapticHandler {
|
||||
|
||||
static class SDLHaptic {
|
||||
public int device_id;
|
||||
public String name;
|
||||
public Vibrator vib;
|
||||
}
|
||||
|
||||
private final ArrayList<SDLHaptic> mHaptics;
|
||||
|
||||
public SDLHapticHandler() {
|
||||
mHaptics = new ArrayList<SDLHaptic>();
|
||||
}
|
||||
|
||||
public void run(int device_id, float intensity, int length) {
|
||||
SDLHaptic haptic = getHaptic(device_id);
|
||||
if (haptic != null) {
|
||||
haptic.vib.vibrate(length);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop(int device_id) {
|
||||
SDLHaptic haptic = getHaptic(device_id);
|
||||
if (haptic != null) {
|
||||
haptic.vib.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public void pollHapticDevices() {
|
||||
|
||||
final int deviceId_VIBRATOR_SERVICE = 999999;
|
||||
boolean hasVibratorService = false;
|
||||
|
||||
int[] deviceIds = InputDevice.getDeviceIds();
|
||||
// It helps processing the device ids in reverse order
|
||||
// For example, in the case of the XBox 360 wireless dongle,
|
||||
// so the first controller seen by SDL matches what the receiver
|
||||
// considers to be the first controller
|
||||
|
||||
for (int i = deviceIds.length - 1; i > -1; i--) {
|
||||
SDLHaptic haptic = getHaptic(deviceIds[i]);
|
||||
if (haptic == null) {
|
||||
InputDevice device = InputDevice.getDevice(deviceIds[i]);
|
||||
Vibrator vib = device.getVibrator();
|
||||
if (vib != null) {
|
||||
if (vib.hasVibrator()) {
|
||||
haptic = new SDLHaptic();
|
||||
haptic.device_id = deviceIds[i];
|
||||
haptic.name = device.getName();
|
||||
haptic.vib = vib;
|
||||
mHaptics.add(haptic);
|
||||
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check VIBRATOR_SERVICE */
|
||||
Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
if (vib != null) {
|
||||
hasVibratorService = vib.hasVibrator();
|
||||
|
||||
if (hasVibratorService) {
|
||||
SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
|
||||
if (haptic == null) {
|
||||
haptic = new SDLHaptic();
|
||||
haptic.device_id = deviceId_VIBRATOR_SERVICE;
|
||||
haptic.name = "VIBRATOR_SERVICE";
|
||||
haptic.vib = vib;
|
||||
mHaptics.add(haptic);
|
||||
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check removed devices */
|
||||
ArrayList<Integer> removedDevices = null;
|
||||
for (SDLHaptic haptic : mHaptics) {
|
||||
int device_id = haptic.device_id;
|
||||
int i;
|
||||
for (i = 0; i < deviceIds.length; i++) {
|
||||
if (device_id == deviceIds[i]) break;
|
||||
}
|
||||
|
||||
if (device_id != deviceId_VIBRATOR_SERVICE || !hasVibratorService) {
|
||||
if (i == deviceIds.length) {
|
||||
if (removedDevices == null) {
|
||||
removedDevices = new ArrayList<Integer>();
|
||||
}
|
||||
removedDevices.add(device_id);
|
||||
}
|
||||
} // else: don't remove the vibrator if it is still present
|
||||
}
|
||||
|
||||
if (removedDevices != null) {
|
||||
for (int device_id : removedDevices) {
|
||||
SDLControllerManager.nativeRemoveHaptic(device_id);
|
||||
for (int i = 0; i < mHaptics.size(); i++) {
|
||||
if (mHaptics.get(i).device_id == device_id) {
|
||||
mHaptics.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected SDLHaptic getHaptic(int device_id) {
|
||||
for (SDLHaptic haptic : mHaptics) {
|
||||
if (haptic.device_id == device_id) {
|
||||
return haptic;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
|
||||
// Generic Motion (mouse hover, joystick...) events go here
|
||||
@Override
|
||||
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||
float x, y;
|
||||
int action;
|
||||
|
||||
switch ( event.getSource() ) {
|
||||
case InputDevice.SOURCE_JOYSTICK:
|
||||
return SDLControllerManager.handleJoystickMotionEvent(event);
|
||||
|
||||
case InputDevice.SOURCE_MOUSE:
|
||||
action = event.getActionMasked();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Event was not managed
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean supportsRelativeMouse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean inRelativeMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean setRelativeMouseEnabled(boolean enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void reclaimRelativeMouseModeIfNeeded()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public float getEventX(MotionEvent event) {
|
||||
return event.getX(0);
|
||||
}
|
||||
|
||||
public float getEventY(MotionEvent event) {
|
||||
return event.getY(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 {
|
||||
// Generic Motion (mouse hover, joystick...) events go here
|
||||
|
||||
private boolean mRelativeModeEnabled;
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||
|
||||
// Handle relative mouse mode
|
||||
if (mRelativeModeEnabled) {
|
||||
if (event.getSource() == InputDevice.SOURCE_MOUSE) {
|
||||
int action = event.getActionMasked();
|
||||
if (action == MotionEvent.ACTION_HOVER_MOVE) {
|
||||
float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
|
||||
float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Event was not managed, call SDLGenericMotionListener_API12 method
|
||||
return super.onGenericMotion(v, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRelativeMouse() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inRelativeMode() {
|
||||
return mRelativeModeEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setRelativeMouseEnabled(boolean enabled) {
|
||||
mRelativeModeEnabled = enabled;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getEventX(MotionEvent event) {
|
||||
if (mRelativeModeEnabled) {
|
||||
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
|
||||
} else {
|
||||
return event.getX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getEventY(MotionEvent event) {
|
||||
if (mRelativeModeEnabled) {
|
||||
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
|
||||
} else {
|
||||
return event.getY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
|
||||
// Generic Motion (mouse hover, joystick...) events go here
|
||||
private boolean mRelativeModeEnabled;
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||
float x, y;
|
||||
int action;
|
||||
|
||||
switch ( event.getSource() ) {
|
||||
case InputDevice.SOURCE_JOYSTICK:
|
||||
return SDLControllerManager.handleJoystickMotionEvent(event);
|
||||
|
||||
case InputDevice.SOURCE_MOUSE:
|
||||
// DeX desktop mouse cursor is a separate non-standard input type.
|
||||
case InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN:
|
||||
action = event.getActionMasked();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case InputDevice.SOURCE_MOUSE_RELATIVE:
|
||||
action = event.getActionMasked();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Event was not managed
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRelativeMouse() {
|
||||
return (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inRelativeMode() {
|
||||
return mRelativeModeEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setRelativeMouseEnabled(boolean enabled) {
|
||||
if (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */) {
|
||||
if (enabled) {
|
||||
SDLActivity.getContentView().requestPointerCapture();
|
||||
} else {
|
||||
SDLActivity.getContentView().releasePointerCapture();
|
||||
}
|
||||
mRelativeModeEnabled = enabled;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reclaimRelativeMouseModeIfNeeded()
|
||||
{
|
||||
if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
|
||||
SDLActivity.getContentView().requestPointerCapture();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getEventX(MotionEvent event) {
|
||||
// Relative mouse in capture mode will only have relative for X/Y
|
||||
return event.getX(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getEventY(MotionEvent event) {
|
||||
// Relative mouse in capture mode will only have relative for X/Y
|
||||
return event.getY(0);
|
||||
}
|
||||
}
|
||||
@@ -1,405 +0,0 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
|
||||
/**
|
||||
SDLSurface. This is what we draw on, so we need to know when it's created
|
||||
in order to do anything useful.
|
||||
|
||||
Because of this, that's where we set up the SDL thread
|
||||
*/
|
||||
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
|
||||
|
||||
// Sensors
|
||||
protected SensorManager mSensorManager;
|
||||
protected Display mDisplay;
|
||||
|
||||
// Keep track of the surface size to normalize touch events
|
||||
protected float mWidth, mHeight;
|
||||
|
||||
// Is SurfaceView ready for rendering
|
||||
public boolean mIsSurfaceReady;
|
||||
|
||||
// Startup
|
||||
public SDLSurface(Context context) {
|
||||
super(context);
|
||||
getHolder().addCallback(this);
|
||||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
|
||||
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
setOnGenericMotionListener(SDLActivity.getMotionListener());
|
||||
|
||||
// Some arbitrary defaults to avoid a potential division by zero
|
||||
mWidth = 1.0f;
|
||||
mHeight = 1.0f;
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
}
|
||||
|
||||
public void handlePause() {
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
|
||||
}
|
||||
|
||||
public void handleResume() {
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
|
||||
}
|
||||
|
||||
public Surface getNativeSurface() {
|
||||
return getHolder().getSurface();
|
||||
}
|
||||
|
||||
// Called when we have a valid drawing surface
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceCreated()");
|
||||
SDLActivity.onNativeSurfaceCreated();
|
||||
}
|
||||
|
||||
// Called when we lose the surface
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceDestroyed()");
|
||||
|
||||
// Transition to pause, if needed
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
|
||||
SDLActivity.handleNativeState();
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
SDLActivity.onNativeSurfaceDestroyed();
|
||||
}
|
||||
|
||||
// Called when the surface is resized
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder,
|
||||
int format, int width, int height) {
|
||||
Log.v("SDL", "surfaceChanged()");
|
||||
|
||||
if (SDLActivity.mSingleton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
int nDeviceWidth = width;
|
||||
int nDeviceHeight = height;
|
||||
try
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= 17 /* Android 4.2 (JELLY_BEAN_MR1) */) {
|
||||
DisplayMetrics realMetrics = new DisplayMetrics();
|
||||
mDisplay.getRealMetrics( realMetrics );
|
||||
nDeviceWidth = realMetrics.widthPixels;
|
||||
nDeviceHeight = realMetrics.heightPixels;
|
||||
}
|
||||
} catch(Exception ignored) {
|
||||
}
|
||||
|
||||
synchronized(SDLActivity.getContext()) {
|
||||
// In case we're waiting on a size change after going fullscreen, send a notification.
|
||||
SDLActivity.getContext().notifyAll();
|
||||
}
|
||||
|
||||
Log.v("SDL", "Window size: " + width + "x" + height);
|
||||
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
|
||||
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
|
||||
SDLActivity.onNativeResize();
|
||||
|
||||
// Prevent a screen distortion glitch,
|
||||
// for instance when the device is in Landscape and a Portrait App is resumed.
|
||||
boolean skip = false;
|
||||
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
|
||||
|
||||
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
|
||||
if (mWidth > mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
|
||||
if (mWidth < mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Special Patch for Square Resolution: Black Berry Passport
|
||||
if (skip) {
|
||||
double min = Math.min(mWidth, mHeight);
|
||||
double max = Math.max(mWidth, mHeight);
|
||||
|
||||
if (max / min < 1.20) {
|
||||
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't skip in MultiWindow.
|
||||
if (skip) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
|
||||
Log.v("SDL", "Don't skip in Multi-Window");
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
Log.v("SDL", "Skip .. Surface is not ready.");
|
||||
mIsSurfaceReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
|
||||
SDLActivity.onNativeSurfaceChanged();
|
||||
|
||||
/* Surface is ready */
|
||||
mIsSurfaceReady = true;
|
||||
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
|
||||
SDLActivity.handleNativeState();
|
||||
}
|
||||
|
||||
// Key events
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
|
||||
}
|
||||
|
||||
// Touch events
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
/* Ref: http://developer.android.com/training/gestures/multi.html */
|
||||
int touchDevId = event.getDeviceId();
|
||||
final int pointerCount = event.getPointerCount();
|
||||
int action = event.getActionMasked();
|
||||
int pointerFingerId;
|
||||
int i = -1;
|
||||
float x,y,p;
|
||||
|
||||
/*
|
||||
* Prevent id to be -1, since it's used in SDL internal for synthetic events
|
||||
* Appears when using Android emulator, eg:
|
||||
* adb shell input mouse tap 100 100
|
||||
* adb shell input touchscreen tap 100 100
|
||||
*/
|
||||
if (touchDevId < 0) {
|
||||
touchDevId -= 1;
|
||||
}
|
||||
|
||||
// 12290 = Samsung DeX mode desktop mouse
|
||||
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
|
||||
// 0x2 = SOURCE_CLASS_POINTER
|
||||
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
|
||||
int mouseButton = 1;
|
||||
try {
|
||||
Object object = event.getClass().getMethod("getButtonState").invoke(event);
|
||||
if (object != null) {
|
||||
mouseButton = (Integer) object;
|
||||
}
|
||||
} catch(Exception ignored) {
|
||||
}
|
||||
|
||||
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
|
||||
// if we are. We'll leverage our existing mouse motion listener
|
||||
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
|
||||
x = motionListener.getEventX(event);
|
||||
y = motionListener.getEventY(event);
|
||||
|
||||
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
|
||||
} else {
|
||||
switch(action) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
for (i = 0; i < pointerCount; i++) {
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// Primary pointer up/down, the index is always zero
|
||||
i = 0;
|
||||
/* fallthrough */
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
// Non primary pointer up/down
|
||||
if (i == -1) {
|
||||
i = event.getActionIndex();
|
||||
}
|
||||
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
for (i = 0; i < pointerCount; i++) {
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sensor events
|
||||
public void enableSensor(int sensortype, boolean enabled) {
|
||||
// TODO: This uses getDefaultSensor - what if we have >1 accels?
|
||||
if (enabled) {
|
||||
mSensorManager.registerListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype),
|
||||
SensorManager.SENSOR_DELAY_GAME, null);
|
||||
} else {
|
||||
mSensorManager.unregisterListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||
|
||||
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
|
||||
// We thus should check here.
|
||||
int newOrientation;
|
||||
|
||||
float x, y;
|
||||
switch (mDisplay.getRotation()) {
|
||||
case Surface.ROTATION_90:
|
||||
x = -event.values[1];
|
||||
y = event.values[0];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
x = event.values[1];
|
||||
y = -event.values[0];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
x = -event.values[0];
|
||||
y = -event.values[1];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
|
||||
break;
|
||||
case Surface.ROTATION_0:
|
||||
default:
|
||||
x = event.values[0];
|
||||
y = event.values[1];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newOrientation != SDLActivity.mCurrentOrientation) {
|
||||
SDLActivity.mCurrentOrientation = newOrientation;
|
||||
SDLActivity.onNativeOrientationChanged(newOrientation);
|
||||
}
|
||||
|
||||
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
|
||||
y / SensorManager.GRAVITY_EARTH,
|
||||
event.values[2] / SensorManager.GRAVITY_EARTH);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Captured pointer events for API 26.
|
||||
public boolean onCapturedPointerEvent(MotionEvent event)
|
||||
{
|
||||
int action = event.getActionMasked();
|
||||
|
||||
float x, y;
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||
|
||||
// Change our action value to what SDL's code expects.
|
||||
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
|
||||
action = MotionEvent.ACTION_DOWN;
|
||||
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
|
||||
action = MotionEvent.ACTION_UP;
|
||||
}
|
||||
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
int button = event.getButtonState();
|
||||
|
||||
SDLActivity.onNativeMouse(button, action, x, y, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
#cmakedefine PACKAGE "@PACKAGE@"
|
||||
#define VERSION "@XSYSTEM35_VERSION@"
|
||||
#ifndef XSYSTEM35_SDL_VERSION
|
||||
#define XSYSTEM35_SDL_VERSION @XSYSTEM35_SDL_VERSION@
|
||||
#endif
|
||||
|
||||
#cmakedefine ENABLE_DEBUGGER @ENABLE_DEBUGGER@
|
||||
#cmakedefine ENABLE_MIDI_SDLMIXER @ENABLE_MIDI_SDLMIXER@
|
||||
|
||||
@@ -84,7 +84,7 @@ endif()
|
||||
|
||||
target_compile_options(modules PRIVATE -Wno-pointer-sign)
|
||||
|
||||
target_link_libraries(modules PRIVATE sdl2 zlib)
|
||||
if (TARGET sdl2_mixer)
|
||||
target_link_libraries(modules PRIVATE sdl2_mixer)
|
||||
target_link_libraries(modules PRIVATE xsystem35_sdl zlib)
|
||||
if (TARGET xsystem35_sdl_mixer)
|
||||
target_link_libraries(modules PRIVATE xsystem35_sdl_mixer)
|
||||
endif()
|
||||
|
||||
+9
-8
@@ -3,7 +3,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
@@ -76,12 +76,13 @@ void gr_copy(surface_t *dst, int dx, int dy, surface_t *src, int sx, int sy, int
|
||||
SDL_Rect r;
|
||||
if (SDL_IntersectRect(&src_rect, &dst_rect, &r)) {
|
||||
SDL_Surface *view = gfx_createSurfaceView(src->sdl_surface, sx, sy, sw, sh);
|
||||
SDL_Surface *tmp = SDL_ConvertSurface(view, dst->sdl_surface->format, 0);
|
||||
SDL_Surface *tmp = sdl_convert_surface(
|
||||
view, sdl_surface_format(dst->sdl_surface));
|
||||
src_rect.x = 0;
|
||||
src_rect.y = 0;
|
||||
SDL_LowerBlit(tmp, &src_rect, dst->sdl_surface, &dst_rect);
|
||||
SDL_FreeSurface(tmp);
|
||||
SDL_FreeSurface(view);
|
||||
sdl_destroy_surface(tmp);
|
||||
sdl_destroy_surface(view);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -99,7 +100,7 @@ void gr_copy_bright(surface_t *dst, int dx, int dy, surface_t *src, int sx, int
|
||||
|
||||
void gr_fill(surface_t *dst, int dx, int dy, int dw, int dh, int r, int g, int b) {
|
||||
SDL_Rect rect = {dx, dy, dw, dh};
|
||||
uint32_t color = SDL_MapRGB(dst->sdl_surface->format, r, g, b);
|
||||
uint32_t color = sdl_map_rgb(dst->sdl_surface, r, g, b);
|
||||
SDL_FillRect(dst->sdl_surface, &rect, color);
|
||||
}
|
||||
|
||||
@@ -227,12 +228,12 @@ void gr_copy_stretch_blend_alpha_map(surface_t *dst, int dx, int dy, int dw, int
|
||||
|
||||
// Fill the rectangle with the specified color (rgb) and blend rate (lv)
|
||||
void gr_fill_alpha_color(surface_t *dst, int dx, int dy, int dw, int dh, int r, int g, int b, int lv) {
|
||||
SDL_Surface *src = SDL_CreateRGBSurfaceWithFormat(0, dw, dh, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_FillRect(src, NULL, SDL_MapRGBA(src->format, r, g, b, lv));
|
||||
SDL_Surface *src = sdl_create_surface(dw, dh, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_FillRect(src, NULL, sdl_map_rgba(src, r, g, b, lv));
|
||||
SDL_SetSurfaceBlendMode(src, SDL_BLENDMODE_BLEND);
|
||||
SDL_Rect dstrect = {dx, dy, dw, dh};
|
||||
SDL_BlitSurface(src, NULL, dst->sdl_surface, &dstrect);
|
||||
SDL_FreeSurface(src);
|
||||
sdl_destroy_surface(src);
|
||||
}
|
||||
|
||||
#include "graph2.c"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "surface.h"
|
||||
@@ -15,7 +15,8 @@ static surface_t *create(int width, int height, bool has_pixel, bool has_alpha)
|
||||
s->height = height;
|
||||
|
||||
if (has_pixel) {
|
||||
s->sdl_surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_RGB888);
|
||||
s->sdl_surface = sdl_create_surface(
|
||||
width, height, 32, SDL_PIXELFORMAT_RGB888);
|
||||
}
|
||||
|
||||
if (has_alpha) {
|
||||
@@ -47,7 +48,7 @@ surface_t *sf_create_pixel(int width, int height) {
|
||||
*/
|
||||
void sf_free(surface_t *s) {
|
||||
if (s == NULL) return;
|
||||
if (s->sdl_surface) SDL_FreeSurface(s->sdl_surface);
|
||||
if (s->sdl_surface) sdl_destroy_surface(s->sdl_surface);
|
||||
if (s->alpha) free(s->alpha);
|
||||
free(s);
|
||||
}
|
||||
@@ -67,7 +68,8 @@ surface_t *sf_dup(surface_t *in) {
|
||||
memcpy(sf, in, sizeof(surface_t));
|
||||
|
||||
if (in->sdl_surface) {
|
||||
sf->sdl_surface = SDL_ConvertSurface(in->sdl_surface, in->sdl_surface->format, 0);
|
||||
sf->sdl_surface = sdl_convert_surface(
|
||||
in->sdl_surface, sdl_surface_format(in->sdl_surface));
|
||||
}
|
||||
|
||||
if (in->alpha) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <stdio.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
@@ -194,5 +194,5 @@ void nt_gr_screencg(int no, int x, int y) {
|
||||
SDL_SetSurfaceBlendMode(sf, SDL_BLENDMODE_ADD);
|
||||
SDL_BlitSurface(sf, NULL, main_surface, &(SDL_Rect){x, y, sf->w, sf->h});
|
||||
ags_updateArea(x, y, sf->w, sf->h);
|
||||
SDL_FreeSurface(sf);
|
||||
sdl_destroy_surface(sf);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
@@ -142,7 +142,7 @@ void ntmsg_set_frame(int type) {
|
||||
nt_sp_set_show(night.sp[SPNO_MSGFRAME_BG], true);
|
||||
nt_sp_set_show(night.sp[SPNO_MSGFRAME_FG], true);
|
||||
|
||||
SDL_FillRect(sf, &msgframe_0, SDL_MapRGBA(sf->format, 16, 32, 64, 192));
|
||||
SDL_FillRect(sf, &msgframe_0, sdl_map_rgba(sf, 16, 32, 64, 192));
|
||||
ntmsg_clear(SPNO_MSGFRAME_FG);
|
||||
nt_sp_update_all(true);
|
||||
break;
|
||||
@@ -151,7 +151,7 @@ void ntmsg_set_frame(int type) {
|
||||
nt_sp_set_show(night.sp[SPNO_MSGFRAME_BG], false);
|
||||
nt_sp_set_show(night.sp[SPNO_MSGFRAME_FG], true);
|
||||
|
||||
SDL_FillRect(sf, &msgframe_1, SDL_MapRGBA(sf->format, 32, 32, 32, 128));
|
||||
SDL_FillRect(sf, &msgframe_1, sdl_map_rgba(sf, 32, 32, 32, 128));
|
||||
ntmsg_clear(SPNO_MSGFRAME_FG);
|
||||
nt_sp_update_all(true);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "sprite.h"
|
||||
@@ -62,7 +62,8 @@ sprite_t *nt_sp_msg_new(int no, int x, int y, int width, int height) {
|
||||
sp->height = height;
|
||||
sp->u.msg.dspcur.x = 0;
|
||||
sp->u.msg.dspcur.y = 0;
|
||||
sp->u.msg.canvas = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
sp->u.msg.canvas = sdl_create_surface(
|
||||
width, height, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
sp->update = ntmsg_update;
|
||||
|
||||
return sp;
|
||||
@@ -76,7 +77,7 @@ void nt_sp_free(sprite_t *sp) {
|
||||
if (sp->cg3) scg_deref(sp->cg3);
|
||||
|
||||
if (sp->type == SPRITE_MSG) {
|
||||
SDL_FreeSurface(sp->u.msg.canvas);
|
||||
sdl_destroy_surface(sp->u.msg.canvas);
|
||||
}
|
||||
|
||||
free(sp);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#define __SPRITE_H__
|
||||
|
||||
#include "portab.h"
|
||||
#include <SDL_rect.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "sactcg.h"
|
||||
|
||||
#define DEFAULT_UPDATE nt_sp_draw
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
@@ -55,7 +55,7 @@ void nt_sp_draw(sprite_t *sp, SDL_Rect *r) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SDL_ISPIXELFORMAT_ALPHA(cg->sf->format->format) || sp->blendrate < 255) {
|
||||
if (SDL_ISPIXELFORMAT_ALPHA(sdl_surface_format(cg->sf)) || sp->blendrate < 255) {
|
||||
SDL_SetSurfaceBlendMode(cg->sf, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetSurfaceAlphaMod(cg->sf, sp->blendrate);
|
||||
} else {
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
@@ -204,5 +204,5 @@ void nt_sp_clear_updatelist(void) {
|
||||
|
||||
// デフォルトの壁紙update
|
||||
void nt_sp_draw_wall(sprite_t *sp, SDL_Rect *area) {
|
||||
SDL_FillRect(main_surface, area, SDL_MapRGB(main_surface->format, 0, 0, 0));
|
||||
SDL_FillRect(main_surface, area, sdl_map_rgb(main_surface, 0, 0, 0));
|
||||
}
|
||||
|
||||
@@ -60,10 +60,11 @@ static void ndd_run(int demonum) {
|
||||
|
||||
cgdata *cg = jpeg_extract(alk->entries[i].data, alk->entries[i].size);
|
||||
if (cg) {
|
||||
SDL_Surface *sf = SDL_CreateRGBSurfaceWithFormatFrom(cg->pic, cg->width, cg->height, 24, cg->width * 3, SDL_PIXELFORMAT_RGB24);
|
||||
SDL_Surface *sf = sdl_create_surface_from(cg->pic, cg->width,
|
||||
cg->height, 24, cg->width * 3, SDL_PIXELFORMAT_RGB24);
|
||||
SDL_BlitSurface(sf, NULL, main_surface, NULL);
|
||||
ags_updateFull();
|
||||
SDL_FreeSurface(sf);
|
||||
sdl_destroy_surface(sf);
|
||||
cgdata_free(cg);
|
||||
} else {
|
||||
WARNING("Cannot decode CG %d in %s", i, alk_path);
|
||||
|
||||
+4
-3
@@ -25,7 +25,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
@@ -147,7 +147,8 @@ static void Init() {
|
||||
}
|
||||
|
||||
// create depth map
|
||||
sact.dmap = SDL_CreateRGBSurface(0, main_surface->w, main_surface->h, 16, 0, 0, 0, 0);
|
||||
sact.dmap = sdl_create_surface_with_masks(
|
||||
main_surface->w, main_surface->h, 16, 0, 0, 0, 0);
|
||||
|
||||
// その他 System35 のデフォルト動作の変更
|
||||
ags_setAntialiasedStringMode(true);
|
||||
@@ -2154,7 +2155,7 @@ static void SACT_reset(void) {
|
||||
sp_reset();
|
||||
|
||||
scg_freeall();
|
||||
SDL_FreeSurface(sact.dmap);
|
||||
sdl_destroy_surface(sact.dmap);
|
||||
memset(&sact, 0, sizeof(sact));
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
#define __SACT_H__
|
||||
|
||||
#include "config.h"
|
||||
#include <SDL_rect.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "portab.h"
|
||||
#include "list.h"
|
||||
#include "ags.h"
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
@@ -90,13 +90,14 @@ void sp_eupdate_amap(int index, int time, int cancel) {
|
||||
sp_update_all(true);
|
||||
return;
|
||||
}
|
||||
SDL_Surface *mask_sf = SDL_CreateRGBSurfaceFrom(mask->pic, mask->width, mask->height, 8, mask->width, 0, 0, 0, 0);
|
||||
SDL_Surface *mask_sf = sdl_create_surface_from(mask->pic, mask->width,
|
||||
mask->height, 8, mask->width, SDL_PIXELFORMAT_INDEX8);
|
||||
sp_update_all(false); // old = gfx_texture, new = main_surface
|
||||
struct effect *eff = effect_sactamask_init(mask_sf);
|
||||
ags_runEffect(time * 10, cancel, (ags_EffectStepFunc)effect_step, eff);
|
||||
effect_finish(eff);
|
||||
ags_updateFull();
|
||||
|
||||
SDL_FreeSurface(mask_sf);
|
||||
sdl_destroy_surface(mask_sf);
|
||||
cgdata_free(mask);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
@@ -73,8 +73,9 @@ static void draw_log() {
|
||||
load_mincho_font();
|
||||
#endif
|
||||
|
||||
SDL_Surface *hline = SDL_CreateRGBSurfaceWithFormat(0, main_surface->w, 3, 32, SDL_PIXELFORMAT_RGB888);
|
||||
SDL_FillRect(hline, NULL, SDL_MapRGB(hline->format, 255, 255, 255));
|
||||
SDL_Surface *hline = sdl_create_surface(
|
||||
main_surface->w, 3, 32, SDL_PIXELFORMAT_RGB888);
|
||||
SDL_FillRect(hline, NULL, sdl_map_rgb(hline, 255, 255, 255));
|
||||
SDL_SetSurfaceBlendMode(hline, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetSurfaceAlphaMod(hline, 128);
|
||||
|
||||
@@ -122,7 +123,7 @@ bool sblog_start(void) {
|
||||
for (int i = 0; i < LOGMSG_LINES; i++)
|
||||
sact.log = list_append(sact.log, logmsg[i]);
|
||||
|
||||
back = SDL_ConvertSurface(main_surface, main_surface->format, 0);
|
||||
back = sdl_convert_surface(main_surface, sdl_surface_format(main_surface));
|
||||
curline = 6;
|
||||
draw_log();
|
||||
return true;
|
||||
@@ -135,7 +136,7 @@ void sblog_end(void) {
|
||||
SDL_BlitSurface(back, NULL, main_surface, NULL);
|
||||
ags_updateFull();
|
||||
|
||||
SDL_FreeSurface(back);
|
||||
sdl_destroy_surface(back);
|
||||
|
||||
// Remove instruction text
|
||||
for (i = 0; i < LOGMSG_LINES; i++) {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
@@ -65,7 +65,8 @@ static int compare_spriteno_smallfirst(const void *a, const void *b) {
|
||||
|
||||
// デフォルトの壁紙update
|
||||
static void sp_draw_wall(sprite_t *sp) {
|
||||
SDL_FillRect(main_surface, &sact.updaterect, SDL_MapRGB(main_surface->format, 0, 0, 0));
|
||||
SDL_FillRect(main_surface, &sact.updaterect,
|
||||
sdl_map_rgb(main_surface, 0, 0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,7 +208,8 @@ void sp_new_msg(int no, int x, int y, int width, int height) {
|
||||
sp->u.msg.buf = NULL;
|
||||
|
||||
// canvas for drawing text
|
||||
sp->u.msg.canvas = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
sp->u.msg.canvas = sdl_create_surface(
|
||||
width, height, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
|
||||
// スプライト再描画用コールバック
|
||||
sp->update = smsg_update;
|
||||
@@ -279,7 +281,7 @@ void sp_free(int no) {
|
||||
|
||||
if (sp->type == SPRITE_MSG) {
|
||||
slist_free(sp->u.msg.buf);
|
||||
SDL_FreeSurface(sp->u.msg.canvas);
|
||||
sdl_destroy_surface(sp->u.msg.canvas);
|
||||
}
|
||||
sact.updatelist = slist_remove(sact.updatelist, sp);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
@@ -38,7 +38,7 @@
|
||||
static void fill_dmap_mask(SDL_Surface *src, int sx, int sy, int dx ,int dy, int w, int h, uint16_t val) {
|
||||
uint8_t *sp, *dp;
|
||||
int x, y;
|
||||
assert(src->format->Amask == 0xff000000);
|
||||
assert(sdl_surface_alpha_mask(src) == 0xff000000);
|
||||
dp = PIXEL_AT(sact.dmap, dx, dy);
|
||||
sp = ALPHA_AT(src, sx, sy);
|
||||
|
||||
@@ -75,7 +75,7 @@ void sp_draw(sprite_t *sp) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SDL_ISPIXELFORMAT_ALPHA(cg->sf->format->format) || sp->blendrate < 255) {
|
||||
if (SDL_ISPIXELFORMAT_ALPHA(sdl_surface_format(cg->sf)) || sp->blendrate < 255) {
|
||||
SDL_SetSurfaceBlendMode(cg->sf, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetSurfaceAlphaMod(cg->sf, sp->blendrate);
|
||||
} else {
|
||||
@@ -116,7 +116,7 @@ void sp_draw_dmap(void* data, void* userdata) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SDL_ISPIXELFORMAT_ALPHA(cg->sf->format->format)) {
|
||||
if (SDL_ISPIXELFORMAT_ALPHA(sdl_surface_format(cg->sf))) {
|
||||
fill_dmap_mask(cg->sf, sx, sy, dx, dy, w, h, sp->no);
|
||||
} else {
|
||||
SDL_FillRect(sact.dmap, &(SDL_Rect){dx, dy, w, h}, sp->no);
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "nact.h"
|
||||
@@ -140,14 +140,14 @@ static void cb_select_release(agsevent_t *e) {
|
||||
}
|
||||
|
||||
static void draw_box(SDL_Surface *dst, int x, int y, int w, int h) {
|
||||
SDL_FillRect(dst, &(SDL_Rect){x, y, w, h}, SDL_MapRGB(dst->format, 0, 0, 0));
|
||||
SDL_FillRect(dst, &(SDL_Rect){x, y, w, h}, sdl_map_rgb(dst, 0, 0, 0));
|
||||
SDL_Rect border_rects[4] = {
|
||||
{x, y, w, 1},
|
||||
{x, y + h - 1, w, 1},
|
||||
{x, y, 1, h},
|
||||
{x + w - 1, y, 1, h}
|
||||
};
|
||||
SDL_FillRects(dst, border_rects, 4, SDL_MapRGB(dst->format, 255, 255, 255));
|
||||
SDL_FillRects(dst, border_rects, 4, sdl_map_rgb(dst, 255, 255, 255));
|
||||
}
|
||||
|
||||
// 選択ウィンドを更新するときの callback
|
||||
@@ -180,7 +180,8 @@ static void setup_selwindow() {
|
||||
int i;
|
||||
|
||||
// 選択肢文字用 canvas
|
||||
sact.sel.charcanvas = SDL_CreateRGBSurfaceWithFormat(0, sp->cg1->sf->w, sp->cg1->sf->h, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
sact.sel.charcanvas = sdl_create_surface(
|
||||
sp->cg1->sf->w, sp->cg1->sf->h, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_SetSurfaceBlendMode(sact.sel.charcanvas, SDL_BLENDMODE_BLEND);
|
||||
|
||||
dt_setfont(sact.sel.font_type, sact.sel.font_size);
|
||||
@@ -228,7 +229,7 @@ static void remove_selwindow() {
|
||||
|
||||
// 作業用 surface の削除
|
||||
if (sact.sel.charcanvas)
|
||||
SDL_FreeSurface(sact.sel.charcanvas);
|
||||
sdl_destroy_surface(sact.sel.charcanvas);
|
||||
sact.sel.charcanvas = NULL;
|
||||
}
|
||||
|
||||
@@ -266,7 +267,7 @@ void ssel_init() {
|
||||
void ssel_reset(void) {
|
||||
ssel_clear();
|
||||
if (sact.sel.charcanvas) {
|
||||
SDL_FreeSurface(sact.sel.charcanvas);
|
||||
sdl_destroy_surface(sact.sel.charcanvas);
|
||||
sact.sel.charcanvas = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,11 +88,11 @@ static vmvar_t *add_p5[SLOT]; /* どこまでアニメーションのコマが
|
||||
|
||||
static void copy_sprite(int sx, int sy, int width, int height, int dx, int dy, int r, int g, int b) {
|
||||
SDL_Surface *sf = nact->ags.dib->sdl_surface;
|
||||
SDL_SetColorKey(sf, SDL_TRUE, SDL_MapRGB(sf->format, r, g, b));
|
||||
sdl_set_surface_color_key(sf, true, sdl_map_rgb(sf, r, g, b));
|
||||
SDL_Rect src_rect = { sx, sy, width, height };
|
||||
SDL_Rect dst_rect = { dx, dy, width, height };
|
||||
SDL_BlitSurface(sf, &src_rect, sf, &dst_rect);
|
||||
SDL_SetColorKey(sf, SDL_FALSE, 0);
|
||||
sdl_set_surface_color_key(sf, false, 0);
|
||||
}
|
||||
|
||||
static void Init() {
|
||||
|
||||
@@ -25,8 +25,9 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "LittleEndian.h"
|
||||
@@ -44,7 +45,7 @@ static struct {
|
||||
} memwav;
|
||||
|
||||
static void free_memory_wav(void) {
|
||||
SDL_FreeWAV(memwav.buf);
|
||||
sdl_free_wav(memwav.buf);
|
||||
memwav.buf = NULL;
|
||||
}
|
||||
|
||||
@@ -132,11 +133,15 @@ static void wavLoadMemory() {
|
||||
WARNING("cannot open WAVE %d", no - 1);
|
||||
return;
|
||||
}
|
||||
if (!SDL_LoadWAV_RW(SDL_RWFromConstMem(dfile->data, dfile->size), 1, &memwav.spec, &memwav.buf, &memwav.len)) {
|
||||
sdl_iostream_t *stream = sdl_io_from_const_memory(
|
||||
dfile->data, dfile->size);
|
||||
if (!stream || !sdl_load_wav_io(
|
||||
stream, true, &memwav.spec, &memwav.buf, &memwav.len)) {
|
||||
WARNING("cannot load WAVE %d", no - 1);
|
||||
return;
|
||||
}
|
||||
if (memwav.spec.channels != 2 || memwav.spec.format != AUDIO_S16LSB) {
|
||||
if (memwav.spec.channels != 2 ||
|
||||
memwav.spec.format != sdl_audio_s16le_format()) {
|
||||
WARNING("unexpected audio format");
|
||||
free_memory_wav();
|
||||
return;
|
||||
|
||||
+59
-53
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
@@ -31,7 +31,7 @@ static void render_quad(SDL_Texture *tex, SDL_Vertex verts[4]) {
|
||||
static void fade_overlay(Uint8 r, Uint8 g, Uint8 b, Uint8 alpha) {
|
||||
SDL_SetRenderDrawBlendMode(gfx_renderer, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(gfx_renderer, r, g, b, alpha);
|
||||
SDL_RenderFillRect(gfx_renderer, NULL);
|
||||
sdl_render_fill_rect(gfx_renderer, NULL);
|
||||
}
|
||||
|
||||
#define NR_SCENES 13
|
||||
@@ -121,7 +121,7 @@ static void Init() {
|
||||
continue;
|
||||
}
|
||||
dd.images[i] = SDL_CreateTextureFromSurface(gfx_renderer, sf);
|
||||
SDL_FreeSurface(sf);
|
||||
sdl_destroy_surface(sf);
|
||||
}
|
||||
*var = 1;
|
||||
TRACE("dDemo.Init %d,%d,%d,%p:", p1, p2, p3, var);
|
||||
@@ -144,7 +144,7 @@ static void render_affine_bg(SDL_Texture *scroll_bg, int elapsed, int duration,
|
||||
if (!scroll_bg) return;
|
||||
|
||||
int bg_w, bg_h;
|
||||
SDL_QueryTexture(scroll_bg, NULL, NULL, &bg_w, &bg_h);
|
||||
sdl_get_texture_size(scroll_bg, &bg_w, &bg_h);
|
||||
|
||||
int xs[4], ys[4];
|
||||
const float angle = angle_deg * (float)M_PI / 180.0f;
|
||||
@@ -183,7 +183,7 @@ static void render_affine_bg(SDL_Texture *scroll_bg, int elapsed, int duration,
|
||||
for (int i = 0; i < 4; i++) {
|
||||
verts[i].position.x = (i == 1 || i == 2) ? w : 0;
|
||||
verts[i].position.y = (i == 2 || i == 3) ? h : (float)y_toggle;
|
||||
verts[i].color = (SDL_Color){255, 255, 255, 255};
|
||||
verts[i].color = sdl_vertex_color(255, 255, 255, 255);
|
||||
verts[i].tex_coord.x = uv[i][0];
|
||||
verts[i].tex_coord.y = uv[i][1];
|
||||
}
|
||||
@@ -194,7 +194,7 @@ static void render_affine_bg(SDL_Texture *scroll_bg, int elapsed, int duration,
|
||||
y_toggle = (y_toggle + 1) & 1;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
verts[i].position.y = (i == 2 || i == 3) ? h : (float)y_toggle;
|
||||
verts[i].color = (SDL_Color){255, 255, 255, 128};
|
||||
verts[i].color = sdl_vertex_color(255, 255, 255, 128);
|
||||
}
|
||||
// Swap mapping: TL<->BR, TR<->BL for 180° rotation effect
|
||||
verts[0].tex_coord.x = uv[2][0]; verts[0].tex_coord.y = uv[2][1];
|
||||
@@ -212,7 +212,7 @@ static void render_scene0(int elapsed) {
|
||||
if (!sprite) return;
|
||||
|
||||
int spr_w, spr_h;
|
||||
SDL_QueryTexture(sprite, NULL, NULL, &spr_w, &spr_h);
|
||||
sdl_get_texture_size(sprite, &spr_w, &spr_h);
|
||||
const int slide_end = 4700;
|
||||
const int duration = scene_duration[0]; // 5400
|
||||
|
||||
@@ -234,12 +234,12 @@ static void render_scene0(int elapsed) {
|
||||
int dest_w = a0 - ((a0 - spr_w) * elapsed) / slide_end;
|
||||
if (dest_w <= 0) continue;
|
||||
SDL_Rect dst = {view_w - 2 * dest_w, dest_y, dest_w, spr_h};
|
||||
SDL_RenderCopy(gfx_renderer, sprite, NULL, &dst);
|
||||
sdl_render_texture(gfx_renderer, sprite, NULL, &dst);
|
||||
}
|
||||
} else {
|
||||
SDL_SetTextureAlphaMod(sprite, 0xd0);
|
||||
SDL_Rect dst = {view_w - 2 * spr_w, dest_y, spr_w, spr_h};
|
||||
SDL_RenderCopy(gfx_renderer, sprite, NULL, &dst);
|
||||
sdl_render_texture(gfx_renderer, sprite, NULL, &dst);
|
||||
}
|
||||
|
||||
// 3. Horizontal accent line (grows rightward over 4700ms)
|
||||
@@ -247,12 +247,12 @@ static void render_scene0(int elapsed) {
|
||||
SDL_SetRenderDrawBlendMode(gfx_renderer, SDL_BLENDMODE_NONE);
|
||||
SDL_SetRenderDrawColor(gfx_renderer, 0xc0, 0xc0, 0xc0, 0xff);
|
||||
SDL_Rect hline = {0, (spr_h + view_h) / 2 - 4, line_w, 2};
|
||||
SDL_RenderFillRect(gfx_renderer, &hline);
|
||||
sdl_render_fill_rect(gfx_renderer, &hline);
|
||||
|
||||
// 4. Vertical accent line (right edge of sprite, grows downward)
|
||||
int line_h = elapsed < slide_end ? 479 * elapsed / slide_end + 1 : 480;
|
||||
SDL_Rect vline = {view_w - spr_w, 0, 2, line_h};
|
||||
SDL_RenderFillRect(gfx_renderer, &vline);
|
||||
sdl_render_fill_rect(gfx_renderer, &vline);
|
||||
}
|
||||
|
||||
static void render_scene1(int elapsed) {
|
||||
@@ -261,7 +261,7 @@ static void render_scene1(int elapsed) {
|
||||
if (!sprite) return;
|
||||
|
||||
int spr_w, spr_h;
|
||||
SDL_QueryTexture(sprite, NULL, NULL, &spr_w, &spr_h);
|
||||
sdl_get_texture_size(sprite, &spr_w, &spr_h);
|
||||
const int duration = scene_duration[1]; // 4752
|
||||
|
||||
// --- Background: DrawAffineSurfaceAlpha50 x2 (alpha=50%) ---
|
||||
@@ -275,7 +275,7 @@ static void render_scene1(int elapsed) {
|
||||
SDL_SetTextureBlendMode(sprite, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(sprite, (Uint8)alpha);
|
||||
SDL_Rect dst = {(view_w - spr_w) / 2, (view_h - spr_h) / 2, spr_w, spr_h};
|
||||
SDL_RenderCopy(gfx_renderer, sprite, NULL, &dst);
|
||||
sdl_render_texture(gfx_renderer, sprite, NULL, &dst);
|
||||
}
|
||||
|
||||
// Fade to black: graph_FillAlphaColor(0,0,0), starts at 4000ms.
|
||||
@@ -397,12 +397,14 @@ static void render_scene_2_4_6_3d(int elapsed, int phase_dur, SDL_Texture *tex1,
|
||||
// Object border quad: solid color RGB(244,221,181) = BGR(0xb5,0xdd,0xf4),
|
||||
// drawn first as a backdrop. The bg quad drawn on top leaves only the thin
|
||||
// border ring visible.
|
||||
for (int i = 0; i < 4; i++) obj_verts[i].color = (SDL_Color){0xf4, 0xdd, 0xb5, 0xff};
|
||||
for (int i = 0; i < 4; i++)
|
||||
obj_verts[i].color = sdl_vertex_color(0xf4, 0xdd, 0xb5, 0xff);
|
||||
SDL_SetRenderDrawBlendMode(gfx_renderer, SDL_BLENDMODE_NONE);
|
||||
render_quad(NULL, obj_verts);
|
||||
|
||||
// Background quad: textured, opaque, drawn on top (covers center of border quad)
|
||||
for (int i = 0; i < 4; i++) bg_verts[i].color = (SDL_Color){255, 255, 255, 255};
|
||||
for (int i = 0; i < 4; i++)
|
||||
bg_verts[i].color = sdl_vertex_color(255, 255, 255, 255);
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_NONE);
|
||||
render_quad(tex, bg_verts);
|
||||
}
|
||||
@@ -425,7 +427,7 @@ static void render_scene_2_4_6(int elapsed, int scene_idx, int sprite_idx, int t
|
||||
if (bg) {
|
||||
SDL_SetTextureBlendMode(bg, SDL_BLENDMODE_NONE);
|
||||
SDL_SetTextureColorMod(bg, 0xff, 0xff, 0xff);
|
||||
SDL_RenderCopy(gfx_renderer, bg, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, bg, NULL, NULL);
|
||||
}
|
||||
|
||||
// 2. 3D rotating quad animation (8-phase timing)
|
||||
@@ -439,23 +441,23 @@ static void render_scene_2_4_6(int elapsed, int scene_idx, int sprite_idx, int t
|
||||
// 3. Scrolling sprite: x moves from -sprite_w (off-screen left) to view_w (off-screen right)
|
||||
int sprite_w = 0, sprite_h = 0;
|
||||
if (sprite)
|
||||
SDL_QueryTexture(sprite, NULL, NULL, &sprite_w, &sprite_h);
|
||||
sdl_get_texture_size(sprite, &sprite_w, &sprite_h);
|
||||
int x = sprite_w ? ((sprite_w + view_w) * elapsed) / duration - sprite_w : 0;
|
||||
|
||||
// Shadow drawn first (color-modded to black), sprite on top (full color)
|
||||
if (sprite_shadow) {
|
||||
int sw, sh;
|
||||
SDL_QueryTexture(sprite_shadow, NULL, NULL, &sw, &sh);
|
||||
sdl_get_texture_size(sprite_shadow, &sw, &sh);
|
||||
SDL_SetTextureBlendMode(sprite_shadow, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureColorMod(sprite_shadow, 0, 0, 0);
|
||||
SDL_Rect dst = {x, 0, sw, sh};
|
||||
SDL_RenderCopy(gfx_renderer, sprite_shadow, NULL, &dst);
|
||||
sdl_render_texture(gfx_renderer, sprite_shadow, NULL, &dst);
|
||||
}
|
||||
if (sprite) {
|
||||
SDL_SetTextureBlendMode(sprite, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureColorMod(sprite, 0xff, 0xff, 0xff);
|
||||
SDL_Rect dst = {x, 0, sprite_w, sprite_h};
|
||||
SDL_RenderCopy(gfx_renderer, sprite, NULL, &dst);
|
||||
sdl_render_texture(gfx_renderer, sprite, NULL, &dst);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,7 +492,7 @@ static void render_scene_3_5_7_9(int elapsed, int image_idx) {
|
||||
if (alpha > 0) {
|
||||
SDL_SetTextureBlendMode(main_img, alpha < 255 ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
|
||||
SDL_SetTextureAlphaMod(main_img, (Uint8)alpha);
|
||||
SDL_RenderCopy(gfx_renderer, main_img, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, main_img, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,10 +502,14 @@ static void render_scene_3_5_7_9(int elapsed, int image_idx) {
|
||||
float t3 = (float)elapsed / (4000.0f * 3.0f); // 0 → 1/3
|
||||
float w = (float)view_w, h = (float)view_h;
|
||||
SDL_Vertex verts[4] = {
|
||||
/* TL */ {{0.f, 0.f}, {255, 255, 255, 128}, {t3, 0.f }},
|
||||
/* TR */ {{w, 0.f}, {255, 255, 255, 128}, {1.0f, t3 }},
|
||||
/* BR */ {{w, h }, {255, 255, 255, 128}, {1.0f - t3, 1.0f }},
|
||||
/* BL */ {{0.f, h }, {255, 255, 255, 128}, {0.f, 1.0f-t3}},
|
||||
{{0.f, 0.f}, sdl_vertex_color(255, 255, 255, 128),
|
||||
{t3, 0.f}},
|
||||
{{w, 0.f}, sdl_vertex_color(255, 255, 255, 128),
|
||||
{1.0f, t3}},
|
||||
{{w, h}, sdl_vertex_color(255, 255, 255, 128),
|
||||
{1.0f - t3, 1.0f}},
|
||||
{{0.f, h}, sdl_vertex_color(255, 255, 255, 128),
|
||||
{0.f, 1.0f - t3}},
|
||||
};
|
||||
SDL_SetTextureBlendMode(overlay, SDL_BLENDMODE_BLEND);
|
||||
render_quad(overlay, verts);
|
||||
@@ -527,7 +533,7 @@ static void render_scene8(int elapsed) {
|
||||
if (imgs[0]) {
|
||||
SDL_SetTextureBlendMode(imgs[0], SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(imgs[0], (Uint8)SDL_min(alpha, 255));
|
||||
SDL_RenderCopy(gfx_renderer, imgs[0], NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, imgs[0], NULL, NULL);
|
||||
}
|
||||
} else if (elapsed < 6 * seg) {
|
||||
// Segments 2–6: crossfade image[idx] → image[idx+1]
|
||||
@@ -536,19 +542,19 @@ static void render_scene8(int elapsed) {
|
||||
if (imgs[idx]) {
|
||||
SDL_SetTextureBlendMode(imgs[idx], SDL_BLENDMODE_NONE);
|
||||
SDL_SetTextureAlphaMod(imgs[idx], 255);
|
||||
SDL_RenderCopy(gfx_renderer, imgs[idx], NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, imgs[idx], NULL, NULL);
|
||||
}
|
||||
if (imgs[idx + 1]) {
|
||||
SDL_SetTextureBlendMode(imgs[idx + 1], SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(imgs[idx + 1], (Uint8)SDL_min(alpha, 255));
|
||||
SDL_RenderCopy(gfx_renderer, imgs[idx + 1], NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, imgs[idx + 1], NULL, NULL);
|
||||
}
|
||||
} else {
|
||||
// Segment 7: hold image[5] at full opacity
|
||||
if (imgs[5]) {
|
||||
SDL_SetTextureBlendMode(imgs[5], SDL_BLENDMODE_NONE);
|
||||
SDL_SetTextureAlphaMod(imgs[5], 255);
|
||||
SDL_RenderCopy(gfx_renderer, imgs[5], NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, imgs[5], NULL, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -588,7 +594,7 @@ static void render_scene10(int elapsed) {
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_NONE);
|
||||
SDL_SetTextureAlphaMod(tex, 255);
|
||||
SDL_SetTextureColorMod(tex, 255, 255, 255);
|
||||
SDL_RenderCopy(gfx_renderer, tex, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, tex, NULL, NULL);
|
||||
}
|
||||
} else if (elapsed < 9610) {
|
||||
// Phase 2: zoom in
|
||||
@@ -607,7 +613,7 @@ static void render_scene10(int elapsed) {
|
||||
SDL_SetTextureBlendMode(surface3, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(surface3, 255);
|
||||
SDL_SetTextureColorMod(surface3, 255, 255, 255);
|
||||
SDL_RenderCopy(gfx_renderer, surface3, &src_rect, NULL);
|
||||
sdl_render_texture(gfx_renderer, surface3, &src_rect, NULL);
|
||||
}
|
||||
} else {
|
||||
// Phase 3: fade out to black
|
||||
@@ -618,7 +624,7 @@ static void render_scene10(int elapsed) {
|
||||
combined < 255 ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
|
||||
SDL_SetTextureAlphaMod(surface3, (Uint8)combined);
|
||||
SDL_SetTextureColorMod(surface3, 255, 255, 255);
|
||||
SDL_RenderCopy(gfx_renderer, surface3, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, surface3, NULL, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -644,12 +650,12 @@ static void render_scene11(int elapsed) {
|
||||
// srcY = (bg_h - view_h) * (duration - elapsed) / duration
|
||||
if (bg) {
|
||||
int bg_w, bg_h;
|
||||
SDL_QueryTexture(bg, NULL, NULL, &bg_w, &bg_h);
|
||||
sdl_get_texture_size(bg, &bg_w, &bg_h);
|
||||
int scroll = (int)((long long)(bg_h - view_h) * elapsed / duration);
|
||||
int src_y = SDL_max(0, (bg_h - view_h) - scroll);
|
||||
SDL_Rect src = {0, src_y, SDL_min(view_w, bg_w), view_h};
|
||||
SDL_SetTextureBlendMode(bg, SDL_BLENDMODE_NONE);
|
||||
SDL_RenderCopy(gfx_renderer, bg, &src, NULL);
|
||||
sdl_render_texture(gfx_renderer, bg, &src, NULL);
|
||||
}
|
||||
|
||||
if (elapsed < unit) {
|
||||
@@ -659,12 +665,12 @@ static void render_scene11(int elapsed) {
|
||||
SDL_Texture *tex = slide[2];
|
||||
if (tex) {
|
||||
int tw, th;
|
||||
SDL_QueryTexture(tex, NULL, NULL, &tw, &th);
|
||||
sdl_get_texture_size(tex, &tw, &th);
|
||||
int y = (int)((long long)(th + view_h) * (elapsed - unit) / unit) - th;
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(tex, 0x80);
|
||||
SDL_Rect dst = {(view_w - tw) / 2, y, tw, th};
|
||||
SDL_RenderCopy(gfx_renderer, tex, NULL, &dst);
|
||||
sdl_render_texture(gfx_renderer, tex, NULL, &dst);
|
||||
}
|
||||
} else if (elapsed < unit * 3) {
|
||||
// Segment 2: overlay[0] (ALK 69) pingpong fade
|
||||
@@ -675,19 +681,19 @@ static void render_scene11(int elapsed) {
|
||||
int alpha = (t2 * 255) / (unit / 2);
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(tex, (Uint8)SDL_min(alpha, 255));
|
||||
SDL_RenderCopy(gfx_renderer, tex, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, tex, NULL, NULL);
|
||||
}
|
||||
} else if (elapsed < unit * 4) {
|
||||
// Segment 3: slide[1] (ALK 62) passes top-to-bottom at 50% alpha
|
||||
SDL_Texture *tex = slide[1];
|
||||
if (tex) {
|
||||
int tw, th;
|
||||
SDL_QueryTexture(tex, NULL, NULL, &tw, &th);
|
||||
sdl_get_texture_size(tex, &tw, &th);
|
||||
int y = (int)((long long)(th + view_h) * (elapsed - unit * 3) / unit) - th;
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(tex, 0x80);
|
||||
SDL_Rect dst = {(view_w - tw) / 2, y, tw, th};
|
||||
SDL_RenderCopy(gfx_renderer, tex, NULL, &dst);
|
||||
sdl_render_texture(gfx_renderer, tex, NULL, &dst);
|
||||
}
|
||||
} else if (elapsed < unit * 5) {
|
||||
// Segment 4: overlay[1] (ALK 70) pingpong fade
|
||||
@@ -698,19 +704,19 @@ static void render_scene11(int elapsed) {
|
||||
int alpha = (t2 * 255) / (unit / 2);
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(tex, (Uint8)SDL_min(alpha, 255));
|
||||
SDL_RenderCopy(gfx_renderer, tex, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, tex, NULL, NULL);
|
||||
}
|
||||
} else if (elapsed < unit * 6) {
|
||||
// Segment 5: slide[0] (ALK 61) passes top-to-bottom at 50% alpha
|
||||
SDL_Texture *tex = slide[0];
|
||||
if (tex) {
|
||||
int tw, th;
|
||||
SDL_QueryTexture(tex, NULL, NULL, &tw, &th);
|
||||
sdl_get_texture_size(tex, &tw, &th);
|
||||
int y = (int)((long long)(th + view_h) * (elapsed - unit * 5) / unit) - th;
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(tex, 0x80);
|
||||
SDL_Rect dst = {(view_w - tw) / 2, y, tw, th};
|
||||
SDL_RenderCopy(gfx_renderer, tex, NULL, &dst);
|
||||
sdl_render_texture(gfx_renderer, tex, NULL, &dst);
|
||||
}
|
||||
} else if (elapsed < unit * 7) {
|
||||
// Segment 6: overlay[2] (ALK 71) pingpong fade
|
||||
@@ -721,7 +727,7 @@ static void render_scene11(int elapsed) {
|
||||
int alpha = (t2 * 255) / (unit / 2);
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(tex, (Uint8)SDL_min(alpha, 255));
|
||||
SDL_RenderCopy(gfx_renderer, tex, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, tex, NULL, NULL);
|
||||
}
|
||||
} else {
|
||||
// Segment 7: white fade out
|
||||
@@ -799,7 +805,7 @@ static void render_scene12_3d(int elapsed) {
|
||||
// NDC → screen
|
||||
verts[i].position.x = (wx * PROJ_SCALE_X / w + 1.0f) * half_w;
|
||||
verts[i].position.y = (wy * PROJ_SCALE_Y / w + 1.0f) * half_h;
|
||||
verts[i].color = (SDL_Color){255, 255, 255, 255};
|
||||
verts[i].color = sdl_vertex_color(255, 255, 255, 255);
|
||||
verts[i].tex_coord.x = muv[i][0];
|
||||
verts[i].tex_coord.y = muv[i][1];
|
||||
}
|
||||
@@ -817,7 +823,7 @@ static void render_scene12(int elapsed) {
|
||||
// 0–2076ms: bg_a with white fade-in overlay
|
||||
if (bg_a) {
|
||||
SDL_SetTextureBlendMode(bg_a, SDL_BLENDMODE_NONE);
|
||||
SDL_RenderCopy(gfx_renderer, bg_a, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, bg_a, NULL, NULL);
|
||||
}
|
||||
int white_alpha = 0xff - (elapsed * 0xff) / 2076;
|
||||
fade_overlay(0xff, 0xff, 0xff, (Uint8)white_alpha);
|
||||
@@ -825,32 +831,32 @@ static void render_scene12(int elapsed) {
|
||||
// 2076–2226ms: bg_a static
|
||||
if (bg_a) {
|
||||
SDL_SetTextureBlendMode(bg_a, SDL_BLENDMODE_NONE);
|
||||
SDL_RenderCopy(gfx_renderer, bg_a, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, bg_a, NULL, NULL);
|
||||
}
|
||||
} else if (elapsed < 3826) {
|
||||
// 2226–3826ms: crossfade bg_a → bg_b
|
||||
if (bg_a) {
|
||||
SDL_SetTextureBlendMode(bg_a, SDL_BLENDMODE_NONE);
|
||||
SDL_RenderCopy(gfx_renderer, bg_a, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, bg_a, NULL, NULL);
|
||||
}
|
||||
if (bg_b) {
|
||||
int alpha = ((elapsed - 2226) * 0xff) / 1600;
|
||||
SDL_SetTextureBlendMode(bg_b, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(bg_b, (Uint8)SDL_min(alpha, 0xff));
|
||||
SDL_RenderCopy(gfx_renderer, bg_b, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, bg_b, NULL, NULL);
|
||||
}
|
||||
} else if (elapsed < 3976) {
|
||||
// 3826–3976ms: bg_b static
|
||||
if (bg_b) {
|
||||
SDL_SetTextureBlendMode(bg_b, SDL_BLENDMODE_NONE);
|
||||
SDL_RenderCopy(gfx_renderer, bg_b, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, bg_b, NULL, NULL);
|
||||
}
|
||||
} else if (elapsed < 7803) {
|
||||
// 3976–7803ms: TODO wave distortion on bg_b; for now just show bg_b,
|
||||
// then fade to white
|
||||
if (bg_b) {
|
||||
SDL_SetTextureBlendMode(bg_b, SDL_BLENDMODE_BLEND);
|
||||
SDL_RenderCopy(gfx_renderer, bg_b, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, bg_b, NULL, NULL);
|
||||
}
|
||||
int white_alpha = ((elapsed - 3976) * 0xff) / 3827;
|
||||
fade_overlay(0xff, 0xff, 0xff, (Uint8)SDL_min(white_alpha, 0xff));
|
||||
@@ -858,19 +864,19 @@ static void render_scene12(int elapsed) {
|
||||
// 7803–9397ms: white bg + 3D rotating quad + overlay fade-in
|
||||
SDL_SetRenderDrawBlendMode(gfx_renderer, SDL_BLENDMODE_NONE);
|
||||
SDL_SetRenderDrawColor(gfx_renderer, 0xff, 0xff, 0xff, 0xff);
|
||||
SDL_RenderFillRect(gfx_renderer, NULL);
|
||||
sdl_render_fill_rect(gfx_renderer, NULL);
|
||||
render_scene12_3d(elapsed);
|
||||
if (overlay) {
|
||||
int alpha = ((elapsed - 7803) * 0xff) / 1594;
|
||||
SDL_SetTextureBlendMode(overlay, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(overlay, (Uint8)SDL_min(alpha, 0xff));
|
||||
SDL_RenderCopy(gfx_renderer, overlay, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, overlay, NULL, NULL);
|
||||
}
|
||||
} else {
|
||||
// 9397–10338ms: overlay static
|
||||
if (overlay) {
|
||||
SDL_SetTextureBlendMode(overlay, SDL_BLENDMODE_NONE);
|
||||
SDL_RenderCopy(gfx_renderer, overlay, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, overlay, NULL, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,6 @@ int dt_drawtext_col(SDL_Surface *sf, int x, int y, char *buf, int r, int g, int
|
||||
SDL_Rect rect = {x, y, glyph->w, glyph->h};
|
||||
SDL_SetSurfaceBlendMode(glyph, SDL_BLENDMODE_NONE);
|
||||
SDL_BlitSurface(glyph, NULL, sf, &rect);
|
||||
SDL_FreeSurface(glyph);
|
||||
sdl_destroy_surface(glyph);
|
||||
return rect.w;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ static void gre_BlendUseAMap(surface_t *dst, int dx, int dy, surface_t *src, int
|
||||
uint8_t *dp = GETOFFSET_PIXEL(dst, dx, dy);
|
||||
uint8_t *ap = GETOFFSET_ALPHA(src, sx, sy);
|
||||
|
||||
switch (dst->sdl_surface->format->BitsPerPixel) {
|
||||
switch (sdl_surface_bits_per_pixel(dst->sdl_surface)) {
|
||||
case 16:
|
||||
for (int y = 0; y < height; y++) {
|
||||
uint16_t *yls = (uint16_t *)(sp + y * src->sdl_surface->pitch);
|
||||
@@ -55,4 +55,3 @@ void gr_blend_alpha_map(surface_t *dst, int dx, int dy, surface_t *src, int sx,
|
||||
|
||||
gre_BlendUseAMap(dst, dx, dy, src, sx, sy, sw, sh);
|
||||
}
|
||||
|
||||
|
||||
+27
-19
@@ -27,7 +27,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "nact.h"
|
||||
@@ -103,7 +103,7 @@ void scg_deref(cginfo_t *cg) {
|
||||
return;
|
||||
|
||||
if (cg->sf)
|
||||
SDL_FreeSurface(cg->sf);
|
||||
sdl_destroy_surface(cg->sf);
|
||||
free(cg);
|
||||
}
|
||||
|
||||
@@ -111,8 +111,9 @@ void scg_deref(cginfo_t *cg) {
|
||||
void scg_create(int wNumCG, int wWidth, int wHeight, int wR, int wG, int wB, int wBlendRate) {
|
||||
SPCG_ASSERT_NO(wNumCG);
|
||||
|
||||
SDL_Surface *sf = SDL_CreateRGBSurfaceWithFormat(0, wWidth, wHeight, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_FillRect(sf, NULL, SDL_MapRGBA(sf->format, wR, wG, wB, wBlendRate));
|
||||
SDL_Surface *sf = sdl_create_surface(
|
||||
wWidth, wHeight, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_FillRect(sf, NULL, sdl_map_rgba(sf, wR, wG, wB, wBlendRate));
|
||||
scg_new(CG_SET, wNumCG, sf);
|
||||
}
|
||||
|
||||
@@ -125,7 +126,7 @@ void scg_create_reverse(int wNumCG, int wNumSrcCG, int wReverseX, int wReverseY)
|
||||
if (!src)
|
||||
return;
|
||||
|
||||
SDL_Surface *sf = SDL_ConvertSurface(src->sf, src->sf->format, 0);
|
||||
SDL_Surface *sf = sdl_convert_surface(src->sf, sdl_surface_format(src->sf));
|
||||
if (wReverseX)
|
||||
gfx_FlipSurfaceHorizontal(sf);
|
||||
if (wReverseY)
|
||||
@@ -142,15 +143,17 @@ void scg_create_stretch(int wNumCG, int wWidth, int wHeight, int wNumSrcCG) {
|
||||
if (!src)
|
||||
return;
|
||||
|
||||
SDL_Surface *sf = SDL_CreateRGBSurfaceWithFormat(0, wWidth, wHeight, src->sf->format->BitsPerPixel, src->sf->format->format);
|
||||
SDL_Surface *sf = sdl_create_surface(wWidth, wHeight,
|
||||
sdl_surface_bits_per_pixel(src->sf), sdl_surface_format(src->sf));
|
||||
SDL_BlitScaled(src->sf, NULL, sf, NULL);
|
||||
scg_new(CG_STRETCH, wNumCG, sf);
|
||||
}
|
||||
|
||||
static SDL_Surface *blend(SDL_Surface *base, int x, int y, SDL_Surface *blend, int mode) {
|
||||
assert(blend->format->format == SDL_PIXELFORMAT_ARGB8888 || blend->format->format == SDL_PIXELFORMAT_XRGB8888);
|
||||
bool src_has_alpha = SDL_ISPIXELFORMAT_ALPHA(blend->format->format);
|
||||
SDL_Surface *dst = SDL_ConvertSurfaceFormat(base, SDL_PIXELFORMAT_ARGB8888, 0);
|
||||
assert(sdl_surface_format(blend) == SDL_PIXELFORMAT_ARGB8888 ||
|
||||
sdl_surface_format(blend) == SDL_PIXELFORMAT_XRGB8888);
|
||||
bool src_has_alpha = SDL_ISPIXELFORMAT_ALPHA(sdl_surface_format(blend));
|
||||
SDL_Surface *dst = sdl_convert_surface(base, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_Rect rect = {x, y, blend->w, blend->h};
|
||||
SDL_IntersectRect(&(SDL_Rect){0, 0, dst->w, dst->h}, &rect, &rect);
|
||||
|
||||
@@ -223,10 +226,11 @@ void scg_create_text(int wNumCG, int wSize, int wR, int wG, int wB, int wText) {
|
||||
FontSpec spec = { .type = FONT_GOTHIC, .weight = FONT_WEIGHT_BOLD, .size = wSize };
|
||||
SDL_Surface *glyph = ags_drawStringToSurface(svar_get(wText), wR, wG, wB, spec);
|
||||
|
||||
SDL_Surface *sf = SDL_CreateRGBSurfaceWithFormat(0, glyph->w, wSize, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_Surface *sf = sdl_create_surface(
|
||||
glyph->w, wSize, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_SetSurfaceBlendMode(glyph, SDL_BLENDMODE_NONE);
|
||||
SDL_BlitSurface(glyph, NULL, sf, NULL);
|
||||
SDL_FreeSurface(glyph);
|
||||
sdl_destroy_surface(glyph);
|
||||
|
||||
scg_new(CG_SET, wNumCG, sf);
|
||||
}
|
||||
@@ -248,10 +252,11 @@ void scg_create_textnum(int wNumCG, int wSize, int wR, int wG, int wB, int wFigs
|
||||
FontSpec spec = { .type = FONT_GOTHIC, .weight = FONT_WEIGHT_BOLD, .size = wSize };
|
||||
SDL_Surface *glyph = ags_drawStringToSurface(s, wR, wG, wB, spec);
|
||||
|
||||
SDL_Surface *sf = SDL_CreateRGBSurfaceWithFormat(0, glyph->w, wSize, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_Surface *sf = sdl_create_surface(
|
||||
glyph->w, wSize, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_SetSurfaceBlendMode(glyph, SDL_BLENDMODE_NONE);
|
||||
SDL_BlitSurface(glyph, NULL, sf, NULL);
|
||||
SDL_FreeSurface(glyph);
|
||||
sdl_destroy_surface(glyph);
|
||||
|
||||
scg_new(CG_SET, wNumCG, sf);
|
||||
}
|
||||
@@ -265,7 +270,8 @@ void scg_copy(int wNumDstCG, int wNumSrcCG) {
|
||||
if (!src)
|
||||
return;
|
||||
|
||||
scg_new(CG_SET, wNumDstCG, SDL_ConvertSurface(src->sf, src->sf->format, 0));
|
||||
scg_new(CG_SET, wNumDstCG,
|
||||
sdl_convert_surface(src->sf, sdl_surface_format(src->sf)));
|
||||
}
|
||||
|
||||
// CGの一部を切りぬいたCGを作成
|
||||
@@ -277,7 +283,8 @@ void scg_cut(int wNumDstCG, int wNumSrcCG, int wX, int wY, int wWidth, int wHeig
|
||||
if (!src)
|
||||
return;
|
||||
|
||||
SDL_Surface *sf = SDL_CreateRGBSurfaceWithFormat(0, wWidth, wHeight, src->sf->format->BitsPerPixel, src->sf->format->format);
|
||||
SDL_Surface *sf = sdl_create_surface(wWidth, wHeight,
|
||||
sdl_surface_bits_per_pixel(src->sf), sdl_surface_format(src->sf));
|
||||
SDL_BlitSurface(src->sf, &(SDL_Rect){wX, wY, wWidth, wHeight}, sf, NULL);
|
||||
|
||||
scg_new(CG_SET, wNumDstCG, sf);
|
||||
@@ -292,8 +299,9 @@ void scg_partcopy(int wNumDstCG, int wNumSrcCG, int wX, int wY, int wWidth, int
|
||||
if (!src)
|
||||
return;
|
||||
|
||||
SDL_Surface *sf = SDL_CreateRGBSurfaceWithFormat(0, src->sf->w, src->sf->h, src->sf->format->BitsPerPixel, src->sf->format->format);
|
||||
SDL_FillRect(sf, NULL, SDL_MapRGBA(sf->format, 0, 0, 0, 255));
|
||||
SDL_Surface *sf = sdl_create_surface(src->sf->w, src->sf->h,
|
||||
sdl_surface_bits_per_pixel(src->sf), sdl_surface_format(src->sf));
|
||||
SDL_FillRect(sf, NULL, sdl_map_rgba(sf, 0, 0, 0, 255));
|
||||
SDL_BlitSurface(src->sf, &(SDL_Rect){wX, wY, wWidth, wHeight}, sf, &(SDL_Rect){wX, wY, wWidth, wHeight});
|
||||
|
||||
scg_new(CG_SET, wNumDstCG, sf);
|
||||
@@ -353,7 +361,7 @@ int scg_querybpp(int wNumCG) {
|
||||
if (wNumCG >= (CGMAX -1)) return 0;
|
||||
if (!cg_store || !cg_store[wNumCG]) return 0;
|
||||
if (cg_store[wNumCG]->sf == NULL) return 0;
|
||||
return cg_store[wNumCG]->sf->format->BitsPerPixel;
|
||||
return sdl_surface_bits_per_pixel(cg_store[wNumCG]->sf);
|
||||
}
|
||||
|
||||
// CGの alphamap が存在するかを取得
|
||||
@@ -361,5 +369,5 @@ bool scg_existalphamap(int wNumCG) {
|
||||
if (wNumCG >= (CGMAX -1)) return false;
|
||||
if (!cg_store || !cg_store[wNumCG]) return false;
|
||||
if (cg_store[wNumCG]->sf == NULL) return false;
|
||||
return SDL_ISPIXELFORMAT_ALPHA(cg_store[wNumCG]->sf->format->format);
|
||||
return SDL_ISPIXELFORMAT_ALPHA(sdl_surface_format(cg_store[wNumCG]->sf));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "portab.h"
|
||||
|
||||
@@ -84,10 +84,11 @@ static void Run() {
|
||||
|
||||
cgdata *cg = jpeg_extract(alk->entries[i].data, alk->entries[i].size);
|
||||
if (cg) {
|
||||
SDL_Surface *sf = SDL_CreateRGBSurfaceWithFormatFrom(cg->pic, cg->width, cg->height, 24, cg->width * 3, SDL_PIXELFORMAT_RGB24);
|
||||
SDL_Surface *sf = sdl_create_surface_from(cg->pic, cg->width,
|
||||
cg->height, 24, cg->width * 3, SDL_PIXELFORMAT_RGB24);
|
||||
SDL_BlitSurface(sf, NULL, main_surface, NULL);
|
||||
ags_updateFull();
|
||||
SDL_FreeSurface(sf);
|
||||
sdl_destroy_surface(sf);
|
||||
cgdata_free(cg);
|
||||
} else {
|
||||
WARNING("Cannot decode CG %d", i);
|
||||
|
||||
+25
-6
@@ -105,12 +105,20 @@ target_sources(xsystem35 PRIVATE
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
target_sources(xsystem35 PRIVATE pcm.emscripten.c bgm.emscripten.c)
|
||||
elseif (XSYSTEM35_SDL_VERSION STREQUAL "3")
|
||||
target_sources(xsystem35 PRIVATE
|
||||
pcm.sdl3mixer.c
|
||||
sdl3_mixer_backend.c
|
||||
bgm.sdl3mixer.c)
|
||||
target_compile_definitions(xsystem35 PRIVATE HAVE_SDL3_MIXER_BACKEND)
|
||||
else()
|
||||
target_sources(xsystem35 PRIVATE pcm.sdlmixer.c bgm.sdlmixer.c)
|
||||
endif()
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
target_sources(xsystem35 PRIVATE cdrom.emscripten.c)
|
||||
elseif (XSYSTEM35_SDL_VERSION STREQUAL "3")
|
||||
target_sources(xsystem35 PRIVATE cdrom.sdl3mixer.c)
|
||||
else()
|
||||
target_sources(xsystem35 PRIVATE cdrom.mp3.c)
|
||||
endif()
|
||||
@@ -120,7 +128,11 @@ if (EMSCRIPTEN)
|
||||
elseif (ANDROID)
|
||||
target_sources(xsystem35 PRIVATE midi.android.c)
|
||||
else()
|
||||
target_sources(xsystem35 PRIVATE midi.sdlmixer.c)
|
||||
if (XSYSTEM35_SDL_VERSION STREQUAL "3")
|
||||
target_sources(xsystem35 PRIVATE midi.sdl3mixer.c)
|
||||
else()
|
||||
target_sources(xsystem35 PRIVATE midi.sdlmixer.c)
|
||||
endif()
|
||||
if (ENABLE_MIDI_PORTMIDI)
|
||||
target_sources(xsystem35 PRIVATE midi.portmidi.c midifile.c)
|
||||
endif()
|
||||
@@ -146,10 +158,10 @@ if (NOT HAVE_LIBINTL)
|
||||
target_sources(xsystem35 PRIVATE nls.c)
|
||||
endif()
|
||||
|
||||
target_link_libraries(src_lib PRIVATE sdl2 freetype2 zlib)
|
||||
target_link_libraries(xsystem35 PRIVATE m zlib sdl2)
|
||||
if (TARGET sdl2_mixer)
|
||||
target_link_libraries(xsystem35 PRIVATE sdl2_mixer)
|
||||
target_link_libraries(src_lib PRIVATE xsystem35_sdl freetype2 zlib)
|
||||
target_link_libraries(xsystem35 PRIVATE m zlib xsystem35_sdl)
|
||||
if (TARGET xsystem35_sdl_mixer)
|
||||
target_link_libraries(xsystem35 PRIVATE xsystem35_sdl_mixer)
|
||||
endif()
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
@@ -196,6 +208,12 @@ else() # non-emscripten, non-android
|
||||
endif()
|
||||
if (ENABLE_MIDI_PORTMIDI)
|
||||
target_link_libraries(xsystem35 PRIVATE ${PORTMIDI})
|
||||
if (WIN32)
|
||||
# Other static dependencies can place winmm before PortMidi on the link
|
||||
# line. Rescan the pair so PortMidi's WinMM imports are resolved.
|
||||
target_link_libraries(xsystem35 PRIVATE
|
||||
"-Wl,--start-group,-lportmidi,-lwinmm,--end-group")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(TARGETS xsystem35 RUNTIME DESTINATION bin)
|
||||
@@ -207,12 +225,13 @@ else() # non-emscripten, non-android
|
||||
gameresource_test.c
|
||||
hankaku_test.c
|
||||
qnt_test.c
|
||||
sdl_compat_test.c
|
||||
variable_test.c
|
||||
)
|
||||
target_compile_options(src_tests PRIVATE -Wno-pointer-sign -Wall)
|
||||
target_compile_definitions(src_tests PRIVATE
|
||||
TEST_FONT_DIR="${PROJECT_SOURCE_DIR}/fonts")
|
||||
target_link_libraries(src_tests PRIVATE src_lib sdl2)
|
||||
target_link_libraries(src_tests PRIVATE src_lib xsystem35_sdl)
|
||||
add_test(NAME src_tests COMMAND src_tests)
|
||||
file(COPY testdata DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@
|
||||
#define __LITTLEENDIAN__
|
||||
|
||||
#include <string.h>
|
||||
#include <SDL_endian.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
static inline int LittleEndian_getDW(const uint8_t *b, int index) {
|
||||
uint32_t t;
|
||||
|
||||
@@ -205,7 +205,7 @@ void ags_getDisplayInfo(enum ags_display display, int *width, int *height, int *
|
||||
SDL_DisplayMode dm;
|
||||
switch (display) {
|
||||
case AGS_DISPLAY_CURRENT:
|
||||
SDL_GetCurrentDisplayMode(0, &dm);
|
||||
sdl_get_current_display_mode(&dm);
|
||||
*width = dm.w;
|
||||
*height = dm.h;
|
||||
*depth = SDL_BITSPERPIXEL(dm.format);
|
||||
@@ -216,13 +216,13 @@ void ags_getDisplayInfo(enum ags_display display, int *width, int *height, int *
|
||||
*depth = nact->ags.world_depth;
|
||||
break;
|
||||
case AGS_DISPLAY_VIEW_AREA:
|
||||
SDL_GetCurrentDisplayMode(0, &dm);
|
||||
sdl_get_current_display_mode(&dm);
|
||||
*width = nact->ags.view_area.w;
|
||||
*height = nact->ags.view_area.h;
|
||||
*depth = SDL_BITSPERPIXEL(dm.format);
|
||||
break;
|
||||
case AGS_DISPLAY_DESKTOP:
|
||||
SDL_GetDesktopDisplayMode(0, &dm);
|
||||
sdl_get_desktop_display_mode(&dm);
|
||||
*width = dm.w;
|
||||
*height = dm.h;
|
||||
*depth = SDL_BITSPERPIXEL(dm.format);
|
||||
@@ -333,10 +333,10 @@ void ags_getPixel(int x, int y, PixelColor *cell) {
|
||||
if (!ags_check_param_xy(&x, &y)) return;
|
||||
|
||||
uint32_t pixel = gfx_getPixel(x, y);
|
||||
if (main_surface->format->BitsPerPixel == 8) {
|
||||
if (sdl_surface_bits_per_pixel(main_surface) == 8) {
|
||||
cell->index = pixel;
|
||||
} else {
|
||||
SDL_GetRGB(pixel, main_surface->format, &cell->r, &cell->g, &cell->b);
|
||||
sdl_get_rgb(pixel, main_surface, &cell->r, &cell->g, &cell->b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <SDL_surface.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "portab.h"
|
||||
#include "cg.h"
|
||||
#include "font.h"
|
||||
@@ -58,7 +58,7 @@ typedef struct agsurface surface_t;
|
||||
#define GETOFFSET_PIXEL(suf, x, y) PIXEL_AT((suf)->sdl_surface, x, y)
|
||||
#define GETOFFSET_ALPHA(suf, x, y) ((suf)->alpha + (y) * (suf)->width + (x))
|
||||
// for SDL_surface
|
||||
#define PIXEL_AT(suf, x, y) ((suf)->pixels + (y) * (suf)->pitch + (x) * (suf)->format->BytesPerPixel)
|
||||
#define PIXEL_AT(suf, x, y) ((suf)->pixels + (y) * (suf)->pitch + (x) * sdl_surface_bytes_per_pixel(suf))
|
||||
#define ALPHA_AT(suf, x, y) (PIXEL_AT(suf, x, y) + (SDL_BYTEORDER == SDL_LIL_ENDIAN ? 3 : 0))
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* bgm.sdl3mixer.c: BGM (*BA.ALD) playback using SDL3_mixer
|
||||
*
|
||||
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
||||
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "ald_manager.h"
|
||||
#include "audio_meta.h"
|
||||
#include "bgm.h"
|
||||
#include "music_private.h"
|
||||
#include "nact.h"
|
||||
#include "sdl3_mixer_backend.h"
|
||||
#include "system.h"
|
||||
|
||||
static DRIFILETYPE dri_type;
|
||||
static int base_no;
|
||||
static int current_no;
|
||||
static int current_vol = 100;
|
||||
static uint32_t start_time;
|
||||
|
||||
static int clamp_volume(int volume)
|
||||
{
|
||||
if (volume < 0)
|
||||
return 0;
|
||||
if (volume > 100)
|
||||
return 100;
|
||||
return volume;
|
||||
}
|
||||
|
||||
static float music_gain(int volume)
|
||||
{
|
||||
float gain = clamp_volume(volume) / 100.0f;
|
||||
return gain * clamp_volume(prv.volval[BGM_VOLVAL_CH]) / 100.0f;
|
||||
}
|
||||
|
||||
static void apply_music_volume(int volume)
|
||||
{
|
||||
current_vol = volume;
|
||||
sdl3_mixer_set_music_gain(SDL3_MIXER_MUSIC_BGM,
|
||||
music_gain(current_vol));
|
||||
}
|
||||
|
||||
void musbgm_reapply_valance(void)
|
||||
{
|
||||
apply_music_volume(current_vol);
|
||||
}
|
||||
|
||||
static void free_music(void)
|
||||
{
|
||||
current_no = 0;
|
||||
sdl3_mixer_stop_music(SDL3_MIXER_MUSIC_BGM, 0);
|
||||
}
|
||||
|
||||
static MIX_Audio *load_music(int no)
|
||||
{
|
||||
free_music();
|
||||
int ald_no = no + base_no - 1;
|
||||
dridata *dfile = ald_getdata(dri_type, ald_no);
|
||||
if (!dfile) {
|
||||
WARNING("Failed to open BGM %d", ald_no);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MIX_Audio *audio = sdl3_mixer_load_music_memory(
|
||||
dfile->data, dfile->size);
|
||||
ald_freedata(dfile);
|
||||
if (!audio)
|
||||
WARNING("Failed to load BGM %d: %s", ald_no, SDL_GetError());
|
||||
return audio;
|
||||
}
|
||||
|
||||
bool musbgm_init(DRIFILETYPE type, int base)
|
||||
{
|
||||
dri_type = type;
|
||||
base_no = base;
|
||||
if (type == DRIFILE_BGM)
|
||||
return bgi_read(nact->files.bgi);
|
||||
return true;
|
||||
}
|
||||
|
||||
void musbgm_exit(void)
|
||||
{
|
||||
free_music();
|
||||
}
|
||||
|
||||
void musbgm_reset(void)
|
||||
{
|
||||
free_music();
|
||||
}
|
||||
|
||||
bool musbgm_play(int no, int time, int volume, int loop_count)
|
||||
{
|
||||
MIX_Audio *audio = load_music(no);
|
||||
if (!audio)
|
||||
return false;
|
||||
|
||||
int loops = loop_count == 0 ? -1 : loop_count - 1;
|
||||
current_vol = volume;
|
||||
if (!sdl3_mixer_play_music(SDL3_MIXER_MUSIC_BGM, audio,
|
||||
loops, time * 10, music_gain(current_vol)))
|
||||
return false;
|
||||
current_no = no;
|
||||
start_time = sys_get_ticks();
|
||||
return true;
|
||||
}
|
||||
|
||||
void musbgm_stop(int no, int time)
|
||||
{
|
||||
if (no == current_no)
|
||||
sdl3_mixer_stop_music(SDL3_MIXER_MUSIC_BGM, time * 10);
|
||||
}
|
||||
|
||||
void musbgm_fade(int no, int time, int volume)
|
||||
{
|
||||
(void)time;
|
||||
if (no == current_no)
|
||||
apply_music_volume(volume);
|
||||
}
|
||||
|
||||
int musbgm_getpos(int no)
|
||||
{
|
||||
if (!musbgm_isplaying(no))
|
||||
return 0;
|
||||
return (sys_get_ticks() - start_time) / 10;
|
||||
}
|
||||
|
||||
int musbgm_getlen(int no)
|
||||
{
|
||||
bgi_t *bgi = bgi_find(no);
|
||||
return bgi ? bgi->len / 441 : 0;
|
||||
}
|
||||
|
||||
bool musbgm_isplaying(int no)
|
||||
{
|
||||
return no == current_no &&
|
||||
sdl3_mixer_music_active(SDL3_MIXER_MUSIC_BGM);
|
||||
}
|
||||
|
||||
void musbgm_stopall(int time)
|
||||
{
|
||||
musbgm_stop(current_no, time);
|
||||
}
|
||||
|
||||
void musbgm_wait(int no, int timeout)
|
||||
{
|
||||
while (timeout-- > 0 && musbgm_isplaying(no))
|
||||
SDL_Delay(10);
|
||||
}
|
||||
+1
-2
@@ -25,8 +25,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <SDL.h>
|
||||
#include <SDL_mixer.h>
|
||||
#include "sdl_mixer_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
|
||||
+1
-2
@@ -26,8 +26,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL_timer.h>
|
||||
#include <SDL_mixer.h>
|
||||
#include "sdl_mixer_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* cdrom.sdl3mixer.c CD audio replacement using SDL3_mixer
|
||||
*
|
||||
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
||||
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ald_manager.h"
|
||||
#include "cdrom.h"
|
||||
#include "music_private.h"
|
||||
#include "sdl3_mixer_backend.h"
|
||||
#include "system.h"
|
||||
|
||||
#define PLAYLIST_MAX 256
|
||||
|
||||
static bool enabled;
|
||||
static char *playlist[PLAYLIST_MAX];
|
||||
static int track_no;
|
||||
static uint32_t start_time;
|
||||
|
||||
static void cdrom_stop(void);
|
||||
|
||||
static int clamp_volume(int volume)
|
||||
{
|
||||
if (volume < 0)
|
||||
return 0;
|
||||
if (volume > 100)
|
||||
return 100;
|
||||
return volume;
|
||||
}
|
||||
|
||||
static float music_gain(void)
|
||||
{
|
||||
return clamp_volume(prv.volval[BGM_VOLVAL_CH]) / 100.0f;
|
||||
}
|
||||
|
||||
static void apply_volume(void)
|
||||
{
|
||||
sdl3_mixer_set_music_gain(SDL3_MIXER_MUSIC_CD, music_gain());
|
||||
}
|
||||
|
||||
static void free_playlist(void)
|
||||
{
|
||||
for (int track = 0; track < PLAYLIST_MAX; track++) {
|
||||
free(playlist[track]);
|
||||
playlist[track] = NULL;
|
||||
}
|
||||
prv.cd_maxtrk = 0;
|
||||
}
|
||||
|
||||
static bool cdrom_init(char *playlist_path)
|
||||
{
|
||||
if (!playlist_path || !playlist_path[0])
|
||||
return false;
|
||||
|
||||
if (enabled)
|
||||
cdrom_stop();
|
||||
enabled = false;
|
||||
free_playlist();
|
||||
FILE *fp = fopen(playlist_path, "r");
|
||||
char buf[256];
|
||||
if (fp) {
|
||||
/* The first line represents track 1, which is data on mixed CDs. */
|
||||
fgets(buf, sizeof(buf), fp);
|
||||
} else {
|
||||
fp = fopen("_inmm.ini", "r");
|
||||
}
|
||||
if (!fp) {
|
||||
if (ald_get_maxno(DRIFILE_MIDI) == 0)
|
||||
NOTICE("cdrom: Cannot open playlist %s", playlist_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int track = 2; track < PLAYLIST_MAX; track++) {
|
||||
if (!fgets(buf, sizeof(buf), fp))
|
||||
break;
|
||||
for (char *s = buf; *s; s++) {
|
||||
if (*s == '\\')
|
||||
*s = '/';
|
||||
else if (*s == '\r' || *s == '\n') {
|
||||
*s = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*buf) {
|
||||
playlist[track] = strdup(buf);
|
||||
if (playlist[track])
|
||||
prv.cd_maxtrk = track;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
NOTICE("cdrom: Loaded playlist from %s", playlist_path);
|
||||
|
||||
track_no = 0;
|
||||
enabled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void cdrom_exit(void)
|
||||
{
|
||||
if (enabled)
|
||||
cdrom_stop();
|
||||
enabled = false;
|
||||
free_playlist();
|
||||
}
|
||||
|
||||
static void cdrom_reset(void)
|
||||
{
|
||||
if (enabled)
|
||||
cdrom_stop();
|
||||
}
|
||||
|
||||
static bool cdrom_start(int track, int loop)
|
||||
{
|
||||
if (!enabled || track < 0 || track >= PLAYLIST_MAX || !playlist[track])
|
||||
return false;
|
||||
cdrom_stop();
|
||||
|
||||
const char *path = playlist[track];
|
||||
#ifdef __ANDROID__
|
||||
/* SDL's Android file IO requires an absolute path for installed tracks. */
|
||||
char absolute_path[PATH_MAX];
|
||||
if (!realpath(path, absolute_path))
|
||||
return false;
|
||||
path = absolute_path;
|
||||
#endif
|
||||
|
||||
MIX_Audio *audio = sdl3_mixer_load_music_file(path);
|
||||
if (!audio) {
|
||||
WARNING("Cannot load %s: %s", path, SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
/* CD playback passes loop directly; this differs from BGM's loop - 1. */
|
||||
int loops = loop == 0 ? -1 : loop;
|
||||
if (!sdl3_mixer_play_music(SDL3_MIXER_MUSIC_CD, audio,
|
||||
loops, 0, music_gain()))
|
||||
return false;
|
||||
|
||||
track_no = track;
|
||||
start_time = sys_get_ticks();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void cdrom_stop(void)
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
sdl3_mixer_stop_music(SDL3_MIXER_MUSIC_CD, 0);
|
||||
track_no = 0;
|
||||
}
|
||||
|
||||
static bool cdrom_get_playing_info(cd_time *info)
|
||||
{
|
||||
if (!enabled || !track_no ||
|
||||
!sdl3_mixer_music_active(SDL3_MIXER_MUSIC_CD))
|
||||
return false;
|
||||
|
||||
int ms = sys_get_ticks() - start_time;
|
||||
info->t = track_no;
|
||||
info->m = ms / (60 * 1000);
|
||||
ms %= 60 * 1000;
|
||||
info->s = ms / 1000;
|
||||
ms %= 1000;
|
||||
info->f = ms * CD_FPS / 1000;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cdrom_is_available(void)
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
|
||||
static void cdrom_reapply_volume(void)
|
||||
{
|
||||
if (enabled && track_no)
|
||||
apply_volume();
|
||||
}
|
||||
|
||||
cdromdevice_t cdrom_mp3 = {
|
||||
.init = cdrom_init,
|
||||
.exit = cdrom_exit,
|
||||
.reset = cdrom_reset,
|
||||
.start = cdrom_start,
|
||||
.stop = cdrom_stop,
|
||||
.getpos = cdrom_get_playing_info,
|
||||
.is_available = cdrom_is_available,
|
||||
.reapply_volume = cdrom_reapply_volume,
|
||||
};
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
@@ -127,17 +127,19 @@ static void cg_mosaic(cgdata *cg) {
|
||||
default:
|
||||
SYSERROR("Unsupported cg depth %d", cg->depth);
|
||||
}
|
||||
SDL_Surface *sf = SDL_CreateRGBSurfaceWithFormatFrom(
|
||||
cg->pic, cg->width, cg->height, cg->depth, cg->width * (cg->depth / 8), format);
|
||||
SDL_Surface *tmp = SDL_CreateRGBSurfaceWithFormat(
|
||||
0, (cg->width + MOSAIC_SIZE - 1) / MOSAIC_SIZE, (cg->height + MOSAIC_SIZE - 1) / MOSAIC_SIZE, cg->depth, format);
|
||||
if (sf->format->palette)
|
||||
SDL_SetSurfacePalette(tmp, sf->format->palette);
|
||||
SDL_Surface *sf = sdl_create_surface_from(cg->pic, cg->width, cg->height,
|
||||
cg->depth, cg->width * (cg->depth / 8), format);
|
||||
SDL_Surface *tmp = sdl_create_surface(
|
||||
(cg->width + MOSAIC_SIZE - 1) / MOSAIC_SIZE,
|
||||
(cg->height + MOSAIC_SIZE - 1) / MOSAIC_SIZE, cg->depth, format);
|
||||
SDL_Palette *palette = sdl_get_surface_palette(sf);
|
||||
if (palette)
|
||||
sdl_set_surface_palette(tmp, palette);
|
||||
// NOTE: SDL_BlitScaled() does not support 8-bit surfaces.
|
||||
SDL_SoftStretch(sf, NULL, tmp, NULL);
|
||||
SDL_SoftStretch(tmp, NULL, sf, NULL);
|
||||
SDL_FreeSurface(tmp);
|
||||
SDL_FreeSurface(sf);
|
||||
sdl_destroy_surface(tmp);
|
||||
sdl_destroy_surface(sf);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -644,34 +646,37 @@ SDL_Surface *cg_load_as_sdlsurface_from_data(uint8_t *data, size_t size, bool mo
|
||||
cg_mosaic(cg);
|
||||
}
|
||||
|
||||
SDL_Surface *sf = SDL_CreateRGBSurfaceWithFormat(0, cg->width, cg->height, 32,
|
||||
SDL_Surface *sf = sdl_create_surface(cg->width, cg->height, 32,
|
||||
(cg->alpha || (type == ALCG_PMS8 && as_alpha)) ? SDL_PIXELFORMAT_ARGB8888 : SDL_PIXELFORMAT_XRGB8888);
|
||||
|
||||
if (type == ALCG_PMS8) {
|
||||
if (as_alpha) {
|
||||
// Treat PMS8 as an alpha map. Palette is ignored.
|
||||
SDL_FillRect(sf, NULL, SDL_MapRGBA(sf->format, 255, 255, 255, 0));
|
||||
SDL_FillRect(sf, NULL, sdl_map_rgba(sf, 255, 255, 255, 0));
|
||||
cg->alpha = cg->pic;
|
||||
cg->pic = NULL;
|
||||
} else {
|
||||
// Treat PMS8 as a paletted image.
|
||||
SDL_Surface *pic = SDL_CreateRGBSurfaceWithFormatFrom(cg->pic, cg->width, cg->height, 8, cg->width, SDL_PIXELFORMAT_INDEX8);
|
||||
SDL_Palette *palette = SDL_AllocPalette(256);
|
||||
SDL_Surface *pic = sdl_create_surface_from(cg->pic, cg->width,
|
||||
cg->height, 8, cg->width, SDL_PIXELFORMAT_INDEX8);
|
||||
SDL_Palette *palette = sdl_create_palette(256);
|
||||
SDL_SetPaletteColors(palette, cg->pal, 0, 256);
|
||||
SDL_SetSurfacePalette(pic, palette);
|
||||
SDL_FreePalette(palette);
|
||||
sdl_set_surface_palette(pic, palette);
|
||||
sdl_destroy_palette(palette);
|
||||
SDL_BlitSurface(pic, NULL, sf, NULL);
|
||||
SDL_FreeSurface(pic);
|
||||
sdl_destroy_surface(pic);
|
||||
}
|
||||
} else {
|
||||
SDL_Surface *pic;
|
||||
if (type == ALCG_PMS16 || type == ALCG_BMP24) {
|
||||
pic = SDL_CreateRGBSurfaceWithFormatFrom(cg->pic, cg->width, cg->height, 16, cg->width * 2, SDL_PIXELFORMAT_RGB565);
|
||||
pic = sdl_create_surface_from(cg->pic, cg->width, cg->height,
|
||||
16, cg->width * 2, SDL_PIXELFORMAT_RGB565);
|
||||
} else {
|
||||
pic = SDL_CreateRGBSurfaceWithFormatFrom(cg->pic, cg->width, cg->height, 24, cg->width * 3, SDL_PIXELFORMAT_RGB24);
|
||||
pic = sdl_create_surface_from(cg->pic, cg->width, cg->height,
|
||||
24, cg->width * 3, SDL_PIXELFORMAT_RGB24);
|
||||
}
|
||||
SDL_BlitSurface(pic, NULL, sf, NULL);
|
||||
SDL_FreeSurface(pic);
|
||||
sdl_destroy_surface(pic);
|
||||
}
|
||||
if (cg->alpha) {
|
||||
// Copy alpha values from cg->alpha to sf->pixels.
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#ifndef __CG__
|
||||
#define __CG__
|
||||
|
||||
#include <SDL_rect.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "cache.h"
|
||||
#include "portab.h"
|
||||
|
||||
|
||||
+2
-2
@@ -56,7 +56,7 @@ void commandTPC() {
|
||||
case 1: sl_pushTextColor(type, nact->sel.MsgFontColor); break;
|
||||
default: WARNING("TPC: unknown type %d", type); break;
|
||||
}
|
||||
TRACE("TPC %d", exp);
|
||||
TRACE("TPC %d", type);
|
||||
}
|
||||
|
||||
void commandTPS() {
|
||||
@@ -67,7 +67,7 @@ void commandTPS() {
|
||||
case 1: sl_pushTextSize(type, nact->sel.MsgFontSize); break;
|
||||
default: WARNING("TPS: unknown type %d", type); break;
|
||||
}
|
||||
TRACE("TPS %d", exp);
|
||||
TRACE("TPS %d", type);
|
||||
}
|
||||
|
||||
void commandTOP() {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
/* $Id: cmdz.c,v 1.35 2003/01/12 10:48:50 chikama Exp $ */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
+12
-12
@@ -23,7 +23,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
@@ -163,18 +163,18 @@ static SDL_Cursor *init_system_cursor(const char *image[]) {
|
||||
|
||||
void cursor_init(void) {
|
||||
cursor[CURSOR_ARROW] = SDL_GetDefaultCursor();
|
||||
cursor[CURSOR_CROSS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
|
||||
cursor[CURSOR_IBEAM] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
|
||||
cursor[CURSOR_CROSS] = sdl_create_system_cursor(SDL_COMPAT_SYSTEM_CURSOR_CROSSHAIR);
|
||||
cursor[CURSOR_IBEAM] = sdl_create_system_cursor(SDL_COMPAT_SYSTEM_CURSOR_TEXT);
|
||||
cursor[CURSOR_ICON] = SDL_GetDefaultCursor();
|
||||
cursor[CURSOR_NO] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
|
||||
cursor[CURSOR_SIZE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
|
||||
cursor[CURSOR_SIZEALL] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
|
||||
cursor[CURSOR_SIZENESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
|
||||
cursor[CURSOR_SIZENS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
|
||||
cursor[CURSOR_SIZENWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
|
||||
cursor[CURSOR_SIZEWE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
|
||||
cursor[CURSOR_NO] = sdl_create_system_cursor(SDL_COMPAT_SYSTEM_CURSOR_NOT_ALLOWED);
|
||||
cursor[CURSOR_SIZE] = sdl_create_system_cursor(SDL_COMPAT_SYSTEM_CURSOR_MOVE);
|
||||
cursor[CURSOR_SIZEALL] = sdl_create_system_cursor(SDL_COMPAT_SYSTEM_CURSOR_MOVE);
|
||||
cursor[CURSOR_SIZENESW] = sdl_create_system_cursor(SDL_COMPAT_SYSTEM_CURSOR_NESW_RESIZE);
|
||||
cursor[CURSOR_SIZENS] = sdl_create_system_cursor(SDL_COMPAT_SYSTEM_CURSOR_NS_RESIZE);
|
||||
cursor[CURSOR_SIZENWSE] = sdl_create_system_cursor(SDL_COMPAT_SYSTEM_CURSOR_NWSE_RESIZE);
|
||||
cursor[CURSOR_SIZEWE] = sdl_create_system_cursor(SDL_COMPAT_SYSTEM_CURSOR_EW_RESIZE);
|
||||
cursor[CURSOR_UPARROW] = init_system_cursor(cursor_uparrow);
|
||||
cursor[CURSOR_WAIT] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
|
||||
cursor[CURSOR_WAIT] = sdl_create_system_cursor(SDL_COMPAT_SYSTEM_CURSOR_WAIT);
|
||||
}
|
||||
|
||||
static bool cursor_new(uint8_t* data, int no, CursorImage *cursorImage, TCursorDirEntry *cursordirentry) {
|
||||
@@ -215,7 +215,7 @@ static bool cursor_new(uint8_t* data, int no, CursorImage *cursorImage, TCursorD
|
||||
}
|
||||
|
||||
if (cursor[no])
|
||||
SDL_FreeCursor(cursor[no]);
|
||||
sdl_destroy_cursor(cursor[no]);
|
||||
cursor[no] = SDL_CreateCursor(buf3, buf4, 32, 32, cursordirentry->wxHotspot, cursordirentry->wyHotspot);
|
||||
|
||||
free(buf1);
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "debugger.h"
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "system.h"
|
||||
#include "debugger.h"
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL_thread.h>
|
||||
#include "sdl_compat.h"
|
||||
#ifdef _WIN32
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
|
||||
+34
-35
@@ -21,7 +21,8 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <SDL.h>
|
||||
#include <stdlib.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "system.h"
|
||||
#include "gfx.h"
|
||||
@@ -139,7 +140,7 @@ static EffectTexture *create_effect_texture(surface_t *as, int x, int y, int w,
|
||||
} else {
|
||||
SDL_Surface *sf = gfx_createSurfaceView(as->sdl_surface, x, y, w, h);
|
||||
t->tx = SDL_CreateTextureFromSurface(gfx_renderer, sf);
|
||||
SDL_FreeSurface(sf);
|
||||
sdl_destroy_surface(sf);
|
||||
t->rect = (SDL_Rect){ 0, 0, w, h };
|
||||
}
|
||||
return t;
|
||||
@@ -160,7 +161,7 @@ static void destroy_effect_texture(EffectTexture *t) {
|
||||
free(t);
|
||||
}
|
||||
|
||||
static int render_effect_texture(EffectTexture *t, const SDL_Rect* srcrect, const SDL_Rect* dstrect) {
|
||||
static bool render_effect_texture(EffectTexture *t, const SDL_Rect* srcrect, const SDL_Rect* dstrect) {
|
||||
SDL_Rect real_srcrect;
|
||||
if (!srcrect) {
|
||||
real_srcrect = t->rect;
|
||||
@@ -169,7 +170,7 @@ static int render_effect_texture(EffectTexture *t, const SDL_Rect* srcrect, cons
|
||||
real_srcrect.x += t->rect.x;
|
||||
real_srcrect.y += t->rect.y;
|
||||
}
|
||||
return SDL_RenderCopy(gfx_renderer, t->tx, &real_srcrect, dstrect);
|
||||
return sdl_render_texture(gfx_renderer, t->tx, &real_srcrect, dstrect);
|
||||
}
|
||||
|
||||
struct effect {
|
||||
@@ -191,14 +192,13 @@ static void eff_init(struct effect *eff, SDL_Rect *rect, EffectTexture *old, Eff
|
||||
|
||||
if (!eff->is_fullscreen)
|
||||
gfx_updateScreen(); // Flush pending display changes.
|
||||
gfx_setEffectRunning(true);
|
||||
}
|
||||
|
||||
static void eff_finish(struct effect *eff, bool present) {
|
||||
if (present) {
|
||||
SDL_RenderClear(gfx_renderer);
|
||||
if (!eff->is_fullscreen)
|
||||
SDL_RenderCopy(gfx_renderer, gfx_texture, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, gfx_texture, NULL, NULL);
|
||||
render_effect_texture(eff->tx_new, NULL, &eff->dst_rect);
|
||||
SDL_RenderPresent(gfx_renderer);
|
||||
}
|
||||
@@ -385,7 +385,7 @@ static void mosaic_step(struct effect *eff, float progress);
|
||||
static void mosaic_free(struct effect *eff);
|
||||
|
||||
static struct effect *mosaic_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
||||
if (!SDL_RenderTargetSupported(gfx_renderer))
|
||||
if (!sdl_render_target_supported(gfx_renderer))
|
||||
return fallback_effect_new(rect, old, new, type);
|
||||
|
||||
struct mosaic_effect *eff = calloc(1, sizeof(struct mosaic_effect));
|
||||
@@ -410,7 +410,7 @@ static void mosaic(EffectTexture *src, SDL_Texture *tmp, SDL_Texture *dst, int w
|
||||
.h = h / scale,
|
||||
};
|
||||
SDL_SetRenderTarget(gfx_renderer, tmp);
|
||||
SDL_RenderCopyF(gfx_renderer, src->tx, &src->rect, &fr);
|
||||
sdl_render_texture_float(gfx_renderer, src->tx, &src->rect, &fr);
|
||||
|
||||
SDL_Rect dstr = {
|
||||
.x = w * (1 - scale) / 2,
|
||||
@@ -420,8 +420,8 @@ static void mosaic(EffectTexture *src, SDL_Texture *tmp, SDL_Texture *dst, int w
|
||||
};
|
||||
SDL_SetRenderTarget(gfx_renderer, dst);
|
||||
|
||||
SDL_SetTextureScaleMode(tmp, SDL_ScaleModeNearest);
|
||||
SDL_RenderCopy(gfx_renderer, tmp, NULL, &dstr);
|
||||
sdl_set_texture_scale_mode_nearest(tmp);
|
||||
sdl_render_texture(gfx_renderer, tmp, NULL, &dstr);
|
||||
SDL_SetRenderTarget(gfx_renderer, NULL);
|
||||
}
|
||||
|
||||
@@ -432,16 +432,16 @@ static void mosaic_step(struct effect *eff, float progress) {
|
||||
const int max_scale = 80;
|
||||
int scale = (1.f - progress) * (max_scale - 1) + 1;
|
||||
mosaic(eff->tx_new, this->tmp_new, this->tx_mosaic, eff->dst_rect.w, eff->dst_rect.h, scale);
|
||||
SDL_RenderCopy(gfx_renderer, this->tx_mosaic, NULL, &eff->dst_rect);
|
||||
sdl_render_texture(gfx_renderer, this->tx_mosaic, NULL, &eff->dst_rect);
|
||||
} else if (eff->type == EFFECT_CROSSFADE_MOSAIC) {
|
||||
const int max_scale = 96;
|
||||
float scale = (progress < 0.5f ? progress : 1.f - progress) * 2 * (max_scale - 1) + 1;
|
||||
mosaic(eff->tx_old, this->tmp_old, this->tx_mosaic, eff->dst_rect.w, eff->dst_rect.h, scale);
|
||||
SDL_RenderCopy(gfx_renderer, this->tx_mosaic, NULL, &eff->dst_rect);
|
||||
sdl_render_texture(gfx_renderer, this->tx_mosaic, NULL, &eff->dst_rect);
|
||||
mosaic(eff->tx_new, this->tmp_new, this->tx_mosaic, eff->dst_rect.w, eff->dst_rect.h, scale);
|
||||
SDL_SetTextureBlendMode(this->tx_mosaic, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(this->tx_mosaic, progress * 255);
|
||||
SDL_RenderCopy(gfx_renderer, this->tx_mosaic, NULL, &eff->dst_rect);
|
||||
sdl_render_texture(gfx_renderer, this->tx_mosaic, NULL, &eff->dst_rect);
|
||||
SDL_SetTextureBlendMode(this->tx_mosaic, SDL_BLENDMODE_NONE);
|
||||
} else {
|
||||
assert(!"Cannot happen");
|
||||
@@ -515,7 +515,7 @@ static void brightness_step(struct effect *eff, float progress) {
|
||||
render_effect_texture(texture, NULL, &eff->dst_rect);
|
||||
SDL_SetRenderDrawBlendMode(gfx_renderer, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(gfx_renderer, color, color, color, alpha);
|
||||
SDL_RenderFillRect(gfx_renderer, &eff->dst_rect);
|
||||
sdl_render_fill_rect(gfx_renderer, &eff->dst_rect);
|
||||
SDL_SetRenderDrawBlendMode(gfx_renderer, SDL_BLENDMODE_NONE);
|
||||
SDL_SetRenderDrawColor(gfx_renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
|
||||
SDL_RenderPresent(gfx_renderer);
|
||||
@@ -532,9 +532,8 @@ static void dithering_fade_step(struct effect *eff, float progress);
|
||||
static void dithering_fade_free(struct effect *eff);
|
||||
|
||||
static EffectTexture *create_dither_pattern_texture(int w, int h, int val) {
|
||||
SDL_PixelFormat *fmt = SDL_AllocFormat(SDL_PIXELFORMAT_ARGB8888);
|
||||
Uint32 col = SDL_MapRGBA(fmt, val, val, val, SDL_ALPHA_OPAQUE);
|
||||
SDL_FreeFormat(fmt);
|
||||
Uint32 col = sdl_map_rgba_format(
|
||||
SDL_PIXELFORMAT_ARGB8888, val, val, val, SDL_ALPHA_OPAQUE);
|
||||
|
||||
Uint32 *pixels = calloc(w * h, sizeof(Uint32));
|
||||
if (!pixels)
|
||||
@@ -611,10 +610,10 @@ static void dithering_fade_step(struct effect *eff, float progress) {
|
||||
static void dithering_fade_free(struct effect *eff) {
|
||||
if (eff->type == EFFECT_DITHERING_FADEOUT) {
|
||||
SDL_SetRenderDrawColor(gfx_renderer, 0, 0, 0, 255);
|
||||
SDL_RenderFillRect(gfx_renderer, &eff->dst_rect);
|
||||
sdl_render_fill_rect(gfx_renderer, &eff->dst_rect);
|
||||
} else if (eff->type == EFFECT_DITHERING_WHITEOUT) {
|
||||
SDL_SetRenderDrawColor(gfx_renderer, 255, 255, 255, 255);
|
||||
SDL_RenderFillRect(gfx_renderer, &eff->dst_rect);
|
||||
sdl_render_fill_rect(gfx_renderer, &eff->dst_rect);
|
||||
}
|
||||
eff_finish(eff, false);
|
||||
free(eff);
|
||||
@@ -1021,7 +1020,7 @@ static void zoom_blend_blur_step(struct effect *eff, float progress);
|
||||
static void zoom_blend_blur_free(struct effect *eff);
|
||||
|
||||
static struct effect *zoom_blend_blur_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new) {
|
||||
if (!SDL_RenderTargetSupported(gfx_renderer))
|
||||
if (!sdl_render_target_supported(gfx_renderer))
|
||||
return fallback_effect_new(rect, old, new, EFFECT_ZOOM_BLEND_BLUR);
|
||||
|
||||
struct zoom_blend_blur_effect *eff = calloc(1, sizeof(struct zoom_blend_blur_effect));
|
||||
@@ -1055,7 +1054,7 @@ static void zoom_blend_blur_step(struct effect *eff, float progress) {
|
||||
|
||||
this->index = (this->index + 1) % ZOOM_BLEND_BLUR_STEPS;
|
||||
|
||||
SDL_RenderFillRect(gfx_renderer, &eff->dst_rect);
|
||||
sdl_render_fill_rect(gfx_renderer, &eff->dst_rect);
|
||||
EffectTexture texture_rect = { NULL, { 0, 0, eff->dst_rect.w, eff->dst_rect.h } };
|
||||
for (int i = 0; i < ZOOM_BLEND_BLUR_STEPS; i++) {
|
||||
texture_rect.tx = this->tx[i];
|
||||
@@ -1093,7 +1092,7 @@ static void linear_blur_step(struct effect *eff, float progress);
|
||||
static void linear_blur_free(struct effect *eff);
|
||||
|
||||
static struct effect *linear_blur_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
||||
if (!SDL_RenderTargetSupported(gfx_renderer))
|
||||
if (!sdl_render_target_supported(gfx_renderer))
|
||||
return fallback_effect_new(rect, old, new, type);
|
||||
|
||||
struct linear_blur_effect *lbe = calloc(1, sizeof(struct linear_blur_effect));
|
||||
@@ -1197,7 +1196,7 @@ static void polygon_mask_step(struct effect *eff, float progress);
|
||||
static void polygon_mask_free(struct effect *eff);
|
||||
|
||||
static struct effect *polygon_mask_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
||||
if (SDL_RenderTargetSupported(gfx_renderer)) {
|
||||
if (sdl_render_target_supported(gfx_renderer)) {
|
||||
struct polygon_mask_effect *pmf = calloc(1, sizeof(struct polygon_mask_effect));
|
||||
if (!pmf)
|
||||
NOMEMERR();
|
||||
@@ -1230,7 +1229,7 @@ static void draw_pentagram(int center_x, int center_y, float radius, float rotat
|
||||
for (int i = 0; i < 10; i++) {
|
||||
v[i].position.x = radius * (p[i].x * cos_r - p[i].y * sin_r) + center_x;
|
||||
v[i].position.y = radius * (p[i].x * sin_r + p[i].y * cos_r) + center_y;
|
||||
v[i].color = (SDL_Color){0, 0, 0, 0};
|
||||
v[i].color = sdl_vertex_color(0, 0, 0, 0);
|
||||
}
|
||||
const int indices[15] = {
|
||||
0, 3, 7,
|
||||
@@ -1258,7 +1257,7 @@ static void draw_hexagram(int center_x, int center_y, float radius, float rotate
|
||||
for (int i = 0; i < 6; i++) {
|
||||
v[i].position.x = radius * (p[i].x * cos_r - p[i].y * sin_r) + center_x;
|
||||
v[i].position.y = radius * (p[i].x * sin_r + p[i].y * cos_r) + center_y;
|
||||
v[i].color = (SDL_Color){0, 0, 0, 0};
|
||||
v[i].color = sdl_vertex_color(0, 0, 0, 0);
|
||||
}
|
||||
SDL_RenderGeometry(gfx_renderer, NULL, v, 6, NULL, 0);
|
||||
}
|
||||
@@ -1429,7 +1428,7 @@ static void polygon_mask_step(struct effect *eff, float progress) {
|
||||
SDL_SetRenderTarget(gfx_renderer, NULL);
|
||||
render_effect_texture(eff->tx_new, NULL, &eff->dst_rect);
|
||||
SDL_SetTextureBlendMode(this->tx_tmp, SDL_BLENDMODE_BLEND);
|
||||
SDL_RenderCopy(gfx_renderer, this->tx_tmp, NULL, &eff->dst_rect);
|
||||
sdl_render_texture(gfx_renderer, this->tx_tmp, NULL, &eff->dst_rect);
|
||||
SDL_RenderPresent(gfx_renderer);
|
||||
}
|
||||
|
||||
@@ -1494,7 +1493,8 @@ static void rotate_step(struct effect *eff, float progress) {
|
||||
r.w *= scale;
|
||||
r.h *= scale;
|
||||
render_effect_texture(bg_texture, NULL, &eff->dst_rect);
|
||||
SDL_RenderCopyEx(gfx_renderer, fg_texture->tx, &fg_texture->rect, &r, angle, NULL, SDL_FLIP_NONE);
|
||||
sdl_render_texture_rotated(gfx_renderer, fg_texture->tx,
|
||||
&fg_texture->rect, &r, angle, NULL, SDL_FLIP_NONE);
|
||||
SDL_RenderPresent(gfx_renderer);
|
||||
}
|
||||
|
||||
@@ -1574,7 +1574,7 @@ static void zigzag_crossfade_step(struct effect *eff, float progress);
|
||||
static void zigzag_crossfade_free(struct effect *eff);
|
||||
|
||||
static struct effect *zigzag_crossfade_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new) {
|
||||
if (!SDL_RenderTargetSupported(gfx_renderer))
|
||||
if (!sdl_render_target_supported(gfx_renderer))
|
||||
return fallback_effect_new(rect, old, new, EFFECT_ZIGZAG_CROSSFADE);
|
||||
|
||||
struct zigzag_crossfade_effect *eff = calloc(1, sizeof(struct zigzag_crossfade_effect));
|
||||
@@ -1609,7 +1609,7 @@ static void wave_warp_v(SDL_Texture *src, SDL_Texture *dst, int w, int h, float
|
||||
for (int x = 0; x < w; x++) {
|
||||
sr.x = dr.x = x;
|
||||
dr.y = sinf(phase + x * (2 * M_PIf / 360 * length)) * amplitude;
|
||||
SDL_RenderCopy(gfx_renderer, src, &sr, &dr);
|
||||
sdl_render_texture(gfx_renderer, src, &sr, &dr);
|
||||
}
|
||||
SDL_SetRenderTarget(gfx_renderer, NULL);
|
||||
}
|
||||
@@ -1623,13 +1623,13 @@ static void zigzag_crossfade_step(struct effect *eff, float progress) {
|
||||
|
||||
wave_warp_h(eff->tx_old, this->tmp1, eff->dst_rect.w, eff->dst_rect.h, amp, len, phase);
|
||||
wave_warp_v(this->tmp1, this->tmp2, eff->dst_rect.w, eff->dst_rect.h, amp, len, phase);
|
||||
SDL_RenderCopy(gfx_renderer, this->tmp2, NULL, &eff->dst_rect);
|
||||
sdl_render_texture(gfx_renderer, this->tmp2, NULL, &eff->dst_rect);
|
||||
|
||||
wave_warp_h(eff->tx_new, this->tmp1, eff->dst_rect.w, eff->dst_rect.h, amp, len, phase);
|
||||
wave_warp_v(this->tmp1, this->tmp2, eff->dst_rect.w, eff->dst_rect.h, amp, len, phase);
|
||||
SDL_SetTextureBlendMode(this->tmp2, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureAlphaMod(this->tmp2, progress * 255);
|
||||
SDL_RenderCopy(gfx_renderer, this->tmp2, NULL, &eff->dst_rect);
|
||||
sdl_render_texture(gfx_renderer, this->tmp2, NULL, &eff->dst_rect);
|
||||
SDL_SetTextureBlendMode(this->tmp2, SDL_BLENDMODE_NONE);
|
||||
|
||||
SDL_RenderPresent(gfx_renderer);
|
||||
@@ -1691,7 +1691,7 @@ static void raster_blend_free(struct effect *eff);
|
||||
static struct effect *raster_blend_new(SDL_Rect *rect, int sx, int sy) {
|
||||
SDL_Surface *sprite = gfx_dib_to_surface_with_alpha(sx, sy, rect->w, rect->h);
|
||||
EffectTexture *texture = create_effect_texture_from_surface(sprite);
|
||||
SDL_FreeSurface(sprite);
|
||||
sdl_destroy_surface(sprite);
|
||||
struct effect *eff = calloc(1, sizeof(struct effect));
|
||||
if (!eff)
|
||||
NOMEMERR();
|
||||
@@ -1877,7 +1877,7 @@ struct effect *sprite_effect_init(SDL_Rect *rect, int dx, int dy, int sx, int sy
|
||||
EffectTexture *tx_old = create_effect_texture(gfx_dibinfo, dx, dy, rect->w, rect->h);
|
||||
SDL_Surface *sprite = gfx_dib_to_surface_colorkey(sx, sy, rect->w, rect->h, col);
|
||||
EffectTexture *tx_new = create_effect_texture_from_surface(sprite);
|
||||
SDL_FreeSurface(sprite);
|
||||
sdl_destroy_surface(sprite);
|
||||
|
||||
switch (type) {
|
||||
case EFFECT_PAN_IN_DOWN:
|
||||
@@ -1907,11 +1907,10 @@ struct effect *effect_sactamask_init(SDL_Surface *mask) {
|
||||
void effect_step(struct effect *eff, float progress) {
|
||||
SDL_RenderClear(gfx_renderer);
|
||||
if (!eff->is_fullscreen)
|
||||
SDL_RenderCopy(gfx_renderer, gfx_texture, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, gfx_texture, NULL, NULL);
|
||||
eff->step(eff, progress);
|
||||
}
|
||||
|
||||
void effect_finish(struct effect *eff) {
|
||||
eff->finish(eff);
|
||||
gfx_setEffectRunning(false);
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
#ifndef __EFFECT_H__
|
||||
#define __EFFECT_H__
|
||||
|
||||
#include <SDL_surface.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "ags.h"
|
||||
|
||||
// Effect types of the CE/CD command.
|
||||
|
||||
+128
-65
@@ -24,7 +24,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
@@ -43,6 +43,12 @@
|
||||
#include "hacks.h"
|
||||
#include "volume.h"
|
||||
|
||||
#if defined(_WIN32) && XSYSTEM35_SDL_VERSION == 2
|
||||
#include <SDL_syswm.h>
|
||||
// Windows defines this name too; the key table below uses input.h's enum.
|
||||
#undef KEY_EXECUTE
|
||||
#endif
|
||||
|
||||
static void get_event(void);
|
||||
static void keyEventProsess(SDL_KeyboardEvent *e, bool pressed);
|
||||
|
||||
@@ -51,6 +57,9 @@ static uint32_t custom_event_type = (uint32_t)-1;
|
||||
enum CustomEventCode {
|
||||
SIMULATE_RIGHT_BUTTON,
|
||||
DEBUGGER_COMMAND,
|
||||
#if defined(_WIN32) && XSYSTEM35_SDL_VERSION == 3
|
||||
WIN_MENU_COMMAND,
|
||||
#endif
|
||||
};
|
||||
|
||||
static SDL_Point mouse_pos;
|
||||
@@ -61,7 +70,7 @@ bool RawKeyInfo[256];
|
||||
/* SDL Joystick */
|
||||
static int joyinfo=0;
|
||||
|
||||
static int keytable[SDL_NUM_SCANCODES] = {
|
||||
static int keytable[SDL_COMPAT_SCANCODE_COUNT] = {
|
||||
[SDL_SCANCODE_A] = KEY_A,
|
||||
[SDL_SCANCODE_B] = KEY_B,
|
||||
[SDL_SCANCODE_C] = KEY_C,
|
||||
@@ -186,18 +195,18 @@ static int keytable[SDL_NUM_SCANCODES] = {
|
||||
static int joy_device_index = -1;
|
||||
static SDL_Joystick *js;
|
||||
|
||||
static bool joy_open(int index) {
|
||||
static bool joy_open(sdl_joystick_device_t device) {
|
||||
if (js)
|
||||
return false;
|
||||
|
||||
js = SDL_JoystickOpen(index);
|
||||
js = sdl_open_joystick(device);
|
||||
if (!js)
|
||||
return false;
|
||||
|
||||
const char *name = SDL_JoystickName(js);
|
||||
int axes = SDL_JoystickNumAxes(js);
|
||||
int buttons = SDL_JoystickNumButtons(js);
|
||||
SDL_JoystickEventState(SDL_ENABLE);
|
||||
const char *name = sdl_get_joystick_name(js);
|
||||
int axes = sdl_get_num_joystick_axes(js);
|
||||
int buttons = sdl_get_num_joystick_buttons(js);
|
||||
sdl_set_joystick_events_enabled(true);
|
||||
NOTICE("SDL joystick '%s' %d axes %d buttons", name, axes, buttons);
|
||||
return true;
|
||||
}
|
||||
@@ -205,14 +214,22 @@ static bool joy_open(int index) {
|
||||
static bool joy_init(void) {
|
||||
SDL_Init(SDL_INIT_JOYSTICK);
|
||||
|
||||
if (joy_device_index >= 0)
|
||||
return joy_open(joy_device_index);
|
||||
|
||||
for (int i = 0; i < SDL_NumJoysticks(); i++) {
|
||||
if (joy_open(i))
|
||||
return true;
|
||||
int count = 0;
|
||||
sdl_joystick_device_t *devices = sdl_get_joystick_devices(&count);
|
||||
bool opened = false;
|
||||
if (joy_device_index >= 0) {
|
||||
if (joy_device_index < count)
|
||||
opened = joy_open(devices[joy_device_index]);
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (joy_open(devices[i])) {
|
||||
opened = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
SDL_free(devices);
|
||||
return opened;
|
||||
}
|
||||
|
||||
void event_init(void) {
|
||||
@@ -223,7 +240,7 @@ void event_init(void) {
|
||||
|
||||
void event_remove(void) {
|
||||
if (js) {
|
||||
SDL_JoystickClose(js);
|
||||
sdl_close_joystick(js);
|
||||
js = NULL;
|
||||
}
|
||||
}
|
||||
@@ -283,9 +300,25 @@ void event_set_mouse_internal_location(int x, int y) {
|
||||
}
|
||||
|
||||
SDL_Point event_get_touch_position(const SDL_TouchFingerEvent *e) {
|
||||
#if XSYSTEM35_SDL_VERSION == 2
|
||||
int view_w, view_h;
|
||||
gfx_getViewSize(&view_w, &view_h);
|
||||
return (SDL_Point){e->x * view_w, e->y * view_h};
|
||||
#else
|
||||
return (SDL_Point){(int)e->x, (int)e->y};
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool touch_is_on_view_edge(const SDL_TouchFingerEvent *e) {
|
||||
#if XSYSTEM35_SDL_VERSION == 2
|
||||
return e->x == 0.0f || e->x == 1.0f ||
|
||||
e->y == 0.0f || e->y == 1.0f;
|
||||
#else
|
||||
int view_w, view_h;
|
||||
gfx_getViewSize(&view_w, &view_h);
|
||||
return e->x <= 0.0f || e->x >= view_w ||
|
||||
e->y <= 0.0f || e->y >= view_h;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Stores a deferred touch event (valid if .timestamp != 0) to add a delay
|
||||
@@ -293,28 +326,33 @@ SDL_Point event_get_touch_position(const SDL_TouchFingerEvent *e) {
|
||||
// touch event. This prevents the game from processing a button down event
|
||||
// before reading the pointer position.
|
||||
static SDL_TouchFingerEvent deferred_touch_event;
|
||||
static uint64_t deferred_touch_deadline;
|
||||
#define TOUCH_EVENT_DELAY 20
|
||||
|
||||
static void defer_touch_event(SDL_TouchFingerEvent *e) {
|
||||
switch (deferred_touch_event.type) {
|
||||
case SDL_FINGERDOWN:
|
||||
case SDL_COMPAT_EVENT_FINGER_DOWN:
|
||||
mouseb |= 1 << SDL_BUTTON_LEFT;
|
||||
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_LEFT)] = true;
|
||||
break;
|
||||
case SDL_FINGERUP:
|
||||
case SDL_COMPAT_EVENT_FINGER_UP:
|
||||
mouseb &= ~(1 << SDL_BUTTON_LEFT | 1 << SDL_BUTTON_RIGHT);
|
||||
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_LEFT)] = false;
|
||||
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_RIGHT)] = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (e)
|
||||
if (e) {
|
||||
deferred_touch_event = *e;
|
||||
else
|
||||
deferred_touch_event.timestamp = 0;
|
||||
deferred_touch_deadline = SDL_GetTicks() + TOUCH_EVENT_DELAY;
|
||||
} else {
|
||||
deferred_touch_deadline = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void fire_deferred_touch_event(void) {
|
||||
if (deferred_touch_event.timestamp && deferred_touch_event.timestamp + TOUCH_EVENT_DELAY < SDL_GetTicks()) {
|
||||
if (deferred_touch_deadline && deferred_touch_deadline < SDL_GetTicks()) {
|
||||
defer_touch_event(NULL);
|
||||
}
|
||||
}
|
||||
@@ -332,41 +370,45 @@ static void rance4v2_hack(void) {
|
||||
}
|
||||
|
||||
void event_handle_event(SDL_Event *e) {
|
||||
if (!gfx_convertEventCoordinates(e))
|
||||
WARNING("Failed to convert event coordinates: %s", SDL_GetError());
|
||||
|
||||
if (modal_handle_event(e))
|
||||
return;
|
||||
|
||||
switch (sdl_get_window_event(e)) {
|
||||
case SDL_COMPAT_WINDOW_EVENT_EXPOSED:
|
||||
gfx_requestRedraw();
|
||||
return;
|
||||
case SDL_COMPAT_WINDOW_EVENT_FOCUS_LOST:
|
||||
volume_on_window_focus(false);
|
||||
return;
|
||||
case SDL_COMPAT_WINDOW_EVENT_FOCUS_GAINED:
|
||||
volume_on_window_focus(true);
|
||||
return;
|
||||
case SDL_COMPAT_WINDOW_EVENT_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (e->type) {
|
||||
case SDL_QUIT:
|
||||
case SDL_COMPAT_EVENT_QUIT:
|
||||
menu_quitmenu_open();
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (e->window.event) {
|
||||
case SDL_WINDOWEVENT_EXPOSED:
|
||||
gfx_requestRedraw();
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
volume_on_window_focus(false);
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
volume_on_window_focus(true);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) && XSYSTEM35_SDL_VERSION == 2
|
||||
case SDL_SYSWMEVENT:
|
||||
win_menu_onSysWMEvent(e->syswm.msg);
|
||||
if (e->syswm.msg->msg.win.msg == WM_COMMAND)
|
||||
win_menu_onCommand((unsigned int)e->syswm.msg->msg.win.wParam);
|
||||
break;
|
||||
#endif
|
||||
case SDL_APP_DIDENTERFOREGROUND:
|
||||
case SDL_COMPAT_EVENT_DID_ENTER_FOREGROUND:
|
||||
gfx_requestRedraw();
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_COMPAT_EVENT_KEY_DOWN:
|
||||
keyEventProsess(&e->key, true);
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
case SDL_COMPAT_EVENT_KEY_UP:
|
||||
keyEventProsess(&e->key, false);
|
||||
switch (e->key.keysym.sym) {
|
||||
switch (sdl_keyboard_event_key(&e->key)) {
|
||||
case SDLK_F1:
|
||||
msgskip_activate(!msgskip_isActivated());
|
||||
break;
|
||||
@@ -375,19 +417,19 @@ void event_handle_event(SDL_Event *e) {
|
||||
break;
|
||||
}
|
||||
#ifdef __ANDROID__
|
||||
if (e->key.keysym.scancode == SDL_SCANCODE_AC_BACK) {
|
||||
if (sdl_keyboard_event_scancode(&e->key) == SDL_SCANCODE_AC_BACK) {
|
||||
menu_quitmenu_open();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
case SDL_COMPAT_EVENT_MOUSE_MOTION:
|
||||
event_set_mouse_internal_location(e->motion.x, e->motion.y);
|
||||
#ifdef _WIN32
|
||||
win_menu_onMouseMotion(e->motion.x, e->motion.y);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case SDL_MOUSEWHEEL:
|
||||
case SDL_COMPAT_EVENT_MOUSE_WHEEL:
|
||||
{
|
||||
int y = e->wheel.y * (e->wheel.direction == SDL_MOUSEWHEEL_FLIPPED ? -1 : 1);
|
||||
if (y > 0)
|
||||
@@ -398,13 +440,13 @@ void event_handle_event(SDL_Event *e) {
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_COMPAT_EVENT_MOUSE_BUTTON_DOWN:
|
||||
mouseb |= (1 << e->button.button);
|
||||
RawKeyInfo[mouse_to_rawkey(e->button.button)] = true;
|
||||
send_agsevent(AGSEVENT_BUTTON_PRESS, mouse_to_agsevent(e->button.button));
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
case SDL_COMPAT_EVENT_MOUSE_BUTTON_UP:
|
||||
mouseb &= (0xffffffff ^ (1 << e->button.button));
|
||||
RawKeyInfo[mouse_to_rawkey(e->button.button)] = false;
|
||||
send_agsevent(AGSEVENT_BUTTON_RELEASE, mouse_to_agsevent(e->button.button));
|
||||
@@ -413,20 +455,22 @@ void event_handle_event(SDL_Event *e) {
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_FINGERDOWN:
|
||||
if (SDL_GetNumTouchFingers(e->tfinger.touchId) >= 3) {
|
||||
case SDL_COMPAT_EVENT_FINGER_DOWN: {
|
||||
int finger_count =
|
||||
sdl_get_num_touch_fingers(sdl_touch_event_id(&e->tfinger));
|
||||
if (finger_count >= 3) {
|
||||
menu_open();
|
||||
} else if (SDL_GetNumTouchFingers(e->tfinger.touchId) >= 2) {
|
||||
} else if (finger_count >= 2) {
|
||||
mouseb &= ~(1 << SDL_BUTTON_LEFT);
|
||||
mouseb |= 1 << SDL_BUTTON_RIGHT;
|
||||
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_LEFT)] = false;
|
||||
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_RIGHT)] = true;
|
||||
send_agsevent(AGSEVENT_BUTTON_PRESS, AGSEVENT_BUTTON_RIGHT);
|
||||
} else {
|
||||
// SDL_RendererEventWatch clamps touch locations outside of the
|
||||
// viewport to 0.0-1.0. Treat such events as right-clicks.
|
||||
// Treat touches on the edge or outside the viewport as
|
||||
// right-clicks.
|
||||
int button;
|
||||
if (e->tfinger.x == 0.0f || e->tfinger.x == 1.0f || e->tfinger.y == 0.0f || e->tfinger.y == 1.0f) {
|
||||
if (touch_is_on_view_edge(&e->tfinger)) {
|
||||
button = SDL_BUTTON_RIGHT;
|
||||
mouseb |= 1 << SDL_BUTTON_RIGHT;
|
||||
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_RIGHT)] = true;
|
||||
@@ -439,9 +483,10 @@ void event_handle_event(SDL_Event *e) {
|
||||
send_agsevent(AGSEVENT_BUTTON_PRESS, mouse_to_agsevent(button));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_FINGERUP:
|
||||
if (SDL_GetNumTouchFingers(e->tfinger.touchId) == 0) {
|
||||
case SDL_COMPAT_EVENT_FINGER_UP:
|
||||
if (sdl_get_num_touch_fingers(sdl_touch_event_id(&e->tfinger)) == 0) {
|
||||
int ags_button = (mouseb & 1 << SDL_BUTTON_LEFT) ? AGSEVENT_BUTTON_LEFT : AGSEVENT_BUTTON_RIGHT;
|
||||
mouse_pos = event_get_touch_position(&e->tfinger);
|
||||
send_agsevent(AGSEVENT_BUTTON_RELEASE, ags_button);
|
||||
@@ -449,16 +494,16 @@ void event_handle_event(SDL_Event *e) {
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_FINGERMOTION:
|
||||
case SDL_COMPAT_EVENT_FINGER_MOTION:
|
||||
mouse_pos = event_get_touch_position(&e->tfinger);
|
||||
send_agsevent(AGSEVENT_MOUSE_MOTION, 0);
|
||||
break;
|
||||
|
||||
case SDL_JOYDEVICEADDED:
|
||||
case SDL_COMPAT_EVENT_JOYSTICK_ADDED:
|
||||
joy_open(e->jdevice.which);
|
||||
break;
|
||||
|
||||
case SDL_JOYAXISMOTION:
|
||||
case SDL_COMPAT_EVENT_JOYSTICK_AXIS_MOTION:
|
||||
if (abs(e->jaxis.value) < 0x4000) {
|
||||
joyinfo &= e->jaxis.axis == 0 ? ~0xc : ~3;
|
||||
} else {
|
||||
@@ -468,10 +513,10 @@ void event_handle_event(SDL_Event *e) {
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_JOYBALLMOTION:
|
||||
case SDL_COMPAT_EVENT_JOYSTICK_BALL_MOTION:
|
||||
break;
|
||||
|
||||
case SDL_JOYHATMOTION:
|
||||
case SDL_COMPAT_EVENT_JOYSTICK_HAT_MOTION:
|
||||
joyinfo &= ~(SYS35KEY_UP | SYS35KEY_DOWN | SYS35KEY_LEFT | SYS35KEY_RIGHT);
|
||||
switch (e->jhat.value) {
|
||||
case SDL_HAT_UP: joyinfo |= SYS35KEY_UP; break;
|
||||
@@ -485,8 +530,8 @@ void event_handle_event(SDL_Event *e) {
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
case SDL_JOYBUTTONUP:
|
||||
case SDL_COMPAT_EVENT_JOYSTICK_BUTTON_DOWN:
|
||||
case SDL_COMPAT_EVENT_JOYSTICK_BUTTON_UP:
|
||||
{
|
||||
int mask = 0;
|
||||
switch (e->jbutton.button) {
|
||||
@@ -503,7 +548,7 @@ void event_handle_event(SDL_Event *e) {
|
||||
#endif
|
||||
}
|
||||
if (mask) {
|
||||
if (e->jbutton.state == SDL_PRESSED)
|
||||
if (sdl_joystick_button_event_pressed(&e->jbutton))
|
||||
joyinfo |= mask;
|
||||
else
|
||||
joyinfo &= ~mask;
|
||||
@@ -527,6 +572,11 @@ void event_handle_event(SDL_Event *e) {
|
||||
case DEBUGGER_COMMAND:
|
||||
dbg_post_command(e->user.data1);
|
||||
break;
|
||||
#if defined(_WIN32) && XSYSTEM35_SDL_VERSION == 3
|
||||
case WIN_MENU_COMMAND:
|
||||
win_menu_onCommand((unsigned int)(uintptr_t)e->user.data1);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -551,7 +601,7 @@ static void get_event(void) {
|
||||
|
||||
/* キー情報の取得 */
|
||||
static void keyEventProsess(SDL_KeyboardEvent *e, bool pressed) {
|
||||
int code = keytable[e->keysym.scancode];
|
||||
int code = keytable[sdl_keyboard_event_scancode(e)];
|
||||
RawKeyInfo[code] = pressed;
|
||||
send_agsevent(pressed ? AGSEVENT_KEY_PRESS : AGSEVENT_KEY_RELEASE, code);
|
||||
}
|
||||
@@ -620,6 +670,19 @@ void event_post_debugger_command(void *data) {
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && XSYSTEM35_SDL_VERSION == 3
|
||||
void event_post_win_menu_command(unsigned int command) {
|
||||
SDL_Event event = {
|
||||
.user = {
|
||||
.type = custom_event_type,
|
||||
.code = WIN_MENU_COMMAND,
|
||||
.data1 = (void *)(uintptr_t)command
|
||||
}
|
||||
};
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void simulate_right_button(int pressed) {
|
||||
|
||||
+4
-1
@@ -21,7 +21,7 @@
|
||||
#define __EVENT_H__
|
||||
|
||||
#include "config.h"
|
||||
#include <SDL_events.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "portab.h"
|
||||
|
||||
void event_init(void);
|
||||
@@ -36,6 +36,9 @@ void event_clear_wheel(void);
|
||||
int event_get_joy(void);
|
||||
void event_reset_input_state(void);
|
||||
void event_post_debugger_command(void *data);
|
||||
#if defined(_WIN32) && XSYSTEM35_SDL_VERSION == 3
|
||||
void event_post_win_menu_command(unsigned int command);
|
||||
#endif
|
||||
void event_handle_event(SDL_Event *e);
|
||||
|
||||
#endif // __EVENT_H__
|
||||
|
||||
+25
-21
@@ -21,7 +21,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
@@ -30,7 +30,7 @@
|
||||
#include FT_TRUETYPE_TABLES_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "win/resources.h"
|
||||
#include "win/resource_io.h"
|
||||
#endif
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
@@ -80,21 +80,25 @@ static struct {
|
||||
} this;
|
||||
|
||||
#if defined(_WIN32) || defined(__ANDROID__)
|
||||
// Create a face from the whole content of `rw`. The memory holding the font
|
||||
// Create a face from the whole content of `stream`. The memory holding the font
|
||||
// file must outlive the face, and faces are never closed, so it is never freed.
|
||||
static FT_Face face_from_rwops(SDL_RWops *rw, int index) {
|
||||
Sint64 size = SDL_RWsize(rw);
|
||||
static FT_Face face_from_io(sdl_iostream_t *stream, int index) {
|
||||
int64_t size = sdl_get_io_size(stream);
|
||||
if (size <= 0) {
|
||||
SDL_RWclose(rw);
|
||||
sdl_close_io(stream);
|
||||
return NULL;
|
||||
}
|
||||
FT_Byte *buf = malloc(size);
|
||||
if (SDL_RWread(rw, buf, 1, size) != (size_t)size) {
|
||||
free(buf);
|
||||
SDL_RWclose(rw);
|
||||
if (!buf) {
|
||||
sdl_close_io(stream);
|
||||
return NULL;
|
||||
}
|
||||
SDL_RWclose(rw);
|
||||
if (sdl_read_io(stream, buf, size) != (size_t)size) {
|
||||
free(buf);
|
||||
sdl_close_io(stream);
|
||||
return NULL;
|
||||
}
|
||||
sdl_close_io(stream);
|
||||
|
||||
FT_Face face;
|
||||
if (FT_New_Memory_Face(ft_library, buf, size, index, &face)) {
|
||||
@@ -118,22 +122,22 @@ static FT_Face open_face(const char *name, int index) {
|
||||
face = NULL;
|
||||
|
||||
if (!face) {
|
||||
SDL_RWops *res = open_resource(name, "fonts");
|
||||
sdl_iostream_t *res = open_resource(name, "fonts");
|
||||
if (res)
|
||||
face = face_from_rwops(res, index);
|
||||
face = face_from_io(res, index);
|
||||
}
|
||||
#endif
|
||||
if (!face && FT_New_Face(ft_library, name, index, &face))
|
||||
face = NULL; // FT_New_Face() does not clear `face` on failure.
|
||||
#ifdef __ANDROID__
|
||||
// The default fonts are stored as apk assets, which can only be opened
|
||||
// through SDL_RWFromFile. (It is not used as the first choice because it
|
||||
// through SDL's IO API. (It is not used as the first choice because it
|
||||
// does not resolve a relative path against the current directory, which a
|
||||
// custom font specified in .xsys35rc may use.)
|
||||
if (!face) {
|
||||
SDL_RWops *rw = SDL_RWFromFile(name, "rb");
|
||||
if (rw)
|
||||
face = face_from_rwops(rw, index);
|
||||
sdl_iostream_t *stream = sdl_io_from_file(name, "rb");
|
||||
if (stream)
|
||||
face = face_from_io(stream, index);
|
||||
}
|
||||
#endif
|
||||
if (face) {
|
||||
@@ -299,7 +303,7 @@ static int text_width(FontSpec spec, FT_Face face, const char *str_utf8, int len
|
||||
// (the glyph is drawn in `color`, its coverage scaled by `color.a` becoming the
|
||||
// alpha value) or an INDEX8 surface (covered pixels are set to the index 1).
|
||||
static void blit_glyph(const FT_Bitmap *bmp, SDL_Surface *dst, int x, int y, SDL_Color color) {
|
||||
int bpp = dst->format->BytesPerPixel;
|
||||
int bpp = sdl_surface_bytes_per_pixel(dst);
|
||||
uint32_t rgb = color.r << 16 | color.g << 8 | color.b;
|
||||
|
||||
for (unsigned int row = 0; row < bmp->rows; row++) {
|
||||
@@ -342,15 +346,15 @@ SDL_Surface *font_render_text(FontSpec spec, const char *str_utf8, SDL_Color col
|
||||
|
||||
SDL_Surface *sf;
|
||||
if (antialias) {
|
||||
sf = SDL_CreateRGBSurfaceWithFormat(0, width, font.height, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
sf = sdl_create_surface(width, font.height, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
if (sf)
|
||||
SDL_SetSurfaceBlendMode(sf, SDL_BLENDMODE_BLEND);
|
||||
} else {
|
||||
sf = SDL_CreateRGBSurfaceWithFormat(0, width, font.height, 8, SDL_PIXELFORMAT_INDEX8);
|
||||
sf = sdl_create_surface(width, font.height, 8, SDL_PIXELFORMAT_INDEX8);
|
||||
if (sf) {
|
||||
SDL_Color pal[2] = {{0, 0, 0, 0}, {color.r, color.g, color.b, 255}};
|
||||
SDL_SetPaletteColors(sf->format->palette, pal, 0, 2);
|
||||
SDL_SetColorKey(sf, SDL_TRUE, 0);
|
||||
SDL_SetPaletteColors(sdl_get_surface_palette(sf), pal, 0, 2);
|
||||
sdl_set_surface_color_key(sf, true, 0);
|
||||
}
|
||||
}
|
||||
if (!sf) {
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
#ifndef __FONT_H__
|
||||
#define __FONT_H__
|
||||
|
||||
#include <SDL_surface.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "cache.h"
|
||||
#include "config.h"
|
||||
#include "portab.h"
|
||||
|
||||
+7
-7
@@ -19,7 +19,7 @@
|
||||
#include "font.h"
|
||||
#include "unittest.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
static const FontType font_types[] = {FONT_GOTHIC, FONT_MINCHO};
|
||||
#define NTYPES ((int)(sizeof(font_types) / sizeof(font_types[0])))
|
||||
@@ -54,8 +54,8 @@ static int measure_h(FontSpec f) {
|
||||
static void assert_same_pixels(SDL_Surface *a, SDL_Surface *b) {
|
||||
ASSERT_EQUAL(a->w, b->w);
|
||||
ASSERT_EQUAL(a->h, b->h);
|
||||
ASSERT_EQUAL(a->format->format, b->format->format);
|
||||
int row_bytes = a->w * a->format->BytesPerPixel;
|
||||
ASSERT_EQUAL(sdl_surface_format(a), sdl_surface_format(b));
|
||||
int row_bytes = a->w * sdl_surface_bytes_per_pixel(a);
|
||||
for (int y = 0; y < a->h; y++) {
|
||||
const uint8_t *ap = (uint8_t *)a->pixels + y * a->pitch;
|
||||
const uint8_t *bp = (uint8_t *)b->pixels + y * b->pitch;
|
||||
@@ -73,8 +73,8 @@ static void glyph_cache_test(void) {
|
||||
ASSERT_TRUE(first);
|
||||
ASSERT_TRUE(cached);
|
||||
assert_same_pixels(first, cached);
|
||||
SDL_FreeSurface(first);
|
||||
SDL_FreeSurface(cached);
|
||||
sdl_destroy_surface(first);
|
||||
sdl_destroy_surface(cached);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,8 +98,8 @@ static void invariants_test(void) {
|
||||
// The antialiased surface goes to gfx_drawAntiAlias_8bpp(),
|
||||
// which reads a 32-bit alpha channel. The other one is
|
||||
// blitted onto the indexed screen surface as is.
|
||||
ASSERT_EQUAL(s->format->BitsPerPixel, aa ? 32 : 8);
|
||||
SDL_FreeSurface(s);
|
||||
ASSERT_EQUAL(sdl_surface_bits_per_pixel(s), aa ? 32 : 8);
|
||||
sdl_destroy_surface(s);
|
||||
}
|
||||
}
|
||||
// gfx_drawString() draws text by the top of the character cell,
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
#define __GFX_H__
|
||||
|
||||
#include "config.h"
|
||||
#include <SDL_surface.h>
|
||||
#include <SDL_video.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "portab.h"
|
||||
#include "ags.h"
|
||||
|
||||
@@ -35,6 +34,7 @@ void gfx_setWorldSize(int width, int height, int depth);
|
||||
void gfx_setViewSize(int w, int h);
|
||||
void gfx_getViewSize(int *width, int *height);
|
||||
SDL_Window *gfx_getWindow(void);
|
||||
bool gfx_convertEventCoordinates(SDL_Event *event);
|
||||
void gfx_warpMouse(int x, int y);
|
||||
SDL_Rect gfx_viewToWindowRect(SDL_Rect rect);
|
||||
void gfx_setWindowTitle(char *name);
|
||||
|
||||
+27
-32
@@ -27,7 +27,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
@@ -45,7 +45,6 @@
|
||||
#include "debugger.h"
|
||||
|
||||
static bool gfx_dirty;
|
||||
static bool gfx_effect_running;
|
||||
|
||||
static void gfx_pal_check(void) {
|
||||
if (nact->ags.pal_changed) {
|
||||
@@ -55,9 +54,10 @@ static void gfx_pal_check(void) {
|
||||
}
|
||||
|
||||
static Uint32 palette_color(uint8_t c) {
|
||||
if (main_surface->format->BitsPerPixel == 8)
|
||||
if (sdl_surface_bits_per_pixel(main_surface) == 8)
|
||||
return c;
|
||||
return SDL_MapRGB(main_surface->format, gfx_palette->colors[c].r, gfx_palette->colors[c].g, gfx_palette->colors[c].b);
|
||||
return sdl_map_rgb(main_surface, gfx_palette->colors[c].r,
|
||||
gfx_palette->colors[c].g, gfx_palette->colors[c].b);
|
||||
}
|
||||
|
||||
SDL_Color gfx_getPaletteColor(uint8_t color) {
|
||||
@@ -65,21 +65,15 @@ SDL_Color gfx_getPaletteColor(uint8_t color) {
|
||||
}
|
||||
|
||||
void gfx_updateScreen(void) {
|
||||
if (!gfx_dirty || gfx_effect_running)
|
||||
if (!gfx_dirty)
|
||||
return;
|
||||
SDL_RenderClear(gfx_renderer);
|
||||
SDL_RenderCopy(gfx_renderer, gfx_texture, NULL, NULL);
|
||||
sdl_render_texture(gfx_renderer, gfx_texture, NULL, NULL);
|
||||
modal_render_overlay();
|
||||
SDL_RenderPresent(gfx_renderer);
|
||||
gfx_dirty = false;
|
||||
}
|
||||
|
||||
void gfx_setEffectRunning(bool running) {
|
||||
gfx_effect_running = running;
|
||||
// Effects present directly. Do not replay pending redraws over their frames.
|
||||
gfx_dirty = false;
|
||||
}
|
||||
|
||||
void gfx_requestRedraw(void) {
|
||||
gfx_dirty = true;
|
||||
}
|
||||
@@ -140,11 +134,11 @@ void gfx_fillRectangle(int x, int y, int w, int h, uint8_t c) {
|
||||
}
|
||||
|
||||
void gfx_fillRectangleRGB(int x, int y, int w, int h, uint8_t r, uint8_t g, uint8_t b) {
|
||||
if (main_surface->format->BitsPerPixel == 8)
|
||||
if (sdl_surface_bits_per_pixel(main_surface) == 8)
|
||||
return;
|
||||
|
||||
SDL_Rect rect = {x, y, w, h};
|
||||
SDL_FillRect(main_surface, &rect, SDL_MapRGB(main_surface->format, r, g, b));
|
||||
SDL_FillRect(main_surface, &rect, sdl_map_rgb(main_surface, r, g, b));
|
||||
}
|
||||
|
||||
void gfx_fillCircle(int left, int top, int diameter, uint8_t c) {
|
||||
@@ -190,13 +184,13 @@ static void gfx_dib_sprite_copy(SDL_Surface *dst, int sx, int sy, int w, int h,
|
||||
gfx_pal_check();
|
||||
|
||||
Uint32 col = palette_color(sp);
|
||||
SDL_SetColorKey(main_surface, SDL_TRUE, col);
|
||||
sdl_set_surface_color_key(main_surface, true, col);
|
||||
|
||||
SDL_Rect r_src = {sx, sy, w, h};
|
||||
SDL_Rect r_dst = {dx, dy, w, h};
|
||||
|
||||
SDL_BlitSurface(main_surface, &r_src, dst, &r_dst);
|
||||
SDL_SetColorKey(main_surface, SDL_FALSE, 0);
|
||||
sdl_set_surface_color_key(main_surface, false, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -207,7 +201,7 @@ void gfx_copyAreaSP(int sx, int sy, int w, int h, int dx, int dy, uint8_t sp) {
|
||||
}
|
||||
|
||||
SDL_Surface *gfx_dib_to_surface_colorkey(int x, int y, int w, int h, int sp) {
|
||||
SDL_Surface *s = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_Surface *s = sdl_create_surface(w, h, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_FillRect(s, NULL, 0);
|
||||
gfx_dib_sprite_copy(s, x, y, w, h, 0, 0, sp);
|
||||
return s;
|
||||
@@ -216,24 +210,24 @@ SDL_Surface *gfx_dib_to_surface_colorkey(int x, int y, int w, int h, int sp) {
|
||||
void gfx_drawImage8(cgdata *cg, int dx, int dy, int sprite_color) {
|
||||
int w = cg->width;
|
||||
int h = cg->height;
|
||||
SDL_Surface *s = SDL_CreateRGBSurfaceWithFormatFrom(
|
||||
SDL_Surface *s = sdl_create_surface_from(
|
||||
cg->pic, w, h, 8, w, SDL_PIXELFORMAT_INDEX8);
|
||||
|
||||
gfx_pal_check();
|
||||
|
||||
if (main_surface->format->BitsPerPixel > 8 && cg->pal) {
|
||||
SDL_SetPaletteColors(s->format->palette, cg->pal, 0, 256);
|
||||
if (sdl_surface_bits_per_pixel(main_surface) > 8 && cg->pal) {
|
||||
SDL_SetPaletteColors(sdl_get_surface_palette(s), cg->pal, 0, 256);
|
||||
} else {
|
||||
SDL_SetSurfacePalette(s, gfx_palette);
|
||||
sdl_set_surface_palette(s, gfx_palette);
|
||||
}
|
||||
|
||||
if (sprite_color != -1)
|
||||
SDL_SetColorKey(s, SDL_TRUE, sprite_color);
|
||||
sdl_set_surface_color_key(s, true, sprite_color);
|
||||
|
||||
SDL_Rect r_dst = {dx, dy, w, h};
|
||||
|
||||
SDL_BlitSurface(s, NULL, main_surface, &r_dst);
|
||||
SDL_FreeSurface(s);
|
||||
sdl_destroy_surface(s);
|
||||
}
|
||||
|
||||
/* 直線描画 */
|
||||
@@ -242,7 +236,7 @@ void gfx_drawLine(int x1, int y1, int x2, int y2, uint8_t c) {
|
||||
|
||||
SDL_Renderer *renderer = SDL_CreateSoftwareRenderer(main_surface);
|
||||
SDL_SetRenderDrawColor(renderer, gfx_palette->colors[c].r, gfx_palette->colors[c].g, gfx_palette->colors[c].b, SDL_ALPHA_OPAQUE);
|
||||
SDL_RenderDrawLine(renderer, x1, y1, x2, y2);
|
||||
sdl_render_line_int(renderer, x1, y1, x2, y2);
|
||||
SDL_DestroyRenderer(renderer);
|
||||
}
|
||||
|
||||
@@ -259,7 +253,7 @@ void gfx_drawLine(int x1, int y1, int x2, int y2, uint8_t c) {
|
||||
SDL_Rect gfx_floodFill(int x, int y, int c) {
|
||||
Uint32 col = palette_color(c);
|
||||
|
||||
switch (main_surface->format->BytesPerPixel) {
|
||||
switch (sdl_surface_bytes_per_pixel(main_surface)) {
|
||||
case 1:
|
||||
return gfx_floodFill_uint8_t(x, y, col);
|
||||
case 2:
|
||||
@@ -301,7 +295,7 @@ void gfx_drawAntiAlias_8bpp(int dstx, int dsty, SDL_Surface *src, uint8_t col) {
|
||||
+ (dsty + y) * main_surface->pitch + dstx;
|
||||
for (int x = 0; x < src->w && dstx + x < main_surface->w; x++) {
|
||||
Uint8 r, g, b, alpha;
|
||||
SDL_GetRGBA(*(Uint32 *)sp, src->format, &r, &g, &b, &alpha);
|
||||
sdl_get_rgba(*(Uint32 *)sp, src, &r, &g, &b, &alpha);
|
||||
alpha >>= 5; // reduce bit depth
|
||||
if (!alpha) {
|
||||
// Transparent, do nothing.
|
||||
@@ -318,7 +312,7 @@ void gfx_drawAntiAlias_8bpp(int dstx, int dsty, SDL_Surface *src, uint8_t col) {
|
||||
cache[alpha << 8 | *dp] = c;
|
||||
*dp = c;
|
||||
}
|
||||
sp += src->format->BytesPerPixel;
|
||||
sp += sdl_surface_bytes_per_pixel(src);
|
||||
dp++;
|
||||
}
|
||||
}
|
||||
@@ -346,14 +340,14 @@ SDL_Rect gfx_drawString(int x, int y, const char *str_utf8, uint8_t col, FontSpe
|
||||
y -= font_cell_overhang(font);
|
||||
r_dst = (SDL_Rect){x, y, fs->w, fs->h};
|
||||
|
||||
if (main_surface->format->BitsPerPixel == 8 && antialias) {
|
||||
if (sdl_surface_bits_per_pixel(main_surface) == 8 && antialias) {
|
||||
gfx_drawAntiAlias_8bpp(x, y, fs, col);
|
||||
} else {
|
||||
SDL_Rect r_src = {0, 0, fs->w, fs->h};
|
||||
SDL_BlitSurface(fs, &r_src, main_surface, &r_dst);
|
||||
}
|
||||
|
||||
SDL_FreeSurface(fs);
|
||||
sdl_destroy_surface(fs);
|
||||
if (r_dst.y < 0) {
|
||||
r_dst.h += r_dst.y;
|
||||
r_dst.y = 0;
|
||||
@@ -365,8 +359,9 @@ SDL_Rect gfx_drawString(int x, int y, const char *str_utf8, uint8_t col, FontSpe
|
||||
* 指定範囲にパレット col を rate の割合で重ねる CK1
|
||||
*/
|
||||
void gfx_wrapColor(int sx, int sy, int w, int h, uint8_t c, int rate) {
|
||||
SDL_Surface *s = SDL_CreateRGBSurfaceWithFormat(0, w, h, main_surface->format->BitsPerPixel, main_surface->format->format);
|
||||
assert(s->format->BitsPerPixel > 8);
|
||||
SDL_Surface *s = sdl_create_surface(w, h,
|
||||
sdl_surface_bits_per_pixel(main_surface), sdl_surface_format(main_surface));
|
||||
assert(sdl_surface_bits_per_pixel(s) > 8);
|
||||
|
||||
SDL_Rect r_src = {0, 0, w, h};
|
||||
SDL_FillRect(s, &r_src, palette_color(c));
|
||||
@@ -375,5 +370,5 @@ void gfx_wrapColor(int sx, int sy, int w, int h, uint8_t c, int rate) {
|
||||
SDL_SetSurfaceAlphaMod(s, rate);
|
||||
SDL_Rect r_dst = {sx, sy, w, h};
|
||||
SDL_BlitSurface(s, &r_src, main_surface, &r_dst);
|
||||
SDL_FreeSurface(s);
|
||||
sdl_destroy_surface(s);
|
||||
}
|
||||
|
||||
+25
-21
@@ -24,7 +24,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
@@ -40,7 +40,7 @@ void gfx_FlipSurfaceHorizontal(SDL_Surface *s) {
|
||||
SDL_FlipSurface(s, SDL_FLIP_HORIZONTAL);
|
||||
#else
|
||||
uint8_t *p = s->pixels;
|
||||
int bpp = s->format->BytesPerPixel;
|
||||
int bpp = sdl_surface_bytes_per_pixel(s);
|
||||
uint8_t tmp[4];
|
||||
for (int y = 0; y < s->h; y++) {
|
||||
uint8_t *p1 = p;
|
||||
@@ -88,7 +88,7 @@ void gfx_scaledCopyArea(int sx, int sy, int sw, int sh, int dx, int dy, int dw,
|
||||
gfx_FlipSurfaceVertical(view);
|
||||
if (mirror & 2)
|
||||
gfx_FlipSurfaceHorizontal(view);
|
||||
SDL_FreeSurface(view);
|
||||
sdl_destroy_surface(view);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,19 +104,19 @@ void gfx_drawImage16(cgdata *cg, surface_t *sf, int dx, int dy, int brightness,
|
||||
if (cg->alpha && alpha_blend) {
|
||||
uint16_t *p_src = (uint16_t *)cg->pic;
|
||||
uint8_t *a_src = cg->alpha;
|
||||
s = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
s = sdl_create_surface(w, h, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
for (int y = 0; y < h; y++) {
|
||||
uint32_t *dst = (uint32_t *)(((uint8_t *)s->pixels) + y * s->pitch);
|
||||
for (int x = 0; x < w; x++) {
|
||||
*dst++ = rgb565_to_rgb888(*p_src++) | *a_src++ << 24;
|
||||
}
|
||||
}
|
||||
} else if (sf->sdl_surface->format->BitsPerPixel > 16) {
|
||||
} else if (sdl_surface_bits_per_pixel(sf->sdl_surface) > 16) {
|
||||
// Convert to RGB888 by ourselves. SDL blit functions use a slightly
|
||||
// different mapping, so the color expanded by SDL may not match the
|
||||
// color key specified in the CX command.
|
||||
uint16_t *src = (uint16_t *)cg->pic;
|
||||
s = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_RGB888);
|
||||
s = sdl_create_surface(w, h, 32, SDL_PIXELFORMAT_RGB888);
|
||||
for (int y = 0; y < h; y++) {
|
||||
uint32_t *dst = (uint32_t *)(((uint8_t *)s->pixels) + y * s->pitch);
|
||||
for (int x = 0; x < w; x++) {
|
||||
@@ -124,8 +124,8 @@ void gfx_drawImage16(cgdata *cg, surface_t *sf, int dx, int dy, int brightness,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s = SDL_CreateRGBSurfaceWithFormatFrom(
|
||||
cg->pic, cg->width, cg->height, 16, cg->width * 2, SDL_PIXELFORMAT_RGB565);
|
||||
s = sdl_create_surface_from(cg->pic, cg->width, cg->height,
|
||||
16, cg->width * 2, SDL_PIXELFORMAT_RGB565);
|
||||
}
|
||||
|
||||
if (brightness != 255)
|
||||
@@ -133,7 +133,7 @@ void gfx_drawImage16(cgdata *cg, surface_t *sf, int dx, int dy, int brightness,
|
||||
|
||||
SDL_Rect r_dst = {dx, dy, w, h};
|
||||
SDL_BlitSurface(s, NULL, sf->sdl_surface, &r_dst);
|
||||
SDL_FreeSurface(s);
|
||||
sdl_destroy_surface(s);
|
||||
}
|
||||
|
||||
void gfx_drawImage24(cgdata *cg, surface_t *sf, int x, int y, int brightness) {
|
||||
@@ -141,14 +141,14 @@ void gfx_drawImage24(cgdata *cg, surface_t *sf, int x, int y, int brightness) {
|
||||
gfx_drawImageAlphaMap(cg, sf, x, y);
|
||||
}
|
||||
|
||||
SDL_Surface *s = SDL_CreateRGBSurfaceWithFormatFrom(
|
||||
cg->pic, cg->width, cg->height, 24, cg->width * 3, SDL_PIXELFORMAT_RGB24);
|
||||
SDL_Surface *s = sdl_create_surface_from(cg->pic, cg->width, cg->height,
|
||||
24, cg->width * 3, SDL_PIXELFORMAT_RGB24);
|
||||
if (brightness != 255)
|
||||
SDL_SetSurfaceColorMod(s, brightness, brightness, brightness);
|
||||
|
||||
SDL_Rect r_dst = {x, y, cg->width, cg->height};
|
||||
SDL_BlitSurface(s, NULL, sf->sdl_surface, &r_dst);
|
||||
SDL_FreeSurface(s);
|
||||
sdl_destroy_surface(s);
|
||||
}
|
||||
|
||||
void gfx_drawImageAlphaMap(cgdata *cg, surface_t *sf, int x, int y) {
|
||||
@@ -165,7 +165,7 @@ void gfx_drawImageAlphaMap(cgdata *cg, surface_t *sf, int x, int y) {
|
||||
}
|
||||
|
||||
SDL_Surface *gfx_dib_to_surface_with_alpha(int x, int y, int w, int h) {
|
||||
SDL_Surface *s = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_Surface *s = sdl_create_surface(w, h, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
|
||||
SDL_Rect r_src = {x, y, w, h};
|
||||
SDL_BlitSurface(main_surface, &r_src, s, NULL);
|
||||
@@ -192,7 +192,7 @@ void gfx_copyAreaSP16_shadow(int sx, int sy, int w, int h, int dx, int dy, int l
|
||||
|
||||
SDL_Rect r_dst = {dx, dy, w, h};
|
||||
SDL_BlitSurface(s, NULL, main_surface, &r_dst);
|
||||
SDL_FreeSurface(s);
|
||||
sdl_destroy_surface(s);
|
||||
}
|
||||
|
||||
void gfx_copyAreaSP16_alphaBlend(int sx, int sy, int w, int h, int dx, int dy, int lv) {
|
||||
@@ -230,7 +230,7 @@ void gfx_copy_to_alpha(int sx, int sy, int w, int h, int dx, int dy, ALPHA_DIB_C
|
||||
uint32_t gfx_getPixel(int x, int y) {
|
||||
uint8_t *p = PIXEL_AT(main_surface, x, y);
|
||||
|
||||
switch (main_surface->format->BytesPerPixel) {
|
||||
switch (sdl_surface_bytes_per_pixel(main_surface)) {
|
||||
case 1:
|
||||
return *p;
|
||||
case 2:
|
||||
@@ -252,10 +252,14 @@ uint32_t gfx_getPixel(int x, int y) {
|
||||
* dib から領域の切り出し
|
||||
*/
|
||||
void* gfx_saveRegion(int x, int y, int w, int h) {
|
||||
SDL_Surface *s = SDL_CreateRGBSurfaceWithFormat(0, w, h, main_surface->format->BitsPerPixel, main_surface->format->format);
|
||||
if (main_surface->format->BitsPerPixel == 8)
|
||||
memcpy(s->format->palette->colors, main_surface->format->palette->colors,
|
||||
sizeof(SDL_Color) * main_surface->format->palette->ncolors);
|
||||
SDL_Surface *s = sdl_create_surface(w, h,
|
||||
sdl_surface_bits_per_pixel(main_surface), sdl_surface_format(main_surface));
|
||||
if (sdl_surface_bits_per_pixel(main_surface) == 8) {
|
||||
SDL_Palette *dst_palette = sdl_get_surface_palette(s);
|
||||
SDL_Palette *src_palette = sdl_get_surface_palette(main_surface);
|
||||
SDL_SetPaletteColors(dst_palette, src_palette->colors, 0,
|
||||
src_palette->ncolors);
|
||||
}
|
||||
SDL_Rect r_src = {x, y, w, h};
|
||||
SDL_Rect r_dst = {0, 0, w, h};
|
||||
SDL_BlitSurface(main_surface, &r_src, s, &r_dst);
|
||||
@@ -266,7 +270,7 @@ void* gfx_saveRegion(int x, int y, int w, int h) {
|
||||
* セーブした領域を破棄
|
||||
*/
|
||||
void gfx_delRegion(void *psrc) {
|
||||
SDL_FreeSurface((SDL_Surface *)psrc);
|
||||
sdl_destroy_surface((SDL_Surface *)psrc);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -287,5 +291,5 @@ void gfx_putRegion(void *psrc, int x, int y) {
|
||||
void gfx_restoreRegion(void *psrc, int x, int y) {
|
||||
SDL_Surface *src = (SDL_Surface *)psrc;
|
||||
gfx_putRegion(src, x ,y);
|
||||
SDL_FreeSurface(src);
|
||||
sdl_destroy_surface(src);
|
||||
}
|
||||
|
||||
+1
-3
@@ -25,7 +25,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "ags.h"
|
||||
@@ -37,8 +37,6 @@ extern surface_t *gfx_dibinfo;
|
||||
extern int view_w;
|
||||
extern int view_h;
|
||||
|
||||
void gfx_setEffectRunning(bool running);
|
||||
|
||||
SDL_Surface *gfx_dib_to_surface_with_alpha(int x, int y, int w, int h);
|
||||
SDL_Surface *gfx_dib_to_surface_colorkey(int x, int y, int w, int h, int col);
|
||||
|
||||
|
||||
+43
-28
@@ -23,7 +23,8 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <SDL.h>
|
||||
#include <stdlib.h>
|
||||
#include "sdl_compat.h"
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/html5.h>
|
||||
@@ -45,6 +46,7 @@ surface_t *gfx_dibinfo;
|
||||
int view_w;
|
||||
int view_h;
|
||||
static bool gfx_fullscreen;
|
||||
static bool gfx_integer_scaling;
|
||||
|
||||
static void window_init(const char *render_driver);
|
||||
static void makeDIB(int width, int height, int depth);
|
||||
@@ -68,9 +70,9 @@ int gfx_Initialize(const char *render_driver) {
|
||||
|
||||
void gfx_Remove(void) {
|
||||
if (gfx_palette)
|
||||
SDL_FreePalette(gfx_palette);
|
||||
sdl_destroy_palette(gfx_palette);
|
||||
if (main_surface)
|
||||
SDL_FreeSurface(main_surface);
|
||||
sdl_destroy_surface(main_surface);
|
||||
if (gfx_renderer)
|
||||
SDL_DestroyRenderer(gfx_renderer);
|
||||
SDL_Quit();
|
||||
@@ -91,7 +93,9 @@ static void window_init(const char *render_driver) {
|
||||
if (render_driver)
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, render_driver);
|
||||
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
|
||||
#if XSYSTEM35_SDL_VERSION == 2
|
||||
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
|
||||
#endif
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
|
||||
@@ -108,21 +112,19 @@ static void window_init(const char *render_driver) {
|
||||
|
||||
#ifdef __ANDROID__
|
||||
SDL_SetHint(SDL_HINT_ANDROID_TRAP_BACK_BUTTON, "1");
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
|
||||
#endif
|
||||
|
||||
gfx_window = SDL_CreateWindow(
|
||||
title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
SYS35_DEFAULT_WIDTH, SYS35_DEFAULT_HEIGHT, SDL_WINDOW_RESIZABLE);
|
||||
gfx_renderer = SDL_CreateRenderer(gfx_window, -1, 0);
|
||||
gfx_window = sdl_create_window(title, SYS35_DEFAULT_WIDTH,
|
||||
SYS35_DEFAULT_HEIGHT, SDL_WINDOW_RESIZABLE);
|
||||
gfx_renderer = sdl_create_renderer(gfx_window);
|
||||
SDL_SetRenderDrawColor(gfx_renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
|
||||
gfx_palette = SDL_AllocPalette(256);
|
||||
gfx_palette = sdl_create_palette(256);
|
||||
}
|
||||
|
||||
static void makeDIB(int width, int height, int depth) {
|
||||
|
||||
if (main_surface) {
|
||||
SDL_FreeSurface(main_surface);
|
||||
sdl_destroy_surface(main_surface);
|
||||
}
|
||||
|
||||
uint32_t format = 0;
|
||||
@@ -142,10 +144,10 @@ static void makeDIB(int width, int height, int depth) {
|
||||
SYSERROR("invalid pixel depth %d", depth);
|
||||
}
|
||||
|
||||
main_surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, depth, format);
|
||||
main_surface = sdl_create_surface(width, height, depth, format);
|
||||
|
||||
if (main_surface->format->BitsPerPixel == 8) {
|
||||
SDL_SetSurfacePalette(main_surface, gfx_palette);
|
||||
if (sdl_surface_bits_per_pixel(main_surface) == 8) {
|
||||
sdl_set_surface_palette(main_surface, gfx_palette);
|
||||
}
|
||||
|
||||
if (gfx_dibinfo) {
|
||||
@@ -158,7 +160,7 @@ static void makeDIB(int width, int height, int depth) {
|
||||
gfx_dibinfo->alpha = NULL;
|
||||
gfx_dibinfo->sdl_surface = main_surface;
|
||||
|
||||
image_setdepth(main_surface->format->BitsPerPixel);
|
||||
image_setdepth(sdl_surface_bits_per_pixel(main_surface));
|
||||
}
|
||||
|
||||
/* offscreen の設定 */
|
||||
@@ -178,10 +180,16 @@ SDL_Window *gfx_getWindow(void) {
|
||||
return gfx_window;
|
||||
}
|
||||
|
||||
bool gfx_convertEventCoordinates(SDL_Event *event) {
|
||||
#if XSYSTEM35_SDL_VERSION == 2
|
||||
return true;
|
||||
#else
|
||||
return SDL_ConvertEventToRenderCoordinates(gfx_renderer, event);
|
||||
#endif
|
||||
}
|
||||
|
||||
static SDL_Point view_to_window_point(int x, int y) {
|
||||
SDL_Point point;
|
||||
SDL_RenderLogicalToWindow(gfx_renderer, x, y, &point.x, &point.y);
|
||||
return point;
|
||||
return sdl_render_coordinates_to_window(gfx_renderer, x, y);
|
||||
}
|
||||
|
||||
void gfx_warpMouse(int x, int y) {
|
||||
@@ -202,11 +210,12 @@ surface_t *gfx_getDIB(void) {
|
||||
|
||||
SDL_Surface *gfx_createSurfaceView(SDL_Surface *sf, int x, int y, int w, int h) {
|
||||
uint8_t *pixels = sf->pixels;
|
||||
pixels += y * sf->pitch + x * sf->format->BytesPerPixel;
|
||||
SDL_Surface *view = SDL_CreateRGBSurfaceWithFormatFrom(
|
||||
pixels, w, h, sf->format->BitsPerPixel, sf->pitch, sf->format->format);
|
||||
if (sf->format->palette)
|
||||
SDL_SetSurfacePalette(view, sf->format->palette);
|
||||
pixels += y * sf->pitch + x * sdl_surface_bytes_per_pixel(sf);
|
||||
SDL_Surface *view = sdl_create_surface_from(pixels, w, h,
|
||||
sdl_surface_bits_per_pixel(sf), sf->pitch, sdl_surface_format(sf));
|
||||
SDL_Palette *palette = sdl_get_surface_palette(sf);
|
||||
if (palette)
|
||||
sdl_set_surface_palette(view, palette);
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -214,7 +223,7 @@ void gfx_setFullscreen(bool on) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (on == gfx_fullscreen)
|
||||
return;
|
||||
SDL_SetWindowFullscreen(gfx_window, on ? SDL_WINDOW_FULLSCREEN_DESKTOP: 0);
|
||||
sdl_set_window_fullscreen(gfx_window, on);
|
||||
gfx_fullscreen = on;
|
||||
#endif
|
||||
}
|
||||
@@ -234,13 +243,17 @@ void gfx_setViewSize(int w, int h) {
|
||||
view_h = h;
|
||||
|
||||
#ifndef __ANDROID__
|
||||
SDL_SetWindowSize(gfx_window, w, h);
|
||||
sdl_set_window_content_size(gfx_window, w, h);
|
||||
#endif
|
||||
SDL_RenderSetLogicalSize(gfx_renderer, w, h);
|
||||
sdl_set_render_logical_presentation(
|
||||
gfx_renderer, w, h, gfx_integer_scaling);
|
||||
if (gfx_texture)
|
||||
SDL_DestroyTexture(gfx_texture);
|
||||
gfx_texture = SDL_CreateTexture(
|
||||
gfx_renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_STREAMING, w, h);
|
||||
#ifdef __ANDROID__
|
||||
SDL_SetTextureScaleMode(gfx_texture, SDL_SCALEMODE_LINEAR);
|
||||
#endif
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM( xsystem35.shell.windowSizeChanged(); );
|
||||
@@ -248,13 +261,15 @@ void gfx_setViewSize(int w, int h) {
|
||||
}
|
||||
|
||||
void gfx_setIntegerScaling(bool enable) {
|
||||
SDL_RenderSetIntegerScale(gfx_renderer, enable);
|
||||
gfx_integer_scaling = enable;
|
||||
sdl_set_render_logical_presentation(
|
||||
gfx_renderer, view_w, view_h, gfx_integer_scaling);
|
||||
}
|
||||
|
||||
bool EMSCRIPTEN_KEEPALIVE save_screenshot(const char* path) {
|
||||
SDL_Rect *r = &nact->ags.view_area;
|
||||
SDL_Surface *view = gfx_createSurfaceView(main_surface, r->x, r->y, r->w, r->h);
|
||||
bool ok = SDL_SaveBMP(view, path) == 0;
|
||||
SDL_FreeSurface(view);
|
||||
bool ok = sdl_save_bmp(view, path);
|
||||
sdl_destroy_surface(view);
|
||||
return ok;
|
||||
}
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
#ifndef __IMAGE__
|
||||
#define __IMAGE__
|
||||
|
||||
#include <SDL_surface.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "cg.h"
|
||||
#include "ags.h"
|
||||
|
||||
|
||||
+6
-6
@@ -30,7 +30,7 @@
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#include <jni.h>
|
||||
#include "system.h"
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
bool input_modal_string(INPUTSTRING_PARAM *p) {
|
||||
static char buf[256];
|
||||
JNIEnv *env = SDL_AndroidGetJNIEnv();
|
||||
JNIEnv *env = SDL_GetAndroidJNIEnv();
|
||||
if ((*env)->PushLocalFrame(env, 16) < 0) {
|
||||
WARNING("Failed to allocate JVM local references");
|
||||
return false;
|
||||
@@ -52,7 +52,7 @@ bool input_modal_string(INPUTSTRING_PARAM *p) {
|
||||
return false;
|
||||
}
|
||||
|
||||
jobject context = SDL_AndroidGetActivity();
|
||||
jobject context = SDL_GetAndroidActivity();
|
||||
jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
|
||||
"inputString", "(" STRING STRING "I)" STRING);
|
||||
jstring newstring = (jstring)(*env)->CallObjectMethod(env, context, mid, msg, oldstring, p->max);
|
||||
@@ -76,7 +76,7 @@ bool input_modal_string_inline(INPUTSTRING_PARAM *p) {
|
||||
}
|
||||
|
||||
bool input_modal_number(INPUTNUM_PARAM *p) {
|
||||
JNIEnv *env = SDL_AndroidGetJNIEnv();
|
||||
JNIEnv *env = SDL_GetAndroidJNIEnv();
|
||||
if ((*env)->PushLocalFrame(env, 16) < 0) {
|
||||
WARNING("Failed to allocate JVM local references");
|
||||
return false;
|
||||
@@ -89,7 +89,7 @@ bool input_modal_number(INPUTNUM_PARAM *p) {
|
||||
return false;
|
||||
}
|
||||
|
||||
jobject context = SDL_AndroidGetActivity();
|
||||
jobject context = SDL_GetAndroidActivity();
|
||||
jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
|
||||
"inputNumber", "(" STRING "III)I");
|
||||
int v = (*env)->CallIntMethod(env, context, mid, msg, p->min, p->max, p->def);
|
||||
@@ -139,7 +139,7 @@ bool input_modal_number(INPUTNUM_PARAM *p) {
|
||||
|
||||
#else // not Android or Emscripten
|
||||
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "nact.h"
|
||||
#include "font.h"
|
||||
|
||||
+6
-6
@@ -50,7 +50,7 @@ EM_JS(void, menu_setSkipState, (bool enabled, bool activated), {
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "event.h"
|
||||
@@ -97,15 +97,15 @@ static bool point_in_rect(int x, int y, mu_Rect r) {
|
||||
|
||||
static bool menu_popup_handler(const SDL_Event *e, modal *modal) {
|
||||
struct popup_state *st = (struct popup_state *)modal;
|
||||
if (e->type == SDL_MOUSEBUTTONUP) {
|
||||
if (e->type == SDL_COMPAT_EVENT_MOUSE_BUTTON_UP) {
|
||||
if (e->button.button == SDL_BUTTON_RIGHT ||
|
||||
!point_in_rect(e->button.x, e->button.y, st->rect))
|
||||
st->base.cancelled = true;
|
||||
} else if (e->type == SDL_FINGERUP) {
|
||||
} else if (e->type == SDL_COMPAT_EVENT_FINGER_UP) {
|
||||
// Arm tap-to-dismiss once the opening gesture's fingers are all lifted.
|
||||
if (SDL_GetNumTouchFingers(e->tfinger.touchId) == 0)
|
||||
if (sdl_get_num_touch_fingers(sdl_touch_event_id(&e->tfinger)) == 0)
|
||||
st->touch_armed = true;
|
||||
} else if (e->type == SDL_FINGERDOWN && st->touch_armed) {
|
||||
} else if (e->type == SDL_COMPAT_EVENT_FINGER_DOWN && st->touch_armed) {
|
||||
// A tap outside the menu dismisses it.
|
||||
SDL_Point p = event_get_touch_position(&e->tfinger);
|
||||
if (!point_in_rect(p.x, p.y, st->rect))
|
||||
@@ -207,7 +207,7 @@ static bool confirm_lose_progress(const char *title, const char *confirm_label)
|
||||
.buttons = buttons,
|
||||
};
|
||||
int buttonid = 0;
|
||||
if (SDL_ShowMessageBox(&messagebox_data, &buttonid) < 0) {
|
||||
if (!sdl_show_message_box(&messagebox_data, &buttonid)) {
|
||||
WARNING("error displaying message box");
|
||||
return false;
|
||||
}
|
||||
|
||||
+1
-2
@@ -32,8 +32,7 @@ extern void menu_init();
|
||||
extern void menu_setSkipState(bool enabled, bool activated);
|
||||
|
||||
#ifdef _WIN32
|
||||
struct SDL_SysWMmsg;
|
||||
void win_menu_onSysWMEvent(struct SDL_SysWMmsg* msg);
|
||||
void win_menu_onCommand(unsigned int command);
|
||||
void win_menu_onMouseMotion(int x, int y);
|
||||
#endif
|
||||
|
||||
|
||||
+8
-8
@@ -17,7 +17,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#include <jni.h>
|
||||
#include "system.h"
|
||||
#include "portab.h"
|
||||
@@ -38,14 +38,14 @@ static void midi_reset(void) {
|
||||
}
|
||||
|
||||
static bool midi_start(int no, int loop, const uint8_t *data, int datalen) {
|
||||
JNIEnv *env = SDL_AndroidGetJNIEnv();
|
||||
JNIEnv *env = SDL_GetAndroidJNIEnv();
|
||||
if ((*env)->PushLocalFrame(env, 16) < 0) {
|
||||
WARNING("Failed to allocate JVM local references");
|
||||
return false;
|
||||
}
|
||||
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, PATH_MAX, "%s/tmp.mid", SDL_AndroidGetInternalStoragePath());
|
||||
snprintf(path, PATH_MAX, "%s/tmp.mid", SDL_GetAndroidInternalStoragePath());
|
||||
FILE* fp = fopen(path, "w");
|
||||
if (!fp) {
|
||||
WARNING("Failed to create temporary file");
|
||||
@@ -62,7 +62,7 @@ static bool midi_start(int no, int loop, const uint8_t *data, int datalen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
jobject context = SDL_AndroidGetActivity();
|
||||
jobject context = SDL_GetAndroidActivity();
|
||||
jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
|
||||
"midiStart", "(Ljava/lang/String;Z)V");
|
||||
(*env)->CallVoidMethod(env, context, mid, path_str, loop == 0);
|
||||
@@ -72,12 +72,12 @@ static bool midi_start(int no, int loop, const uint8_t *data, int datalen) {
|
||||
}
|
||||
|
||||
static void midi_stop(void) {
|
||||
JNIEnv *env = SDL_AndroidGetJNIEnv();
|
||||
JNIEnv *env = SDL_GetAndroidJNIEnv();
|
||||
if ((*env)->PushLocalFrame(env, 16) < 0) {
|
||||
WARNING("Failed to allocate JVM local references");
|
||||
return;
|
||||
}
|
||||
jobject context = SDL_AndroidGetActivity();
|
||||
jobject context = SDL_GetAndroidActivity();
|
||||
jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
|
||||
"midiStop", "()V");
|
||||
(*env)->CallVoidMethod(env, context, mid);
|
||||
@@ -96,12 +96,12 @@ static bool midi_get_playing_info(midiplaystate *st) {
|
||||
st->in_play = false;
|
||||
st->loc_ms = 0;
|
||||
|
||||
JNIEnv *env = SDL_AndroidGetJNIEnv();
|
||||
JNIEnv *env = SDL_GetAndroidJNIEnv();
|
||||
if ((*env)->PushLocalFrame(env, 16) < 0) {
|
||||
WARNING("Failed to allocate JVM local references");
|
||||
return false;
|
||||
}
|
||||
jobject context = SDL_AndroidGetActivity();
|
||||
jobject context = SDL_GetAndroidActivity();
|
||||
jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
|
||||
"midiCurrentPosition", "()I");
|
||||
int pos = (*env)->CallIntMethod(env, context, mid);
|
||||
|
||||
+7
-8
@@ -24,10 +24,9 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <portmidi.h>
|
||||
#include <SDL_atomic.h>
|
||||
#include <SDL_thread.h>
|
||||
#include <SDL_timer.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
@@ -63,7 +62,7 @@ static PmDeviceID device_id;
|
||||
static SDL_Thread *thread;
|
||||
static struct msgq *queue;
|
||||
static int start_time;
|
||||
static SDL_atomic_t atomic_seq;
|
||||
static sdl_atomic_int_t atomic_seq;
|
||||
|
||||
static void enqueue(struct midi_message msg) {
|
||||
struct midi_message *buf = malloc(sizeof(struct midi_message));
|
||||
@@ -227,7 +226,7 @@ static int midi_thread(void* unused) {
|
||||
midi_playloop(stream, msg.music);
|
||||
send_reset(stream);
|
||||
}
|
||||
SDL_AtomicCAS(&atomic_seq, msg.seq, 0);
|
||||
sdl_compare_and_swap_atomic_int(&atomic_seq, msg.seq, 0);
|
||||
mf_remove_midifile(msg.music);
|
||||
break;
|
||||
|
||||
@@ -300,7 +299,7 @@ static bool midi_start(int no, int loop, const uint8_t *data, int datalen) {
|
||||
WARNING("error reading midi file");
|
||||
return false;
|
||||
}
|
||||
SDL_AtomicSet(&atomic_seq, ++seq);
|
||||
sdl_set_atomic_int(&atomic_seq, ++seq);
|
||||
ENQUEUE(CMD_PLAY, seq, midi);
|
||||
|
||||
start_time = SDL_GetTicks();
|
||||
@@ -310,7 +309,7 @@ static bool midi_start(int no, int loop, const uint8_t *data, int datalen) {
|
||||
|
||||
static void midi_stop(void) {
|
||||
if (queue) {
|
||||
SDL_AtomicSet(&atomic_seq, 0);
|
||||
sdl_set_atomic_int(&atomic_seq, 0);
|
||||
ENQUEUE(CMD_STOP);
|
||||
}
|
||||
}
|
||||
@@ -326,7 +325,7 @@ static void midi_unpause(void) {
|
||||
}
|
||||
|
||||
static bool midi_get_playing_info(midiplaystate *st) {
|
||||
if (SDL_AtomicGet(&atomic_seq) == 0) {
|
||||
if (sdl_get_atomic_int(&atomic_seq) == 0) {
|
||||
st->in_play = false;
|
||||
st->loc_ms = 0;
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* midi.sdl3mixer.c MIDI playback using SDL3_mixer
|
||||
*
|
||||
* Copyright (C) 2019 <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "midi.h"
|
||||
#include "music_private.h"
|
||||
#include "sdl3_mixer_backend.h"
|
||||
#include "system.h"
|
||||
|
||||
#define FLUIDSYNTH_SOUNDFONT_PATH \
|
||||
"SDL_mixer.decoder.fluidsynth.soundfont_path"
|
||||
|
||||
static uint32_t start_time;
|
||||
static uint32_t fade_tick;
|
||||
static int current_vol = 100;
|
||||
|
||||
static bool has_decoder(const char *name)
|
||||
{
|
||||
for (int i = 0; i < MIX_GetNumAudioDecoders(); i++) {
|
||||
if (!strcmp(MIX_GetAudioDecoder(i), name))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int clamp_volume(int volume)
|
||||
{
|
||||
if (volume < 0)
|
||||
return 0;
|
||||
if (volume > 100)
|
||||
return 100;
|
||||
return volume;
|
||||
}
|
||||
|
||||
static float music_gain(void)
|
||||
{
|
||||
float gain = clamp_volume(current_vol) / 100.0f;
|
||||
return gain * clamp_volume(prv.volval[BGM_VOLVAL_CH]) / 100.0f;
|
||||
}
|
||||
|
||||
static void apply_volume(void)
|
||||
{
|
||||
sdl3_mixer_set_music_gain(SDL3_MIXER_MUSIC_MIDI, music_gain());
|
||||
}
|
||||
|
||||
static char *last_soundfont_path(const char *paths)
|
||||
{
|
||||
const char *last = NULL;
|
||||
size_t last_length = 0;
|
||||
const char *segment = paths;
|
||||
for (const char *p = paths;; p++) {
|
||||
if (*p != ';' && *p)
|
||||
continue;
|
||||
if (p != segment) {
|
||||
last = segment;
|
||||
last_length = p - segment;
|
||||
}
|
||||
if (!*p)
|
||||
break;
|
||||
segment = p + 1;
|
||||
}
|
||||
return last ? SDL_strndup(last, last_length) : NULL;
|
||||
}
|
||||
|
||||
static MIX_Audio *load_midi(const uint8_t *data, int length)
|
||||
{
|
||||
if (length <= 0)
|
||||
return NULL;
|
||||
SDL_IOStream *io = SDL_IOFromConstMem(data, length);
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
if (!io || !props) {
|
||||
if (io)
|
||||
SDL_CloseIO(io);
|
||||
if (props)
|
||||
SDL_DestroyProperties(props);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool success =
|
||||
SDL_SetPointerProperty(props,
|
||||
MIX_PROP_AUDIO_LOAD_IOSTREAM_POINTER, io) &&
|
||||
SDL_SetBooleanProperty(props,
|
||||
MIX_PROP_AUDIO_LOAD_CLOSEIO_BOOLEAN, true) &&
|
||||
SDL_SetPointerProperty(props,
|
||||
MIX_PROP_AUDIO_LOAD_PREFERRED_MIXER_POINTER,
|
||||
sdl3_mixer_backend_get());
|
||||
|
||||
const char *soundfonts = getenv("SDL_SOUNDFONTS");
|
||||
char *soundfont_path = NULL;
|
||||
if (success && soundfonts && *soundfonts && has_decoder("FLUIDSYNTH")) {
|
||||
soundfont_path = last_soundfont_path(soundfonts);
|
||||
success = soundfont_path &&
|
||||
SDL_SetStringProperty(props, MIX_PROP_AUDIO_DECODER_STRING,
|
||||
"FLUIDSYNTH") &&
|
||||
SDL_SetStringProperty(props, FLUIDSYNTH_SOUNDFONT_PATH,
|
||||
soundfont_path);
|
||||
}
|
||||
|
||||
MIX_Audio *audio = success ? MIX_LoadAudioWithProperties(props) : NULL;
|
||||
if (!success)
|
||||
SDL_CloseIO(io);
|
||||
SDL_free(soundfont_path);
|
||||
SDL_DestroyProperties(props);
|
||||
return audio;
|
||||
}
|
||||
|
||||
static bool midi_initialize(int subdevice)
|
||||
{
|
||||
(void)subdevice;
|
||||
return sdl3_mixer_backend_get() &&
|
||||
(has_decoder("FLUIDSYNTH") || has_decoder("TIMIDITY"));
|
||||
}
|
||||
|
||||
static void midi_stop(void)
|
||||
{
|
||||
sdl3_mixer_stop_music(SDL3_MIXER_MUSIC_MIDI, 0);
|
||||
}
|
||||
|
||||
static void midi_exit(void)
|
||||
{
|
||||
midi_stop();
|
||||
}
|
||||
|
||||
static void midi_reset(void)
|
||||
{
|
||||
midi_stop();
|
||||
}
|
||||
|
||||
static bool midi_start(int no, int loop, const uint8_t *data, int length)
|
||||
{
|
||||
(void)no;
|
||||
midi_stop();
|
||||
MIX_Audio *audio = load_midi(data, length);
|
||||
if (!audio) {
|
||||
WARNING("Cannot load MIDI: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
/* MIDI passes loop directly; zero means infinite playback. */
|
||||
int loops = loop == 0 ? -1 : loop;
|
||||
if (!sdl3_mixer_play_music(SDL3_MIXER_MUSIC_MIDI, audio,
|
||||
loops, 0, music_gain())) {
|
||||
WARNING("Cannot play MIDI: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
start_time = sys_get_ticks();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void midi_pause(void)
|
||||
{
|
||||
sdl3_mixer_pause_music(SDL3_MIXER_MUSIC_MIDI);
|
||||
}
|
||||
|
||||
static void midi_unpause(void)
|
||||
{
|
||||
sdl3_mixer_resume_music(SDL3_MIXER_MUSIC_MIDI);
|
||||
}
|
||||
|
||||
static bool midi_get_playing_info(midiplaystate *state)
|
||||
{
|
||||
if (!sdl3_mixer_music_active(SDL3_MIXER_MUSIC_MIDI)) {
|
||||
state->in_play = false;
|
||||
state->loc_ms = 0;
|
||||
return true;
|
||||
}
|
||||
state->in_play = true;
|
||||
state->loc_ms = sys_get_ticks() - start_time;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int midi_getflag(int mode, int index)
|
||||
{
|
||||
(void)mode;
|
||||
(void)index;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool midi_setflag(int mode, int index, int value)
|
||||
{
|
||||
(void)mode;
|
||||
(void)index;
|
||||
(void)value;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool midi_fadestart(int time, int volume, int stop)
|
||||
{
|
||||
if (time == 0) {
|
||||
current_vol = volume;
|
||||
apply_volume();
|
||||
if (stop)
|
||||
midi_stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (volume == 0) {
|
||||
/* Match SDL2_mixer: a fade to silence always stops the music. */
|
||||
sdl3_mixer_stop_music(SDL3_MIXER_MUSIC_MIDI, time);
|
||||
fade_tick = sys_get_ticks() + time;
|
||||
return true;
|
||||
}
|
||||
WARNING("(time=%d, volume=%d, stop=%d) unsupported",
|
||||
time, volume, stop);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool midi_fading(void)
|
||||
{
|
||||
return sys_get_ticks() < fade_tick;
|
||||
}
|
||||
|
||||
static void midi_reapply_volume(void)
|
||||
{
|
||||
apply_volume();
|
||||
}
|
||||
|
||||
mididevice_t midi_sdlmixer = {
|
||||
.init = midi_initialize,
|
||||
.exit = midi_exit,
|
||||
.reset = midi_reset,
|
||||
.start = midi_start,
|
||||
.stop = midi_stop,
|
||||
.pause = midi_pause,
|
||||
.unpause = midi_unpause,
|
||||
.getpos = midi_get_playing_info,
|
||||
.getflag = midi_getflag,
|
||||
.setflag = midi_setflag,
|
||||
.fadestart = midi_fadestart,
|
||||
.fading = midi_fading,
|
||||
.reapply_volume = midi_reapply_volume,
|
||||
};
|
||||
+1
-2
@@ -19,8 +19,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_mixer.h>
|
||||
#include "sdl_mixer_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
|
||||
+35
-32
@@ -23,7 +23,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
@@ -109,22 +109,22 @@ static void init_context(void) {
|
||||
|
||||
bool modal_default_handler(const SDL_Event *e, modal *modal) {
|
||||
switch (e->type) {
|
||||
case SDL_QUIT:
|
||||
case SDL_COMPAT_EVENT_QUIT:
|
||||
return false;
|
||||
case SDL_MOUSEMOTION:
|
||||
case SDL_COMPAT_EVENT_MOUSE_MOTION:
|
||||
mu_input_mousemove(ctx, e->motion.x, e->motion.y);
|
||||
break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
case SDL_COMPAT_EVENT_MOUSE_WHEEL:
|
||||
mu_input_scroll(ctx, 0, e->wheel.y * -30);
|
||||
break;
|
||||
case SDL_TEXTINPUT:
|
||||
case SDL_COMPAT_EVENT_TEXT_INPUT:
|
||||
mu_input_text(ctx, e->text.text);
|
||||
break;
|
||||
case SDL_TEXTEDITING:
|
||||
case SDL_COMPAT_EVENT_TEXT_EDITING:
|
||||
mu_input_preedit(ctx, e->edit.text);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP: {
|
||||
case SDL_COMPAT_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_COMPAT_EVENT_MOUSE_BUTTON_UP: {
|
||||
int b = 0;
|
||||
switch (e->button.button) {
|
||||
case SDL_BUTTON_LEFT: b = MU_MOUSE_LEFT; break;
|
||||
@@ -133,7 +133,7 @@ bool modal_default_handler(const SDL_Event *e, modal *modal) {
|
||||
}
|
||||
if (!b)
|
||||
break;
|
||||
if (e->type == SDL_MOUSEBUTTONDOWN)
|
||||
if (e->type == SDL_COMPAT_EVENT_MOUSE_BUTTON_DOWN)
|
||||
mu_input_mousedown(ctx, e->button.x, e->button.y, b);
|
||||
else
|
||||
mu_input_mouseup(ctx, e->button.x, e->button.y, b);
|
||||
@@ -141,18 +141,18 @@ bool modal_default_handler(const SDL_Event *e, modal *modal) {
|
||||
}
|
||||
// Touch handling (see touch_phase above). The press/release is driven
|
||||
// from the frame loop in modal_run(), not emitted here directly.
|
||||
case SDL_FINGERDOWN:
|
||||
case SDL_COMPAT_EVENT_FINGER_DOWN:
|
||||
touch_pos = event_get_touch_position(&e->tfinger);
|
||||
mu_input_mousemove(ctx, touch_pos.x, touch_pos.y);
|
||||
touch_phase = TOUCH_HOVER;
|
||||
touch_hover_frames = TOUCH_HOVER_FRAMES;
|
||||
touch_release_after_press = false;
|
||||
break;
|
||||
case SDL_FINGERMOTION:
|
||||
case SDL_COMPAT_EVENT_FINGER_MOTION:
|
||||
touch_pos = event_get_touch_position(&e->tfinger);
|
||||
mu_input_mousemove(ctx, touch_pos.x, touch_pos.y);
|
||||
break;
|
||||
case SDL_FINGERUP:
|
||||
case SDL_COMPAT_EVENT_FINGER_UP:
|
||||
touch_pos = event_get_touch_position(&e->tfinger);
|
||||
if (touch_phase == TOUCH_PRESS) {
|
||||
mu_input_mouseup(ctx, touch_pos.x, touch_pos.y, MU_MOUSE_LEFT);
|
||||
@@ -162,14 +162,15 @@ bool modal_default_handler(const SDL_Event *e, modal *modal) {
|
||||
touch_release_after_press = true;
|
||||
}
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP: {
|
||||
case SDL_COMPAT_EVENT_KEY_DOWN:
|
||||
case SDL_COMPAT_EVENT_KEY_UP: {
|
||||
// Esc cancels an IME composition first, not the modal.
|
||||
if (e->type == SDL_KEYDOWN && e->key.keysym.sym == SDLK_ESCAPE &&
|
||||
if (e->type == SDL_COMPAT_EVENT_KEY_DOWN &&
|
||||
sdl_keyboard_event_key(&e->key) == SDLK_ESCAPE &&
|
||||
!*ctx->preedit)
|
||||
modal->cancelled = true;
|
||||
int c = 0;
|
||||
switch (e->key.keysym.sym) {
|
||||
switch (sdl_keyboard_event_key(&e->key)) {
|
||||
case SDLK_LSHIFT: case SDLK_RSHIFT: c = MU_KEY_SHIFT; break;
|
||||
case SDLK_LCTRL: case SDLK_RCTRL: c = MU_KEY_CTRL; break;
|
||||
case SDLK_LALT: case SDLK_RALT: c = MU_KEY_ALT; break;
|
||||
@@ -184,7 +185,7 @@ bool modal_default_handler(const SDL_Event *e, modal *modal) {
|
||||
}
|
||||
if (!c)
|
||||
break;
|
||||
if (e->type == SDL_KEYDOWN)
|
||||
if (e->type == SDL_COMPAT_EVENT_KEY_DOWN)
|
||||
mu_input_keydown(ctx, c);
|
||||
else
|
||||
mu_input_keyup(ctx, c);
|
||||
@@ -204,10 +205,10 @@ static void render_text(mu_Font font, const char *str, mu_Vec2 pos, mu_Color col
|
||||
SDL_Texture *t = SDL_CreateTextureFromSurface(gfx_renderer, s);
|
||||
if (t) {
|
||||
SDL_Rect dst = { pos.x, pos.y, s->w, s->h };
|
||||
SDL_RenderCopy(gfx_renderer, t, NULL, &dst);
|
||||
sdl_render_texture(gfx_renderer, t, NULL, &dst);
|
||||
SDL_DestroyTexture(t);
|
||||
}
|
||||
SDL_FreeSurface(s);
|
||||
sdl_destroy_surface(s);
|
||||
}
|
||||
|
||||
// Draw a line thickened into a square brush of the given width, so it looks
|
||||
@@ -217,7 +218,7 @@ static void draw_thick_line(float x1, float y1, float x2, float y2, int thicknes
|
||||
for (int dx = 0; dx <= thickness; dx++) {
|
||||
for (int dy = 0; dy <= thickness; dy++) {
|
||||
float ox = dx - half, oy = dy - half;
|
||||
SDL_RenderDrawLineF(gfx_renderer, x1 + ox, y1 + oy, x2 + ox, y2 + oy);
|
||||
sdl_render_line(gfx_renderer, x1 + ox, y1 + oy, x2 + ox, y2 + oy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,7 +254,7 @@ static void render_icon(int id, mu_Rect rect, mu_Color color) {
|
||||
}
|
||||
default: {
|
||||
SDL_Rect r = { rect.x + rect.w / 2 - 2, rect.y + rect.h / 2 - 2, 4, 4 };
|
||||
SDL_RenderFillRect(gfx_renderer, &r);
|
||||
sdl_render_fill_rect(gfx_renderer, &r);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -265,7 +266,7 @@ static void modal_render(void) {
|
||||
// Dim the game behind the menu.
|
||||
if (!current_modal->no_dim) {
|
||||
SDL_SetRenderDrawColor(gfx_renderer, 0, 0, 0, 110);
|
||||
SDL_RenderFillRect(gfx_renderer, NULL);
|
||||
sdl_render_fill_rect(gfx_renderer, NULL);
|
||||
}
|
||||
|
||||
mu_Command *cmd = NULL;
|
||||
@@ -276,7 +277,7 @@ static void modal_render(void) {
|
||||
SDL_SetRenderDrawColor(gfx_renderer, c.r, c.g, c.b, c.a);
|
||||
SDL_Rect r = { cmd->rect.rect.x, cmd->rect.rect.y,
|
||||
cmd->rect.rect.w, cmd->rect.rect.h };
|
||||
SDL_RenderFillRect(gfx_renderer, &r);
|
||||
sdl_render_fill_rect(gfx_renderer, &r);
|
||||
break;
|
||||
}
|
||||
case MU_COMMAND_TEXT:
|
||||
@@ -288,12 +289,12 @@ static void modal_render(void) {
|
||||
case MU_COMMAND_CLIP: {
|
||||
SDL_Rect r = { cmd->clip.rect.x, cmd->clip.rect.y,
|
||||
cmd->clip.rect.w, cmd->clip.rect.h };
|
||||
SDL_RenderSetClipRect(gfx_renderer, &r);
|
||||
sdl_set_render_clip_rect(gfx_renderer, &r);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
SDL_RenderSetClipRect(gfx_renderer, NULL);
|
||||
sdl_set_render_clip_rect(gfx_renderer, NULL);
|
||||
SDL_SetRenderDrawColor(gfx_renderer, 0, 0, 0, 255);
|
||||
SDL_SetRenderDrawBlendMode(gfx_renderer, SDL_BLENDMODE_NONE);
|
||||
}
|
||||
@@ -302,20 +303,21 @@ static void modal_render(void) {
|
||||
// IME candidate window near it. The rect is in logical (view) coordinates.
|
||||
static void update_text_input(void) {
|
||||
static SDL_Rect last_rect;
|
||||
SDL_Window *window = gfx_getWindow();
|
||||
if (!ctx->text_input) {
|
||||
if (SDL_IsTextInputActive())
|
||||
SDL_StopTextInput();
|
||||
if (sdl_text_input_active(window))
|
||||
sdl_stop_text_input(window);
|
||||
return;
|
||||
}
|
||||
if (!SDL_IsTextInputActive()) {
|
||||
SDL_StartTextInput();
|
||||
if (!sdl_text_input_active(window)) {
|
||||
sdl_start_text_input(window);
|
||||
last_rect = (SDL_Rect){0, 0, 0, 0};
|
||||
}
|
||||
mu_Rect r = ctx->text_input_rect;
|
||||
SDL_Rect wr = gfx_viewToWindowRect((SDL_Rect){r.x, r.y, r.w, r.h});
|
||||
if (memcmp(&wr, &last_rect, sizeof(wr))) {
|
||||
last_rect = wr;
|
||||
SDL_SetTextInputRect(&wr);
|
||||
sdl_set_text_input_rect(window, &wr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,8 +384,9 @@ void modal_run(modal *m) {
|
||||
current_modal = NULL;
|
||||
free(ctx);
|
||||
ctx = NULL;
|
||||
if (SDL_IsTextInputActive())
|
||||
SDL_StopTextInput();
|
||||
SDL_Window *window = gfx_getWindow();
|
||||
if (sdl_text_input_active(window))
|
||||
sdl_stop_text_input(window);
|
||||
gfx_requestRedraw(); // repaint once more to clear the overlay
|
||||
|
||||
// The gesture that opened/dismissed the dialog must not leave the engine
|
||||
|
||||
+15
-14
@@ -27,14 +27,14 @@ struct msgq_elem {
|
||||
|
||||
struct msgq *msgq_new(void) {
|
||||
struct msgq *q = calloc(1, sizeof(struct msgq));
|
||||
q->mutex = SDL_CreateMutex();
|
||||
q->cond_nonempty = SDL_CreateCond();
|
||||
q->mutex = sdl_create_mutex();
|
||||
q->cond_nonempty = sdl_create_condition();
|
||||
return q;
|
||||
}
|
||||
|
||||
void msgq_free(struct msgq *q) {
|
||||
SDL_DestroyMutex(q->mutex);
|
||||
SDL_DestroyCond(q->cond_nonempty);
|
||||
sdl_destroy_mutex(q->mutex);
|
||||
sdl_destroy_condition(q->cond_nonempty);
|
||||
free(q);
|
||||
}
|
||||
|
||||
@@ -43,28 +43,28 @@ void msgq_enqueue(struct msgq *q, void *msg) {
|
||||
e->msg = msg;
|
||||
e->next = NULL;
|
||||
|
||||
SDL_LockMutex(q->mutex);
|
||||
sdl_lock_mutex(q->mutex);
|
||||
if (!q->head) {
|
||||
q->head = q->last = e;
|
||||
} else {
|
||||
q->last->next = e;
|
||||
q->last = e;
|
||||
}
|
||||
SDL_UnlockMutex(q->mutex);
|
||||
SDL_CondSignal(q->cond_nonempty);
|
||||
sdl_unlock_mutex(q->mutex);
|
||||
sdl_signal_condition(q->cond_nonempty);
|
||||
}
|
||||
|
||||
void *msgq_dequeue(struct msgq *q) {
|
||||
SDL_LockMutex(q->mutex);
|
||||
sdl_lock_mutex(q->mutex);
|
||||
while (!q->head)
|
||||
SDL_CondWait(q->cond_nonempty, q->mutex);
|
||||
sdl_wait_condition(q->cond_nonempty, q->mutex);
|
||||
|
||||
struct msgq_elem *e = q->head;
|
||||
q->head = e->next;
|
||||
if (!e->next)
|
||||
q->last = NULL;
|
||||
|
||||
SDL_UnlockMutex(q->mutex);
|
||||
sdl_unlock_mutex(q->mutex);
|
||||
|
||||
void *msg = e->msg;
|
||||
free(e);
|
||||
@@ -72,13 +72,14 @@ void *msgq_dequeue(struct msgq *q) {
|
||||
}
|
||||
|
||||
void *msgq_dequeue_timeout(struct msgq *q, uint32_t timeout_ms) {
|
||||
SDL_LockMutex(q->mutex);
|
||||
sdl_lock_mutex(q->mutex);
|
||||
|
||||
while (!q->head && SDL_CondWaitTimeout(q->cond_nonempty, q->mutex, timeout_ms) == 0)
|
||||
while (!q->head &&
|
||||
sdl_wait_condition_timeout(q->cond_nonempty, q->mutex, timeout_ms))
|
||||
;
|
||||
|
||||
if (!q->head) { // timed out
|
||||
SDL_UnlockMutex(q->mutex);
|
||||
sdl_unlock_mutex(q->mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -87,7 +88,7 @@ void *msgq_dequeue_timeout(struct msgq *q, uint32_t timeout_ms) {
|
||||
if (!e->next)
|
||||
q->last = NULL;
|
||||
|
||||
SDL_UnlockMutex(q->mutex);
|
||||
sdl_unlock_mutex(q->mutex);
|
||||
|
||||
void *msg = e->msg;
|
||||
free(e);
|
||||
|
||||
+3
-3
@@ -21,13 +21,13 @@
|
||||
#define __MSGQUEUE_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <SDL_mutex.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
struct msgq_elem;
|
||||
|
||||
struct msgq {
|
||||
SDL_mutex *mutex;
|
||||
SDL_cond *cond_nonempty;
|
||||
sdl_mutex_t *mutex;
|
||||
sdl_condition_t *cond_nonempty;
|
||||
struct msgq_elem *head;
|
||||
struct msgq_elem *last;
|
||||
};
|
||||
|
||||
@@ -24,12 +24,18 @@
|
||||
#include "system.h"
|
||||
#include "music.h"
|
||||
#include "music_private.h"
|
||||
#ifdef HAVE_SDL3_MIXER_BACKEND
|
||||
#include "sdl3_mixer_backend.h"
|
||||
#endif
|
||||
|
||||
struct _musprvdat musprv;
|
||||
|
||||
bool mus_init(int audio_buffer_size) {
|
||||
for (int i = 0; i < 16; i++)
|
||||
prv.volval[i] = 100;
|
||||
#ifdef HAVE_SDL3_MIXER_BACKEND
|
||||
sdl3_mixer_backend_init(audio_buffer_size);
|
||||
#endif
|
||||
musbgm_init(DRIFILE_BGM, 0);
|
||||
muscd_init();
|
||||
musmidi_init();
|
||||
@@ -42,6 +48,9 @@ void mus_exit(void) {
|
||||
muscd_exit();
|
||||
if (prv.midi_valid) musmidi_exit();
|
||||
if (prv.pcm_valid) muspcm_exit();
|
||||
#ifdef HAVE_SDL3_MIXER_BACKEND
|
||||
sdl3_mixer_backend_exit();
|
||||
#endif
|
||||
}
|
||||
|
||||
void mus_reset(void) {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "nls.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <SDL_locale.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
// Defines the translation tables and the nls_catalogs registry.
|
||||
#include "nls_catalog.h"
|
||||
@@ -35,12 +35,21 @@ static const struct nls_entry *find_catalog(const char *lang) {
|
||||
}
|
||||
|
||||
void builtin_nls_init(void) {
|
||||
#if XSYSTEM35_SDL_VERSION == 2
|
||||
SDL_Locale *locales = SDL_GetPreferredLocales();
|
||||
if (!locales)
|
||||
return;
|
||||
// Pick the catalog for the highest-priority locale that has one.
|
||||
for (const SDL_Locale *l = locales; l->language && !active_table; l++)
|
||||
active_table = find_catalog(l->language);
|
||||
#else
|
||||
SDL_Locale **locales = SDL_GetPreferredLocales(NULL);
|
||||
if (!locales)
|
||||
return;
|
||||
// Pick the catalog for the highest-priority locale that has one.
|
||||
for (SDL_Locale **l = locales; *l && !active_table; l++)
|
||||
active_table = find_catalog((*l)->language);
|
||||
#endif
|
||||
SDL_free(locales);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* pcm.sdl3mixer.c PCM audio using SDL3_mixer
|
||||
*
|
||||
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
||||
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sdl_mixer_compat.h"
|
||||
|
||||
#include "ald_manager.h"
|
||||
#include "audio_meta.h"
|
||||
#include "dri.h"
|
||||
#include "music_pcm.h"
|
||||
#include "music_private.h"
|
||||
#include "nact.h"
|
||||
#include "sdl3_mixer_backend.h"
|
||||
#include "system.h"
|
||||
|
||||
#define SAMPLE_RATE 44100
|
||||
#define BYTES_PER_SAMPLE_FRAME 4
|
||||
#define PCM_SLOTS (128 + 1)
|
||||
#define DECODE_BLOCK_SIZE 16384
|
||||
|
||||
static struct {
|
||||
MIX_Audio *audio;
|
||||
MIX_Track *track;
|
||||
uint32_t start_time;
|
||||
int volume;
|
||||
} slots[PCM_SLOTS];
|
||||
|
||||
struct decoded_audio {
|
||||
uint8_t *data;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
static int clamp_volume(int volume)
|
||||
{
|
||||
if (volume < 0)
|
||||
return 0;
|
||||
if (volume > 100)
|
||||
return 100;
|
||||
return volume;
|
||||
}
|
||||
|
||||
static void apply_volume(int slot)
|
||||
{
|
||||
if (!slots[slot].track)
|
||||
return;
|
||||
int channel = prv.vol_pcm_sub[slot];
|
||||
if ((unsigned)channel >= 16)
|
||||
channel = 0;
|
||||
float gain = clamp_volume(slots[slot].volume) / 100.0f;
|
||||
gain *= clamp_volume(prv.volval[channel]) / 100.0f;
|
||||
MIX_SetTrackGain(slots[slot].track, gain);
|
||||
}
|
||||
|
||||
static MIX_Audio *load_audio(const void *data, size_t size)
|
||||
{
|
||||
SDL_IOStream *io = SDL_IOFromConstMem(data, size);
|
||||
if (!io)
|
||||
return NULL;
|
||||
return MIX_LoadAudio_IO(sdl3_mixer_backend_get(), io, true, true);
|
||||
}
|
||||
|
||||
static MIX_Audio *load_asset(DRIFILETYPE type, int no)
|
||||
{
|
||||
dridata *dfile = ald_getdata(type, no - 1);
|
||||
if (!dfile) {
|
||||
WARNING("Audio asset fail to open %d", no - 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MIX_Audio *audio = load_audio(dfile->data, dfile->size);
|
||||
ald_freedata(dfile);
|
||||
if (!audio)
|
||||
WARNING("Audio asset %d is not valid: %s", no - 1, SDL_GetError());
|
||||
return audio;
|
||||
}
|
||||
|
||||
static bool decode_asset(DRIFILETYPE type, int no, struct decoded_audio *out)
|
||||
{
|
||||
dridata *dfile = ald_getdata(type, no - 1);
|
||||
if (!dfile)
|
||||
return false;
|
||||
|
||||
SDL_IOStream *io = SDL_IOFromConstMem(dfile->data, dfile->size);
|
||||
MIX_AudioDecoder *decoder = io
|
||||
? MIX_CreateAudioDecoder_IO(io, true, 0) : NULL;
|
||||
if (!decoder) {
|
||||
ald_freedata(dfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_AudioSpec spec = {
|
||||
.format = SDL_AUDIO_S16LE,
|
||||
.channels = 2,
|
||||
.freq = SAMPLE_RATE,
|
||||
};
|
||||
uint8_t *data = NULL;
|
||||
size_t length = 0;
|
||||
bool success = true;
|
||||
for (;;) {
|
||||
if (length > SIZE_MAX - DECODE_BLOCK_SIZE) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
uint8_t *new_data = SDL_realloc(data, length + DECODE_BLOCK_SIZE);
|
||||
if (!new_data) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
data = new_data;
|
||||
int decoded = MIX_DecodeAudio(decoder, data + length,
|
||||
DECODE_BLOCK_SIZE, &spec);
|
||||
if (decoded < 0 || decoded % BYTES_PER_SAMPLE_FRAME != 0) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
if (decoded == 0)
|
||||
break;
|
||||
length += decoded;
|
||||
}
|
||||
|
||||
MIX_DestroyAudioDecoder(decoder);
|
||||
ald_freedata(dfile);
|
||||
if (!success || length == 0) {
|
||||
SDL_free(data);
|
||||
return false;
|
||||
}
|
||||
out->data = data;
|
||||
out->length = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
static MIX_Audio *mix_lr(int no_left, int no_right)
|
||||
{
|
||||
struct decoded_audio left = {0};
|
||||
struct decoded_audio right = {0};
|
||||
if (!decode_asset(DRIFILE_WAVE, no_left, &left) ||
|
||||
!decode_asset(DRIFILE_WAVE, no_right, &right)) {
|
||||
SDL_free(left.data);
|
||||
SDL_free(right.data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t length = max(left.length, right.length);
|
||||
int16_t *mixed = SDL_calloc(1, length);
|
||||
if (!mixed) {
|
||||
SDL_free(left.data);
|
||||
SDL_free(right.data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int16_t *left_samples = (int16_t *)left.data;
|
||||
int16_t *right_samples = (int16_t *)right.data;
|
||||
for (size_t i = 0; i < left.length / BYTES_PER_SAMPLE_FRAME; i++)
|
||||
mixed[i * 2] = left_samples[i * 2];
|
||||
for (size_t i = 0; i < right.length / BYTES_PER_SAMPLE_FRAME; i++)
|
||||
mixed[i * 2 + 1] = right_samples[i * 2 + 1];
|
||||
|
||||
SDL_AudioSpec spec = {
|
||||
.format = SDL_AUDIO_S16LE,
|
||||
.channels = 2,
|
||||
.freq = SAMPLE_RATE,
|
||||
};
|
||||
MIX_Audio *audio = MIX_LoadRawAudio(
|
||||
sdl3_mixer_backend_get(), mixed, length, &spec);
|
||||
SDL_free(mixed);
|
||||
SDL_free(left.data);
|
||||
SDL_free(right.data);
|
||||
return audio;
|
||||
}
|
||||
|
||||
static bool load_slot(int slot, MIX_Audio *audio)
|
||||
{
|
||||
if (!audio)
|
||||
return false;
|
||||
muspcm_unload(slot);
|
||||
if (!MIX_SetTrackAudio(slots[slot].track, audio)) {
|
||||
MIX_DestroyAudio(audio);
|
||||
return false;
|
||||
}
|
||||
slots[slot].audio = audio;
|
||||
apply_volume(slot);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool muspcm_init(int audio_buffer_size)
|
||||
{
|
||||
(void)audio_buffer_size;
|
||||
MIX_Mixer *mixer = sdl3_mixer_backend_get();
|
||||
if (!mixer)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < PCM_SLOTS; i++) {
|
||||
slots[i].track = MIX_CreateTrack(mixer);
|
||||
slots[i].volume = 100;
|
||||
if (!slots[i].track) {
|
||||
muspcm_exit();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
wai_load(nact->files.wai);
|
||||
return true;
|
||||
}
|
||||
|
||||
void muspcm_exit(void)
|
||||
{
|
||||
for (int i = 0; i < PCM_SLOTS; i++) {
|
||||
muspcm_unload(i);
|
||||
MIX_DestroyTrack(slots[i].track);
|
||||
slots[i].track = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void muspcm_reset(void)
|
||||
{
|
||||
for (int i = 0; i < PCM_SLOTS; i++)
|
||||
muspcm_unload(i);
|
||||
}
|
||||
|
||||
bool muspcm_load_no(int slot, int no)
|
||||
{
|
||||
if ((unsigned)slot >= PCM_SLOTS)
|
||||
return false;
|
||||
MIX_Audio *audio = load_asset(DRIFILE_WAVE, no);
|
||||
if (!audio)
|
||||
return false;
|
||||
if (wai_loaded()) {
|
||||
int channel = wai_mixch(no);
|
||||
prv.vol_pcm_sub[slot] = channel < 0 ? 0 : channel;
|
||||
} else {
|
||||
prv.vol_pcm_sub[slot] = SE_VOLVAL_CH;
|
||||
}
|
||||
return load_slot(slot, audio);
|
||||
}
|
||||
|
||||
bool muspcm_load_bgm(int slot, int no)
|
||||
{
|
||||
if ((unsigned)slot >= PCM_SLOTS)
|
||||
return false;
|
||||
MIX_Audio *audio = load_asset(DRIFILE_BGM, no);
|
||||
if (!audio)
|
||||
return false;
|
||||
prv.vol_pcm_sub[slot] = 0;
|
||||
return load_slot(slot, audio);
|
||||
}
|
||||
|
||||
bool muspcm_load_mixlr(int slot, int no_left, int no_right)
|
||||
{
|
||||
if ((unsigned)slot >= PCM_SLOTS)
|
||||
return false;
|
||||
MIX_Audio *audio = mix_lr(no_left, no_right);
|
||||
if (!audio)
|
||||
return false;
|
||||
prv.vol_pcm_sub[slot] = SE_VOLVAL_CH;
|
||||
return load_slot(slot, audio);
|
||||
}
|
||||
|
||||
bool muspcm_load_data(int slot, uint8_t *data, uint32_t length)
|
||||
{
|
||||
if ((unsigned)slot >= PCM_SLOTS)
|
||||
return false;
|
||||
MIX_Audio *audio = load_audio(data, length);
|
||||
if (!audio)
|
||||
return false;
|
||||
prv.vol_pcm_sub[slot] = SE_VOLVAL_CH;
|
||||
return load_slot(slot, audio);
|
||||
}
|
||||
|
||||
void muspcm_unload(int slot)
|
||||
{
|
||||
if ((unsigned)slot >= PCM_SLOTS || !slots[slot].audio)
|
||||
return;
|
||||
MIX_StopTrack(slots[slot].track, 0);
|
||||
MIX_SetTrackAudio(slots[slot].track, NULL);
|
||||
MIX_DestroyAudio(slots[slot].audio);
|
||||
slots[slot].audio = NULL;
|
||||
}
|
||||
|
||||
bool muspcm_start(int slot, int loop)
|
||||
{
|
||||
if ((unsigned)slot >= PCM_SLOTS || !slots[slot].audio)
|
||||
return false;
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
if (!props)
|
||||
return false;
|
||||
bool success = SDL_SetNumberProperty(props, MIX_PROP_PLAY_LOOPS_NUMBER,
|
||||
loop <= 0 ? -1 : loop - 1) &&
|
||||
MIX_PlayTrack(slots[slot].track, props);
|
||||
SDL_DestroyProperties(props);
|
||||
if (success)
|
||||
slots[slot].start_time = sys_get_ticks();
|
||||
return success;
|
||||
}
|
||||
|
||||
void muspcm_stop(int slot)
|
||||
{
|
||||
if ((unsigned)slot < PCM_SLOTS)
|
||||
MIX_StopTrack(slots[slot].track, 0);
|
||||
}
|
||||
|
||||
void muspcm_fadeout(int slot, int msec)
|
||||
{
|
||||
if ((unsigned)slot >= PCM_SLOTS || !slots[slot].audio)
|
||||
return;
|
||||
Sint64 frames = msec > 0
|
||||
? MIX_TrackMSToFrames(slots[slot].track, msec) : 0;
|
||||
if (frames < 0)
|
||||
frames = 0;
|
||||
MIX_StopTrack(slots[slot].track, frames);
|
||||
}
|
||||
|
||||
void muspcm_pause(int slot)
|
||||
{
|
||||
if ((unsigned)slot < PCM_SLOTS)
|
||||
MIX_PauseTrack(slots[slot].track);
|
||||
}
|
||||
|
||||
void muspcm_unpause(int slot)
|
||||
{
|
||||
if ((unsigned)slot < PCM_SLOTS)
|
||||
MIX_ResumeTrack(slots[slot].track);
|
||||
}
|
||||
|
||||
static bool slot_active(int slot)
|
||||
{
|
||||
return MIX_TrackPlaying(slots[slot].track) ||
|
||||
MIX_TrackPaused(slots[slot].track);
|
||||
}
|
||||
|
||||
int muspcm_getpos(int slot)
|
||||
{
|
||||
if ((unsigned)slot >= PCM_SLOTS || !slot_active(slot))
|
||||
return 0;
|
||||
int position = sys_get_ticks() - slots[slot].start_time;
|
||||
return position ? position : 1;
|
||||
}
|
||||
|
||||
void muspcm_setvol(int slot, int volume)
|
||||
{
|
||||
if ((unsigned)slot >= PCM_SLOTS)
|
||||
return;
|
||||
slots[slot].volume = volume;
|
||||
apply_volume(slot);
|
||||
}
|
||||
|
||||
int muspcm_getwavelen(int slot)
|
||||
{
|
||||
if ((unsigned)slot >= PCM_SLOTS || !slots[slot].audio)
|
||||
return 0;
|
||||
Sint64 frames = MIX_GetAudioDuration(slots[slot].audio);
|
||||
if (frames < 0)
|
||||
return 0;
|
||||
Sint64 length = MIX_AudioFramesToMS(slots[slot].audio, frames);
|
||||
if (length < 0)
|
||||
return 0;
|
||||
return length > 65535 ? 65535 : (int)length;
|
||||
}
|
||||
|
||||
bool muspcm_isplaying(int slot)
|
||||
{
|
||||
return (unsigned)slot < PCM_SLOTS && slot_active(slot);
|
||||
}
|
||||
|
||||
void muspcm_waitend(int slot)
|
||||
{
|
||||
(void)slot;
|
||||
WARNING("not implemented");
|
||||
}
|
||||
|
||||
void muspcm_reapply_valance(void)
|
||||
{
|
||||
for (int slot = 0; slot < PCM_SLOTS; slot++)
|
||||
apply_volume(slot);
|
||||
}
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <SDL_mixer.h>
|
||||
#include "sdl_mixer_compat.h"
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
#include "sdl3_mixer_backend.h"
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#define SAMPLE_RATE 44100
|
||||
|
||||
static MIX_Mixer *mixer;
|
||||
static MIX_Track *music_track;
|
||||
static MIX_Audio *music_audio;
|
||||
static enum sdl3_mixer_music_owner music_owner;
|
||||
|
||||
static void clear_music(void)
|
||||
{
|
||||
if (!music_audio)
|
||||
return;
|
||||
MIX_StopTrack(music_track, 0);
|
||||
MIX_SetTrackAudio(music_track, NULL);
|
||||
MIX_DestroyAudio(music_audio);
|
||||
music_audio = NULL;
|
||||
music_owner = SDL3_MIXER_MUSIC_NONE;
|
||||
}
|
||||
|
||||
static void reap_finished_music(void)
|
||||
{
|
||||
if (music_audio && !MIX_TrackPlaying(music_track) &&
|
||||
!MIX_TrackPaused(music_track))
|
||||
clear_music();
|
||||
}
|
||||
|
||||
bool sdl3_mixer_backend_init(int audio_buffer_size)
|
||||
{
|
||||
if (mixer)
|
||||
return true;
|
||||
|
||||
if (audio_buffer_size > 0) {
|
||||
char value[32];
|
||||
SDL_snprintf(value, sizeof(value), "%d", audio_buffer_size);
|
||||
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, value);
|
||||
}
|
||||
|
||||
if (!MIX_Init()) {
|
||||
WARNING("Cannot initialize SDL_mixer: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_AudioSpec spec = {
|
||||
.format = SDL_AUDIO_S16LE,
|
||||
.channels = 2,
|
||||
.freq = SAMPLE_RATE,
|
||||
};
|
||||
mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec);
|
||||
if (!mixer) {
|
||||
WARNING("Cannot open audio device: %s", SDL_GetError());
|
||||
MIX_Quit();
|
||||
return false;
|
||||
}
|
||||
music_track = MIX_CreateTrack(mixer);
|
||||
if (!music_track) {
|
||||
WARNING("Cannot create music track: %s", SDL_GetError());
|
||||
MIX_DestroyMixer(mixer);
|
||||
mixer = NULL;
|
||||
MIX_Quit();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void sdl3_mixer_backend_exit(void)
|
||||
{
|
||||
if (!mixer)
|
||||
return;
|
||||
clear_music();
|
||||
MIX_DestroyTrack(music_track);
|
||||
music_track = NULL;
|
||||
MIX_DestroyMixer(mixer);
|
||||
mixer = NULL;
|
||||
MIX_Quit();
|
||||
}
|
||||
|
||||
MIX_Mixer *sdl3_mixer_backend_get(void)
|
||||
{
|
||||
return mixer;
|
||||
}
|
||||
|
||||
MIX_Audio *sdl3_mixer_load_music_memory(const void *data, size_t size)
|
||||
{
|
||||
if (!mixer)
|
||||
return NULL;
|
||||
SDL_IOStream *io = SDL_IOFromConstMem(data, size);
|
||||
if (!io)
|
||||
return NULL;
|
||||
return MIX_LoadAudio_IO(mixer, io, false, true);
|
||||
}
|
||||
|
||||
MIX_Audio *sdl3_mixer_load_music_file(const char *path)
|
||||
{
|
||||
return mixer ? MIX_LoadAudio(mixer, path, false) : NULL;
|
||||
}
|
||||
|
||||
bool sdl3_mixer_play_music(enum sdl3_mixer_music_owner owner,
|
||||
MIX_Audio *audio, int loops, int fade_in_ms, float gain)
|
||||
{
|
||||
if (!music_track || !audio || owner == SDL3_MIXER_MUSIC_NONE) {
|
||||
MIX_DestroyAudio(audio);
|
||||
return false;
|
||||
}
|
||||
|
||||
clear_music();
|
||||
if (!MIX_SetTrackAudio(music_track, audio)) {
|
||||
MIX_DestroyAudio(audio);
|
||||
return false;
|
||||
}
|
||||
music_audio = audio;
|
||||
music_owner = owner;
|
||||
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
bool success = props &&
|
||||
MIX_SetTrackGain(music_track, gain) &&
|
||||
SDL_SetNumberProperty(props, MIX_PROP_PLAY_LOOPS_NUMBER, loops) &&
|
||||
SDL_SetNumberProperty(props,
|
||||
MIX_PROP_PLAY_FADE_IN_MILLISECONDS_NUMBER, fade_in_ms) &&
|
||||
MIX_PlayTrack(music_track, props);
|
||||
if (props)
|
||||
SDL_DestroyProperties(props);
|
||||
if (!success)
|
||||
clear_music();
|
||||
return success;
|
||||
}
|
||||
|
||||
void sdl3_mixer_stop_music(enum sdl3_mixer_music_owner owner, int fade_out_ms)
|
||||
{
|
||||
reap_finished_music();
|
||||
if (music_owner != owner)
|
||||
return;
|
||||
if (fade_out_ms <= 0) {
|
||||
clear_music();
|
||||
return;
|
||||
}
|
||||
Sint64 frames = MIX_TrackMSToFrames(music_track, fade_out_ms);
|
||||
if (frames <= 0 || !MIX_StopTrack(music_track, frames))
|
||||
clear_music();
|
||||
}
|
||||
|
||||
void sdl3_mixer_pause_music(enum sdl3_mixer_music_owner owner)
|
||||
{
|
||||
reap_finished_music();
|
||||
if (music_owner == owner)
|
||||
MIX_PauseTrack(music_track);
|
||||
}
|
||||
|
||||
void sdl3_mixer_resume_music(enum sdl3_mixer_music_owner owner)
|
||||
{
|
||||
reap_finished_music();
|
||||
if (music_owner == owner)
|
||||
MIX_ResumeTrack(music_track);
|
||||
}
|
||||
|
||||
bool sdl3_mixer_music_active(enum sdl3_mixer_music_owner owner)
|
||||
{
|
||||
reap_finished_music();
|
||||
return music_owner == owner &&
|
||||
(MIX_TrackPlaying(music_track) || MIX_TrackPaused(music_track));
|
||||
}
|
||||
|
||||
void sdl3_mixer_set_music_gain(enum sdl3_mixer_music_owner owner, float gain)
|
||||
{
|
||||
reap_finished_music();
|
||||
if (music_owner == owner)
|
||||
MIX_SetTrackGain(music_track, gain);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef XSYSTEM35_SDL3_MIXER_BACKEND_H
|
||||
#define XSYSTEM35_SDL3_MIXER_BACKEND_H
|
||||
|
||||
#include "portab.h"
|
||||
#include "sdl_mixer_compat.h"
|
||||
|
||||
enum sdl3_mixer_music_owner {
|
||||
SDL3_MIXER_MUSIC_NONE,
|
||||
SDL3_MIXER_MUSIC_BGM,
|
||||
SDL3_MIXER_MUSIC_CD,
|
||||
SDL3_MIXER_MUSIC_MIDI,
|
||||
};
|
||||
|
||||
bool sdl3_mixer_backend_init(int audio_buffer_size);
|
||||
void sdl3_mixer_backend_exit(void);
|
||||
MIX_Mixer *sdl3_mixer_backend_get(void);
|
||||
|
||||
MIX_Audio *sdl3_mixer_load_music_memory(const void *data, size_t size);
|
||||
MIX_Audio *sdl3_mixer_load_music_file(const char *path);
|
||||
|
||||
/* play_music takes ownership of audio, including when playback fails. */
|
||||
bool sdl3_mixer_play_music(enum sdl3_mixer_music_owner owner,
|
||||
MIX_Audio *audio, int loops, int fade_in_ms, float gain);
|
||||
void sdl3_mixer_stop_music(enum sdl3_mixer_music_owner owner, int fade_out_ms);
|
||||
void sdl3_mixer_pause_music(enum sdl3_mixer_music_owner owner);
|
||||
void sdl3_mixer_resume_music(enum sdl3_mixer_music_owner owner);
|
||||
bool sdl3_mixer_music_active(enum sdl3_mixer_music_owner owner);
|
||||
void sdl3_mixer_set_music_gain(enum sdl3_mixer_music_owner owner, float gain);
|
||||
|
||||
#endif /* XSYSTEM35_SDL3_MIXER_BACKEND_H */
|
||||
+1162
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2026 <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "sdl_compat.h"
|
||||
#include "unittest.h"
|
||||
|
||||
void sdl_compat_test(void)
|
||||
{
|
||||
SDL_Event event = {0};
|
||||
ASSERT_EQUAL(sdl_get_window_event(&event),
|
||||
SDL_COMPAT_WINDOW_EVENT_NONE);
|
||||
#if XSYSTEM35_SDL_VERSION == 2
|
||||
event.type = SDL_WINDOWEVENT;
|
||||
event.window.event = SDL_WINDOWEVENT_EXPOSED;
|
||||
#else
|
||||
event.type = SDL_EVENT_WINDOW_EXPOSED;
|
||||
#endif
|
||||
ASSERT_EQUAL(sdl_get_window_event(&event),
|
||||
SDL_COMPAT_WINDOW_EVENT_EXPOSED);
|
||||
|
||||
SDL_KeyboardEvent keyboard_event = {0};
|
||||
#if XSYSTEM35_SDL_VERSION == 2
|
||||
keyboard_event.keysym.scancode = SDL_SCANCODE_F1;
|
||||
keyboard_event.keysym.sym = SDLK_F1;
|
||||
#else
|
||||
keyboard_event.scancode = SDL_SCANCODE_F1;
|
||||
keyboard_event.key = SDLK_F1;
|
||||
#endif
|
||||
ASSERT_EQUAL(sdl_keyboard_event_scancode(&keyboard_event),
|
||||
SDL_SCANCODE_F1);
|
||||
ASSERT_EQUAL(sdl_keyboard_event_key(&keyboard_event), SDLK_F1);
|
||||
|
||||
SDL_TouchFingerEvent touch_event = {0};
|
||||
#if XSYSTEM35_SDL_VERSION == 2
|
||||
touch_event.touchId = 42;
|
||||
#else
|
||||
touch_event.touchID = 42;
|
||||
#endif
|
||||
ASSERT_EQUAL(sdl_touch_event_id(&touch_event), 42);
|
||||
|
||||
SDL_JoyButtonEvent joystick_button_event = {0};
|
||||
#if XSYSTEM35_SDL_VERSION == 2
|
||||
joystick_button_event.state = SDL_PRESSED;
|
||||
#else
|
||||
joystick_button_event.down = true;
|
||||
#endif
|
||||
ASSERT_TRUE(sdl_joystick_button_event_pressed(&joystick_button_event));
|
||||
|
||||
static const uint8_t data[] = {1, 2, 3, 4};
|
||||
sdl_iostream_t *stream = sdl_io_from_const_memory(data, sizeof(data));
|
||||
ASSERT_TRUE(stream);
|
||||
ASSERT_EQUAL(sdl_get_io_size(stream), sizeof(data));
|
||||
uint8_t buffer[3];
|
||||
ASSERT_EQUAL(sdl_read_io(stream, buffer, sizeof(buffer)), sizeof(buffer));
|
||||
ASSERT_EQUAL(buffer[0], 1);
|
||||
ASSERT_EQUAL(buffer[1], 2);
|
||||
ASSERT_EQUAL(buffer[2], 3);
|
||||
ASSERT_TRUE(sdl_close_io(stream));
|
||||
|
||||
SDL_Surface *source = sdl_create_surface(
|
||||
4, 4, 32, SDL_PIXELFORMAT_XRGB8888);
|
||||
SDL_Surface *destination = sdl_create_surface(
|
||||
4, 4, 32, SDL_PIXELFORMAT_XRGB8888);
|
||||
ASSERT_TRUE(source);
|
||||
ASSERT_TRUE(destination);
|
||||
|
||||
SDL_Rect destination_rect = {-1, 1, 99, 99};
|
||||
ASSERT_TRUE(SDL_BlitSurface(
|
||||
source, NULL, destination, &destination_rect));
|
||||
ASSERT_EQUAL(destination_rect.x, 0);
|
||||
ASSERT_EQUAL(destination_rect.y, 1);
|
||||
ASSERT_EQUAL(destination_rect.w, 3);
|
||||
ASSERT_EQUAL(destination_rect.h, 3);
|
||||
|
||||
SDL_Rect source_rect = {-1, 1, 4, 4};
|
||||
destination_rect = (SDL_Rect){1, -1, 99, 99};
|
||||
ASSERT_TRUE(SDL_BlitSurface(
|
||||
source, &source_rect, destination, &destination_rect));
|
||||
ASSERT_EQUAL(destination_rect.x, 2);
|
||||
ASSERT_EQUAL(destination_rect.y, 0);
|
||||
ASSERT_EQUAL(destination_rect.w, 2);
|
||||
ASSERT_EQUAL(destination_rect.h, 2);
|
||||
|
||||
source_rect = (SDL_Rect){10, 10, 1, 1};
|
||||
destination_rect = (SDL_Rect){1, 2, 99, 99};
|
||||
ASSERT_TRUE(SDL_BlitSurface(
|
||||
source, &source_rect, destination, &destination_rect));
|
||||
ASSERT_EQUAL(destination_rect.x, 1);
|
||||
ASSERT_EQUAL(destination_rect.y, 2);
|
||||
ASSERT_EQUAL(destination_rect.w, 0);
|
||||
ASSERT_EQUAL(destination_rect.h, 0);
|
||||
|
||||
sdl_destroy_surface(destination);
|
||||
sdl_destroy_surface(source);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef XSYSTEM35_SDL_MIXER_COMPAT_H
|
||||
#define XSYSTEM35_SDL_MIXER_COMPAT_H
|
||||
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#if XSYSTEM35_SDL_VERSION == 2
|
||||
#include <SDL_mixer.h>
|
||||
#elif XSYSTEM35_SDL_VERSION == 3
|
||||
#include <SDL3_mixer/SDL_mixer.h>
|
||||
#else
|
||||
#error "Unsupported XSYSTEM35_SDL_VERSION"
|
||||
#endif
|
||||
|
||||
#endif /* XSYSTEM35_SDL_MIXER_COMPAT_H */
|
||||
@@ -25,6 +25,7 @@ void cache_test(void);
|
||||
void gameresource_test(void);
|
||||
void hankaku_test(void);
|
||||
void qnt_test(void);
|
||||
void sdl_compat_test(void);
|
||||
void variable_test(void);
|
||||
|
||||
void sys_error(char *format, ...) {
|
||||
@@ -49,5 +50,6 @@ int main() {
|
||||
variable_test();
|
||||
hankaku_test();
|
||||
qnt_test();
|
||||
sdl_compat_test();
|
||||
return 0;
|
||||
}
|
||||
|
||||
+4
-1
@@ -17,8 +17,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "system.h"
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "debugger.h"
|
||||
#include "gfx.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user