Compare commits

..
30 Commits
Author SHA1 Message Date
kichikuou 48458ac034 Statically link SDL3 on Windows and drop DLL collection 2026-09-23 08:04:59 +09:00
kichikuou ffdd362f35 Limit SDL2 SysWM header to Windows users 2026-09-23 07:24:01 +09:00
kichikuou 78519bb5ef CI: Use emscripten 6.0.10 2026-09-23 07:14:32 +09:00
kichikuou 5989fe72e2 Fetch SDL3_mixer when the package is unavailable 2026-09-23 07:14:15 +09:00
kichikuou 66d0636197 Port Windows menu integration to SDL3 2026-09-23 07:05:14 +09:00
kichikuou 6c8590f8d4 SDL: Fix screenshot and input compatibility 2026-09-23 06:46:02 +09:00
kichikuou 3cbe4c73d8 CI: Add SDL2 and SDL3 build matrix
Document the supported SDL configurations and build dependencies for each
platform. Keep Android on SDL3 only and identify every packaged artifact by
its SDL version.
2026-09-22 21:39:05 +09:00
kichikuou 80da6cd782 Android: Use SDL3_mixer AAR
Use the official SDL3_mixer Android archive through Prefab instead of
building SDL3_mixer from source. Verify both release archives and their
extracted AAR files with fixed SHA-256 hashes.
2026-09-22 21:27:40 +09:00
kichikuou 2f73abd486 Android: Migrate to SDL3
Use the official SDL 3.4.16 AAR through Prefab and build SDL_mixer
3.2.4 from fixed source. Remove the bundled SDL2 Java sources and make
Android an SDL3-only target.
2026-09-22 21:10:36 +09:00
kichikuou 5844226b2f Emscripten: Fix SDL3 compilation
Include the standard I/O declarations used by system.c directly and handle
SDL3's pointer-array locale API in the built-in NLS fallback.
2026-09-22 20:53:24 +09:00
kichikuou 87596c58c5 SDL: Keep mixer conversions simple
Remove the helper layer and unit tests added solely for simple gain and
time expressions. Keep these conversions directly in their SDL3_mixer
backends instead of shaping production code around testability.
2026-09-22 20:39:28 +09:00
kichikuou 212a7bf138 SDL: Test mixer time and volume conversions
Share SDL3_mixer gain and 10-millisecond time conversions across the
PCM, BGM, CD, and MIDI backends. Add SDL3 unit coverage for volume
clamping, combined gain, time units, and tick counter wraparound.
2026-09-22 20:34:26 +09:00
kichikuou 652724a9ab SDL: Keep MIDI decoder selection automatic
Do not force the Timidity decoder based on TIMIDITY_CFG. Leave decoder
selection to SDL3_mixer unless SDL_SOUNDFONTS requires FluidSynth for
SDL2_mixer-compatible SoundFont handling.
2026-09-22 16:05:19 +09:00
kichikuou 658debdcf3 SDL: Add SDL3 MIDI backend
Play MIDI through the shared SDL3_mixer music track while preserving
the existing loop, pause, volume, position, and fade behavior. Support
FluidSynth SoundFonts and Timidity selection through their established
environment variables.
2026-09-22 16:00:21 +09:00
kichikuou ed4fbc1f19 SDL: Add SDL3 CD audio backend
Load playlist tracks through SDL3_mixer and play them on the shared
owner-aware music track. Preserve the CD-specific loop convention,
volume valancer, and playback position reporting.
2026-09-22 15:51:54 +09:00
kichikuou c1910382f8 SDL: Add SDL3 BGM backend
Add an owner-aware music track shared by the SDL3 BGM, CD, and MIDI
backends. Use it to preserve exclusive music playback while moving BGM
loading, looping, fades, and volume control to SDL3_mixer.
2026-09-22 15:47:24 +09:00
kichikuou 495d6146f3 SDL: Add SDL3 PCM backend
Create shared SDL3_mixer device ownership and map the 129 PCM slots to
MIX_Track objects. Decode left/right WAV data through the public decoder
API before combining channels.
2026-09-22 15:41:00 +09:00
kichikuou 6f01734215 SDL: Add event and input compatibility
Map SDL2 and SDL3 event names, keyboard fields, touch queries, joystick
device identifiers, message boxes, and text input APIs through the common
interface.  Keep the deferred touch deadline on the millisecond clock so
SDL3's nanosecond event timestamps are not mixed with SDL_GetTicks().
2026-09-22 15:33:51 +09:00
kichikuou b55574c162 SDL: Add cursor compatibility
Map system cursor shapes to their SDL2 and SDL3 names and hide the
cursor destruction API difference.  Also include the allocation
declarations required by the SDL3 debug build.
2026-09-22 15:27:28 +09:00
kichikuou 9f92186905 SDL: Add display mode compatibility
Hide the SDL2 display index and SDL3 display ID and return-value
differences behind the compatibility layer.
2026-09-22 15:24:07 +09:00
kichikuou d80d82912a Build: Fix debug compilation errors
Include the standard allocation declarations where needed and use the
parsed command argument in TPC and TPS trace messages.
2026-09-22 15:23:45 +09:00
kichikuou eeb4953213 SDL: Add IO stream compatibility
Unify SDL2 RWops and SDL3 IOStream operations for files, memory,
font resources, and WAV decoding. Keep stream close ownership explicit
and cover memory stream reads in both SDL configurations.
2026-09-22 15:16:24 +09:00
kichikuou 5abc9e8177 SDL: Preserve blit destination updates
Emulate SDL2's clipped destination rectangle output when using SDL3.
Cover source clipping, destination clipping, and empty blits in the
SDL compatibility tests.
2026-09-22 15:10:05 +09:00
kichikuou 5d7ec418b1 SDL: Add renderer compatibility layer
Convert SDL2 integer render rectangles to SDL3 floating-point rectangles
inside the compatibility layer. Preserve logical presentation, integer
scaling, HiDPI sizing, texture filtering, and vertex color semantics.
2026-09-22 15:03:02 +09:00
kichikuou e488123345 SDL: Add thread primitive compatibility
Wrap mutexes, condition variables, and atomic integers with common SDL2
and SDL3 types and semantics.  Normalize timed condition waits to report
whether the condition was signaled.
2026-09-22 14:47:34 +09:00
kichikuou 5eea90c9a8 SDL: Add surface compatibility layer
Route surface formats, palettes, creation, conversion, and destruction
through SDL2/SDL3 compatibility helpers.  Preserve indexed-surface palette
ownership and adapt renamed fill, stretch, blit, and rectangle APIs.
2026-09-22 14:42:55 +09:00
kichikuou 677ecb5a0f Build: Add SDL version selection
Add the compile-time SDL major version option and propagate it through
interface targets. Route SDL includes through C99 compatibility headers
while keeping SDL2 as the desktop and Emscripten default.
2026-09-22 14:21:09 +09:00
kichikuou 1918350b19 Update censor lists for Rance 1-4 2026-09-21 14:26:11 +09:00
kichikuou 9504ee7b65 Add censor lists for Rance 1-4
Contributed by @RottenBlock and Vivvers (x.com/VivversX).
2026-09-21 13:17:23 +09:00
kichikuou f033a507d3 ZZ2: Return platform name from SDL
Fixes #82.
2026-09-20 12:23:02 +09:00
114 changed files with 3846 additions and 6336 deletions
+1
View File
@@ -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
+15 -9
View File
@@ -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
View File
@@ -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_ \
+21 -9
View File
@@ -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
View File
@@ -1,6 +1,7 @@
out
android/.gradle
android/.idea
android/app/aars
android/app/.cxx
android/app/.externalNativeBuild
android/app/build
+63 -9
View File
@@ -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})
+66 -22
View File
@@ -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.
+5
View File
@@ -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.
+72 -1
View File
@@ -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
View File
@@ -1 +0,0 @@
include $(call all-subdir-makefiles)
-10
View File
@@ -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
+5 -23
View File
@@ -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)
-18
View File
@@ -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)
-13
View File
@@ -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;
}
}
+3
View File
@@ -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@
+2 -2
View File
@@ -12,8 +12,8 @@ or in your `.xsys35rc` file. For example:
xsystem35 -censor misc/censor/kichikuou.txt
```
The list files contain image numbers (integers), one per line. Lines starting
with `#` are treated as comments.
The list files contain image numbers (integers), one per line. A `#` starts a
comment that continues to the end of the line.
## Contributing
+30
View File
@@ -0,0 +1,30 @@
# Censor list for ランス -光をもとめて- (Rance: The Quest for Hikari)
# Images marked with a * might be allowed in an 18+ stream.
5
6
8
22
26
27
29
32
39
40
41
51
56
57
75
76
77
78 # *
80
82
89
91
96 # *
97 # *
98
99
101
208
+59
View File
@@ -0,0 +1,59 @@
# Censor list for ランスII (Rance II)
# Images marked with a * might be allowed in an 18+ stream.
26
39 # *
40
41
77
78
86
98
103
104
121
122
123
124
130
131
132
133
134
140
141
142
148
156
157
158
159
161
166
171
172
185
187
191
195
197
200
201
234
236
245
246
247
272
274
275
276
278
280
281
282
284
289
292
299
300
302
+121
View File
@@ -0,0 +1,121 @@
# Censor list for Rance 3
# Images marked with a * might be allowed in an 18+ stream.
1 # *
2 # *
3
6
8
9
32 # *
53 # *
54
70
71
72
73
75
77 # *
78
79
112 # *
113
114
115
116
117
118 # *
150
151
152
153
154
155
166
167
180
181
183
184
185
186
187
188
189
190
195
196
208
209
210
211
212
213
215
216
217
218
219
231
232
233
234
235
236
238
239
260
261
263
264
265
266
268
269
330
331
332
334
335
336
337
338
339
420
421
422
423
424
425
426
427
428
429
440
441
442
443
444
445
447
473
474
475
476
477
479
519 # *
521 # *
523
524
525
526
530
533 # *
534
535
536
537
570
571
572
581
+168
View File
@@ -0,0 +1,168 @@
# Censor list for Rance IV
# Images marked with a * might be allowed in an 18+ stream.
1
2
10
20
26
28
29
30 # *
31
32
33
34
35
36
37
38
39 # *
40
41
42
43
44
45
46
48
49
75
76
77
78
79
122
126
140
142
148
149
162 # *
163
164
165 # *
166
167
168
169 # *
170
171
172
176
177
178
185
186
188
189
205
207
217 # *
220
221
222
223
228
241
242
251
252
253
254
260
261
262 # *
264 # *
265
266
267
268
269
280
281
283
284
285
286 # *
287
288
292
294
299
370
371
372
373
376
378
382
384
385 # *
386
387
388
389
414 # *
416
428 # *
531 # *
532
533
534 # *
535
550
551
552
553
554
555
562 # *
565
615
625
626
711
723
1055
1056
1057
1058
1059
1060
1061
1062
1545 # *
1546 # *
1547 # *
1548 # *
1549 # *
1653 # *
1654 # *
1655 # *
1656 # *
1657 # *
1658 # *
1659 # *
1660 # *
1661 # *
1662 # *
1663 # *
1664 # *
1665 # *
1728
1729
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
+3 -3
View File
@@ -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
View File
@@ -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"
+6 -4
View File
@@ -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) {
+2 -2
View File
@@ -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 -3
View File
@@ -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);
+4 -3
View File
@@ -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);
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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 {
+2 -2
View File
@@ -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));
}
+3 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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"
+4 -3
View File
@@ -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);
}
+6 -5
View File
@@ -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++) {
+6 -4
View File
@@ -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);
+4 -4
View File
@@ -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);
+7 -6
View File
@@ -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;
}
}
+2 -2
View File
@@ -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() {
+9 -4
View File
@@ -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
View File
@@ -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);
}
}
}
+1 -1
View File
@@ -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;
}
+1 -2
View File
@@ -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
View File
@@ -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
View File
@@ -1,6 +1,7 @@
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <zlib.h>
#include "portab.h"
+3 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+5 -5
View File
@@ -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);
}
}
+2 -2
View File
@@ -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 {
+156
View File
@@ -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
View File
@@ -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
View File
@@ -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"
+201
View File
@@ -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,
};
+24 -19
View File
@@ -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.
+1 -1
View File
@@ -24,7 +24,7 @@
#ifndef __CG__
#define __CG__
#include <SDL_rect.h>
#include "sdl_compat.h"
#include "cache.h"
#include "portab.h"
+2 -2
View File
@@ -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() {
+3 -18
View File
@@ -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>
@@ -376,24 +377,8 @@ void commandZZ1() {
void commandZZ2() {
/* 機種文字列を文字列領域 num に返す(MAX12文字)*/
int num = getCaliValue();
#if defined(linux)
static uint8_t str[] = {0x82, 0x6B, 0x82, 0x89, 0x82, 0x8E, 0x82, 0x95, 0x82, 0x98, 0};
#elif defined(__FreeBSD__)
static uint8_t str[] = {0x82, 0x65, 0x82, 0x92, 0x82, 0x85, 0x82, 0x85, 0x82, 0x61, 0x82, 0x72, 0x82, 0x63, 0};
#elif defined(__OpenBSD__)
static uint8_t str[] = {0x82, 0x6E, 0x82, 0x90, 0x82, 0x85, 0x82, 0x8E, 0x82, 0x61, 0x82, 0x72, 0x82, 0x63, 0};
#elif defined(__NetBSD__)
static uint8_t str[] = {0x82, 0x6D, 0x82, 0x85, 0x82, 0x94, 0x82, 0x61, 0x82, 0x72, 0x82, 0x63, 0};
#elif defined(sun)
static uint8_t str[] = {0x82, 0x72, 0x82, 0x95, 0x82, 0x8E, 0x82, 0x6E, 0x82, 0x72, 0};
#elif defined(__osf__)
static uint8_t str[] = {0x82, 0x63, 0x82, 0x89, 0x82, 0x87, 0x82, 0x89, 0x82, 0x94, 0x82, 0x81, 0x82, 0x8C, 0x82, 0x74, 0x82, 0x6D, 0x82, 0x68, 0x82, 0x77, 0};
#elif defined(sgi)
static uint8_t str[] = {0x82, 0x68, 0x82, 0x71, 0x82, 0x68, 0x82, 0x77, 0};
#else
static uint8_t str[] = {0x82, 0x74, 0x82, 0x8e, 0x82, 0x8b, 0x82, 0x8e, 0x82, 0x8f, 0x82, 0x97, 0x82, 0x8e, 0};
#endif
svar_set(num, str);
svar_set(num, SDL_GetPlatform());
TRACE("ZZ2 %d:",num);
}
+12 -12
View File
@@ -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);
+2
View File
@@ -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"
+1
View File
@@ -18,6 +18,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "system.h"
#include "debugger.h"
+1 -1
View File
@@ -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 -33
View File
@@ -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 {
@@ -197,7 +198,7 @@ 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);
}
@@ -384,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));
@@ -409,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,
@@ -419,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);
}
@@ -431,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");
@@ -514,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);
@@ -531,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)
@@ -610,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);
@@ -1020,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));
@@ -1054,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];
@@ -1092,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));
@@ -1196,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();
@@ -1229,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,
@@ -1257,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);
}
@@ -1428,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);
}
@@ -1493,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);
}
@@ -1573,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));
@@ -1608,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);
}
@@ -1622,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);
@@ -1690,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();
@@ -1876,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:
@@ -1906,7 +1907,7 @@ 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);
}
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+2 -2
View File
@@ -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);
+26 -24
View File
@@ -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
@@ -54,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) {
@@ -67,7 +68,7 @@ void gfx_updateScreen(void) {
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;
@@ -133,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) {
@@ -183,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);
}
/*
@@ -200,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;
@@ -209,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);
}
/* 直線描画 */
@@ -235,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);
}
@@ -252,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:
@@ -294,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.
@@ -311,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++;
}
}
@@ -339,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;
@@ -358,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));
@@ -368,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
View File
@@ -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 -1
View File
@@ -25,7 +25,7 @@
#include "config.h"
#include <SDL.h>
#include "sdl_compat.h"
#include "portab.h"
#include "ags.h"
+43 -28
View File
@@ -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
View File
@@ -24,7 +24,7 @@
#ifndef __IMAGE__
#define __IMAGE__
#include <SDL_surface.h>
#include "sdl_compat.h"
#include "cg.h"
#include "ags.h"
+6 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+242
View File
@@ -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
View File
@@ -19,8 +19,7 @@
*
*/
#include <SDL.h>
#include <SDL_mixer.h>
#include "sdl_mixer_compat.h"
#include "portab.h"
#include "system.h"
+35 -32
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+9
View File
@@ -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) {
+10 -1
View File
@@ -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);
}
+383
View File
@@ -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
View File
@@ -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"
+170
View File
@@ -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);
}
+30
View File
@@ -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 */

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