1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-05-05 22:05:13 +00:00

Start refactoring, iOS port, bump to 1.0.0

This commit is contained in:
Alula 2024-08-03 03:01:35 +02:00
parent 97d85aa841
commit 43cb5d4293
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
56 changed files with 1822 additions and 944 deletions

View file

@ -1,158 +0,0 @@
version: "0.101.0-{build}-{branch}"
skip_commits:
files:
- README.md
- LICENSE
- app/
- drsandroid/
- drshorizon/
environment:
global:
PROJECT_NAME: doukutsu-rs
matrix:
- channel: stable
target: x86_64-pc-windows-msvc
target_name: win64
arch_name: x86_64
job_name: windows-x64
appveyor_build_worker_image: Visual Studio 2019
- channel: stable
target: i686-pc-windows-msvc
target_name: win32
arch_name: i686
job_name: windows-x32
appveyor_build_worker_image: Visual Studio 2019
- channel: stable
target: x86_64-unknown-linux-gnu
target_name: linux
job_name: linux-x64
appveyor_build_worker_image: Ubuntu
- channel: stable
target: x86_64-apple-darwin
target_name: mac-intel
job_name: mac-x64
appveyor_build_worker_image: macos-monterey
- channel: stable
target: aarch64-apple-darwin
target_name: mac-m1
job_name: mac-arm64
appveyor_build_worker_image: macos-monterey
matrix:
fast_finish: true
for:
-
matrix:
only:
- appveyor_build_worker_image: Visual Studio 2019
install:
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- rustup-init -yv --default-toolchain %channel% --default-host %target%
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
- rustup update
- rustup default %channel%
- rustc -vV
- cargo -vV
cache:
- '%USERPROFILE%\.cache'
- '%USERPROFILE%\.cargo\bin'
- '%USERPROFILE%\.cargo\registry\index'
- '%USERPROFILE%\.cargo\registry\cache'
- '%USERPROFILE%\.cargo\git\db'
- '%USERPROFILE%\.rustup'
- 'target'
build_script:
#- set DRS_BUILD_VERSION_OVERRIDE=%APPVEYOR_BUILD_VERSION%
- if "%APPVEYOR_REPO_TAG%" == "true" (set DRS_BUILD_VERSION_OVERRIDE=%APPVEYOR_REPO_TAG_NAME%) else (set DRS_BUILD_VERSION_OVERRIDE=%APPVEYOR_BUILD_VERSION%)
- set CARGO_INCREMENTAL=1
- cargo build --release --bin doukutsu-rs
- mkdir release
- copy LICENSE release\LICENSE
- copy target\release\doukutsu-rs.exe release\doukutsu-rs.%arch_name%.exe
- cd release
- 7z a ../doukutsu-rs_%target_name%.zip *
- appveyor PushArtifact ../doukutsu-rs_%target_name%.zip
-
matrix:
only:
- appveyor_build_worker_image: macos-monterey
init:
- ps: |
if ($env:APPVEYOR_REPO_TAG -eq "true")
{
Update-AppveyorBuild -Version "$env:APPVEYOR_REPO_TAG_NAME"
}
install:
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -yv --default-toolchain $channel
- export PATH=$PATH:$HOME/.cargo/bin
- rustup update
- rustup default $channel
- rustup target add $target
- rustc -vV
- cargo -vV
- cargo install cargo-bundle --force
cache:
- '$HOME/.cache'
- '$HOME/.cargo/bin'
- '$HOME/.cargo/registry/index'
- '$HOME/.cargo/registry/cache'
- '$HOME/.cargo/git/db'
- '$HOME/.rustup'
- 'target'
build_script:
#- export DRS_BUILD_VERSION_OVERRIDE=$APPVEYOR_BUILD_VERSION
- if [ "$APPVEYOR_REPO_TAG" = "true" ]; then export DRS_BUILD_VERSION_OVERRIDE=$APPVEYOR_REPO_TAG_NAME; else export DRS_BUILD_VERSION_OVERRIDE=$APPVEYOR_BUILD_VERSION; fi
- CARGO_INCREMENTAL=1 cargo bundle --release --target $target
- mkdir release
- cp LICENSE ./release/LICENSE
- cp -a target/$target/release/bundle/osx/doukutsu-rs.app ./release/doukutsu-rs.app
- cd release
- codesign -s - -f ./doukutsu-rs.app/Contents/MacOS/doukutsu-rs
- 7z a ../doukutsu-rs_$target_name.zip *
- appveyor PushArtifact ../doukutsu-rs_$target_name.zip
-
matrix:
only:
- appveyor_build_worker_image: Ubuntu
install:
- sudo apt-get update && sudo apt-get -y install libasound2-dev libudev-dev libgl1-mesa-dev pkg-config
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -yv --default-toolchain $channel --default-host $target
- export PATH=$PATH:$HOME/.cargo/bin
- rustup update
- rustup default $channel
- rustc -vV
- cargo -vV
cache:
- '$HOME/.cache'
- '$HOME/.cargo/bin'
- '$HOME/.cargo/registry/index'
- '$HOME/.cargo/registry/cache'
- '$HOME/.cargo/git/db'
- '$HOME/.rustup'
- 'target'
build_script:
#- export DRS_BUILD_VERSION_OVERRIDE=$APPVEYOR_BUILD_VERSION
- if [ "$APPVEYOR_REPO_TAG" = "true" ]; then export DRS_BUILD_VERSION_OVERRIDE=$APPVEYOR_REPO_TAG_NAME; else export DRS_BUILD_VERSION_OVERRIDE=$APPVEYOR_BUILD_VERSION; fi
- RUSTFLAGS="-C link-arg=-s" CARGO_INCREMENTAL=1 cargo build --release --bin doukutsu-rs
- mkdir release
- cp LICENSE ./release/LICENSE
- cp -a target/release/doukutsu-rs ./release/doukutsu-rs.x86_64.elf
- cd release
- 7z a ../doukutsu-rs_$target_name.zip *
- appveyor PushArtifact ../doukutsu-rs_$target_name.zip

View file

@ -1,6 +0,0 @@
[target.aarch64-linux-android]
rustflags = [
"-C", "link-arg=-lc++_static",
"-C", "link-arg=-lc++abi",
"-C", "link-arg=-lEGL",
]

View file

@ -19,7 +19,9 @@ defaults:
shell: bash
env:
VERSION: "0.101.0"
# TODO: get version from Cargo:
# export DRS_CARGO_VERSION=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "doukutsu-rs").version')
VERSION: "1.0.0"
jobs:
build:

5
.gitignore vendored
View file

@ -3,6 +3,7 @@
# Cave Story (copyrighted) data files
/data/
drs*/data
# Generated by Cargo
# will have compiled files and executables
@ -18,6 +19,9 @@ target/
# cave story saves
Profile.dat*
# XCode
xcuserdata/
# General
.DS_Store
.AppleDouble
@ -60,4 +64,5 @@ ehthumbs_vista.db
# Recycle Bin used on file shares
$RECYCLE.BIN/
3rdparty/
*.log

View file

@ -1,10 +1,9 @@
[package]
name = "doukutsu-rs"
description = "A re-implementation of Cave Story (Doukutsu Monogatari) engine"
version = "0.101.0"
authors = ["Alula", "dawnDus"]
version = "1.0.0"
edition = "2021"
rust-version = "1.65"
rust-version = "1.78"
[lib]
crate-type = ["lib"]
@ -31,9 +30,9 @@ codegen-units = 256
[package.metadata.bundle]
name = "doukutsu-rs"
identifier = "io.github.doukutsu_rs"
version = "0.101.0"
version = "1.0.0"
resources = ["data"]
copyright = "Copyright (c) 2020-2023 doukutsu-rs contributors"
copyright = "Copyright (c) 2020-2024 doukutsu-rs contributors"
category = "Game"
osx_minimum_system_version = "10.12"
@ -42,7 +41,7 @@ default = ["default-base", "backend-sdl", "render-opengl", "exe", "webbrowser",
default-base = ["ogg-playback"]
ogg-playback = ["lewton"]
backend-sdl = ["sdl2", "sdl2-sys"]
backend-glutin = ["winit", "glutin", "render-opengl"]
backend-winit = ["winit", "glutin", "render-opengl"]
backend-horizon = []
render-opengl = []
discord-rpc = ["discord-rich-presence"]
@ -52,7 +51,7 @@ exe = []
android = []
[dependencies]
#glutin = { path = "./3rdparty/glutin/glutin", optional = true }
glutin = { path = "./3rdparty/glutin/glutin", optional = true }
#winit = { path = "./3rdparty/winit", optional = true, default_features = false, features = ["x11"] }
#sdl2 = { path = "./3rdparty/rust-sdl2", optional = true, features = ["unsafe_textures", "bundled", "static-link"] }
#sdl2-sys = { path = "./3rdparty/rust-sdl2/sdl2-sys", optional = true, features = ["bundled", "static-link"] }
@ -66,7 +65,7 @@ discord-rich-presence = { version = "0.2", optional = true }
downcast = "0.11"
encoding_rs = "0.8.33"
fern = "0.6.2"
glutin = { git = "https://github.com/doukutsu-rs/glutin.git", rev = "2dd95f042e6e090d36f577cbea125560dd99bd27", optional = true, default_features = false, features = ["x11"] }
#glutin = { git = "https://github.com/doukutsu-rs/glutin.git", rev = "2dd95f042e6e090d36f577cbea125560dd99bd27", optional = true, default_features = false, features = ["x11"] }
imgui = { git = "https://github.com/imgui-rs/imgui-rs.git", rev = "5d771a83b82c5cc3dd58cca3f969d900369262e6" }
image = { version = "0.24", default-features = false, features = ["png", "bmp"] }
itertools = "0.10"
@ -89,8 +88,8 @@ strum = "0.24"
strum_macros = "0.24"
# remove and replace when extract_if is in stable
vec_mut_scan = "0.4"
webbrowser = { version = "0.8.6", optional = true }
winit = { git = "https://github.com/doukutsu-rs/winit.git", rev = "878f206d19af01b0977277929eee5e32667453c0", optional = true, default_features = false, features = ["x11"] }
webbrowser = { version = "1.0.1", optional = true }
winit = { version = "0.30.2", optional = true, default_features = false, features = ["x11"] }
xmltree = "0.10"
#hack to not link SDL_image on Windows(causes a linker error)
@ -103,8 +102,8 @@ winapi = { version = "0.3", features = ["winuser"] }
[target.'cfg(target_os = "windows")'.build-dependencies]
winres = "0.1"
[target.'cfg(target_os = "macos")'.dependencies]
objc = "0.2"
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
objc2 = "0.5.2"
[target.'cfg(target_os = "android")'.dependencies]
ndk = "0.7"
@ -114,4 +113,4 @@ jni = "0.20"
[target.'cfg(target_os = "horizon")'.dependencies]
#deko3d = { path = "./3rdparty/deko3d" }
deko3d = { git = "https://github.com/doukutsu-rs/deko3d-rs", branch = "master" }
deko3d = { git = "https://github.com/doukutsu-rs/deko3d-rs", rev = "acddd174321c842367971faeb9aebcdb99ddfef3" }

View file

@ -17,4 +17,4 @@ crate-type = ["cdylib"]
ndk = "0.7"
ndk-glue = "0.7"
ndk-sys = "0.4"
doukutsu-rs = { path = "../", default-features = false, features = ["default-base", "backend-glutin", "webbrowser"] }
doukutsu-rs = { path = "../", default-features = false, features = ["default-base", "backend-winit", "webbrowser"] }

24
drsios/Cargo.toml Normal file
View file

@ -0,0 +1,24 @@
[package]
name = "drsios"
description = "doukutsu-rs targeted for iOS"
version = "0.1.0"
edition = "2021"
[profile.release]
opt-level = 3
lto = "off"
codegen-units = 256
incremental = true
[profile.dev]
opt-level = 3
lto = "off"
overflow-checks = false
codegen-units = 256
incremental = true
[lib]
crate-type = ["staticlib"]
[dependencies]
doukutsu-rs = { path = "../", default-features = false, features = ["default-base", "backend-winit", "webbrowser"] }

12
drsios/build.rs Normal file
View file

@ -0,0 +1,12 @@
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rustc-link-lib=framework=AudioToolbox");
println!("cargo:rustc-link-lib=framework=AudioUnit");
println!("cargo:rustc-link-lib=framework=CoreAudio");
println!("cargo:rustc-link-lib=framework=CoreFoundation");
println!("cargo:rustc-link-lib=framework=Foundation");
println!("cargo:rustc-link-lib=framework=OpenGLES");
println!("cargo:rustc-link-lib=framework=Security");
println!("cargo:rustc-link-lib=framework=UIKit");
}

View file

@ -0,0 +1,499 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
B449CF882AA678FB00509937 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B449CF872AA678FB00509937 /* Launch Screen.storyboard */; };
B449CF8A2AA6902200509937 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B449CF892AA6902200509937 /* GLKit.framework */; };
B4A816142AA583DD00F87BB6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B4A816132AA583DD00F87BB6 /* Assets.xcassets */; };
B4A816192AA583DD00F87BB6 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4A816182AA583DD00F87BB6 /* main.mm */; };
B4A8162B2AA58A9B00F87BB6 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4A816202AA5885100F87BB6 /* OpenGLES.framework */; };
B4A8162C2AA58AA500F87BB6 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4A816242AA5889100F87BB6 /* AudioToolbox.framework */; };
B4A8162D2AA58AA500F87BB6 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4A816222AA5885600F87BB6 /* CoreAudio.framework */; };
B4A816352AA58C4B00F87BB6 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4A816342AA58C4B00F87BB6 /* AVFoundation.framework */; };
B4A816362AA5980E00F87BB6 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4A816262AA588B600F87BB6 /* UIKit.framework */; };
B4A816382AA5981300F87BB6 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4A816372AA5981300F87BB6 /* Security.framework */; };
B4CD7F9E2C2423700043BFEC /* Cargo.toml in Sources */ = {isa = PBXBuildFile; fileRef = B4CD7F9D2C2423500043BFEC /* Cargo.toml */; settings = {COMPILER_FLAGS = "--lib"; }; };
B4CD7FA02C242CB10043BFEC /* libdrsios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B4CD7F942C2422440043BFEC /* libdrsios.a */; };
/* End PBXBuildFile section */
/* Begin PBXBuildRule section */
B4CD7F9F2C2423880043BFEC /* PBXBuildRule */ = {
isa = PBXBuildRule;
compilerSpec = com.apple.compilers.proxy.script;
dependencyFile = "$(DERIVED_FILE_DIR)/$(ARCHS)-$(EXECUTABLE_NAME).d";
filePatterns = "*/Cargo.toml";
fileType = pattern.proxy;
inputFiles = (
);
isEditable = 1;
outputFiles = (
"$(TARGET_BUILD_DIR)/$(EXECUTABLE_NAME)",
);
runOncePerArchitecture = 0;
script = "# generated with cargo-xcode 1.11.0\nset -euo pipefail;\nexport PATH=\"$HOME/.cargo/bin:$PATH:/usr/local/bin:/opt/homebrew/bin\";\n# don't use ios/watchos linker for build scripts and proc macros\nexport CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER=/usr/bin/cc\nexport CARGO_TARGET_X86_64_APPLE_DARWIN_LINKER=/usr/bin/cc\nexport NO_COLOR=1\n\ncase \"$PLATFORM_NAME\" in\n \"macosx\")\n CARGO_XCODE_TARGET_OS=darwin\n if [ \"${IS_MACCATALYST-NO}\" = YES ]; then\n CARGO_XCODE_TARGET_OS=ios-macabi\n fi\n ;;\n \"iphoneos\") CARGO_XCODE_TARGET_OS=ios ;;\n \"iphonesimulator\") CARGO_XCODE_TARGET_OS=ios-sim ;;\n \"appletvos\" | \"appletvsimulator\") CARGO_XCODE_TARGET_OS=tvos ;;\n \"watchos\") CARGO_XCODE_TARGET_OS=watchos ;;\n \"watchsimulator\") CARGO_XCODE_TARGET_OS=watchos-sim ;;\n \"xros\") CARGO_XCODE_TARGET_OS=visionos ;;\n \"xrsimulator\") CARGO_XCODE_TARGET_OS=visionos-sim ;;\n *)\n CARGO_XCODE_TARGET_OS=\"$PLATFORM_NAME\"\n echo >&2 \"warning: cargo-xcode needs to be updated to handle $PLATFORM_NAME\"\n ;;\nesac\n\nCARGO_XCODE_TARGET_TRIPLES=\"\"\nCARGO_XCODE_TARGET_FLAGS=\"\"\nLIPO_ARGS=\"\"\nfor arch in $ARCHS; do\n if [[ \"$arch\" == \"arm64\" ]]; then arch=aarch64; fi\n if [[ \"$arch\" == \"i386\" && \"$CARGO_XCODE_TARGET_OS\" != \"ios\" ]]; then arch=i686; fi\n triple=\"${arch}-apple-$CARGO_XCODE_TARGET_OS\"\n CARGO_XCODE_TARGET_TRIPLES+=\" $triple\"\n CARGO_XCODE_TARGET_FLAGS+=\" --target=$triple\"\n LIPO_ARGS+=\"$CARGO_TARGET_DIR/$triple/$CARGO_XCODE_BUILD_PROFILE/$CARGO_XCODE_CARGO_FILE_NAME\n\"\ndone\n\necho >&2 \"Cargo $CARGO_XCODE_BUILD_PROFILE $ACTION for $PLATFORM_NAME $ARCHS =$CARGO_XCODE_TARGET_TRIPLES; using ${SDK_NAMES:-}. \\$PATH is:\"\ntr >&2 : '\\n' <<<\"$PATH\"\n\nif command -v rustup &> /dev/null; then\n for triple in $CARGO_XCODE_TARGET_TRIPLES; do\n if ! rustup target list --installed | grep -Eq \"^$triple$\"; then\n echo >&2 \"warning: this build requires rustup toolchain for $triple, but it isn't installed (will try rustup next)\"\n rustup target add \"$triple\" || {\n echo >&2 \"warning: can't install $triple, will try nightly -Zbuild-std\";\n OTHER_INPUT_FILE_FLAGS+=\" -Zbuild-std\";\n if [ -z \"${RUSTUP_TOOLCHAIN:-}\" ]; then\n export RUSTUP_TOOLCHAIN=nightly\n fi\n break;\n }\n fi\n done\nfi\n\nif [ \"$CARGO_XCODE_BUILD_PROFILE\" = release ]; then\n OTHER_INPUT_FILE_FLAGS=\"$OTHER_INPUT_FILE_FLAGS --release\"\nfi\n\nif [ \"$ACTION\" = clean ]; then\n cargo clean --verbose --manifest-path=\"$SCRIPT_INPUT_FILE\" $CARGO_XCODE_TARGET_FLAGS $OTHER_INPUT_FILE_FLAGS;\n rm -f \"$SCRIPT_OUTPUT_FILE_0\"\n exit 0\nfi\n\necho cargo build --manifest-path=\"$SCRIPT_INPUT_FILE\" --features=\"${CARGO_XCODE_FEATURES:-}\" $CARGO_XCODE_TARGET_FLAGS $OTHER_INPUT_FILE_FLAGS --verbose --message-format=short\n{ cargo build --manifest-path=\"$SCRIPT_INPUT_FILE\" --features=\"${CARGO_XCODE_FEATURES:-}\" $CARGO_XCODE_TARGET_FLAGS $OTHER_INPUT_FILE_FLAGS --verbose --message-format=short 2>&1 | sed -E 's/^([^ :]+:[0-9]+:[0-9]+: error)/\\1: /' >&2; } || { echo >&2 \"$SCRIPT_INPUT_FILE: error: cargo-xcode project build failed; $CARGO_XCODE_TARGET_TRIPLES\"; exit 1; }\n\ntr '\\n' '\\0' <<<\"$LIPO_ARGS\" | xargs -0 lipo -create -output \"$SCRIPT_OUTPUT_FILE_0\"\n\nif [ ${LD_DYLIB_INSTALL_NAME:+1} ]; then\n install_name_tool -id \"$LD_DYLIB_INSTALL_NAME\" \"$SCRIPT_OUTPUT_FILE_0\"\nfi\n\nDEP_FILE_DST=\"$DERIVED_FILE_DIR/${ARCHS}-${EXECUTABLE_NAME}.d\"\necho \"\" > \"$DEP_FILE_DST\"\nfor triple in $CARGO_XCODE_TARGET_TRIPLES; do\n BUILT_SRC=\"$CARGO_TARGET_DIR/$triple/$CARGO_XCODE_BUILD_PROFILE/$CARGO_XCODE_CARGO_FILE_NAME\"\n\n # cargo generates a dep file, but for its own path, so append our rename to it\n DEP_FILE_SRC=\"$CARGO_TARGET_DIR/$triple/$CARGO_XCODE_BUILD_PROFILE/$CARGO_XCODE_CARGO_DEP_FILE_NAME\"\n if [ -f \"$DEP_FILE_SRC\" ]; then\n cat \"$DEP_FILE_SRC\" >> \"$DEP_FILE_DST\"\n fi\n echo >> \"$DEP_FILE_DST\" \"${SCRIPT_OUTPUT_FILE_0/ /\\\\ /}: ${BUILT_SRC/ /\\\\ /}\"\ndone\ncat \"$DEP_FILE_DST\"\n\necho \"success: $ACTION of $SCRIPT_OUTPUT_FILE_0 for $CARGO_XCODE_TARGET_TRIPLES\"\n";
};
/* End PBXBuildRule section */
/* Begin PBXFileReference section */
B449CF872AA678FB00509937 /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = "<group>"; };
B449CF892AA6902200509937 /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; };
B4A816012AA583DB00F87BB6 /* doukutsu-rs.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "doukutsu-rs.app"; sourceTree = BUILT_PRODUCTS_DIR; };
B4A816132AA583DD00F87BB6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
B4A816182AA583DD00F87BB6 /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
B4A816202AA5885100F87BB6 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
B4A816222AA5885600F87BB6 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
B4A816242AA5889100F87BB6 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
B4A816262AA588B600F87BB6 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
B4A8162E2AA58AA500F87BB6 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
B4A816322AA58BEE00F87BB6 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
B4A816342AA58C4B00F87BB6 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
B4A816372AA5981300F87BB6 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
B4A816392AA59D8600F87BB6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
B4CD7F942C2422440043BFEC /* libdrsios.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdrsios.a; sourceTree = BUILT_PRODUCTS_DIR; };
B4CD7F9D2C2423500043BFEC /* Cargo.toml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Cargo.toml; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
B4A815FE2AA583DB00F87BB6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B4CD7FA02C242CB10043BFEC /* libdrsios.a in Frameworks */,
B4A816382AA5981300F87BB6 /* Security.framework in Frameworks */,
B4A816362AA5980E00F87BB6 /* UIKit.framework in Frameworks */,
B4A816352AA58C4B00F87BB6 /* AVFoundation.framework in Frameworks */,
B4A8162C2AA58AA500F87BB6 /* AudioToolbox.framework in Frameworks */,
B449CF8A2AA6902200509937 /* GLKit.framework in Frameworks */,
B4A8162D2AA58AA500F87BB6 /* CoreAudio.framework in Frameworks */,
B4A8162B2AA58A9B00F87BB6 /* OpenGLES.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
B4A815F82AA583DB00F87BB6 = {
isa = PBXGroup;
children = (
B4CD7F9D2C2423500043BFEC /* Cargo.toml */,
B4A816032AA583DB00F87BB6 /* doukutsu-rs */,
B4A816022AA583DB00F87BB6 /* Products */,
B4A8161F2AA5885100F87BB6 /* Frameworks */,
);
sourceTree = "<group>";
};
B4A816022AA583DB00F87BB6 /* Products */ = {
isa = PBXGroup;
children = (
B4A816012AA583DB00F87BB6 /* doukutsu-rs.app */,
B4CD7F942C2422440043BFEC /* libdrsios.a */,
);
name = Products;
sourceTree = "<group>";
};
B4A816032AA583DB00F87BB6 /* doukutsu-rs */ = {
isa = PBXGroup;
children = (
B4A816392AA59D8600F87BB6 /* Info.plist */,
B4A816132AA583DD00F87BB6 /* Assets.xcassets */,
B4A816182AA583DD00F87BB6 /* main.mm */,
B449CF872AA678FB00509937 /* Launch Screen.storyboard */,
);
path = "doukutsu-rs";
sourceTree = "<group>";
};
B4A8161F2AA5885100F87BB6 /* Frameworks */ = {
isa = PBXGroup;
children = (
B449CF892AA6902200509937 /* GLKit.framework */,
B4A816372AA5981300F87BB6 /* Security.framework */,
B4A816342AA58C4B00F87BB6 /* AVFoundation.framework */,
B4A816322AA58BEE00F87BB6 /* libc++.tbd */,
B4A8162E2AA58AA500F87BB6 /* AudioUnit.framework */,
B4A816262AA588B600F87BB6 /* UIKit.framework */,
B4A816242AA5889100F87BB6 /* AudioToolbox.framework */,
B4A816222AA5885600F87BB6 /* CoreAudio.framework */,
B4A816202AA5885100F87BB6 /* OpenGLES.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
B4A816002AA583DB00F87BB6 /* doukutsu-rs */ = {
isa = PBXNativeTarget;
buildConfigurationList = B4A8161C2AA583DD00F87BB6 /* Build configuration list for PBXNativeTarget "doukutsu-rs" */;
buildPhases = (
B4A815FD2AA583DB00F87BB6 /* Sources */,
B4A815FE2AA583DB00F87BB6 /* Frameworks */,
B4A815FF2AA583DB00F87BB6 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "doukutsu-rs";
productName = "doukutsu-rs";
productReference = B4A816012AA583DB00F87BB6 /* doukutsu-rs.app */;
productType = "com.apple.product-type.application";
};
B4CD7F932C2422430043BFEC /* drsios.a */ = {
isa = PBXNativeTarget;
buildConfigurationList = B4CD7F9A2C2422440043BFEC /* Build configuration list for PBXNativeTarget "drsios.a" */;
buildPhases = (
B4CD7F902C2422430043BFEC /* Sources */,
);
buildRules = (
B4CD7F9F2C2423880043BFEC /* PBXBuildRule */,
);
dependencies = (
);
name = drsios.a;
productName = drsios.a;
productReference = B4CD7F942C2422440043BFEC /* libdrsios.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
B4A815F92AA583DB00F87BB6 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastUpgradeCheck = 1500;
TargetAttributes = {
B4A816002AA583DB00F87BB6 = {
CreatedOnToolsVersion = 15.0;
LastSwiftMigration = 1500;
};
B4CD7F932C2422430043BFEC = {
CreatedOnToolsVersion = 15.0;
};
};
};
buildConfigurationList = B4A815FC2AA583DB00F87BB6 /* Build configuration list for PBXProject "doukutsu-rs" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = B4A815F82AA583DB00F87BB6;
productRefGroup = B4A816022AA583DB00F87BB6 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
B4A816002AA583DB00F87BB6 /* doukutsu-rs */,
B4CD7F932C2422430043BFEC /* drsios.a */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
B4A815FF2AA583DB00F87BB6 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B449CF882AA678FB00509937 /* Launch Screen.storyboard in Resources */,
B4A816142AA583DD00F87BB6 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
B4A815FD2AA583DB00F87BB6 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B4A816192AA583DD00F87BB6 /* main.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B4CD7F902C2422430043BFEC /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B4CD7F9E2C2423700043BFEC /* Cargo.toml in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
B4A8161A2AA583DD00F87BB6 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
CARGO_XCODE_BUILD_PROFILE = debug;
CARGO_XCODE_FEATURES = "";
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
RUSTUP_TOOLCHAIN = "";
SDKROOT = iphoneos;
};
name = Debug;
};
B4A8161B2AA583DD00F87BB6 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
CARGO_XCODE_BUILD_PROFILE = release;
CARGO_XCODE_FEATURES = "";
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
RUSTUP_TOOLCHAIN = "";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
B4A8161D2AA583DD00F87BB6 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "doukutsu-rs/Info.plist";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.games";
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen.storyboard";
INFOPLIST_KEY_UIRequiresFullScreen = YES;
INFOPLIST_KEY_UIStatusBarHidden = YES;
INFOPLIST_KEY_UIStatusBarStyle = "";
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "io.github.doukutsu-rs";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TVOS_DEPLOYMENT_TARGET = 12.0;
};
name = Debug;
};
B4A8161E2AA583DD00F87BB6 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "doukutsu-rs/Info.plist";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.games";
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen.storyboard";
INFOPLIST_KEY_UIRequiresFullScreen = YES;
INFOPLIST_KEY_UIStatusBarHidden = YES;
INFOPLIST_KEY_UIStatusBarStyle = "";
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "io.github.doukutsu-rs";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TVOS_DEPLOYMENT_TARGET = 12.0;
};
name = Release;
};
B4CD7F9B2C2422440043BFEC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CARGO_XCODE_CARGO_DEP_FILE_NAME = libdrsios.d;
CARGO_XCODE_CARGO_FILE_NAME = libdrsios.a;
CODE_SIGN_STYLE = Automatic;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MACOSX_DEPLOYMENT_TARGET = 10.15;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = drsios;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
TVOS_DEPLOYMENT_TARGET = 12.0;
};
name = Debug;
};
B4CD7F9C2C2422440043BFEC /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CARGO_XCODE_CARGO_DEP_FILE_NAME = libdrsios.d;
CARGO_XCODE_CARGO_FILE_NAME = libdrsios.a;
CODE_SIGN_STYLE = Automatic;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MACOSX_DEPLOYMENT_TARGET = 10.15;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = drsios;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
TVOS_DEPLOYMENT_TARGET = 12.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
B4A815FC2AA583DB00F87BB6 /* Build configuration list for PBXProject "doukutsu-rs" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B4A8161A2AA583DD00F87BB6 /* Debug */,
B4A8161B2AA583DD00F87BB6 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B4A8161C2AA583DD00F87BB6 /* Build configuration list for PBXNativeTarget "doukutsu-rs" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B4A8161D2AA583DD00F87BB6 /* Debug */,
B4A8161E2AA583DD00F87BB6 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B4CD7F9A2C2422440043BFEC /* Build configuration list for PBXNativeTarget "drsios.a" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B4CD7F9B2C2422440043BFEC /* Debug */,
B4CD7F9C2C2422440043BFEC /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = B4A815F92AA583DB00F87BB6 /* Project object */;
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

View file

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B4A816002AA583DB00F87BB6"
BuildableName = "doukutsu-rs.app"
BlueprintName = "doukutsu-rs"
ReferencedContainer = "container:doukutsu-rs.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUFrameCaptureMode = "2"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B4A816002AA583DB00F87BB6"
BuildableName = "doukutsu-rs.app"
BlueprintName = "doukutsu-rs"
ReferencedContainer = "container:doukutsu-rs.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B4A816002AA583DB00F87BB6"
BuildableName = "doukutsu-rs.app"
BlueprintName = "doukutsu-rs"
ReferencedContainer = "container:doukutsu-rs.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,14 @@
{
"images" : [
{
"filename" : "icon.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

View file

@ -0,0 +1,17 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"origin" : "bottom-left",
"interpretation" : "non-premultiplied-colors"
},
"textures" : [
{
"idiom" : "universal",
"filename" : "Universal.mipmapset"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View file

@ -0,0 +1,12 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"levels" : [
{
"filename" : "ColorMap.png",
"mipmap-level" : "base"
}
]
}

View file

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,241 @@
{
"images" : [
{
"extent" : "full-screen",
"idiom" : "iphone",
"minimum-system-version" : "12.0",
"orientation" : "portrait",
"scale" : "3x",
"subtype" : "2688h"
},
{
"extent" : "full-screen",
"filename" : "Splash-iXSML.png",
"idiom" : "iphone",
"minimum-system-version" : "12.0",
"orientation" : "landscape",
"scale" : "3x",
"subtype" : "2688h"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"minimum-system-version" : "12.0",
"orientation" : "portrait",
"scale" : "2x",
"subtype" : "1792h"
},
{
"extent" : "full-screen",
"filename" : "Splash-iXRL.png",
"idiom" : "iphone",
"minimum-system-version" : "12.0",
"orientation" : "landscape",
"scale" : "2x",
"subtype" : "1792h"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"minimum-system-version" : "12.0",
"orientation" : "portrait",
"scale" : "2x",
"subtype" : "2388h"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"minimum-system-version" : "12.0",
"orientation" : "landscape",
"scale" : "2x",
"subtype" : "2388h"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"minimum-system-version" : "11.0",
"orientation" : "portrait",
"scale" : "3x",
"subtype" : "2436h"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"minimum-system-version" : "11.0",
"orientation" : "landscape",
"scale" : "3x",
"subtype" : "2436h"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"minimum-system-version" : "10.0",
"orientation" : "portrait",
"scale" : "2x",
"subtype" : "2224h"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"minimum-system-version" : "10.0",
"orientation" : "landscape",
"scale" : "2x",
"subtype" : "2224h"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"minimum-system-version" : "9.0",
"orientation" : "portrait",
"scale" : "2x",
"subtype" : "1366h"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"minimum-system-version" : "9.0",
"orientation" : "landscape",
"scale" : "2x",
"subtype" : "1366h"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"minimum-system-version" : "8.0",
"orientation" : "portrait",
"scale" : "3x",
"subtype" : "736h"
},
{
"extent" : "full-screen",
"filename" : "Splash-R55L.png",
"idiom" : "iphone",
"minimum-system-version" : "8.0",
"orientation" : "landscape",
"scale" : "3x",
"subtype" : "736h"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"minimum-system-version" : "8.0",
"orientation" : "portrait",
"scale" : "2x",
"subtype" : "667h"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"minimum-system-version" : "7.0",
"orientation" : "portrait",
"scale" : "2x"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"minimum-system-version" : "7.0",
"orientation" : "portrait",
"scale" : "2x",
"subtype" : "retina4"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"minimum-system-version" : "7.0",
"orientation" : "portrait",
"scale" : "1x"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"minimum-system-version" : "7.0",
"orientation" : "landscape",
"scale" : "1x"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"minimum-system-version" : "7.0",
"orientation" : "portrait",
"scale" : "2x"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"minimum-system-version" : "7.0",
"orientation" : "landscape",
"scale" : "2x"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"orientation" : "portrait",
"scale" : "1x"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"orientation" : "portrait",
"scale" : "2x"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"orientation" : "portrait",
"scale" : "2x",
"subtype" : "retina4"
},
{
"extent" : "to-status-bar",
"idiom" : "ipad",
"orientation" : "portrait",
"scale" : "1x"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"orientation" : "portrait",
"scale" : "1x"
},
{
"extent" : "to-status-bar",
"idiom" : "ipad",
"orientation" : "landscape",
"scale" : "1x"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"orientation" : "landscape",
"scale" : "1x"
},
{
"extent" : "to-status-bar",
"idiom" : "ipad",
"orientation" : "portrait",
"scale" : "2x"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"orientation" : "portrait",
"scale" : "2x"
},
{
"extent" : "to-status-bar",
"idiom" : "ipad",
"orientation" : "landscape",
"scale" : "2x"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"orientation" : "landscape",
"scale" : "2x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "background_splash.jpg",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "text_logo.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDuration</key>
<true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIFileSharingEnabled</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22154" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_12" orientation="landscape" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22129"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController modalPresentationStyle="fullScreen" id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="852" height="393"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" image="Splash" id="mMn-vp-aKn">
<rect key="frame" x="0.0" y="0.0" width="852" height="393"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="0.95164114239999997" green="0.61896461250000001" blue="0.29107719659999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" fixedFrame="YES" image="TextLogo" translatesAutoresizingMaskIntoConstraints="NO" id="b2C-7G-af7">
<rect key="frame" x="0.0" y="0.0" width="852" height="393"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
</imageView>
</subviews>
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
<color key="backgroundColor" red="0.80491966010000005" green="0.44847327469999998" blue="0.38965839149999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="Splash" width="750" height="600"/>
<image name="TextLogo" width="640" height="240"/>
</resources>
</document>

View file

@ -0,0 +1,6 @@
extern "C" void drs_main(void);
int main(int argc, char * argv[]) {
drs_main();
return 0;
}

6
drsios/src/lib.rs Normal file
View file

@ -0,0 +1,6 @@
#[no_mangle]
pub extern "C" fn drs_main() {
let options = doukutsu_rs::game::LaunchOptions { server_mode: false, editor: false };
doukutsu_rs::game::init(options).unwrap();
}

BIN
res/background_splash.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 KiB

BIN
res/text_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View file

@ -187,3 +187,9 @@ impl DiscordRPC {
let _ = self.client.close();
}
}
impl Drop for DiscordRPC {
fn drop(&mut self) {
self.dispose();
}
}

17
src/doukutsu_rs.h Normal file
View file

@ -0,0 +1,17 @@
#pragma once
namespace doukutsu_rs {
namespace game {
/**
* @brief (Opaque) game instance
*/
class Game;
struct LaunchOptions {
bool server_mode;
bool editor;
};
} // namespace game
} // namespace doukutsu_rs

View file

@ -119,9 +119,9 @@ pub fn init_backend(headless: bool, size_hint: (u16, u16)) -> GameResult<Box<dyn
return crate::framework::backend_horizon::HorizonBackend::new();
}
#[cfg(all(feature = "backend-glutin"))]
#[cfg(all(feature = "backend-winit"))]
{
return crate::framework::backend_glutin::GlutinBackend::new();
return crate::framework::backend_winit::WinitBackend::new();
}
#[cfg(feature = "backend-sdl")]

View file

@ -1,582 +0,0 @@
use std::any::Any;
use std::cell::{RefCell, UnsafeCell};
use std::ffi::c_void;
use std::io::Read;
use std::mem;
use std::rc::Rc;
use std::sync::Arc;
use std::vec::Vec;
use glutin::event::{ElementState, Event, TouchPhase, VirtualKeyCode, WindowEvent};
use glutin::event_loop::{ControlFlow, EventLoop};
use glutin::window::WindowBuilder;
use glutin::{Api, ContextBuilder, GlProfile, GlRequest, PossiblyCurrent, WindowedContext};
use imgui::{DrawCmdParams, DrawData, DrawIdx, DrawVert};
use winit::window::Icon;
use crate::common::Rect;
use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand};
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::filesystem;
use crate::framework::gl;
use crate::framework::keyboard::ScanCode;
use crate::framework::render_opengl::{GLContext, OpenGLRenderer};
use crate::game::Game;
use crate::game::GAME_SUSPENDED;
use crate::input::touch_controls::TouchPoint;
pub struct GlutinBackend;
impl GlutinBackend {
pub fn new() -> GameResult<Box<dyn Backend>> {
Ok(Box::new(GlutinBackend))
}
}
impl Backend for GlutinBackend {
fn create_event_loop(&self, _ctx: &Context) -> GameResult<Box<dyn BackendEventLoop>> {
#[cfg(target_os = "android")]
loop {
match ndk_glue::native_window().as_ref() {
Some(_) => {
log::info!("NativeWindow Found: {:?}", ndk_glue::native_window());
break;
}
None => (),
}
}
Ok(Box::new(GlutinEventLoop { refs: Rc::new(UnsafeCell::new(None)) }))
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct GlutinEventLoop {
refs: Rc<UnsafeCell<Option<WindowedContext<PossiblyCurrent>>>>,
}
impl GlutinEventLoop {
fn get_context(&self, ctx: &Context, event_loop: &EventLoop<()>) -> &mut WindowedContext<PossiblyCurrent> {
let mut refs = unsafe { &mut *self.refs.get() };
if refs.is_none() {
let mut window = WindowBuilder::new();
let windowed_context = ContextBuilder::new();
let windowed_context = windowed_context.with_gl(GlRequest::Specific(Api::OpenGl, (3, 0)));
#[cfg(target_os = "android")]
let windowed_context = windowed_context.with_gl(GlRequest::Specific(Api::OpenGlEs, (2, 0)));
let windowed_context = windowed_context
.with_gl_profile(GlProfile::Core)
.with_gl_debug_flag(false)
.with_pixel_format(24, 8)
.with_vsync(true);
#[cfg(target_os = "windows")]
{
use glutin::platform::windows::WindowBuilderExtWindows;
window = window.with_drag_and_drop(false);
}
window = window.with_title("doukutsu-rs");
#[cfg(not(any(target_os = "windows", target_os = "android", target_os = "horizon")))]
{
let mut file = filesystem::open(&ctx, "/builtin/icon.bmp").unwrap();
let mut buf: Vec<u8> = Vec::new();
file.read_to_end(&mut buf);
let mut img = match image::load_from_memory_with_format(buf.as_slice(), image::ImageFormat::Bmp) {
Ok(image) => image.into_rgba8(),
Err(e) => panic!("Cannot set window icon")
};
let (width, height) = img.dimensions();
let icon = Icon::from_rgba(img.into_raw(), width, height).unwrap();
window = window.with_window_icon(Some(icon));
}
let windowed_context = windowed_context.build_windowed(window, event_loop).unwrap();
let windowed_context = unsafe { windowed_context.make_current().unwrap() };
#[cfg(target_os = "android")]
if let Some(nwin) = ndk_glue::native_window().as_ref() {
unsafe {
windowed_context.surface_created(nwin.ptr().as_ptr() as *mut std::ffi::c_void);
}
}
refs.replace(windowed_context);
}
refs.as_mut().unwrap()
}
}
#[cfg(target_os = "android")]
fn request_android_redraw() {
match ndk_glue::native_window().as_ref() {
Some(native_window) => {
let a_native_window: *mut ndk_sys::ANativeWindow = native_window.ptr().as_ptr();
let a_native_activity: *mut ndk_sys::ANativeActivity = ndk_glue::native_activity().ptr().as_ptr();
unsafe {
match (*(*a_native_activity).callbacks).onNativeWindowRedrawNeeded {
Some(callback) => callback(a_native_activity, a_native_window),
None => (),
};
};
}
None => (),
}
}
#[cfg(target_os = "android")]
fn get_insets() -> GameResult<(f32, f32, f32, f32)> {
unsafe {
use jni::objects::JObject;
use jni::JavaVM;
let vm_ptr = ndk_glue::native_activity().vm();
let vm = JavaVM::from_raw(vm_ptr)?;
let vm_env = vm.attach_current_thread()?;
let class = vm_env.new_global_ref(JObject::from_raw(ndk_glue::native_activity().activity()))?;
let field = vm_env.get_field(class.as_obj(), "displayInsets", "[I")?.to_jni().l as jni::sys::jintArray;
let mut elements = [0; 4];
vm_env.get_int_array_region(field, 0, &mut elements)?;
vm_env.delete_local_ref(JObject::from_raw(field));
//Game always runs with horizontal orientation so top and bottom cutouts not needed and only wastes piece of the screen
elements[1] = 0;
elements[3] = 0;
Ok((elements[0] as f32, elements[1] as f32, elements[2] as f32, elements[3] as f32))
}
}
fn get_scaled_size(width: u32, height: u32) -> (f32, f32) {
let scaled_height = ((height / 480).max(1) * 480) as f32;
let scaled_width = (width as f32 * (scaled_height as f32 / height as f32)).floor();
(scaled_width, scaled_height)
}
impl BackendEventLoop for GlutinEventLoop {
fn run(&mut self, game: &mut Game, ctx: &mut Context) {
let event_loop = EventLoop::new();
let state_ref = unsafe { &mut *game.state.get() };
let window: &'static mut WindowedContext<PossiblyCurrent> =
unsafe { std::mem::transmute(self.get_context(&ctx, &event_loop)) };
{
let size = window.window().inner_size();
ctx.real_screen_size = (size.width, size.height);
ctx.screen_size = get_scaled_size(size.width.max(1), size.height.max(1));
state_ref.handle_resize(ctx).unwrap();
}
// it won't ever return
let (game, ctx): (&'static mut Game, &'static mut Context) =
unsafe { (std::mem::transmute(game), std::mem::transmute(ctx)) };
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent { event: WindowEvent::CloseRequested, window_id }
if window_id == window.window().id() =>
{
state_ref.shutdown();
}
Event::Resumed => {
{
let mut mutex = GAME_SUSPENDED.lock().unwrap();
*mutex = false;
}
#[cfg(target_os = "android")]
if let Some(nwin) = ndk_glue::native_window().as_ref() {
state_ref.graphics_reset();
unsafe {
window.surface_created(nwin.ptr().as_ptr() as *mut std::ffi::c_void);
request_android_redraw();
}
}
state_ref.sound_manager.resume();
}
Event::Suspended => {
{
let mut mutex = GAME_SUSPENDED.lock().unwrap();
*mutex = true;
}
#[cfg(target_os = "android")]
unsafe {
window.surface_destroyed();
}
state_ref.sound_manager.pause();
}
Event::WindowEvent { event: WindowEvent::Resized(size), window_id }
if window_id == window.window().id() =>
{
if let Some(renderer) = &ctx.renderer {
if let Ok(imgui) = renderer.imgui() {
imgui.io_mut().display_size = [size.width as f32, size.height as f32];
}
ctx.real_screen_size = (size.width, size.height);
ctx.screen_size = get_scaled_size(size.width.max(1), size.height.max(1));
state_ref.handle_resize(ctx).unwrap();
}
}
Event::WindowEvent { event: WindowEvent::Touch(touch), window_id }
if window_id == window.window().id() =>
{
let mut controls = &mut state_ref.touch_controls;
let scale = state_ref.scale as f64;
let loc_x = (touch.location.x * ctx.screen_size.0 as f64 / ctx.real_screen_size.0 as f64) / scale;
let loc_y = (touch.location.y * ctx.screen_size.1 as f64 / ctx.real_screen_size.1 as f64) / scale;
match touch.phase {
TouchPhase::Started | TouchPhase::Moved => {
if let Some(point) = controls.points.iter_mut().find(|p| p.id == touch.id) {
point.last_position = point.position;
point.position = (loc_x, loc_y);
} else {
controls.touch_id_counter = controls.touch_id_counter.wrapping_add(1);
let point = TouchPoint {
id: touch.id,
touch_id: controls.touch_id_counter,
position: (loc_x, loc_y),
last_position: (0.0, 0.0),
};
controls.points.push(point);
if touch.phase == TouchPhase::Started {
controls.clicks.push(point);
}
}
}
TouchPhase::Ended | TouchPhase::Cancelled => {
controls.points.retain(|p| p.id != touch.id);
controls.clicks.retain(|p| p.id != touch.id);
}
}
}
Event::WindowEvent { event: WindowEvent::KeyboardInput { input, .. }, window_id }
if window_id == window.window().id() =>
{
if let Some(keycode) = input.virtual_keycode {
if let Some(drs_scan) = conv_keycode(keycode) {
let key_state = match input.state {
ElementState::Pressed => true,
ElementState::Released => false,
};
ctx.keyboard_context.set_key(drs_scan, key_state);
}
}
}
Event::RedrawRequested(id) if id == window.window().id() => {
{
let mutex = GAME_SUSPENDED.lock().unwrap();
if *mutex {
return;
}
}
#[cfg(not(target_os = "android"))]
{
if let Err(err) = game.draw(ctx) {
log::error!("Failed to draw frame: {}", err);
}
window.window().request_redraw();
}
#[cfg(target_os = "android")]
request_android_redraw();
}
Event::MainEventsCleared => {
if state_ref.shutdown {
log::info!("Shutting down...");
*control_flow = ControlFlow::Exit;
return;
}
{
let mutex = GAME_SUSPENDED.lock().unwrap();
if *mutex {
return;
}
}
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
{
if state_ref.settings.window_mode.get_glutin_fullscreen_type() != window.window().fullscreen() {
let fullscreen_type = state_ref.settings.window_mode.get_glutin_fullscreen_type();
let cursor_visible = state_ref.settings.window_mode.should_display_mouse_cursor();
window.window().set_fullscreen(fullscreen_type);
window.window().set_cursor_visible(cursor_visible);
}
}
game.update(ctx).unwrap();
#[cfg(target_os = "android")]
{
match get_insets() {
Ok(insets) => {
ctx.screen_insets = insets;
}
Err(e) => {
log::error!("Failed to update insets: {}", e);
}
}
if let Err(err) = game.draw(ctx) {
log::error!("Failed to draw frame: {}", err);
}
}
if state_ref.next_scene.is_some() {
mem::swap(&mut game.scene, &mut state_ref.next_scene);
state_ref.next_scene = None;
game.scene.as_mut().unwrap().init(state_ref, ctx).unwrap();
game.loops = 0;
state_ref.frame_time = 0.0;
}
}
_ => (),
}
});
}
fn new_renderer(&self, ctx: *mut Context) -> GameResult<Box<dyn BackendRenderer>> {
let mut imgui = imgui::Context::create();
imgui.io_mut().display_size = [640.0, 480.0];
let refs = self.refs.clone();
let user_data = Rc::into_raw(refs) as *mut c_void;
unsafe fn get_proc_address(user_data: &mut *mut c_void, name: &str) -> *const c_void {
let refs = Rc::from_raw(*user_data as *mut UnsafeCell<Option<WindowedContext<PossiblyCurrent>>>);
let result = {
let refs = &mut *refs.get();
if let Some(refs) = refs {
refs.get_proc_address(name)
} else {
std::ptr::null()
}
};
*user_data = Rc::into_raw(refs) as *mut c_void;
result
}
unsafe fn swap_buffers(user_data: &mut *mut c_void) {
let refs = Rc::from_raw(*user_data as *mut UnsafeCell<Option<WindowedContext<PossiblyCurrent>>>);
{
let refs = &mut *refs.get();
if let Some(refs) = refs {
refs.swap_buffers();
}
}
*user_data = Rc::into_raw(refs) as *mut c_void;
}
let gl_context = GLContext { gles2_mode: true, is_sdl: false, get_proc_address, swap_buffers, user_data, ctx };
Ok(Box::new(OpenGLRenderer::new(gl_context, UnsafeCell::new(imgui))))
}
fn as_any(&self) -> &dyn Any {
self
}
}
fn conv_keycode(code: VirtualKeyCode) -> Option<ScanCode> {
match code {
VirtualKeyCode::Key1 => Some(ScanCode::Key1),
VirtualKeyCode::Key2 => Some(ScanCode::Key2),
VirtualKeyCode::Key3 => Some(ScanCode::Key3),
VirtualKeyCode::Key4 => Some(ScanCode::Key4),
VirtualKeyCode::Key5 => Some(ScanCode::Key5),
VirtualKeyCode::Key6 => Some(ScanCode::Key6),
VirtualKeyCode::Key7 => Some(ScanCode::Key7),
VirtualKeyCode::Key8 => Some(ScanCode::Key8),
VirtualKeyCode::Key9 => Some(ScanCode::Key9),
VirtualKeyCode::Key0 => Some(ScanCode::Key0),
VirtualKeyCode::A => Some(ScanCode::A),
VirtualKeyCode::B => Some(ScanCode::B),
VirtualKeyCode::C => Some(ScanCode::C),
VirtualKeyCode::D => Some(ScanCode::D),
VirtualKeyCode::E => Some(ScanCode::E),
VirtualKeyCode::F => Some(ScanCode::F),
VirtualKeyCode::G => Some(ScanCode::G),
VirtualKeyCode::H => Some(ScanCode::H),
VirtualKeyCode::I => Some(ScanCode::I),
VirtualKeyCode::J => Some(ScanCode::J),
VirtualKeyCode::K => Some(ScanCode::K),
VirtualKeyCode::L => Some(ScanCode::L),
VirtualKeyCode::M => Some(ScanCode::M),
VirtualKeyCode::N => Some(ScanCode::N),
VirtualKeyCode::O => Some(ScanCode::O),
VirtualKeyCode::P => Some(ScanCode::P),
VirtualKeyCode::Q => Some(ScanCode::Q),
VirtualKeyCode::R => Some(ScanCode::R),
VirtualKeyCode::S => Some(ScanCode::S),
VirtualKeyCode::T => Some(ScanCode::T),
VirtualKeyCode::U => Some(ScanCode::U),
VirtualKeyCode::V => Some(ScanCode::V),
VirtualKeyCode::W => Some(ScanCode::W),
VirtualKeyCode::X => Some(ScanCode::X),
VirtualKeyCode::Y => Some(ScanCode::Y),
VirtualKeyCode::Z => Some(ScanCode::Z),
VirtualKeyCode::Escape => Some(ScanCode::Escape),
VirtualKeyCode::F1 => Some(ScanCode::F1),
VirtualKeyCode::F2 => Some(ScanCode::F2),
VirtualKeyCode::F3 => Some(ScanCode::F3),
VirtualKeyCode::F4 => Some(ScanCode::F4),
VirtualKeyCode::F5 => Some(ScanCode::F5),
VirtualKeyCode::F6 => Some(ScanCode::F6),
VirtualKeyCode::F7 => Some(ScanCode::F7),
VirtualKeyCode::F8 => Some(ScanCode::F8),
VirtualKeyCode::F9 => Some(ScanCode::F9),
VirtualKeyCode::F10 => Some(ScanCode::F10),
VirtualKeyCode::F11 => Some(ScanCode::F11),
VirtualKeyCode::F12 => Some(ScanCode::F12),
VirtualKeyCode::F13 => Some(ScanCode::F13),
VirtualKeyCode::F14 => Some(ScanCode::F14),
VirtualKeyCode::F15 => Some(ScanCode::F15),
VirtualKeyCode::F16 => Some(ScanCode::F16),
VirtualKeyCode::F17 => Some(ScanCode::F17),
VirtualKeyCode::F18 => Some(ScanCode::F18),
VirtualKeyCode::F19 => Some(ScanCode::F19),
VirtualKeyCode::F20 => Some(ScanCode::F20),
VirtualKeyCode::F21 => Some(ScanCode::F21),
VirtualKeyCode::F22 => Some(ScanCode::F22),
VirtualKeyCode::F23 => Some(ScanCode::F23),
VirtualKeyCode::F24 => Some(ScanCode::F24),
VirtualKeyCode::Snapshot => Some(ScanCode::Snapshot),
VirtualKeyCode::Scroll => Some(ScanCode::Scroll),
VirtualKeyCode::Pause => Some(ScanCode::Pause),
VirtualKeyCode::Insert => Some(ScanCode::Insert),
VirtualKeyCode::Home => Some(ScanCode::Home),
VirtualKeyCode::Delete => Some(ScanCode::Delete),
VirtualKeyCode::End => Some(ScanCode::End),
VirtualKeyCode::PageDown => Some(ScanCode::PageDown),
VirtualKeyCode::PageUp => Some(ScanCode::PageUp),
VirtualKeyCode::Left => Some(ScanCode::Left),
VirtualKeyCode::Up => Some(ScanCode::Up),
VirtualKeyCode::Right => Some(ScanCode::Right),
VirtualKeyCode::Down => Some(ScanCode::Down),
VirtualKeyCode::Back => Some(ScanCode::Back),
VirtualKeyCode::Return => Some(ScanCode::Return),
VirtualKeyCode::Space => Some(ScanCode::Space),
VirtualKeyCode::Compose => Some(ScanCode::Compose),
VirtualKeyCode::Caret => Some(ScanCode::Caret),
VirtualKeyCode::Numlock => Some(ScanCode::Numlock),
VirtualKeyCode::Numpad0 => Some(ScanCode::Numpad0),
VirtualKeyCode::Numpad1 => Some(ScanCode::Numpad1),
VirtualKeyCode::Numpad2 => Some(ScanCode::Numpad2),
VirtualKeyCode::Numpad3 => Some(ScanCode::Numpad3),
VirtualKeyCode::Numpad4 => Some(ScanCode::Numpad4),
VirtualKeyCode::Numpad5 => Some(ScanCode::Numpad5),
VirtualKeyCode::Numpad6 => Some(ScanCode::Numpad6),
VirtualKeyCode::Numpad7 => Some(ScanCode::Numpad7),
VirtualKeyCode::Numpad8 => Some(ScanCode::Numpad8),
VirtualKeyCode::Numpad9 => Some(ScanCode::Numpad9),
VirtualKeyCode::NumpadAdd => Some(ScanCode::NumpadAdd),
VirtualKeyCode::NumpadDivide => Some(ScanCode::NumpadDivide),
VirtualKeyCode::NumpadDecimal => Some(ScanCode::NumpadDecimal),
VirtualKeyCode::NumpadComma => Some(ScanCode::NumpadComma),
VirtualKeyCode::NumpadEnter => Some(ScanCode::NumpadEnter),
VirtualKeyCode::NumpadEquals => Some(ScanCode::NumpadEquals),
VirtualKeyCode::NumpadMultiply => Some(ScanCode::NumpadMultiply),
VirtualKeyCode::NumpadSubtract => Some(ScanCode::NumpadSubtract),
VirtualKeyCode::AbntC1 => Some(ScanCode::AbntC1),
VirtualKeyCode::AbntC2 => Some(ScanCode::AbntC2),
VirtualKeyCode::Apostrophe => Some(ScanCode::Apostrophe),
VirtualKeyCode::Apps => Some(ScanCode::Apps),
VirtualKeyCode::Asterisk => Some(ScanCode::Asterisk),
VirtualKeyCode::At => Some(ScanCode::At),
VirtualKeyCode::Ax => Some(ScanCode::Ax),
VirtualKeyCode::Backslash => Some(ScanCode::Backslash),
VirtualKeyCode::Calculator => Some(ScanCode::Calculator),
VirtualKeyCode::Capital => Some(ScanCode::Capital),
VirtualKeyCode::Colon => Some(ScanCode::Colon),
VirtualKeyCode::Comma => Some(ScanCode::Comma),
VirtualKeyCode::Convert => Some(ScanCode::Convert),
VirtualKeyCode::Equals => Some(ScanCode::Equals),
VirtualKeyCode::Grave => Some(ScanCode::Grave),
VirtualKeyCode::Kana => Some(ScanCode::Kana),
VirtualKeyCode::Kanji => Some(ScanCode::Kanji),
VirtualKeyCode::LAlt => Some(ScanCode::LAlt),
VirtualKeyCode::LBracket => Some(ScanCode::LBracket),
VirtualKeyCode::LControl => Some(ScanCode::LControl),
VirtualKeyCode::LShift => Some(ScanCode::LShift),
VirtualKeyCode::LWin => Some(ScanCode::LWin),
VirtualKeyCode::Mail => Some(ScanCode::Mail),
VirtualKeyCode::MediaSelect => Some(ScanCode::MediaSelect),
VirtualKeyCode::MediaStop => Some(ScanCode::MediaStop),
VirtualKeyCode::Minus => Some(ScanCode::Minus),
VirtualKeyCode::Mute => Some(ScanCode::Mute),
VirtualKeyCode::MyComputer => Some(ScanCode::MyComputer),
VirtualKeyCode::NavigateForward => Some(ScanCode::NavigateForward),
VirtualKeyCode::NavigateBackward => Some(ScanCode::NavigateBackward),
VirtualKeyCode::NextTrack => Some(ScanCode::NextTrack),
VirtualKeyCode::NoConvert => Some(ScanCode::NoConvert),
VirtualKeyCode::OEM102 => Some(ScanCode::OEM102),
VirtualKeyCode::Period => Some(ScanCode::Period),
VirtualKeyCode::PlayPause => Some(ScanCode::PlayPause),
VirtualKeyCode::Plus => Some(ScanCode::Plus),
VirtualKeyCode::Power => Some(ScanCode::Power),
VirtualKeyCode::PrevTrack => Some(ScanCode::PrevTrack),
VirtualKeyCode::RAlt => Some(ScanCode::RAlt),
VirtualKeyCode::RBracket => Some(ScanCode::RBracket),
VirtualKeyCode::RControl => Some(ScanCode::RControl),
VirtualKeyCode::RShift => Some(ScanCode::RShift),
VirtualKeyCode::RWin => Some(ScanCode::RWin),
VirtualKeyCode::Semicolon => Some(ScanCode::Semicolon),
VirtualKeyCode::Slash => Some(ScanCode::Slash),
VirtualKeyCode::Sleep => Some(ScanCode::Sleep),
VirtualKeyCode::Stop => Some(ScanCode::Stop),
VirtualKeyCode::Sysrq => Some(ScanCode::Sysrq),
VirtualKeyCode::Tab => Some(ScanCode::Tab),
VirtualKeyCode::Underline => Some(ScanCode::Underline),
VirtualKeyCode::Unlabeled => Some(ScanCode::Unlabeled),
VirtualKeyCode::VolumeDown => Some(ScanCode::VolumeDown),
VirtualKeyCode::VolumeUp => Some(ScanCode::VolumeUp),
VirtualKeyCode::Wake => Some(ScanCode::Wake),
VirtualKeyCode::WebBack => Some(ScanCode::WebBack),
VirtualKeyCode::WebFavorites => Some(ScanCode::WebFavorites),
VirtualKeyCode::WebForward => Some(ScanCode::WebForward),
VirtualKeyCode::WebHome => Some(ScanCode::WebHome),
VirtualKeyCode::WebRefresh => Some(ScanCode::WebRefresh),
VirtualKeyCode::WebSearch => Some(ScanCode::WebSearch),
VirtualKeyCode::WebStop => Some(ScanCode::WebStop),
VirtualKeyCode::Yen => Some(ScanCode::Yen),
VirtualKeyCode::Copy => Some(ScanCode::Copy),
VirtualKeyCode::Paste => Some(ScanCode::Paste),
VirtualKeyCode::Cut => Some(ScanCode::Cut),
}
}

View file

@ -397,7 +397,7 @@ impl BackendEventLoop for HorizonEventLoop {
game.update(ctx).unwrap();
if state_ref.shutdown {
if ctx.shutdown_requested {
log::info!("Shutting down...");
break;
}

View file

@ -35,25 +35,23 @@ pub struct NullEventLoop;
impl BackendEventLoop for NullEventLoop {
fn run(&mut self, game: &mut Game, ctx: &mut Context) {
let state_ref = unsafe { &mut *game.state.get() };
ctx.screen_size = (640.0, 480.0);
state_ref.handle_resize(ctx).unwrap();
game.state.get_mut().handle_resize(ctx).unwrap();
loop {
game.update(ctx).unwrap();
if state_ref.shutdown {
if ctx.shutdown_requested {
log::info!("Shutting down...");
break;
}
if state_ref.next_scene.is_some() {
mem::swap(&mut game.scene, &mut state_ref.next_scene);
state_ref.next_scene = None;
game.scene.as_mut().unwrap().init(state_ref, ctx).unwrap();
if game.state.get_mut().next_scene.is_some() {
mem::swap(game.scene.get_mut(), &mut game.state.get_mut().next_scene);
game.state.get_mut().next_scene = None;
game.scene.borrow_mut().as_mut().unwrap().init(game.state.get_mut(), ctx).unwrap();
game.loops = 0;
state_ref.frame_time = 0.0;
game.state.get_mut().frame_time = 0.0;
}
std::thread::sleep(std::time::Duration::from_millis(10));
@ -96,6 +94,13 @@ impl BackendTexture for NullTexture {
pub struct NullRenderer(RefCell<imgui::Context>);
impl NullRenderer {
pub fn new(mut imgui: imgui::Context) -> Self {
let _ = imgui.fonts().build_alpha8_texture();
NullRenderer(RefCell::new(imgui))
}
}
impl BackendRenderer for NullRenderer {
fn renderer_name(&self) -> String {
"Null".to_owned()

View file

@ -1,6 +1,6 @@
use core::mem;
use std::any::Any;
use std::cell::{RefCell, UnsafeCell};
use std::cell::RefCell;
use std::ffi::c_void;
use std::io::Read;
use std::ops::Deref;
@ -172,7 +172,7 @@ impl SDL2EventLoop {
gl_attr.set_context_profile(GLProfile::Compatibility);
gl_attr.set_context_version(2, 1);
let mut win_builder = video.window("Cave Story (doukutsu-rs)", size_hint.0 as _, size_hint.1 as _);
let mut win_builder = video.window(&ctx.window_title, size_hint.0 as _, size_hint.1 as _);
win_builder.position_centered();
win_builder.resizable();
@ -185,14 +185,13 @@ impl SDL2EventLoop {
let mut file = filesystem::open(&ctx, "/builtin/icon.bmp").unwrap();
let mut buf: Vec<u8> = Vec::new();
file.read_to_end(&mut buf)?;
let mut rwops = RWops::from_bytes(buf.as_slice()).unwrap();
let icon = Surface::load_bmp_rw(&mut rwops).unwrap();
window.set_icon(icon);
}
let opengl_available = if let Ok(v) = std::env::var("CAVESTORY_NO_OPENGL") { v != "1" } else { true };
let event_loop = SDL2EventLoop {
@ -210,18 +209,60 @@ impl SDL2EventLoop {
Ok(Box::new(event_loop))
}
fn set_seamless_titlebar(&mut self, enabled: bool) {
#[cfg(target_os = "macos")]
#[allow(non_upper_case_globals)]
unsafe {
use objc2::ffi::*;
use objc2::*;
const NSWindowTitleVisible: i32 = 0;
const NSWindowTitleHidden: i32 = 1;
const NSWindowStyleMaskFullSizeContentView: u32 = 1 << 15;
// safety: fields are initialized by SDL_GetWindowWMInfo
let mut winfo: sdl2_sys::SDL_SysWMinfo = mem::MaybeUninit::zeroed().assume_init();
winfo.version.major = sdl2_sys::SDL_MAJOR_VERSION as _;
winfo.version.minor = sdl2_sys::SDL_MINOR_VERSION as _;
winfo.version.patch = sdl2_sys::SDL_PATCHLEVEL as _;
let mut whandle = self.refs.deref().borrow().window.window().raw();
if sdl2_sys::SDL_GetWindowWMInfo(whandle, &mut winfo as *mut _) != sdl2_sys::SDL_bool::SDL_FALSE {
let window = winfo.info.x11.display as *mut objc2::runtime::AnyObject;
if enabled {
let _: () = msg_send![window, setTitlebarAppearsTransparent:YES];
let _: () = msg_send![window, setTitleVisibility:NSWindowTitleHidden];
} else {
let _: () = msg_send![window, setTitlebarAppearsTransparent:NO];
let _: () = msg_send![window, setTitleVisibility:NSWindowTitleVisible];
}
let mut style_mask: u32 = msg_send![window, styleMask];
if enabled {
style_mask |= NSWindowStyleMaskFullSizeContentView;
} else {
style_mask &= !NSWindowStyleMaskFullSizeContentView;
}
let _: () = msg_send![window, setStyleMask: style_mask];
}
}
let _ = enabled;
}
}
impl BackendEventLoop for SDL2EventLoop {
fn run(&mut self, game: &mut Game, ctx: &mut Context) {
let state = unsafe { &mut *game.state.get() };
let imgui = unsafe {
(&*(ctx.renderer.as_ref().unwrap() as *const Box<dyn BackendRenderer>)).imgui().unwrap()
};
let imgui = unsafe { (&*(ctx.renderer.as_ref().unwrap() as *const Box<dyn BackendRenderer>)).imgui().unwrap() };
let mut imgui_sdl2 = ImguiSdl2::new(imgui, self.refs.deref().borrow().window.window());
{
let state = game.state.get_mut();
let (width, height) = self.refs.deref().borrow().window.window().size();
ctx.screen_size = (width.max(1) as f32, height.max(1) as f32);
@ -230,39 +271,16 @@ impl BackendEventLoop for SDL2EventLoop {
}
loop {
#[cfg(target_os = "macos")]
unsafe {
use objc::*;
// no UB: fields are initialized by SDL_GetWindowWMInfo
let mut winfo: sdl2_sys::SDL_SysWMinfo = mem::MaybeUninit::uninit().assume_init();
winfo.version.major = sdl2_sys::SDL_MAJOR_VERSION as _;
winfo.version.minor = sdl2_sys::SDL_MINOR_VERSION as _;
winfo.version.patch = sdl2_sys::SDL_PATCHLEVEL as _;
let mut whandle = self.refs.deref().borrow().window.window().raw();
if sdl2_sys::SDL_GetWindowWMInfo(whandle, &mut winfo as *mut _) != sdl2_sys::SDL_bool::SDL_FALSE {
let window = winfo.info.x11.display as *mut objc::runtime::Object;
let _: () = msg_send![window, setTitlebarAppearsTransparent:1];
let _: () = msg_send![window, setTitleVisibility:1]; // NSWindowTitleHidden
let mut style_mask: u32 = msg_send![window, styleMask];
style_mask |= 1 << 15; // NSWindowStyleMaskFullSizeContentView
let _: () = msg_send![window, setStyleMask: style_mask];
}
}
for event in self.event_pump.poll_iter() {
imgui_sdl2.handle_event(imgui, &event);
match event {
Event::Quit { .. } => {
state.shutdown();
ctx.shutdown_requested = true;
}
Event::Window { win_event, .. } => match win_event {
WindowEvent::FocusGained | WindowEvent::Shown => {
let state = game.state.get_mut();
if state.settings.pause_on_focus_loss {
{
let mut mutex = GAME_SUSPENDED.lock().unwrap();
@ -274,6 +292,7 @@ impl BackendEventLoop for SDL2EventLoop {
}
}
WindowEvent::FocusLost | WindowEvent::Hidden => {
let state = game.state.get_mut();
if state.settings.pause_on_focus_loss {
let mut mutex = GAME_SUSPENDED.lock().unwrap();
*mutex = true;
@ -282,6 +301,7 @@ impl BackendEventLoop for SDL2EventLoop {
}
}
WindowEvent::SizeChanged(width, height) => {
let state = game.state.get_mut();
ctx.screen_size = (width.max(1) as f32, height.max(1) as f32);
if let Some(renderer) = &ctx.renderer {
@ -296,13 +316,14 @@ impl BackendEventLoop for SDL2EventLoop {
Event::KeyDown { scancode: Some(scancode), repeat, keymod, .. } => {
if let Some(drs_scan) = conv_scancode(scancode) {
if !repeat {
if let Some(scene) = &mut game.scene {
scene.process_debug_keys(state, ctx, drs_scan);
if let Some(scene) = game.scene.get_mut() {
scene.process_debug_keys(&mut game.state.borrow_mut(), ctx, drs_scan);
}
if keymod.intersects(keyboard::Mod::RALTMOD | keyboard::Mod::LALTMOD)
&& drs_scan == ScanCode::Return
{
let state = game.state.get_mut();
let new_mode = match state.settings.window_mode {
WindowMode::Windowed => WindowMode::Fullscreen,
WindowMode::Fullscreen => WindowMode::Windowed,
@ -336,6 +357,7 @@ impl BackendEventLoop for SDL2EventLoop {
let game_controller = &self.refs.borrow().game_controller;
if game_controller.is_game_controller(which) {
let state = game.state.get_mut();
let controller = game_controller.open(which).unwrap();
let id = controller.instance_id();
@ -377,7 +399,7 @@ impl BackendEventLoop for SDL2EventLoop {
}
}
if state.shutdown {
if ctx.shutdown_requested {
log::info!("Shutting down...");
break;
}
@ -391,6 +413,7 @@ impl BackendEventLoop for SDL2EventLoop {
}
{
let state = game.state.get_mut();
if state.settings.window_mode.get_sdl2_fullscreen_type() != self.refs.borrow().fullscreen_type {
let mut refs = self.refs.borrow_mut();
let window = refs.window.window_mut();
@ -399,11 +422,7 @@ impl BackendEventLoop for SDL2EventLoop {
let show_cursor = state.settings.window_mode.should_display_mouse_cursor();
window.set_fullscreen(fullscreen_type);
window
.subsystem()
.sdl()
.mouse()
.show_cursor(show_cursor);
window.subsystem().sdl().mouse().show_cursor(show_cursor);
refs.fullscreen_type = fullscreen_type;
}
@ -411,9 +430,10 @@ impl BackendEventLoop for SDL2EventLoop {
game.update(ctx).unwrap();
if let Some(_) = &state.next_scene {
game.scene = mem::take(&mut state.next_scene);
game.scene.as_mut().unwrap().init(state, ctx).unwrap();
if game.state.get_mut().next_scene.is_some() {
let mut state = game.state.borrow_mut();
*game.scene.get_mut() = mem::take(&mut state.next_scene);
game.scene.get_mut().as_mut().unwrap().init(&mut state, ctx).unwrap();
game.loops = 0;
state.frame_time = 0.0;
}
@ -444,9 +464,10 @@ impl BackendEventLoop for SDL2EventLoop {
}
}
let mut imgui = init_imgui()?;
#[cfg(feature = "render-opengl")]
if *self.opengl_available.borrow() {
let mut imgui = init_imgui()?;
let mut key_map = &mut imgui.io_mut().key_map;
key_map[ImGuiKey_Backspace as usize] = Scancode::Backspace as u32;
key_map[ImGuiKey_Delete as usize] = Scancode::Delete as u32;
@ -484,14 +505,15 @@ impl BackendEventLoop for SDL2EventLoop {
let gl_context =
GLContext { gles2_mode: false, is_sdl: true, get_proc_address, swap_buffers, user_data, ctx };
return Ok(Box::new(OpenGLRenderer::new(gl_context, UnsafeCell::new(imgui))));
} else {
return Ok(Box::new(OpenGLRenderer::new(gl_context, imgui)));
}
{
let mut refs = self.refs.borrow_mut();
let window = std::mem::take(&mut refs.window);
refs.window = window.make_canvas()?;
return Ok(Box::new(SDL2Renderer::new(self.refs.clone(), imgui)?));
}
SDL2Renderer::new(self.refs.clone())
}
fn as_any(&self) -> &dyn Any {
@ -548,9 +570,7 @@ struct SDL2Renderer {
impl SDL2Renderer {
#[allow(clippy::new_ret_no_self)]
pub fn new(refs: Rc<RefCell<SDL2Context>>) -> GameResult<Box<dyn BackendRenderer>> {
let mut imgui = init_imgui()?;
pub fn new(refs: Rc<RefCell<SDL2Context>>, mut imgui: imgui::Context) -> GameResult<SDL2Renderer> {
imgui.set_renderer_name("SDL2Renderer".to_owned());
let imgui_font_tex = {
let refs = refs.clone();
@ -591,11 +611,7 @@ impl SDL2Renderer {
};
imgui.fonts().tex_id = TextureId::new(imgui_font_tex.texture.as_ref().unwrap().raw() as usize);
Ok(Box::new(SDL2Renderer {
refs,
imgui: Rc::new(RefCell::new(imgui)),
imgui_font_tex,
}))
Ok((SDL2Renderer { refs, imgui: Rc::new(RefCell::new(imgui)), imgui_font_tex }))
}
}
@ -637,7 +653,7 @@ fn max3(x: f32, y: f32, z: f32) -> f32 {
impl BackendRenderer for SDL2Renderer {
fn renderer_name(&self) -> String {
"SDL2_Renderer (fallback)".to_owned()
"*COMPATIBILITY* SDL2_Renderer".to_owned()
}
fn clear(&mut self, color: Color) {

View file

@ -0,0 +1,492 @@
use std::any::Any;
use std::cell::{RefCell, UnsafeCell};
use std::ffi::c_void;
use std::io::Read;
use std::mem;
use std::ptr::null_mut;
use std::rc::Rc;
use std::sync::Arc;
use std::vec::Vec;
use imgui::{DrawCmdParams, DrawData, DrawIdx, DrawVert};
use winit::application::ApplicationHandler;
use winit::dpi::LogicalSize;
use winit::event::{ElementState, TouchPhase, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::keyboard::{KeyCode, PhysicalKey};
use winit::window::{self, Icon, Window, WindowAttributes, WindowId};
use crate::common::Rect;
use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand};
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::filesystem;
use crate::framework::gl;
use crate::framework::keyboard::ScanCode;
use crate::framework::render_opengl::{GLContext, OpenGLRenderer};
use crate::game::GAME_SUSPENDED;
use crate::game::{stage, Game};
use crate::input::touch_controls::TouchPoint;
use super::error::GameError;
use super::ui::init_imgui;
pub struct WinitBackend;
impl WinitBackend {
pub fn new() -> GameResult<Box<dyn Backend>> {
Ok(Box::new(WinitBackend))
}
}
impl Backend for WinitBackend {
fn create_event_loop(&self, ctx: &Context) -> GameResult<Box<dyn BackendEventLoop>> {
let event_loop =
EventLoop::new().map_err(|e| GameError::WindowError(format!("Failed to create event loop: {}", e)))?;
Ok(Box::new(WinitEventLoop {
event_loop: Some(event_loop),
window: None,
game_ref: null_mut(),
ctx_ref: null_mut(),
}))
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct WinitEventLoop {
event_loop: Option<EventLoop<()>>,
window: Option<Window>,
game_ref: *mut Game,
ctx_ref: *mut Context,
}
impl WinitEventLoop {
fn get_insets(&self) -> GameResult<(f32, f32, f32, f32)> {
#[cfg(target_os = "android")]
unsafe {
use jni::objects::JObject;
use jni::JavaVM;
let vm_ptr = ndk_glue::native_activity().vm();
let vm = JavaVM::from_raw(vm_ptr)?;
let vm_env = vm.attach_current_thread()?;
let class = vm_env.new_global_ref(JObject::from_raw(ndk_glue::native_activity().activity()))?;
let field = vm_env.get_field(class.as_obj(), "displayInsets", "[I")?.to_jni().l as jni::sys::jintArray;
let mut elements = [0; 4];
vm_env.get_int_array_region(field, 0, &mut elements)?;
vm_env.delete_local_ref(JObject::from_raw(field));
//Game always runs with horizontal orientation so top and bottom cutouts are not needed and only waste piece of the screen
elements[1] = 0;
elements[3] = 0;
return Ok((elements[0] as f32, elements[1] as f32, elements[2] as f32, elements[3] as f32));
}
Ok((0.0, 0.0, 0.0, 0.0))
}
#[inline]
fn game<'a, 'b: 'a>(&'a self) -> &'b mut Game {
unsafe { &mut *self.game_ref }
}
#[inline]
fn ctx<'a, 'b: 'a>(&'a self) -> &'b mut Context {
unsafe { &mut *self.ctx_ref }
}
fn create_initial_window(&mut self, event_loop: &ActiveEventLoop) {
let attrs = Window::default_attributes();
let attrs = attrs.with_title(self.ctx().window_title.clone());
// TODO: how to handle those errors?
let window = event_loop.create_window(attrs).unwrap();
{
let size = window.inner_size();
let ctx = self.ctx();
let game = self.game();
ctx.real_screen_size = (size.width, size.height);
ctx.screen_size = get_scaled_size(size.width.max(1), size.height.max(1));
game.state.get_mut().handle_resize(ctx).unwrap();
}
self.window = Some(window);
}
}
fn get_scaled_size(width: u32, height: u32) -> (f32, f32) {
let scaled_height = ((height / 480).max(1) * 480) as f32;
let scaled_width = (width as f32 * (scaled_height as f32 / height as f32)).floor();
(scaled_width, scaled_height)
}
impl ApplicationHandler for WinitEventLoop {
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
let mut mutex = GAME_SUSPENDED.lock().unwrap();
*mutex = true;
self.game().state.borrow_mut().sound_manager.resume();
}
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
self.create_initial_window(event_loop);
let mut mutex = GAME_SUSPENDED.lock().unwrap();
*mutex = false;
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
let window = if let Some(window) = &self.window {
window
} else {
return;
};
let ctx = self.ctx();
match event {
WindowEvent::CloseRequested if window_id == window.id() => {
ctx.shutdown_requested = true;
}
WindowEvent::Resized(size) if window_id == window.id() => {
if let Some(renderer) = &ctx.renderer {
if let Ok(imgui) = renderer.imgui() {
imgui.io_mut().display_size = [size.width as f32, size.height as f32];
}
ctx.real_screen_size = (size.width, size.height);
ctx.screen_size = get_scaled_size(size.width.max(1), size.height.max(1));
self.game().state.get_mut().handle_resize(ctx).unwrap();
}
}
WindowEvent::Touch(touch) if window_id == window.id() => {
let state_ref = self.game().state.get_mut();
let mut controls = &mut state_ref.touch_controls;
let scale = state_ref.scale as f64;
let loc_x = (touch.location.x * ctx.screen_size.0 as f64 / ctx.real_screen_size.0 as f64) / scale;
let loc_y = (touch.location.y * ctx.screen_size.1 as f64 / ctx.real_screen_size.1 as f64) / scale;
match touch.phase {
TouchPhase::Started | TouchPhase::Moved => {
if let Some(point) = controls.points.iter_mut().find(|p| p.id == touch.id) {
point.last_position = point.position;
point.position = (loc_x, loc_y);
} else {
controls.touch_id_counter = controls.touch_id_counter.wrapping_add(1);
let point = TouchPoint {
id: touch.id,
touch_id: controls.touch_id_counter,
position: (loc_x, loc_y),
last_position: (0.0, 0.0),
};
controls.points.push(point);
if touch.phase == TouchPhase::Started {
controls.clicks.push(point);
}
}
}
TouchPhase::Ended | TouchPhase::Cancelled => {
controls.points.retain(|p| p.id != touch.id);
controls.clicks.retain(|p| p.id != touch.id);
}
}
}
WindowEvent::KeyboardInput { event, .. } if window_id == window.id() => {
if let PhysicalKey::Code(key_code) = event.physical_key {
if let Some(drs_scan) = conv_keycode(key_code) {
let key_state = match event.state {
ElementState::Pressed => true,
ElementState::Released => false,
};
ctx.keyboard_context.set_key(drs_scan, key_state);
}
}
}
WindowEvent::RedrawRequested if window_id == window.id() => {
{
let mutex = GAME_SUSPENDED.lock().unwrap();
if *mutex {
return;
}
}
{
if let Err(err) = self.game().draw(ctx) {
log::error!("Failed to draw frame: {}", err);
}
window.request_redraw();
}
}
_ => (),
}
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
let ctx = self.ctx();
let game = self.game();
if ctx.shutdown_requested {
log::info!("Shutting down...");
event_loop.exit();
return;
}
{
let mutex = GAME_SUSPENDED.lock().unwrap();
if *mutex {
return;
}
}
let window = if let Some(window) = &self.window {
window
} else {
return;
};
// #[cfg(not(any(target_os = "android", target_os = "ios")))]
// {
// if state_ref.settings.window_mode.get_glutin_fullscreen_type() != window.fullscreen() {
// let fullscreen_type = state_ref.settings.window_mode.get_glutin_fullscreen_type();
// let cursor_visible = state_ref.settings.window_mode.should_display_mouse_cursor();
// window.set_fullscreen(fullscreen_type);
// window.set_cursor_visible(cursor_visible);
// }
// }
game.update(ctx).unwrap();
match self.get_insets() {
Ok(insets) => {
ctx.screen_insets = insets;
}
Err(e) => {
log::error!("Failed to update insets: {}", e);
}
}
window.request_redraw();
if game.state.get_mut().next_scene.is_some() {
let mut state_ref = game.state.borrow_mut();
mem::swap(game.scene.get_mut(), &mut state_ref.next_scene);
state_ref.next_scene = None;
game.scene.borrow_mut().as_mut().unwrap().init(&mut state_ref, ctx).unwrap();
game.loops = 0;
state_ref.frame_time = 0.0;
}
}
}
impl BackendEventLoop for WinitEventLoop {
fn run(&mut self, game: &mut Game, ctx: &mut Context) {
self.game_ref = game as *mut _;
self.ctx_ref = ctx as *mut _;
let event_loop = std::mem::take(&mut self.event_loop).unwrap();
event_loop.run_app(self);
}
fn new_renderer(&self, ctx: *mut Context) -> GameResult<Box<dyn BackendRenderer>> {
let mut imgui = init_imgui()?;
imgui.io_mut().display_size = [640.0, 480.0];
// let refs = self.refs.clone();
// let user_data = Rc::into_raw(refs) as *mut c_void;
// unsafe fn get_proc_address(user_data: &mut *mut c_void, name: &str) -> *const c_void {
// let refs = Rc::from_raw(*user_data as *mut UnsafeCell<Option<WindowedContext<PossiblyCurrent>>>);
// let result = {
// let refs = &mut *refs.get();
// if let Some(refs) = refs {
// refs.get_proc_address(name)
// } else {
// std::ptr::null()
// }
// };
// *user_data = Rc::into_raw(refs) as *mut c_void;
// result
// }
// unsafe fn swap_buffers(user_data: &mut *mut c_void) {
// let refs = Rc::from_raw(*user_data as *mut UnsafeCell<Option<WindowedContext<PossiblyCurrent>>>);
// {
// let refs = &mut *refs.get();
// if let Some(refs) = refs {
// refs.swap_buffers();
// }
// }
// *user_data = Rc::into_raw(refs) as *mut c_void;
// }
// let gl_context = GLContext { gles2_mode: true, is_sdl: false, get_proc_address, swap_buffers, user_data, ctx };
// Ok(Box::new(OpenGLRenderer::new(gl_context, imgui)))
Ok(Box::new(super::backend_null::NullRenderer::new(imgui)))
}
fn as_any(&self) -> &dyn Any {
self
}
}
fn conv_keycode(code: KeyCode) -> Option<ScanCode> {
match code {
KeyCode::KeyA => Some(ScanCode::A),
KeyCode::KeyB => Some(ScanCode::B),
KeyCode::KeyC => Some(ScanCode::C),
KeyCode::KeyD => Some(ScanCode::D),
KeyCode::KeyE => Some(ScanCode::E),
KeyCode::KeyF => Some(ScanCode::F),
KeyCode::KeyG => Some(ScanCode::G),
KeyCode::KeyH => Some(ScanCode::H),
KeyCode::KeyI => Some(ScanCode::I),
KeyCode::KeyJ => Some(ScanCode::J),
KeyCode::KeyK => Some(ScanCode::K),
KeyCode::KeyL => Some(ScanCode::L),
KeyCode::KeyM => Some(ScanCode::M),
KeyCode::KeyN => Some(ScanCode::N),
KeyCode::KeyO => Some(ScanCode::O),
KeyCode::KeyP => Some(ScanCode::P),
KeyCode::KeyQ => Some(ScanCode::Q),
KeyCode::KeyR => Some(ScanCode::R),
KeyCode::KeyS => Some(ScanCode::S),
KeyCode::KeyT => Some(ScanCode::T),
KeyCode::KeyU => Some(ScanCode::U),
KeyCode::KeyV => Some(ScanCode::V),
KeyCode::KeyW => Some(ScanCode::W),
KeyCode::KeyX => Some(ScanCode::X),
KeyCode::KeyY => Some(ScanCode::Y),
KeyCode::KeyZ => Some(ScanCode::Z),
KeyCode::Digit1 => Some(ScanCode::Key1),
KeyCode::Digit2 => Some(ScanCode::Key2),
KeyCode::Digit3 => Some(ScanCode::Key3),
KeyCode::Digit4 => Some(ScanCode::Key4),
KeyCode::Digit5 => Some(ScanCode::Key5),
KeyCode::Digit6 => Some(ScanCode::Key6),
KeyCode::Digit7 => Some(ScanCode::Key7),
KeyCode::Digit8 => Some(ScanCode::Key8),
KeyCode::Digit9 => Some(ScanCode::Key9),
KeyCode::Digit0 => Some(ScanCode::Key0),
KeyCode::Enter => Some(ScanCode::Return),
KeyCode::Escape => Some(ScanCode::Escape),
KeyCode::Backspace => Some(ScanCode::Backspace),
KeyCode::Tab => Some(ScanCode::Tab),
KeyCode::Space => Some(ScanCode::Space),
KeyCode::Minus => Some(ScanCode::Minus),
KeyCode::Equal => Some(ScanCode::Equals),
KeyCode::BracketLeft => Some(ScanCode::LBracket),
KeyCode::BracketRight => Some(ScanCode::RBracket),
KeyCode::Backslash => Some(ScanCode::Backslash),
KeyCode::Semicolon => Some(ScanCode::Semicolon),
KeyCode::Quote => Some(ScanCode::Apostrophe),
KeyCode::Backquote => Some(ScanCode::Grave),
KeyCode::Comma => Some(ScanCode::Comma),
KeyCode::Period => Some(ScanCode::Period),
KeyCode::Slash => Some(ScanCode::Slash),
KeyCode::CapsLock => Some(ScanCode::Capslock),
KeyCode::F1 => Some(ScanCode::F1),
KeyCode::F2 => Some(ScanCode::F2),
KeyCode::F3 => Some(ScanCode::F3),
KeyCode::F4 => Some(ScanCode::F4),
KeyCode::F5 => Some(ScanCode::F5),
KeyCode::F6 => Some(ScanCode::F6),
KeyCode::F7 => Some(ScanCode::F7),
KeyCode::F8 => Some(ScanCode::F8),
KeyCode::F9 => Some(ScanCode::F9),
KeyCode::F10 => Some(ScanCode::F10),
KeyCode::F11 => Some(ScanCode::F11),
KeyCode::F12 => Some(ScanCode::F12),
KeyCode::PrintScreen => Some(ScanCode::Sysrq),
KeyCode::ScrollLock => Some(ScanCode::Scrolllock),
KeyCode::Pause => Some(ScanCode::Pause),
KeyCode::Insert => Some(ScanCode::Insert),
KeyCode::Home => Some(ScanCode::Home),
KeyCode::PageUp => Some(ScanCode::PageUp),
KeyCode::Delete => Some(ScanCode::Delete),
KeyCode::End => Some(ScanCode::End),
KeyCode::PageDown => Some(ScanCode::PageDown),
KeyCode::ArrowRight => Some(ScanCode::Right),
KeyCode::ArrowLeft => Some(ScanCode::Left),
KeyCode::ArrowDown => Some(ScanCode::Down),
KeyCode::ArrowUp => Some(ScanCode::Up),
KeyCode::NumLock => Some(ScanCode::Numlock),
KeyCode::NumpadDivide => Some(ScanCode::NumpadDivide),
KeyCode::NumpadMultiply => Some(ScanCode::NumpadMultiply),
KeyCode::NumpadSubtract => Some(ScanCode::NumpadSubtract),
KeyCode::NumpadAdd => Some(ScanCode::NumpadAdd),
KeyCode::NumpadEnter => Some(ScanCode::NumpadEnter),
KeyCode::Numpad1 => Some(ScanCode::Numpad1),
KeyCode::Numpad2 => Some(ScanCode::Numpad2),
KeyCode::Numpad3 => Some(ScanCode::Numpad3),
KeyCode::Numpad4 => Some(ScanCode::Numpad4),
KeyCode::Numpad5 => Some(ScanCode::Numpad5),
KeyCode::Numpad6 => Some(ScanCode::Numpad6),
KeyCode::Numpad7 => Some(ScanCode::Numpad7),
KeyCode::Numpad8 => Some(ScanCode::Numpad8),
KeyCode::Numpad9 => Some(ScanCode::Numpad9),
KeyCode::Numpad0 => Some(ScanCode::Numpad0),
KeyCode::ContextMenu => Some(ScanCode::Apps),
KeyCode::Power => Some(ScanCode::Power),
KeyCode::NumpadEqual => Some(ScanCode::NumpadEquals),
KeyCode::F13 => Some(ScanCode::F13),
KeyCode::F14 => Some(ScanCode::F14),
KeyCode::F15 => Some(ScanCode::F15),
KeyCode::F16 => Some(ScanCode::F16),
KeyCode::F17 => Some(ScanCode::F17),
KeyCode::F18 => Some(ScanCode::F18),
KeyCode::F19 => Some(ScanCode::F19),
KeyCode::F20 => Some(ScanCode::F20),
KeyCode::F21 => Some(ScanCode::F21),
KeyCode::F22 => Some(ScanCode::F22),
KeyCode::F23 => Some(ScanCode::F23),
KeyCode::F24 => Some(ScanCode::F24),
KeyCode::MediaStop => Some(ScanCode::Stop),
KeyCode::Cut => Some(ScanCode::Cut),
KeyCode::Copy => Some(ScanCode::Copy),
KeyCode::Paste => Some(ScanCode::Paste),
KeyCode::AudioVolumeMute => Some(ScanCode::Mute),
KeyCode::AudioVolumeUp => Some(ScanCode::VolumeUp),
KeyCode::AudioVolumeDown => Some(ScanCode::VolumeDown),
KeyCode::NumpadComma => Some(ScanCode::NumpadComma),
KeyCode::ControlLeft => Some(ScanCode::LControl),
KeyCode::ShiftLeft => Some(ScanCode::LShift),
KeyCode::AltLeft => Some(ScanCode::LAlt),
KeyCode::SuperLeft => Some(ScanCode::LWin),
KeyCode::ControlRight => Some(ScanCode::RControl),
KeyCode::ShiftRight => Some(ScanCode::RShift),
KeyCode::AltRight => Some(ScanCode::RAlt),
KeyCode::SuperRight => Some(ScanCode::RWin),
KeyCode::MediaTrackNext => Some(ScanCode::NextTrack),
KeyCode::MediaTrackPrevious => Some(ScanCode::PrevTrack),
KeyCode::MediaStop => Some(ScanCode::MediaStop),
KeyCode::MediaPlayPause => Some(ScanCode::PlayPause),
KeyCode::MediaSelect => Some(ScanCode::MediaSelect),
KeyCode::LaunchMail => Some(ScanCode::Mail),
KeyCode::LaunchApp2 => Some(ScanCode::Calculator),
KeyCode::Sleep => Some(ScanCode::Sleep),
_ => None,
}
}

View file

@ -8,7 +8,9 @@ use crate::game::Game;
pub struct Context {
pub headless: bool,
pub shutdown_requested: bool,
pub size_hint: (u16, u16),
pub window_title: String,
pub(crate) filesystem: Filesystem,
pub(crate) renderer: Option<Box<dyn BackendRenderer>>,
pub(crate) gamepad_context: GamepadContext,
@ -23,7 +25,9 @@ impl Context {
pub fn new() -> Context {
Context {
headless: false,
shutdown_requested: false,
size_hint: (640, 480),
window_title: "Game".to_string(),
filesystem: Filesystem::new(),
renderer: None,
gamepad_context: GamepadContext::new(),

View file

@ -112,9 +112,6 @@ pub enum ScanCode {
/// The space bar.
Space,
/// The "Compose" key on Linux.
Compose,
NonUsHash,
NonUsBackslash,
Caret,
@ -139,8 +136,6 @@ pub enum ScanCode {
NumpadMultiply,
NumpadSubtract,
AbntC1,
AbntC2,
Apostrophe,
Apps,
Asterisk,

View file

@ -1,8 +1,8 @@
#![allow(unused)]
pub mod backend;
#[cfg(feature = "backend-glutin")]
pub mod backend_glutin;
#[cfg(feature = "backend-winit")]
pub mod backend_winit;
#[cfg(feature = "backend-horizon")]
pub mod backend_horizon;
pub mod backend_null;

View file

@ -21,6 +21,12 @@ use crate::framework::graphics::{BlendMode, VSyncMode};
use crate::framework::util::{field_offset, return_param};
use crate::game::GAME_SUSPENDED;
pub trait GLPlatformFunctions {
fn get_proc_address(&self, name: &str) -> *const c_void;
fn swap_buffers(&self);
}
pub struct GLContext {
pub gles2_mode: bool,
pub is_sdl: bool,
@ -446,6 +452,7 @@ struct RenderData {
tex_shader: RenderShader,
fill_shader: RenderShader,
fill_water_shader: RenderShader,
render_fbo: GLint,
vbo: GLuint,
ebo: GLuint,
font_texture: GLuint,
@ -462,6 +469,7 @@ impl RenderData {
tex_shader: RenderShader::default(),
fill_shader: RenderShader::default(),
fill_water_shader: RenderShader::default(),
render_fbo: 0,
vbo: 0,
ebo: 0,
font_texture: 0,
@ -481,12 +489,24 @@ impl RenderData {
let fshdr_fill_water = if gles2_mode { FRAGMENT_SHADER_COLOR_GLES } else { FRAGMENT_SHADER_WATER };
unsafe {
// iOS has "unusual" framebuffer setup, where we can't rely on 0 as the system provided render target.
self.render_fbo = return_param(|x| gl.gl.GetIntegerv(gl::FRAMEBUFFER_BINDING, x));
self.tex_shader =
RenderShader::compile(gl, vshdr_basic, fshdr_tex).unwrap_or_else(|_| RenderShader::default());
RenderShader::compile(gl, vshdr_basic, fshdr_tex).unwrap_or_else(|e| {
log::error!("Failed to compile texture shader: {}", e);
RenderShader::default()
});
self.fill_shader =
RenderShader::compile(gl, vshdr_basic, fshdr_fill).unwrap_or_else(|_| RenderShader::default());
RenderShader::compile(gl, vshdr_basic, fshdr_fill).unwrap_or_else(|e| {
log::error!("Failed to compile fill shader: {}", e);
RenderShader::default()
});
self.fill_water_shader =
RenderShader::compile(gl, vshdr_basic, fshdr_fill_water).unwrap_or_else(|_| RenderShader::default());
RenderShader::compile(gl, vshdr_basic, fshdr_fill_water).unwrap_or_else(|e| {
log::error!("Failed to compile fill water shader: {}", e);
RenderShader::default()
});
self.vbo = return_param(|x| gl.gl.GenBuffers(1, x));
self.ebo = return_param(|x| gl.gl.GenBuffers(1, x));
@ -594,10 +614,10 @@ pub struct OpenGLRenderer {
}
impl OpenGLRenderer {
pub fn new(refs: GLContext, imgui: UnsafeCell<imgui::Context>) -> OpenGLRenderer {
pub fn new(refs: GLContext, imgui: imgui::Context) -> OpenGLRenderer {
OpenGLRenderer {
refs,
imgui,
imgui: UnsafeCell::new(imgui),
render_data: RenderData::new(),
context_active: Arc::new(RefCell::new(true)),
def_matrix: [[0.0; 4]; 4],
@ -622,9 +642,9 @@ impl OpenGLRenderer {
impl BackendRenderer for OpenGLRenderer {
fn renderer_name(&self) -> String {
if self.refs.gles2_mode {
"OpenGL ES 2.0".to_string()
"*COMPATIBILITY* OpenGL ES 2.0".to_string()
} else {
"OpenGL 2.1".to_string()
"*COMPATIBILITY* OpenGL 2.1".to_string()
}
}
@ -647,7 +667,7 @@ impl BackendRenderer for OpenGLRenderer {
unsafe {
if let Some((_, gl)) = self.get_context() {
gl.gl.BindFramebuffer(gl::FRAMEBUFFER, 0);
gl.gl.BindFramebuffer(gl::FRAMEBUFFER, self.render_data.render_fbo as _);
gl.gl.ClearColor(0.0, 0.0, 0.0, 1.0);
gl.gl.Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
@ -674,7 +694,7 @@ impl BackendRenderer for OpenGLRenderer {
BackendShader::Texture,
)?;
gl.gl.Finish();
//gl.gl.Finish();
}
if let Some((context, _)) = self.get_context() {
@ -719,7 +739,7 @@ impl BackendRenderer for OpenGLRenderer {
let (width_u, height_u) = (width as u32, height as u32);
if self.render_data.last_size != (width_u, height_u) {
self.render_data.last_size = (width_u, height_u);
gl.gl.BindFramebuffer(gl::FRAMEBUFFER, 0);
gl.gl.BindFramebuffer(gl::FRAMEBUFFER, self.render_data.render_fbo as _);
gl.gl.BindTexture(gl::TEXTURE_2D, self.render_data.surf_texture);
gl.gl.TexImage2D(
@ -822,7 +842,7 @@ impl BackendRenderer for OpenGLRenderer {
gl.gl.Viewport(0, 0, width as _, height as _);
gl.gl.ClearColor(0.0, 0.0, 0.0, 0.0);
gl.gl.Clear(gl::COLOR_BUFFER_BIT);
gl.gl.BindFramebuffer(gl::FRAMEBUFFER, 0);
gl.gl.BindFramebuffer(gl::FRAMEBUFFER, self.render_data.render_fbo as _);
// todo error checking: glCheckFramebufferStatus()

View file

@ -23,7 +23,7 @@ impl FilesystemContainer {
}
pub fn mount_fs(&mut self, context: &mut Context) -> GameResult {
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
#[cfg(not(any(target_os = "android", target_os = "ios", target_os = "horizon")))]
let resource_dir = if let Ok(data_dir) = std::env::var("CAVESTORY_DATA_DIR") {
PathBuf::from(data_dir)
} else {
@ -63,18 +63,18 @@ impl FilesystemContainer {
resource_dir
};
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
#[cfg(not(any(target_os = "android", target_os = "ios", target_os = "horizon")))]
log::info!("Resource directory: {:?}", resource_dir);
log::info!("Initializing engine...");
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
#[cfg(not(any(target_os = "android", target_os = "ios", target_os = "horizon")))]
{
mount_vfs(context, Box::new(PhysicalFS::new(&resource_dir, true)));
self.game_path = resource_dir.clone();
}
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
#[cfg(not(any(target_os = "android", target_os = "ios", target_os = "horizon")))]
let project_dirs = match directories::ProjectDirs::from("", "", "doukutsu-rs") {
Some(dirs) => dirs,
None => {
@ -84,6 +84,7 @@ impl FilesystemContainer {
)));
}
};
#[cfg(target_os = "android")]
{
let mut data_path =
@ -104,6 +105,38 @@ impl FilesystemContainer {
self.user_path = user_path.clone();
self.game_path = data_path.clone();
}
#[cfg(target_os = "ios")]
unsafe {
use objc2::*;
use objc2::runtime::Object;
// [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
let file_manager: *mut Object = msg_send![class!(NSFileManager), defaultManager];
let document_dir: *mut Object = msg_send![file_manager, URLsForDirectory:9 /* NSDocumentDirectory */ inDomains:1 /* NSUserDomainMask */];
let document_dir: *mut Object = msg_send![document_dir, lastObject];
let path = std::ffi::CStr::from_ptr(msg_send![document_dir, fileSystemRepresentation]);
let path = std::str::from_utf8(path.to_bytes()).unwrap();
let mut data_path = PathBuf::from(path);
let mut user_path = data_path.clone();
data_path.push("data");
user_path.push("saves");
let _ = std::fs::create_dir_all(&data_path);
let _ = std::fs::create_dir_all(&user_path);
log::info!("iOS data directories: data_path={:?} user_path={:?}", &data_path, &user_path);
mount_vfs(context, Box::new(PhysicalFS::new(&data_path, true)));
mount_user_vfs(context, Box::new(PhysicalFS::new(&user_path, false)));
self.user_path = user_path.clone();
self.game_path = data_path.clone();
}
#[cfg(target_os = "horizon")]
{
let mut data_path = PathBuf::from("sdmc:/switch/doukutsu-rs/data");
@ -125,7 +158,7 @@ impl FilesystemContainer {
self.game_path = data_path.clone();
}
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
#[cfg(not(any(target_os = "android", target_os = "ios", target_os = "horizon")))]
{
let mut user_dir = resource_dir.clone();
user_dir.pop();
@ -212,7 +245,22 @@ impl FilesystemContainer {
return Ok(());
}
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
#[cfg(target_os = "ios")]
unsafe {
use objc2::*;
use objc2::runtime::*;
let url = "shareddocuments://".to_owned() + &path.to_string_lossy();
let url_nsstring: *mut Object = msg_send![class!(NSString), stringWithUTF8String: url.as_ptr() as *const std::os::raw::c_char];
let nsurl: *mut Object = msg_send![class!(NSURL), URLWithString: url_nsstring];
let shared_app: *mut Object = msg_send![class!(UIApplication), sharedApplication];
let _: BOOL = msg_send![shared_app, openURL: nsurl];
return Ok(());
}
#[cfg(not(any(target_os = "android", target_os = "ios", target_os = "horizon")))]
open::that(path).map_err(|e| {
use crate::framework::error::GameError;
GameError::FilesystemError(format!("Failed to open directory: {}", e))

View file

@ -1,5 +1,5 @@
use std::backtrace::Backtrace;
use std::cell::UnsafeCell;
use std::cell::RefCell;
use std::panic::PanicInfo;
use std::path::PathBuf;
use std::sync::Mutex;
@ -35,6 +35,7 @@ pub mod shared_game_state;
pub mod stage;
pub mod weapon;
#[repr(C)]
pub struct LaunchOptions {
pub server_mode: bool,
pub editor: bool,
@ -45,8 +46,8 @@ lazy_static! {
}
pub struct Game {
pub(crate) scene: Option<Box<dyn Scene>>,
pub(crate) state: UnsafeCell<SharedGameState>,
pub(crate) scene: RefCell<Option<Box<dyn Scene>>>,
pub(crate) state: RefCell<SharedGameState>,
ui: UI,
start_time: Instant,
last_tick: u128,
@ -60,9 +61,9 @@ pub struct Game {
impl Game {
fn new(ctx: &mut Context) -> GameResult<Game> {
let s = Game {
scene: None,
scene: RefCell::new( None),
ui: UI::new(ctx)?,
state: UnsafeCell::new(SharedGameState::new(ctx)?),
state: RefCell::new(SharedGameState::new(ctx)?),
start_time: Instant::now(),
last_tick: 0,
next_tick: 0,
@ -76,34 +77,38 @@ impl Game {
}
pub(crate) fn update(&mut self, ctx: &mut Context) -> GameResult {
if let Some(scene) = &mut self.scene {
let state_ref = unsafe { &mut *self.state.get() };
if let Some(scene) = self.scene.get_mut() {
let state_ref = self.state.get_mut();
let speed =
if state_ref.textscript_vm.mode == ScriptMode::Map && state_ref.textscript_vm.flags.cutscene_skip() {
4.0 * state_ref.settings.speed
} else {
1.0 * state_ref.settings.speed
};
let speed = if state_ref.textscript_vm.mode == ScriptMode::Map
&& state_ref.textscript_vm.flags.cutscene_skip()
{
4.0
} else {
1.0
} * state_ref.settings.speed;
match state_ref.settings.timing_mode {
TimingMode::_50Hz | TimingMode::_60Hz => {
let last_tick = self.next_tick;
while self.start_time.elapsed().as_nanos() >= self.next_tick && self.loops < 10 {
let delta = state_ref.settings.timing_mode.get_delta();
if (speed - 1.0).abs() < 0.01 {
self.next_tick += state_ref.settings.timing_mode.get_delta() as u128;
self.next_tick += delta as u128;
} else {
self.next_tick += (state_ref.settings.timing_mode.get_delta() as f64 / speed) as u128;
self.next_tick += (delta as f64 / speed) as u128;
}
self.loops += 1;
}
if self.loops == 10 {
let delta = state_ref.settings.timing_mode.get_delta();
log::warn!("Frame skip is way too high, a long system lag occurred?");
self.last_tick = self.start_time.elapsed().as_nanos();
self.next_tick =
self.last_tick + (state_ref.settings.timing_mode.get_delta() as f64 / speed) as u128;
self.next_tick = self.last_tick + (delta as f64 / speed) as u128;
self.loops = 0;
}
@ -126,8 +131,6 @@ impl Game {
}
pub(crate) fn draw(&mut self, ctx: &mut Context) -> GameResult {
let state_ref = unsafe { &mut *self.state.get() };
match ctx.vsync_mode {
VSyncMode::Uncapped | VSyncMode::VSync => {
self.present = true;
@ -142,7 +145,8 @@ impl Game {
_ => std::hint::unreachable_unchecked(),
};
let delta = (state_ref.settings.timing_mode.get_delta() / divisor) as u64;
let delta = self.state.get_mut().settings.timing_mode.get_delta();
let delta = (delta / divisor) as u64;
let now = self.start_time.elapsed().as_nanos();
if now > self.next_tick_draw + delta as u128 * 4 {
@ -164,11 +168,11 @@ impl Game {
if ctx.headless {
self.loops = 0;
state_ref.frame_time = 1.0;
self.state.get_mut().frame_time = 1.0;
return Ok(());
}
if state_ref.settings.timing_mode != TimingMode::FrameSynchronized {
if self.state.get_mut().settings.timing_mode != TimingMode::FrameSynchronized {
let mut elapsed = self.start_time.elapsed().as_nanos();
// Even with the non-monotonic Instant mitigation at the start of the event loop, there's still a chance of it not working.
@ -179,18 +183,20 @@ impl Game {
let n1 = (elapsed - self.last_tick) as f64;
let n2 = (self.next_tick - self.last_tick) as f64;
state_ref.frame_time = if state_ref.settings.motion_interpolation { n1 / n2 } else { 1.0 };
self.state.get_mut().frame_time = if self.state.get_mut().settings.motion_interpolation { n1 / n2 } else { 1.0 };
}
unsafe {
G_MAG = if state_ref.settings.subpixel_coords { state_ref.scale } else { 1.0 };
I_MAG = state_ref.scale;
G_MAG = if self.state.get_mut().settings.subpixel_coords { self.state.get_mut().scale } else { 1.0 };
I_MAG = self.state.get_mut().scale;
}
self.loops = 0;
graphics::prepare_draw(ctx)?;
graphics::clear(ctx, [0.0, 0.0, 0.0, 1.0].into());
if let Some(scene) = &mut self.scene {
if let Some(scene) = self.scene.get_mut() {
let state_ref = self.state.get_mut();
scene.draw(state_ref, ctx)?;
if state_ref.settings.touch_controls && state_ref.settings.display_touch_controls {
state_ref.touch_controls.draw(
@ -215,14 +221,13 @@ impl Game {
}
}
// For the most part this is just a copy-paste of the code from FilesystemContainer because it logs
// For the most part this is just a copy-paste of the code from FilesystemContainer because it logs
// some messages during init, but the default logger cannot be replaced with another
// one or deinited(so we can't create the console-only logger and replace it by the
// console&file logger after FilesystemContainer has been initialized)
fn get_logs_dir() -> GameResult<PathBuf> {
let mut logs_dir: PathBuf;
#[cfg(target_os = "android")]
{
logs_dir = PathBuf::from(ndk_glue::native_activity().internal_data_path().to_string_lossy().to_string());
@ -250,45 +255,29 @@ fn get_logs_dir() -> GameResult<PathBuf> {
logs_dir.push("logs");
Ok(logs_dir)
}
fn init_logger() -> GameResult {
let logs_dir = get_logs_dir()?;
let _ = std::fs::create_dir_all(&logs_dir);
let mut dispatcher = fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{} [{}] {}",
record.level(),
record.module_path().unwrap().to_owned(),
message
))
out.finish(format_args!("{} [{}] {}", record.level(), record.module_path().unwrap().to_owned(), message))
})
.level(log::LevelFilter::Debug)
.chain(
fern::Dispatch::new()
.chain(std::io::stderr())
);
.chain(fern::Dispatch::new().chain(std::io::stderr()));
let date = chrono::Utc::now();
let mut file = logs_dir.clone();
file.push(format!("log_{}", date.format("%Y-%m-%d")));
file.set_extension("txt");
dispatcher = dispatcher.chain(
fern::Dispatch::new()
.level(log::LevelFilter::Info)
.chain(fern::log_file(file).unwrap())
);
dispatcher =
dispatcher.chain(fern::Dispatch::new().level(log::LevelFilter::Info).chain(fern::log_file(file).unwrap()));
dispatcher.apply()?;
//log::info!("===GAME LAUNCH===");
Ok(())
}
@ -310,6 +299,8 @@ pub fn init(options: LaunchOptions) -> GameResult {
let mut context = Box::pin(Context::new());
context.window_title = "Cave Story (doukutsu-rs)".to_string();
let mut fs_container = FilesystemContainer::new();
fs_container.mount_fs(&mut context)?;

View file

@ -434,7 +434,7 @@ impl Default for Settings {
light_cone: true,
subpixel_coords: true,
motion_interpolation: true,
touch_controls: cfg!(target_os = "android"),
touch_controls: cfg!(any(target_os = "android", target_os = "ios")),
display_touch_controls: true,
soundtrack: "Organya".to_string(),
bgm_volume: 1.0,

View file

@ -87,14 +87,6 @@ impl WindowMode {
}
}
#[cfg(feature = "backend-glutin")]
pub fn get_glutin_fullscreen_type(&self) -> Option<glutin::window::Fullscreen> {
match self {
WindowMode::Windowed => None,
WindowMode::Fullscreen => Some(glutin::window::Fullscreen::Borderless(None)),
}
}
pub fn should_display_mouse_cursor(&self) -> bool {
match self {
WindowMode::Windowed => true,
@ -346,7 +338,6 @@ pub struct SharedGameState {
pub more_rust: bool,
#[cfg(feature = "discord-rpc")]
pub discord_rpc: DiscordRPC,
pub shutdown: bool,
}
impl SharedGameState {
@ -503,7 +494,6 @@ impl SharedGameState {
more_rust,
#[cfg(feature = "discord-rpc")]
discord_rpc: DiscordRPC::new(discord_rpc_app_id),
shutdown: false,
})
}
@ -754,13 +744,6 @@ impl SharedGameState {
self.settings.timing_mode.get_tps() as f64 * self.settings.speed
}
pub fn shutdown(&mut self) {
self.shutdown = true;
#[cfg(feature = "discord-rpc")]
self.discord_rpc.dispose();
}
// Stops SFX 40/41/58 (CPS and CSS)
pub fn stop_noise(&mut self) {
self.sound_manager.stop_sfx(40);

View file

@ -1,4 +1,4 @@
use imgui::{CollapsingHeader, Condition, ImStr, ImString, Slider, Window};
use imgui::{CollapsingHeader, Condition, ImStr, ImString};
use itertools::Itertools;
use crate::framework::context::Context;
@ -173,7 +173,7 @@ impl LiveDebugger {
ui.text(format!("Game speed ({:.1} TPS):", state.current_tps()));
let mut speed = state.settings.speed;
ui.slider("", 0.1, 3.0, &mut speed);
ui.slider("##Speed", 0.1, 3.0, &mut speed);
ui.same_line();
if ui.button("Reset") {
speed = 1.0
@ -254,7 +254,7 @@ impl LiveDebugger {
let stages: Vec<&ImStr> = self.stages.iter().map(|e| e.as_ref()).collect();
ui.push_item_width(-1.0);
ui.list_box("", &mut self.selected_stage, &stages, 10);
ui.list_box("##SelectedStage", &mut self.selected_stage, &stages, 10);
if ui.button("Load") {
match GameScene::new(state, ctx, self.selected_stage as usize) {
@ -338,7 +338,7 @@ impl LiveDebugger {
)));
ui.push_item_width(-1.0);
ui.list_box("", &mut self.selected_event, &events, 10);
ui.list_box("##SelectedEvent", &mut self.selected_event, &events, 10);
if ui.button("Execute") {
assert_eq!(self.event_ids.len(), self.events.len());

View file

@ -48,11 +48,11 @@ enum MainMenuEntry {
impl Default for MainMenuEntry {
fn default() -> Self {
#[cfg(target_os = "android")]
#[cfg(any(target_os = "android", target_os = "ios"))]
return MainMenuEntry::DisplayTouchControls;
#[cfg(not(target_os = "android"))]
return MainMenuEntry::SelectedPlayer;
#[allow(unreachable_code)]
MainMenuEntry::SelectedPlayer
}
}
@ -200,7 +200,7 @@ impl ControlsMenu {
}
pub fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
#[cfg(not(target_os = "android"))]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
self.main.push_entry(
MainMenuEntry::SelectedPlayer,

View file

@ -595,8 +595,7 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
graphics::draw_rect(ctx, bar_rect, Color::new(1.0, 1.0, 1.0, 1.0))?;
}
#[cfg(target_os = "android")]
{
if state.settings.touch_controls {
state.font.builder().x(self.x as f32 - 25.0).y(y).shadow(true).draw(
"<",
ctx,

View file

@ -270,7 +270,7 @@ impl PauseMenu {
state.next_scene = Some(Box::new(TitleScene::new()));
}
PauseMenuEntry::Quit => {
state.shutdown();
ctx.shutdown_requested = true;
}
_ => (),
},

View file

@ -145,7 +145,7 @@ enum AdvancedMenuEntry {
Title,
OpenUserData,
OpenGameData,
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
#[cfg(not(any(target_os = "android", target_os = "ios", target_os = "horizon")))]
MakePortable,
Back,
}
@ -247,7 +247,7 @@ impl SettingsMenu {
],
),
);
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
#[cfg(not(any(target_os = "android", target_os = "ios", target_os = "horizon")))]
self.graphics.push_entry(
GraphicsMenuEntry::WindowMode,
MenuEntry::Options(
@ -411,7 +411,7 @@ impl SettingsMenu {
MenuEntry::Active(state.loc.t("menus.options_menu.advanced_menu.open_game_data").to_owned()),
);
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
#[cfg(not(any(target_os = "android", target_os = "ios", target_os = "horizon")))]
if let Some(fs_container) = &state.fs_container {
if !fs_container.is_portable && self.on_title {
self.advanced.push_entry(
@ -1081,7 +1081,7 @@ impl SettingsMenu {
}
}
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
#[cfg(not(any(target_os = "android", target_os = "ios", target_os = "horizon")))]
MenuSelectionResult::Selected(AdvancedMenuEntry::MakePortable, _) => {
self.current = CurrentMenu::PortableMenu;
}

View file

@ -354,7 +354,7 @@ impl Scene for TitleScene {
state.next_scene = Some(Box::new(JukeboxScene::new()));
}
MenuSelectionResult::Selected(MainMenuEntry::Quit, _) => {
state.shutdown();
ctx.shutdown_requested = true;
}
_ => {}
},